diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md new file mode 100644 index 000000000..7e61cd7c8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -0,0 +1,36 @@ +--- +name: "[ BUG ] " +about: 关于wvp的bug,与zlm有关的建议直接在zlm的issue中提问 +title: 'BUG' +labels: 'wvp的bug' +assignees: '' + +--- + +**环境信息:** + + - 1. 部署方式 wvp-pro docker / zlm(docker) + 编译wvp-pro/ wvp-prp + zlm都是编译部署/ + - 2. 部署环境 windows / ubuntu/ centos ... + - 3. 端口开放情况 + - 4. 是否是公网部署 + - 5. 是否使用https + - 6. 方便的话提供下使用的设备品牌或平台 + - 7. 你做过哪些尝试 + - 8. 代码更新时间 + +**描述错误** +描述下您遇到的问题 + +**如何复现** +有明确复现步骤的问题会很容易被解决 + +**截图** + +**抓包文件** + +**日志** +``` +日志内容放这里, 文件的话请直接上传 +``` + + diff --git a/.github/ISSUE_TEMPLATE/new.md b/.github/ISSUE_TEMPLATE/new.md new file mode 100644 index 000000000..796142189 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/new.md @@ -0,0 +1,13 @@ +--- +name: "[ 新功能 ]" +about: 新功能 +title: '希望wVP实现的新功能,此功能应与你的具体业务无关' +labels: '' +assignees: '' + +--- + +**项目的详细需求** + +**这样的实现什么作用** + diff --git a/.github/ISSUE_TEMPLATE/solve.md b/.github/ISSUE_TEMPLATE/solve.md new file mode 100644 index 000000000..75a0eed76 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/solve.md @@ -0,0 +1,31 @@ +--- +name: "[ 技术咨询 ] " +about: 对于使用中遇到问题 +title: '技术咨询' +labels: '技术咨询' +assignees: '' + +--- + +**环境信息:** + + - 1. 部署方式 wvp-pro docker / zlm(docker) + 编译wvp-pro/ wvp-prp + zlm都是编译部署/ + - 2. 部署环境 windows / ubuntu/ centos ... + - 3. 端口开放情况 + - 4. 是否是公网部署 + - 5. 是否使用https + - 6. 方便的话提供下使用的设备品牌或平台 + - 7. 你做过哪些尝试 + - 8. 代码更新时间 + + +**内容描述:** + +**截图** + +**抓包文件** + +**日志** +``` +日志内容放这里, 文件的话请直接上传 +``` diff --git a/.gitignore b/.gitignore index 54054a0f3..776ebe10e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ # Log file *.log - +logs/* # BlueJ files *.ctxt @@ -26,3 +26,5 @@ hs_err_pid* /.idea/ /target/ +/src/main/resources/static/ +certificates diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..94f5d6832 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "be.teletask.onvif-java"] + path = be.teletask.onvif-java + url = https://gitee.com/pan648540858/be.teletask.onvif-java.git diff --git a/DOCKERFILE b/DOCKERFILE index cc86e2740..f2a2ffa01 100644 --- a/DOCKERFILE +++ b/DOCKERFILE @@ -1,32 +1,130 @@ -FROM ubuntu:19.10 +#很久没维护了,已经与定前版本不匹配 + + +FROM ubuntu:20.04 AS build + +ARG DEBIAN_FRONTEND=noninteractive +ENV TZ=Asia/Shanghai EXPOSE 18080/tcp + EXPOSE 5060/tcp +EXPOSE 5060/udp + +EXPOSE 6379/tcp + +EXPOSE 18081/tcp + +EXPOSE 80/tcp +EXPOSE 1935/tcp +EXPOSE 554/tcp +EXPOSE 554/udp +EXPOSE 30000-30500/tcp +EXPOSE 30000-30500/udp + +ENV LC_ALL zh_CN.UTF-8 # 使用了自己的settings.xml作为maven的源,加快打包速度 RUN apt-get update && \ - apt-get install -y --no-install-recommends openjdk-11-jre git maven nodejs npm && \ - cd /home && \ - git clone https://github.com/648540858/wiki.git && \ - cp wiki/config/settings.xml /usr/share/maven/conf/ && \ - git clone https://github.com/648540858/wvp-GB28181.git && \ - cd /home/wvp-GB28181/web_src && \ - npm install npm -g && \ - npm install nrm -g && \ - nrm use taobao && \ - npm install && \ + DEBIAN_FRONTEND="noninteractive" && \ + apt-get install -y --no-install-recommends openjdk-11-jre git maven nodejs npm build-essential tcl language-pack-zh-hans \ + cmake curl vim ca-certificates tzdata libmysqlclient-dev redis-server libssl-dev libx264-dev libfaac-dev ffmpeg +WORKDIR /home + +RUN git clone https://gitee.com/pan648540858/maven.git && \ + cp maven/settings.xml /usr/share/maven/conf/ && \ + git clone https://gitee.com/pan648540858/wvp-GB28181.git && \ + git clone https://gitee.com/pan648540858/wvp-pro-assist.git + # 编译前端界面 +WORKDIR /home/wvp-GB28181/web_src + +RUN npm install && \ npm run build && \ mkdir -p /opt/wvp/config && \ - cd /home/wvp-GB28181 && \ - mvn compile && \ + mkdir -p /opt/assist/config && \ + cp /home/wvp-GB28181/src/main/resources/application-dev.yml /opt/wvp/config/application.yml && \ + cp /home/wvp-pro-assist/src/main/resources/application-dev.yml /opt/assist/config/application.yml + + # wvp打包 +WORKDIR /home/wvp-GB28181 +RUN mvn compile && \ mvn package && \ - cp /home/wvp-GB28181/target/wvp-1.5.10.RELEASE.jar /opt/wvp/ && \ - rm -rf /home/wiki && \ + cp /home/wvp-GB28181/target/wvp*.jar /opt/wvp/ + + # wvp 录像管理打包 +WORKDIR /home/wvp-pro-assist +RUN mvn compile && \ + mvn package && \ + cp /home/wvp-pro-assist/target/*.jar /opt/assist/ + + # zlm打包 +WORKDIR /home +RUN mkdir -p /opt/media && \ + git clone --depth=1 https://gitee.com/xia-chu/ZLMediaKit && \ + cd ZLMediaKit && git submodule update --init --recursive && \ + mkdir -p build release/linux/Release/ &&\ + cd build && \ + cmake -DCMAKE_BUILD_TYPE=Release .. && \ + make -j4 && \ + rm -rf ../release/linux/Release/config.ini && \ + cp -r ../release/linux/Release/* /opt/media && \ + mkdir -p /opt/media/www/record + + + # 清理 + RUN rm -rf /home/wiki && \ rm -rf /home/wvp-GB28181 && \ apt-get autoremove -y git maven nodejs npm && \ apt-get clean -y && \ rm -rf /var/lib/apt/lists/*dic WORKDIR /opt/wvp +RUN echo '#!/bin/bash' > run.sh && \ + echo 'echo ${WVP_IP}' >> run.sh && \ + echo 'echo ${WVP_CONFIG}' >> run.sh && \ + echo 'redis-server --daemonize yes --bind 0.0.0.0' >> run.sh && \ + echo 'cd /opt/assist' >> 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 [-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 +RUN chmod +x run.sh -CMD java -jar wvp-1.5.10.RELEASE.jar --spring.config.location=/opt/wvp/config/application.yml \ No newline at end of file +FROM ubuntu:20.04 + +ARG DEBIAN_FRONTEND=noninteractive +ENV TZ=Asia/Shanghai + +EXPOSE 18080/tcp + +EXPOSE 5060/tcp +EXPOSE 5060/udp + +EXPOSE 6379/tcp + +EXPOSE 18081/tcp + +EXPOSE 80/tcp +EXPOSE 1935/tcp +EXPOSE 554/tcp +EXPOSE 554/udp +EXPOSE 30000-30500/tcp +EXPOSE 30000-30500/udp + +ENV LC_ALL zh_CN.UTF-8 + +RUN apt-get update && \ + DEBIAN_FRONTEND="noninteractive" && \ + apt-get install -y --no-install-recommends openjdk-11-jre tcl language-pack-zh-hans \ + ca-certificates tzdata libmysqlclient21 redis-server libssl1.1 libx264-155 libfaac0 ffmpeg && \ + apt-get autoremove -y && \ + apt-get clean -y && \ + rm -rf /var/lib/apt/lists/*dic + +WORKDIR /opt/wvp +COPY --from=build /opt /opt +CMD ["sh", "run.sh"] diff --git a/README.md b/README.md index 03ee27640..10cd12a26 100644 --- a/README.md +++ b/README.md @@ -1,73 +1,131 @@ -# wvp -WEB VIDEO PLATFORM是一个基于GB28181-2016标准实现的网络视频平台,负责实现核心信令与设备管理后台部分,支持NAT穿透,支持海康、大华、宇视等品牌的IPC、NVR、DVR接入。 -流媒体服务基于ZLMediaKit-https://github.com/xiongziliang/ZLMediaKit -前段页面基于MediaServerUI进行修改. +![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) +[![JAVA](https://img.shields.io/badge/language-java-red.svg)](https://en.cppreference.com/) +[![platform](https://img.shields.io/badge/platform-linux%20|%20macos%20|%20windows-blue.svg)](https://github.com/xia-chu/ZLMediaKit) +[![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接入。支持国标级联,支持将不带国标功能的摄像机/直播流/直播推流转发到其他国标平台。 + +流媒体服务基于@夏楚 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等设备接入. -# 项目目标 -旨在打造一个易配置,易使用,便于维护的28181国标信令系统, 依托优秀的开源流媒体服务框架ZLMediaKit, 实现一个完整易用GB28181平台. +支持浏览器无插件播放摄像头视频。 +支持国标设备(摄像机、平台、NVR等)设备接入 +支持非国标(onvif, rtsp, rtmp,直播设备等等)设备接入,充分利旧。 +支持国标级联。多平台级联。跨网视频预览。 +支持跨网网闸平台互联。 + + +# 文档 +wvp使用文档 [https://doc.wvp-pro.cn](https://doc.wvp-pro.cn) +ZLM使用文档 [https://github.com/ZLMediaKit/ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit) +> wvp文档由gitee提供服务,如果遇到打不开请多刷新几次。 + +# 付费社群 +[![社群](doc/_media/shequ.png "shequ")](https://t.zsxq.com/0d8VAD3Dm) +> 收费是为了提供更好的服务,也是对作者更大的激励。加入星球的用户三天后可以私信我留下微信号,我会拉大家入群。加入三天内不满意可以直接退款,大家不需要有顾虑,来白嫖三天也不是不可以。 # gitee同步仓库 -https://gitee.com/18010473990/wvp-GB28181.git +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. 视频预览; -2. 云台控制(方向、缩放控制); -3. 视频设备信息同步; -4. 离在线监控; -5. 录像查询与回放(基于NVR\DVR,暂不支持快进、seek操作); -6. 无人观看自动断流; -7. 支持UDP和TCP两种国标信令传输模式; +# 功能特性 +- [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] 心跳保活 + - [X] 通道选择 + - [X] 通道推送 + - [X] 点播 + - [X] 云台控制 + - [X] 平台状态查询 + - [X] 平台信息查询 + - [X] 平台远程启动 + - [X] 每个级联平台可自定义的虚拟目录 + - [X] 目录订阅与通知 + - [X] 录像查看与播放 + - [X] GPS订阅与通知(直播推流) +- [X] 支持自动配置ZLM媒体服务, 减少因配置问题所出现的问题; +- [X] 多流媒体节点,自动选择负载最低的节点使用。 +- [X] 支持启用udp多端口模式, 提高udp模式下媒体传输性能; +- [X] 支持公网部署; +- [X] 支持wvp与zlm分开部署,提升平台并发能力 +- [X] 支持拉流RTSP/RTMP,分发为各种流格式,或者推送到其他国标平台 +- [X] 支持推流RTSP/RTMP,分发为各种流格式,或者推送到其他国标平台 +- [X] 支持推流鉴权 +- [X] 支持接口鉴权 +- [X] 云端录像,推流/代理/国标视频均可以录制在云端服务器,支持预览和下载 +- [X] 支持打包可执行jar和war +- [X] 支持跨域请求,支持前后端分离部署 +- [X] 支持Mysql,Postgresql,金仓等数据库 +- [X] 支持Onvif(目前在onvif分支,需要安装onvif服务,服务请在知识星球获取) -# 新支持特性 -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] 设备目录通知处理 -16. 移动位置查询和显示,可通过配置文件设置移动位置历史是否存储 +# 授权协议 +本项目自有代码使用宽松的MIT协议,在保留版权信息的情况下可以自由应用于各自商用、非商业的项目。 但是本项目也零碎的使用了一些其他的开源代码,在商用的情况下请自行替代或剔除; 由于使用本项目而产生的商业纠纷或侵权行为一概与本项目及开发者无关,请自行承担法律风险。 在使用本项目代码时,也应该在授权协议中同时表明本项目依赖的第三方库的协议 -# 待实现: -上级级联 -推流列表 -拉流列表 -web界面系统设置 -使用mysql作为数据库 +# 技术支持 -# 项目部署 -参考:[WIKI](https://github.com/648540858/wvp-GB28181-pro/wiki) +[知识星球](https://t.zsxq.com/0d8VAD3Dm)专栏列表: +- [使用入门系列一:WVP-PRO能做什么](https://t.zsxq.com/0dLguVoSp) -# gitee同步仓库 -https://gitee.com/18010473990/wvp-GB28181.git - -# 使用帮助 -QQ群: 901799015, 542509000(ZLM大群) -QQ私信一般不回, 精力有限.欢迎大家在群里讨论. +有偿技术支持,请发送邮件到648540858@qq.com # 致谢 -感谢作者[夏楚](https://github.com/xiongziliang) 提供这么棒的开源流媒体服务框架 +感谢作者[夏楚](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) + diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100644 index 000000000..0f3c4c977 --- /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 000000000..c2cbfba4e --- /dev/null +++ b/doc/README.md @@ -0,0 +1,106 @@ +# 介绍 + +> 开箱即用的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许可协议。保留版权的情况下可以用于商业项目。 +- 支持多流媒体节点负载均衡。 + +# 付费社群 +[![社群](_media/shequ.png "shequ")](https://t.zsxq.com/0d8VAD3Dm) +> 收费是为了提供更好的服务,也是对作者更大的激励。加入星球的用户三天后可以私信我留下微信号,我会拉大家入群。加入三天内不满意可以直接退款,大家不需要有顾虑,来白嫖三天也不是不可以。 + +# 我们实现了哪些国标功能 +**作为上级平台** +- [X] 注册 +- [X] 注销 +- [X] 实时视音频点播 +- [X] 设备控制 + - [X] 云台控制 + - [X] 远程启动 + - [X] 录像控制 + - [X] 报警布防/撤防 + - [X] 报警复位 + - [X] 强制关键帧 + - [X] 拉框放大 + - [X] 拉框缩小 + - [X] 看守位控制 + - [X] 设备配置 +- [X] 报警事件通知和分发 +- [X] 设备目录订阅 +- [X] 网络设备信息查询 + - [X] 设备目录查询 + - [X] 设备状态查询 + - [X] 设备配置查询 + - [X] 设备预置位查询 +- [X] 状态信息报送 +- [X] 设备视音频文件检索 +- [X] 历史视音频的回放 + - [X] 播放 + - [X] 暂停 + - [X] 进/退 + - [X] 停止 +- [X] 视音频文件下载 +- [X] 校时 +- [X] 订阅和通知 + - [X] 事件订阅 + - [X] 移动设备位置订阅 + - [X] 报警订阅 + - [X] 目录订阅 +- [ ] 语音广播 +- [ ] 语音对讲 + +**作为下级平台** +- [X] 注册 +- [X] 注销 +- [X] 实时视音频点播 +- [X] 设备控制 + - [X] 云台控制 + - [ ] 远程启动 + - [X] 录像控制 + - [X] 报警布防/撤防 + - [X] 报警复位 + - [X] 强制关键帧 + - [X] 拉框放大 + - [X] 拉框缩小 + - [X] 看守位控制 + - [ ] 设备配置 +- [ ] 报警事件通知和分发 +- [X] 设备目录订阅 +- [X] 网络设备信息查询 + - [X] 设备目录查询 + - [X] 设备状态查询 + - [ ] 设备配置查询 + - [X] 设备预置位查询 +- [X] 状态信息报送 +- [X] 设备视音频文件检索 +- [X] 历史视音频的回放 + - [X] 播放 + - [x] 暂停 + - [x] 进/退 + - [x] 停止 +- [X] 视音频文件下载 +- [ ] ~~校时~~ +- [X] 订阅和通知 + - [X] 事件订阅 + - [X] 移动设备位置订阅 + - [ ] 报警订阅 + - [X] 目录订阅 +- [ ] 语音广播 +- [ ] 语音对讲 + + + + +# 社区 +代码目前托管在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 000000000..9ba8280b7 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 000000000..4dd62cf26 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 000000000..f95f5d5ac 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 000000000..9db85b55e 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 000000000..6a0c550d9 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 000000000..31995c3cb 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 000000000..030502d0b 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 000000000..cb0f3d519 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 000000000..d6fe87770 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 000000000..6be1128f2 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 000000000..247120453 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 000000000..f167811d3 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 000000000..b09e8cd39 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 000000000..02b303e36 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 000000000..5ca4faf9d 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 000000000..f9f2f55f9 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 000000000..efe688cfd 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 000000000..f548cec77 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 000000000..6959c7800 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 000000000..04c42bc14 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 000000000..1a8edbf02 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 000000000..02fa66f12 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 000000000..708e901a3 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 000000000..5259e0b52 --- /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 000000000..7942bda4b --- /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 000000000..7bd6c7929 --- /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 000000000..4ac4f8f12 --- /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 000000000..516830fde --- /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 000000000..ceed545ea --- /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 000000000..c406b3bbb --- /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 000000000..f77a428c0 --- /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/config.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 000000000..1b4aef324 --- /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 000000000..55ecb2680 --- /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 000000000..74e632d81 --- /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 000000000..ef501826e --- /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 000000000..0c472386a --- /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 000000000..f727264e9 --- /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 000000000..94412ecdc --- /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 000000000..48f8d8e48 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 000000000..aad1859d0 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 000000000..e78690000 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 000000000..1a9d58b45 --- /dev/null +++ b/doc/_content/introduction/compile.md @@ -0,0 +1,100 @@ + +# 编译 +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.npmmirror.com install +npm run build +``` +编译如果报错, 一般都是网络问题, 导致的依赖包下载失败 +编译完成后在src/main/resources下出现static目录 +**编译完成一般是这个样子,中间没有报红的错误信息** +![编译成功](_media/img.png) + +### 5.3 生成可执行jar +```bash +cd wvp-GB28181-pro +mvn package +``` +### 5.4 生成war +```bash +cd wvp-GB28181-pro +mvn package -P war +``` +编译如果报错, 一般都是网络问题, 导致的依赖包下载失败 +编译完成后在target目录下出现wvp-pro-***.jar/wvp-pro-***.war。 +接下来[配置服务](./_content/introduction/config.md) + + + + + + + + diff --git a/doc/_content/introduction/config.md b/doc/_content/introduction/config.md new file mode 100644 index 000000000..9e8ac0f63 --- /dev/null +++ b/doc/_content/introduction/config.md @@ -0,0 +1,163 @@ + +# 配置 +对于首次测试或者新手同学,我建议在局域网测试,并且关闭服务器与客户机的防火墙测试。建议部署在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 +wvp支持多种数据库,包括Mysql,Postgresql,金仓等,配置任选一种即可。 +### 2.1 数据库配置 +#### 2.1.1 初始化数据库 +首先使用创建数据库,然后使用sql/初始化.sql初始化数据库,如果是从旧版升级上来的,使用升级sql更新。 +#### 2.1.2 Mysql数据库配置 +数据库名称以wvp为例 +```yaml +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true&allowPublicKeyRetrieval=true + username: root + password: 12345678 + +mybatis: + configuration: + map-underscore-to-camel-case: true +``` +#### 2.1.3 Postgresql数据库配置 +数据库名称以wvp为例 +```yaml +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: org.postgresql.Driver + url: jdbc:postgresql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true&allowPublicKeyRetrieval=true + username: root + password: 12345678 + +pagehelper: + helper-dialect: postgresql +``` +#### 2.1.4 金仓数据库配置 +数据库名称以wvp为例 +```yaml +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.kingbase8.Driver + url: jdbc:kingbase8://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=utf8 + username: root + password: 12345678 + +pagehelper: + helper-dialect: postgresql +``` +### 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 000000000..188efb049 --- /dev/null +++ b/doc/_content/introduction/deployment.md @@ -0,0 +1,58 @@ + + +# 部署 +**请仔细阅读以下内容** +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 +**jar包:** +```shell +nohup java -jar wvp-pro-*.jar & +``` +**war包:** +下载Tomcat后将war包放入webapps中,启动Tomcat以解压war包,停止Tomcat后,删除ROOT目录以及war包,将解压后的war包目录重命名为ROOT,将配置文件中的Server.port配置为与Tomcat端口一致 +然后启动Tomcat。 +**启动ZLM** +```shell +nohup ./MediaServer -d -m 3 & +``` +### 前后端分离部署 +前后端部署目前在最新的版本已经支持,请使用3月15日之后的版本部署 +前端编译后的文件在`src/main/resources/static`中,将此目录下的文件部署。 +前后端分离部署最大的问题是跨域的解决,之前版本使用cookie完成登录流程,而cookie是不可以在复杂跨域中使用的。所以当前版本使用JWT生成的TOKEN作为认证凭据, +部署前端后需要在wvp中配置前端访问的地址以完成跨域流程。 +**配置前端服务器** +1. 假如你的服务有公网域名为xxx.com,公网IP为11.11.11.11, 那么你可以在wvp中这样配置: +```yaml +user-settings: + # 跨域配置,配置你访问前端页面的地址即可, 可以配置多个 + allowed-origins: + - http://xxx.com:8008 + - http://11.11.11.11:8008 +``` +配置不是必须的,你使用哪个ip/域名访问就配置哪个即可。修改配置后重启wvp以使配置生效。 +2. 在`src/main/resources/static/static/js/config.js`下配置服务器的地址,也就是wvp服务的地址 +```javascript +window.baseUrl = "http://xxx.com:18080" +``` +`这里的地址是需要客户电脑能访问到的,因为请求是客户端电脑发起,与代理不同` +[接入设备](./_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 000000000..d6c29d71c 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 000000000..5b74d95b3 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 000000000..4aaa7fed9 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 000000000..27f8a96a7 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 000000000..aa3b88eef 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 000000000..76e6fafda 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 000000000..81267ffe2 --- /dev/null +++ b/doc/_content/qa/bug.md @@ -0,0 +1,12 @@ + +# 反馈bug +代码是在不断的完善的,不断修改会修复旧的问题也有可能引入新的问题,所以遇到BUG是很正常的一件事。所以遇到问题不要烦燥,咱们就事论事就好了。 +## 如何反馈 +1. 在知识星球提问。 +2. 更新代码,很可能你遇到问题别人已经更早的遇到了,或者是作者自己发现了,已经解决了,所以你可以更新代码再次进行测试; +3. 可以在github提ISSUE,我几乎每天都会去看issue,你的问题我会尽快给予答复; +> 有偿支持可以给我发邮件, 648540858@qq.com + +## 社群 +[![社群](../../_media/shequ.png "shequ")](https://t.zsxq.com/0d8VAD3Dm) +> 收费是为了提供更好的服务,也是对作者更大的激励。加入星球的用户三天后可以私信我留下微信号,我会拉大家入群。加入三天内不满意可以直接退款,大家不需要有顾虑,来白嫖三天也不是不可以。 \ No newline at end of file diff --git a/doc/_content/qa/development.md b/doc/_content/qa/development.md new file mode 100644 index 000000000..fec7b70cf --- /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 000000000..d6c29d71c 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 000000000..c8e9c02bb --- /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 000000000..d18459c54 --- /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 000000000..1dd533f2c --- /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 000000000..a9bc95fcd 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 000000000..e08e4e1e0 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 000000000..2af0ecc6f 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 000000000..7dd270de8 --- /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 000000000..ecf62e9f1 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 000000000..2dc8cc8ad 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 000000000..7e2ddde4e 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 000000000..5fc5ef410 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 000000000..d5df7cebd 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 000000000..47daffcdf 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 000000000..6c67ef472 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 000000000..fc204aaaa 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 000000000..9b436417c 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 000000000..c3aa1ff65 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 000000000..c4c1b09ee --- /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 000000000..d5a892e2a --- /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 000000000..9b436417c 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 000000000..fbfdcc661 --- /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 000000000..fad9589fe --- /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 000000000..eb3524f9a --- /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/1372762149.jpg b/doc/_media/1372762149.jpg new file mode 100644 index 000000000..471ec072a Binary files /dev/null and b/doc/_media/1372762149.jpg differ diff --git a/doc/_media/2.png b/doc/_media/2.png new file mode 100644 index 000000000..dd982e1e5 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 000000000..a52620f2f 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 000000000..bef780eb2 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 000000000..3943a5277 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 000000000..913d29490 Binary files /dev/null and b/doc/_media/3.png differ diff --git a/doc/_media/903207146.jpg b/doc/_media/903207146.jpg new file mode 100644 index 000000000..0bbc7e67c Binary files /dev/null and b/doc/_media/903207146.jpg differ diff --git a/doc/_media/favicon.ico b/doc/_media/favicon.ico new file mode 100644 index 000000000..bc5f8e662 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 000000000..15200e69b 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 000000000..cc8078db3 Binary files /dev/null and b/doc/_media/logo-mini.png differ diff --git a/doc/_media/logo.jpg b/doc/_media/logo.jpg new file mode 100644 index 000000000..bcc9fb6d5 Binary files /dev/null and b/doc/_media/logo.jpg differ diff --git a/doc/_media/logo.png b/doc/_media/logo.png new file mode 100644 index 000000000..c5da2d4b9 Binary files /dev/null and b/doc/_media/logo.png differ diff --git a/doc/_media/shequ.png b/doc/_media/shequ.png new file mode 100644 index 000000000..c5aec9807 Binary files /dev/null and b/doc/_media/shequ.png differ diff --git a/doc/_media/weixin.jpg b/doc/_media/weixin.jpg new file mode 100644 index 000000000..eda1260ec 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 000000000..973996b79 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 000000000..22ac93ae0 --- /dev/null +++ b/doc/_navbar.md @@ -0,0 +1 @@ + diff --git a/doc/_sidebar.md b/doc/_sidebar.md new file mode 100644 index 000000000..e49ff915a --- /dev/null +++ b/doc/_sidebar.md @@ -0,0 +1,33 @@ + + +* **编译与部署** + * [测试](_content/introduction/test.md) + * [编译](_content/introduction/compile.md) + * [配置](_content/introduction/config.md) + * [部署](_content/introduction/deployment.md) +* **功能与使用** + * [接入设备](_content/ability/device.md) + * [设备使用](_content/ability/device_use.md) + * [国标级联](_content/ability/cascade2.md) + * [推流列表](_content/ability/push.md) + * [拉流代理](_content/ability/proxy.md) + * [电子地图](_content/ability/gis.md) + * [节点管理](_content/ability/node_manger.md) + * [云端录像](_content/ability/cloud_record.md) + * [不间断录像](_content/ability/continuous_recording.md) +* **流程与原理** + * [统一编码规则](_content/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 000000000..580603da4 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,59 @@ + + + + + WVP-PRO文档 + + + + + + + + + +
加载中
+ + + + + + + + + + + + diff --git a/doc/lib/css/vue.css b/doc/lib/css/vue.css new file mode 100644 index 000000000..847f385ac --- /dev/null +++ b/doc/lib/css/vue.css @@ -0,0 +1 @@ +@import url("https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600");*{-webkit-font-smoothing:antialiased;-webkit-overflow-scrolling:touch;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-text-size-adjust:none;-webkit-touch-callout:none;box-sizing:border-box}body:not(.ready){overflow:hidden}body:not(.ready) .app-nav,body:not(.ready)>nav,body:not(.ready) [data-cloak]{display:none}div#app{font-size:30px;font-weight:lighter;margin:40vh auto;text-align:center}div#app:empty:before{content:"Loading..."}img.emoji{height:1.2em}img.emoji,span.emoji{vertical-align:middle}span.emoji{font-family:Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-size:1.2em}.progress{background-color:#42b983;background-color:var(--theme-color,#42b983);height:2px;left:0;position:fixed;right:0;top:0;transition:width .2s,opacity .4s;width:0;z-index:999999}.search .search-keyword,.search a:hover{color:#42b983;color:var(--theme-color,#42b983)}.search .search-keyword{font-style:normal;font-weight:700}body,html{height:100%}body{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:#34495e;font-family:Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:15px;letter-spacing:0;margin:0;overflow-x:hidden}img{max-width:100%}a[disabled]{cursor:not-allowed;opacity:.6}kbd{border:1px solid #ccc;border-radius:3px;display:inline-block;font-size:12px!important;line-height:12px;margin-bottom:3px;padding:3px 5px;vertical-align:middle}li input[type=checkbox]{margin:0 .2em .25em 0;vertical-align:middle}.app-nav{margin:25px 60px 0 0;position:absolute;right:0;text-align:right;z-index:10}.app-nav.no-badge{margin-right:25px}.app-nav p{margin:0}.app-nav>a{margin:0 1rem;padding:5px 0}.app-nav li,.app-nav ul{display:inline-block;list-style:none;margin:0}.app-nav a{color:inherit;font-size:16px;text-decoration:none;transition:color .3s}.app-nav a.active,.app-nav a:hover{color:#42b983;color:var(--theme-color,#42b983)}.app-nav a.active{border-bottom:2px solid #42b983;border-bottom:2px solid var(--theme-color,#42b983)}.app-nav li{display:inline-block;margin:0 1rem;padding:5px 0;position:relative;cursor:pointer}.app-nav li ul{background-color:#fff;border:1px solid;border-color:#ddd #ddd #ccc;border-radius:4px;box-sizing:border-box;display:none;max-height:calc(100vh - 61px);overflow-y:auto;padding:10px 0;position:absolute;right:-15px;text-align:left;top:100%;white-space:nowrap}.app-nav li ul li{display:block;font-size:14px;line-height:1rem;margin:8px 14px;white-space:nowrap}.app-nav li ul a{display:block;font-size:inherit;margin:0;padding:0}.app-nav li ul a.active{border-bottom:0}.app-nav li:hover ul{display:block}.github-corner{border-bottom:0;position:fixed;right:0;text-decoration:none;top:0;z-index:1}.github-corner:hover .octo-arm{animation:octocat-wave .56s ease-in-out}.github-corner svg{color:#fff;fill:#42b983;fill:var(--theme-color,#42b983);height:80px;width:80px}main{display:block;position:relative;width:100vw;height:100%;z-index:0}main.hidden{display:none}.anchor{display:inline-block;text-decoration:none;transition:all .3s}.anchor span{color:#34495e}.anchor:hover{text-decoration:underline}.sidebar{border-right:1px solid rgba(0,0,0,.07);overflow-y:auto;padding:40px 0 0;position:absolute;top:0;bottom:0;left:0;transition:transform .25s ease-out;width:300px;z-index:20}.sidebar>h1{margin:0 auto 1rem;font-size:1.5rem;font-weight:300;text-align:center}.sidebar>h1 a{color:inherit;text-decoration:none}.sidebar>h1 .app-nav{display:block;position:static}.sidebar .sidebar-nav{line-height:2em;padding-bottom:40px}.sidebar li.collapse .app-sub-sidebar{display:none}.sidebar ul{margin:0 0 0 15px;padding:0}.sidebar li>p{font-weight:700;margin:0}.sidebar ul,.sidebar ul li{list-style:none}.sidebar ul li a{border-bottom:none;display:block}.sidebar ul li ul{padding-left:20px}.sidebar::-webkit-scrollbar{width:4px}.sidebar::-webkit-scrollbar-thumb{background:transparent;border-radius:4px}.sidebar:hover::-webkit-scrollbar-thumb{background:hsla(0,0%,53.3%,.4)}.sidebar:hover::-webkit-scrollbar-track{background:hsla(0,0%,53.3%,.1)}.sidebar-toggle{background-color:transparent;background-color:hsla(0,0%,100%,.8);border:0;outline:none;padding:10px;position:absolute;bottom:0;left:0;text-align:center;transition:opacity .3s;width:284px;z-index:30;cursor:pointer}.sidebar-toggle:hover .sidebar-toggle-button{opacity:.4}.sidebar-toggle span{background-color:#42b983;background-color:var(--theme-color,#42b983);display:block;margin-bottom:4px;width:16px;height:2px}body.sticky .sidebar,body.sticky .sidebar-toggle{position:fixed}.content{padding-top:60px;position:absolute;top:0;right:0;bottom:0;left:300px;transition:left .25s ease}.markdown-section{margin:0 auto;max-width:80%;padding:30px 15px 40px;position:relative}.markdown-section>*{box-sizing:border-box;font-size:inherit}.markdown-section>:first-child{margin-top:0!important}.markdown-section hr{border:none;border-bottom:1px solid #eee;margin:2em 0}.markdown-section iframe{border:1px solid #eee;width:1px;min-width:100%}.markdown-section table{border-collapse:collapse;border-spacing:0;display:block;margin-bottom:1rem;overflow:auto;width:100%}.markdown-section th{font-weight:700}.markdown-section td,.markdown-section th{border:1px solid #ddd;padding:6px 13px}.markdown-section tr{border-top:1px solid #ccc}.markdown-section p.tip,.markdown-section tr:nth-child(2n){background-color:#f8f8f8}.markdown-section p.tip{border-bottom-right-radius:2px;border-left:4px solid #f66;border-top-right-radius:2px;margin:2em 0;padding:12px 24px 12px 30px;position:relative}.markdown-section p.tip:before{background-color:#f66;border-radius:100%;color:#fff;content:"!";font-family:Dosis,Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:14px;font-weight:700;left:-12px;line-height:20px;position:absolute;height:20px;width:20px;text-align:center;top:14px}.markdown-section p.tip code{background-color:#efefef}.markdown-section p.tip em{color:#34495e}.markdown-section p.warn{background:rgba(66,185,131,.1);border-radius:2px;padding:1rem}.markdown-section ul.task-list>li{list-style-type:none}body.close .sidebar{transform:translateX(-300px)}body.close .sidebar-toggle{width:auto}body.close .content{left:0}@media print{.app-nav,.github-corner,.sidebar,.sidebar-toggle{display:none}}@media screen and (max-width:768px){.github-corner,.sidebar,.sidebar-toggle{position:fixed}.app-nav{margin-top:16px}.app-nav li ul{top:30px}main{height:auto;min-height:100vh;overflow-x:hidden}.sidebar{left:-300px;transition:transform .25s ease-out}.content{left:0;max-width:100vw;position:static;padding-top:20px;transition:transform .25s ease}.app-nav,.github-corner{transition:transform .25s ease-out}.sidebar-toggle{background-color:transparent;width:auto;padding:30px 30px 10px 10px}body.close .sidebar{transform:translateX(300px)}body.close .sidebar-toggle{background-color:hsla(0,0%,100%,.8);transition:background-color 1s;width:284px;padding:10px}body.close .content{transform:translateX(300px)}body.close .app-nav,body.close .github-corner{display:none}.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave .56s ease-in-out}}@keyframes octocat-wave{0%,to{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}section.cover{position:relative;align-items:center;background-position:50%;background-repeat:no-repeat;background-size:cover;min-height:100vh;width:100%;display:none}section.cover.show{display:flex}section.cover.has-mask .mask{background-color:#fff;opacity:.8;position:absolute;top:0;bottom:0;width:100%}section.cover .cover-main{flex:1;margin:0 16px;text-align:center;position:relative}section.cover a{color:inherit}section.cover a,section.cover a:hover{text-decoration:none}section.cover p{line-height:1.5rem;margin:1em 0}section.cover h1{color:inherit;font-size:2.5rem;font-weight:300;margin:.625rem 0 2.5rem;position:relative;text-align:center}section.cover h1 a{display:block}section.cover h1 small{bottom:-.4375rem;font-size:1rem;position:absolute}section.cover blockquote{font-size:1.5rem;text-align:center}section.cover ul{line-height:1.8;list-style-type:none;margin:1em auto;max-width:500px;padding:0}section.cover .cover-main>p:last-child a{border-radius:2rem;border:1px solid #42b983;border-color:var(--theme-color,#42b983);box-sizing:border-box;color:#42b983;color:var(--theme-color,#42b983);display:inline-block;font-size:1.05rem;letter-spacing:.1rem;margin:.5rem 1rem;padding:.75em 2rem;text-decoration:none;transition:all .15s ease}section.cover .cover-main>p:last-child a:last-child{background-color:#42b983;background-color:var(--theme-color,#42b983);color:#fff}section.cover .cover-main>p:last-child a:last-child:hover{color:inherit;opacity:.8}section.cover .cover-main>p:last-child a:hover{color:inherit}section.cover blockquote>p>a{border-bottom:2px solid #42b983;border-bottom:2px solid var(--theme-color,#42b983);transition:color .3s}section.cover blockquote>p>a:hover{color:#42b983;color:var(--theme-color,#42b983)}.sidebar,body{background-color:#fff}.sidebar{color:#364149}.sidebar li{margin:6px 0}.sidebar ul li a{color:#505d6b;font-size:14px;font-weight:400;overflow:hidden;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.sidebar ul li a:hover{text-decoration:underline}.sidebar ul li ul{padding:0}.sidebar ul li.active>a{border-right:2px solid;color:#42b983;color:var(--theme-color,#42b983);font-weight:600}.app-sub-sidebar li:before{content:"-";padding-right:4px;float:left}.markdown-section h1,.markdown-section h2,.markdown-section h3,.markdown-section h4,.markdown-section strong{color:#2c3e50;font-weight:600}.markdown-section a{color:#42b983;color:var(--theme-color,#42b983);font-weight:600}.markdown-section h1{font-size:2rem;margin:0 0 1rem}.markdown-section h2{font-size:1.75rem;margin:45px 0 .8rem}.markdown-section h3{font-size:1.5rem;margin:40px 0 .6rem}.markdown-section h4{font-size:1.25rem}.markdown-section h5{font-size:1rem}.markdown-section h6{color:#777;font-size:1rem}.markdown-section figure,.markdown-section p{margin:1.2em 0}.markdown-section ol,.markdown-section p,.markdown-section ul{line-height:1.6rem;word-spacing:.05rem}.markdown-section ol,.markdown-section ul{padding-left:1.5rem}.markdown-section blockquote{border-left:4px solid #42b983;border-left:4px solid var(--theme-color,#42b983);color:#858585;margin:2em 0;padding-left:20px}.markdown-section blockquote p{font-weight:600;margin-left:0}.markdown-section iframe{margin:1em 0}.markdown-section em{color:#7f8c8d}.markdown-section code,.markdown-section output:after,.markdown-section pre{font-family:Roboto Mono,Monaco,courier,monospace}.markdown-section code,.markdown-section pre{background-color:#f8f8f8}.markdown-section output,.markdown-section pre{margin:1.2em 0;position:relative}.markdown-section output,.markdown-section pre>code{border-radius:2px;display:block}.markdown-section output:after,.markdown-section pre>code{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial}.markdown-section code{border-radius:2px;color:#e96900;margin:0 2px;padding:3px 5px;white-space:pre-wrap}.markdown-section>:not(h1):not(h2):not(h3):not(h4):not(h5):not(h6) code{font-size:.8rem}.markdown-section pre{padding:0 1.4rem;line-height:1.5rem;overflow:auto;word-wrap:normal}.markdown-section pre>code{color:#525252;font-size:.8rem;padding:2.2em 5px;line-height:inherit;margin:0 2px;max-width:inherit;overflow:inherit;white-space:inherit}.markdown-section output{padding:1.7rem 1.4rem;border:1px dotted #ccc}.markdown-section output>:first-child{margin-top:0}.markdown-section output>:last-child{margin-bottom:0}.markdown-section code:after,.markdown-section code:before,.markdown-section output:after,.markdown-section output:before{letter-spacing:.05rem}.markdown-section output:after,.markdown-section pre:after{color:#ccc;font-size:.6rem;font-weight:600;height:15px;line-height:15px;padding:5px 10px 0;position:absolute;right:0;text-align:right;top:0;content:attr(data-lang)}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#8e908c}.token.namespace{opacity:.7}.token.boolean,.token.number{color:#c76b29}.token.punctuation{color:#525252}.token.property{color:#c08b30}.token.tag{color:#2973b7}.token.string{color:#42b983;color:var(--theme-color,#42b983)}.token.selector{color:#6679cc}.token.attr-name{color:#2973b7}.language-css .token.string,.style .token.string,.token.entity,.token.url{color:#22a2c9}.token.attr-value,.token.control,.token.directive,.token.unit{color:#42b983;color:var(--theme-color,#42b983)}.token.function,.token.keyword{color:#e96900}.token.atrule,.token.regex,.token.statement{color:#22a2c9}.token.placeholder,.token.variable{color:#3d8fd1}.token.deleted{text-decoration:line-through}.token.inserted{border-bottom:1px dotted #202746;text-decoration:none}.token.italic{font-style:italic}.token.bold,.token.important{font-weight:700}.token.important{color:#c94922}.token.entity{cursor:help}code .token{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;min-height:1.5rem;position:relative;left:auto} \ No newline at end of file diff --git a/doc/lib/js/docsify-copy-code.min.js b/doc/lib/js/docsify-copy-code.min.js new file mode 100644 index 000000000..9766a7888 --- /dev/null +++ b/doc/lib/js/docsify-copy-code.min.js @@ -0,0 +1,9 @@ +/*! + * docsify-copy-code + * v2.1.1 + * https://github.com/jperasmus/docsify-copy-code + * (c) 2017-2020 JP Erasmus + * MIT license + */ +!function(){"use strict";function s(o){return(s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(o){return typeof o}:function(o){return o&&"function"==typeof Symbol&&o.constructor===Symbol&&o!==Symbol.prototype?"symbol":typeof o})(o)}!function(o,e){void 0===e&&(e={});var t=e.insertAt;if(o&&"undefined"!=typeof document){var n=document.head||document.getElementsByTagName("head")[0],c=document.createElement("style");c.type="text/css","top"===t&&n.firstChild?n.insertBefore(c,n.firstChild):n.appendChild(c),c.styleSheet?c.styleSheet.cssText=o:c.appendChild(document.createTextNode(o))}}(".docsify-copy-code-button,.docsify-copy-code-button span{cursor:pointer;transition:all .25s ease}.docsify-copy-code-button{position:absolute;z-index:1;top:0;right:0;overflow:visible;padding:.65em .8em;border:0;border-radius:0;outline:0;font-size:1em;background:grey;background:var(--theme-color,grey);color:#fff;opacity:0}.docsify-copy-code-button span{border-radius:3px;background:inherit;pointer-events:none}.docsify-copy-code-button .error,.docsify-copy-code-button .success{position:absolute;z-index:-100;top:50%;right:0;padding:.5em .65em;font-size:.825em;opacity:0;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.docsify-copy-code-button.error .error,.docsify-copy-code-button.success .success{right:100%;opacity:1;-webkit-transform:translate(-115%,-50%);transform:translate(-115%,-50%)}.docsify-copy-code-button:focus,pre:hover .docsify-copy-code-button{opacity:1}"),document.querySelector('link[href*="docsify-copy-code"]')&&console.warn("[Deprecation] Link to external docsify-copy-code stylesheet is no longer necessary."),window.DocsifyCopyCodePlugin={init:function(){return function(o,e){o.ready(function(){console.warn("[Deprecation] Manually initializing docsify-copy-code using window.DocsifyCopyCodePlugin.init() is no longer necessary.")})}}},window.$docsify=window.$docsify||{},window.$docsify.plugins=[function(o,r){o.doneEach(function(){var o=Array.apply(null,document.querySelectorAll("pre[data-lang]")),c={buttonText:"Copy to clipboard",errorText:"Error",successText:"Copied"};r.config.copyCode&&Object.keys(c).forEach(function(t){var n=r.config.copyCode[t];"string"==typeof n?c[t]=n:"object"===s(n)&&Object.keys(n).some(function(o){var e=-1',''.concat(c.buttonText,""),''.concat(c.errorText,""),''.concat(c.successText,""),""].join("");o.forEach(function(o){o.insertAdjacentHTML("beforeend",e)})}),o.mounted(function(){document.querySelector(".content").addEventListener("click",function(o){if(o.target.classList.contains("docsify-copy-code-button")){var e="BUTTON"===o.target.tagName?o.target:o.target.parentNode,t=document.createRange(),n=e.parentNode.querySelector("code"),c=window.getSelection();t.selectNode(n),c.removeAllRanges(),c.addRange(t);try{document.execCommand("copy")&&(e.classList.add("success"),setTimeout(function(){e.classList.remove("success")},1e3))}catch(o){console.error("docsify-copy-code: ".concat(o)),e.classList.add("error"),setTimeout(function(){e.classList.remove("error")},1e3)}"function"==typeof(c=window.getSelection()).removeRange?c.removeRange(t):"function"==typeof c.removeAllRanges&&c.removeAllRanges()}})})}].concat(window.$docsify.plugins||[])}(); +//# sourceMappingURL=docsify-copy-code.min.js.map diff --git a/doc/lib/js/docsify-plantuml.min.js b/doc/lib/js/docsify-plantuml.min.js new file mode 100644 index 000000000..169076747 --- /dev/null +++ b/doc/lib/js/docsify-plantuml.min.js @@ -0,0 +1 @@ +!function(t){function e(a){if(n[a])return n[a].exports;var r=n[a]={i:a,l:!1,exports:{}};return t[a].call(r.exports,r,r.exports,e),r.l=!0,r.exports}var n={};e.m=t,e.c=n,e.d=function(t,n,a){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:a})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=0)}([function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var a=n(1);window.$docsify||(window.$docsify={}),window.$docsify.plugins=(window.$docsify.plugins||[]).concat(a.a)},function(t,e,n){"use strict";function a(t,e){var t=Object(o.a)(e.skin)+t,n=e.serverPath||"//www.plantuml.com/plantuml/svg/";if(e.renderSvgAsObject){var a=n+Object(l.encode)(r(t));return''}var a=n+Object(l.encode)(t);return''}function r(t){function e(t,e){for(var n=(a+e).split("/"),r=[],i=0,s=n.length;i>2,i=(3&t)<<4|e>>4,s=(15&e)<<2|n>>6,o=63&n,l="";return l+=a(63&r),l+=a(63&i),l+=a(63&s),l+=a(63&o)}e.exports=function(t){for(var e="",n=0;n0?e.windowBits=-e.windowBits:e.gzip&&e.windowBits>0&&e.windowBits<16&&(e.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new f,this.strm.avail_out=0;var n=o.deflateInit2(this.strm,e.level,e.method,e.windowBits,e.memLevel,e.strategy);if(n!==c)throw new Error(d[n]);if(e.header&&o.deflateSetHeader(this.strm,e.header),e.dictionary){var r;if(r="string"==typeof e.dictionary?h.string2buf(e.dictionary):"[object ArrayBuffer]"===u.call(e.dictionary)?new Uint8Array(e.dictionary):e.dictionary,(n=o.deflateSetDictionary(this.strm,r))!==c)throw new Error(d[n]);this._dict_set=!0}}function r(t,e){var n=new a(e);if(n.push(t,!0),n.err)throw n.msg||d[n.err];return n.result}function i(t,e){return e=e||{},e.raw=!0,r(t,e)}function s(t,e){return e=e||{},e.gzip=!0,r(t,e)}var o=t("./zlib/deflate"),l=t("./utils/common"),h=t("./utils/strings"),d=t("./zlib/messages"),f=t("./zlib/zstream"),u=Object.prototype.toString,c=0,_=-1,g=0,p=8;a.prototype.push=function(t,e){var n,a,r=this.strm,i=this.options.chunkSize;if(this.ended)return!1;a=e===~~e?e:!0===e?4:0,"string"==typeof t?r.input=h.string2buf(t):"[object ArrayBuffer]"===u.call(t)?r.input=new Uint8Array(t):r.input=t,r.next_in=0,r.avail_in=r.input.length;do{if(0===r.avail_out&&(r.output=new l.Buf8(i),r.next_out=0,r.avail_out=i),1!==(n=o.deflate(r,a))&&n!==c)return this.onEnd(n),this.ended=!0,!1;0!==r.avail_out&&(0!==r.avail_in||4!==a&&2!==a)||("string"===this.options.to?this.onData(h.buf2binstring(l.shrinkBuf(r.output,r.next_out))):this.onData(l.shrinkBuf(r.output,r.next_out)))}while((r.avail_in>0||0===r.avail_out)&&1!==n);return 4===a?(n=o.deflateEnd(this.strm),this.onEnd(n),this.ended=!0,n===c):2!==a||(this.onEnd(c),r.avail_out=0,!0)},a.prototype.onData=function(t){this.chunks.push(t)},a.prototype.onEnd=function(t){t===c&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=l.flattenChunks(this.chunks)),this.chunks=[],this.err=t,this.msg=this.strm.msg},n.Deflate=a,n.deflate=r,n.deflateRaw=i,n.gzip=s},{"./utils/common":5,"./utils/strings":6,"./zlib/deflate":9,"./zlib/messages":10,"./zlib/zstream":12}],5:[function(t,e,n){"use strict";function a(t,e){return Object.prototype.hasOwnProperty.call(t,e)}var r="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Int32Array;n.assign=function(t){for(var e=Array.prototype.slice.call(arguments,1);e.length;){var n=e.shift();if(n){if("object"!=typeof n)throw new TypeError(n+"must be non-object");for(var r in n)a(n,r)&&(t[r]=n[r])}}return t},n.shrinkBuf=function(t,e){return t.length===e?t:t.subarray?t.subarray(0,e):(t.length=e,t)};var i={arraySet:function(t,e,n,a,r){if(e.subarray&&t.subarray)return void t.set(e.subarray(n,n+a),r);for(var i=0;i=252?6:l>=248?5:l>=240?4:l>=224?3:l>=192?2:1;o[254]=o[254]=1,n.string2buf=function(t){var e,n,a,i,s,o=t.length,l=0;for(i=0;i>>6,e[s++]=128|63&n):n<65536?(e[s++]=224|n>>>12,e[s++]=128|n>>>6&63,e[s++]=128|63&n):(e[s++]=240|n>>>18,e[s++]=128|n>>>12&63,e[s++]=128|n>>>6&63,e[s++]=128|63&n);return e},n.buf2binstring=function(t){return a(t,t.length)},n.binstring2buf=function(t){for(var e=new r.Buf8(t.length),n=0,a=e.length;n4)h[r++]=65533,n+=s-1;else{for(i&=2===s?31:3===s?15:7;s>1&&n1?h[r++]=65533:i<65536?h[r++]=i:(i-=65536,h[r++]=55296|i>>10&1023,h[r++]=56320|1023&i)}return a(h,r)},n.utf8border=function(t,e){var n;for(e=e||t.length,e>t.length&&(e=t.length),n=e-1;n>=0&&128==(192&t[n]);)n--;return n<0?e:0===n?e:n+o[t[n]]>e?n:e}},{"./common":5}],7:[function(t,e,n){"use strict";function a(t,e,n,a){for(var r=65535&t|0,i=t>>>16&65535|0,s=0;0!==n;){s=n>2e3?2e3:n,n-=s;do{r=r+e[a++]|0,i=i+r|0}while(--s);r%=65521,i%=65521}return r|i<<16|0}e.exports=a},{}],8:[function(t,e,n){"use strict";function a(t,e,n,a){var i=r,s=a+n;t^=-1;for(var o=a;o>>8^i[255&(t^e[o])];return-1^t}var r=function(){for(var t,e=[],n=0;n<256;n++){t=n;for(var a=0;a<8;a++)t=1&t?3988292384^t>>>1:t>>>1;e[n]=t}return e}();e.exports=a},{}],9:[function(t,e,n){"use strict";function a(t,e){return t.msg=D[e],e}function r(t){return(t<<1)-(t>4?9:0)}function i(t){for(var e=t.length;--e>=0;)t[e]=0}function s(t){var e=t.state,n=e.pending;n>t.avail_out&&(n=t.avail_out),0!==n&&(O.arraySet(t.output,e.pending_buf,e.pending_out,n,t.next_out),t.next_out+=n,e.pending_out+=n,t.total_out+=n,t.avail_out-=n,e.pending-=n,0===e.pending&&(e.pending_out=0))}function o(t,e){Z._tr_flush_block(t,t.block_start>=0?t.block_start:-1,t.strstart-t.block_start,e),t.block_start=t.strstart,s(t.strm)}function l(t,e){t.pending_buf[t.pending++]=e}function h(t,e){t.pending_buf[t.pending++]=e>>>8&255,t.pending_buf[t.pending++]=255&e}function d(t,e,n,a){var r=t.avail_in;return r>a&&(r=a),0===r?0:(t.avail_in-=r,O.arraySet(e,t.input,t.next_in,r,n),1===t.state.wrap?t.adler=N(t.adler,e,r,n):2===t.state.wrap&&(t.adler=R(t.adler,e,r,n)),t.next_in+=r,t.total_in+=r,r)}function f(t,e){var n,a,r=t.max_chain_length,i=t.strstart,s=t.prev_length,o=t.nice_match,l=t.strstart>t.w_size-ht?t.strstart-(t.w_size-ht):0,h=t.window,d=t.w_mask,f=t.prev,u=t.strstart+lt,c=h[i+s-1],_=h[i+s];t.prev_length>=t.good_match&&(r>>=2),o>t.lookahead&&(o=t.lookahead);do{if(n=e,h[n+s]===_&&h[n+s-1]===c&&h[n]===h[i]&&h[++n]===h[i+1]){i+=2,n++;do{}while(h[++i]===h[++n]&&h[++i]===h[++n]&&h[++i]===h[++n]&&h[++i]===h[++n]&&h[++i]===h[++n]&&h[++i]===h[++n]&&h[++i]===h[++n]&&h[++i]===h[++n]&&is){if(t.match_start=e,s=a,a>=o)break;c=h[i+s-1],_=h[i+s]}}}while((e=f[e&d])>l&&0!=--r);return s<=t.lookahead?s:t.lookahead}function u(t){var e,n,a,r,i,s=t.w_size;do{if(r=t.window_size-t.lookahead-t.strstart,t.strstart>=s+(s-ht)){O.arraySet(t.window,t.window,s,s,0),t.match_start-=s,t.strstart-=s,t.block_start-=s,n=t.hash_size,e=n;do{a=t.head[--e],t.head[e]=a>=s?a-s:0}while(--n);n=s,e=n;do{a=t.prev[--e],t.prev[e]=a>=s?a-s:0}while(--n);r+=s}if(0===t.strm.avail_in)break;if(n=d(t.strm,t.window,t.strstart+t.lookahead,r),t.lookahead+=n,t.lookahead+t.insert>=ot)for(i=t.strstart-t.insert,t.ins_h=t.window[i],t.ins_h=(t.ins_h<t.pending_buf_size-5&&(n=t.pending_buf_size-5);;){if(t.lookahead<=1){if(u(t),0===t.lookahead&&e===I)return bt;if(0===t.lookahead)break}t.strstart+=t.lookahead,t.lookahead=0;var a=t.block_start+n;if((0===t.strstart||t.strstart>=a)&&(t.lookahead=t.strstart-a,t.strstart=a,o(t,!1),0===t.strm.avail_out))return bt;if(t.strstart-t.block_start>=t.w_size-ht&&(o(t,!1),0===t.strm.avail_out))return bt}return t.insert=0,e===j?(o(t,!0),0===t.strm.avail_out?vt:kt):(t.strstart>t.block_start&&(o(t,!1),t.strm.avail_out),bt)}function _(t,e){for(var n,a;;){if(t.lookahead=ot&&(t.ins_h=(t.ins_h<=ot)if(a=Z._tr_tally(t,t.strstart-t.match_start,t.match_length-ot),t.lookahead-=t.match_length,t.match_length<=t.max_lazy_match&&t.lookahead>=ot){t.match_length--;do{t.strstart++,t.ins_h=(t.ins_h<=ot&&(t.ins_h=(t.ins_h<4096)&&(t.match_length=ot-1)),t.prev_length>=ot&&t.match_length<=t.prev_length){r=t.strstart+t.lookahead-ot,a=Z._tr_tally(t,t.strstart-1-t.prev_match,t.prev_length-ot),t.lookahead-=t.prev_length-1,t.prev_length-=2;do{++t.strstart<=r&&(t.ins_h=(t.ins_h<=ot&&t.strstart>0&&(r=t.strstart-1,(a=s[r])===s[++r]&&a===s[++r]&&a===s[++r])){i=t.strstart+lt;do{}while(a===s[++r]&&a===s[++r]&&a===s[++r]&&a===s[++r]&&a===s[++r]&&a===s[++r]&&a===s[++r]&&a===s[++r]&&rt.lookahead&&(t.match_length=t.lookahead)}if(t.match_length>=ot?(n=Z._tr_tally(t,1,t.match_length-ot),t.lookahead-=t.match_length,t.strstart+=t.match_length,t.match_length=0):(n=Z._tr_tally(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++),n&&(o(t,!1),0===t.strm.avail_out))return bt}return t.insert=0,e===j?(o(t,!0),0===t.strm.avail_out?vt:kt):t.last_lit&&(o(t,!1),0===t.strm.avail_out)?bt:wt}function m(t,e){for(var n;;){if(0===t.lookahead&&(u(t),0===t.lookahead)){if(e===I)return bt;break}if(t.match_length=0,n=Z._tr_tally(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++,n&&(o(t,!1),0===t.strm.avail_out))return bt}return t.insert=0,e===j?(o(t,!0),0===t.strm.avail_out?vt:kt):t.last_lit&&(o(t,!1),0===t.strm.avail_out)?bt:wt}function b(t,e,n,a,r){this.good_length=t,this.max_lazy=e,this.nice_length=n,this.max_chain=a,this.func=r}function w(t){t.window_size=2*t.w_size,i(t.head),t.max_lazy_match=E[t.level].max_lazy,t.good_match=E[t.level].good_length,t.nice_match=E[t.level].nice_length,t.max_chain_length=E[t.level].max_chain,t.strstart=0,t.block_start=0,t.lookahead=0,t.insert=0,t.match_length=t.prev_length=ot-1,t.match_available=0,t.ins_h=0}function v(){this.strm=null,this.status=0,this.pending_buf=null,this.pending_buf_size=0,this.pending_out=0,this.pending=0,this.wrap=0,this.gzhead=null,this.gzindex=0,this.method=Q,this.last_flush=-1,this.w_size=0,this.w_bits=0,this.w_mask=0,this.window=null,this.window_size=0,this.prev=null,this.head=null,this.ins_h=0,this.hash_size=0,this.hash_bits=0,this.hash_mask=0,this.hash_shift=0,this.block_start=0,this.match_length=0,this.prev_match=0,this.match_available=0,this.strstart=0,this.match_start=0,this.lookahead=0,this.prev_length=0,this.max_chain_length=0,this.max_lazy_match=0,this.level=0,this.strategy=0,this.good_match=0,this.nice_match=0,this.dyn_ltree=new O.Buf16(2*it),this.dyn_dtree=new O.Buf16(2*(2*at+1)),this.bl_tree=new O.Buf16(2*(2*rt+1)),i(this.dyn_ltree),i(this.dyn_dtree),i(this.bl_tree),this.l_desc=null,this.d_desc=null,this.bl_desc=null,this.bl_count=new O.Buf16(st+1),this.heap=new O.Buf16(2*nt+1),i(this.heap),this.heap_len=0,this.heap_max=0,this.depth=new O.Buf16(2*nt+1),i(this.depth),this.l_buf=0,this.lit_bufsize=0,this.last_lit=0,this.d_buf=0,this.opt_len=0,this.static_len=0,this.matches=0,this.insert=0,this.bi_buf=0,this.bi_valid=0}function k(t){var e;return t&&t.state?(t.total_in=t.total_out=0,t.data_type=J,e=t.state,e.pending=0,e.pending_out=0,e.wrap<0&&(e.wrap=-e.wrap),e.status=e.wrap?ft:pt,t.adler=2===e.wrap?0:1,e.last_flush=I,Z._tr_init(e),L):a(t,M)}function y(t){var e=k(t);return e===L&&w(t.state),e}function x(t,e){return t&&t.state?2!==t.state.wrap?M:(t.state.gzhead=e,L):M}function z(t,e,n,r,i,s){if(!t)return M;var o=1;if(e===K&&(e=6),r<0?(o=0,r=-r):r>15&&(o=2,r-=16),i<1||i>V||n!==Q||r<8||r>15||e<0||e>9||s<0||s>G)return a(t,M);8===r&&(r=9);var l=new v;return t.state=l,l.strm=t,l.wrap=o,l.gzhead=null,l.w_bits=r,l.w_size=1<F||e<0)return t?a(t,M):M;if(o=t.state,!t.output||!t.input&&0!==t.avail_in||o.status===mt&&e!==j)return a(t,0===t.avail_out?W:M);if(o.strm=t,n=o.last_flush,o.last_flush=e,o.status===ft)if(2===o.wrap)t.adler=0,l(o,31),l(o,139),l(o,8),o.gzhead?(l(o,(o.gzhead.text?1:0)+(o.gzhead.hcrc?2:0)+(o.gzhead.extra?4:0)+(o.gzhead.name?8:0)+(o.gzhead.comment?16:0)),l(o,255&o.gzhead.time),l(o,o.gzhead.time>>8&255),l(o,o.gzhead.time>>16&255),l(o,o.gzhead.time>>24&255),l(o,9===o.level?2:o.strategy>=$||o.level<2?4:0),l(o,255&o.gzhead.os),o.gzhead.extra&&o.gzhead.extra.length&&(l(o,255&o.gzhead.extra.length),l(o,o.gzhead.extra.length>>8&255)),o.gzhead.hcrc&&(t.adler=R(t.adler,o.pending_buf,o.pending,0)),o.gzindex=0,o.status=ut):(l(o,0),l(o,0),l(o,0),l(o,0),l(o,0),l(o,9===o.level?2:o.strategy>=$||o.level<2?4:0),l(o,yt),o.status=pt);else{var u=Q+(o.w_bits-8<<4)<<8,c=-1;c=o.strategy>=$||o.level<2?0:o.level<6?1:6===o.level?2:3,u|=c<<6,0!==o.strstart&&(u|=dt),u+=31-u%31,o.status=pt,h(o,u),0!==o.strstart&&(h(o,t.adler>>>16),h(o,65535&t.adler)),t.adler=1}if(o.status===ut)if(o.gzhead.extra){for(d=o.pending;o.gzindex<(65535&o.gzhead.extra.length)&&(o.pending!==o.pending_buf_size||(o.gzhead.hcrc&&o.pending>d&&(t.adler=R(t.adler,o.pending_buf,o.pending-d,d)),s(t),d=o.pending,o.pending!==o.pending_buf_size));)l(o,255&o.gzhead.extra[o.gzindex]),o.gzindex++;o.gzhead.hcrc&&o.pending>d&&(t.adler=R(t.adler,o.pending_buf,o.pending-d,d)),o.gzindex===o.gzhead.extra.length&&(o.gzindex=0,o.status=ct)}else o.status=ct;if(o.status===ct)if(o.gzhead.name){d=o.pending;do{if(o.pending===o.pending_buf_size&&(o.gzhead.hcrc&&o.pending>d&&(t.adler=R(t.adler,o.pending_buf,o.pending-d,d)),s(t),d=o.pending,o.pending===o.pending_buf_size)){f=1;break}f=o.gzindexd&&(t.adler=R(t.adler,o.pending_buf,o.pending-d,d)),0===f&&(o.gzindex=0,o.status=_t)}else o.status=_t;if(o.status===_t)if(o.gzhead.comment){d=o.pending;do{if(o.pending===o.pending_buf_size&&(o.gzhead.hcrc&&o.pending>d&&(t.adler=R(t.adler,o.pending_buf,o.pending-d,d)),s(t),d=o.pending,o.pending===o.pending_buf_size)){f=1;break}f=o.gzindexd&&(t.adler=R(t.adler,o.pending_buf,o.pending-d,d)),0===f&&(o.status=gt)}else o.status=gt;if(o.status===gt&&(o.gzhead.hcrc?(o.pending+2>o.pending_buf_size&&s(t),o.pending+2<=o.pending_buf_size&&(l(o,255&t.adler),l(o,t.adler>>8&255),t.adler=0,o.status=pt)):o.status=pt),0!==o.pending){if(s(t),0===t.avail_out)return o.last_flush=-1,L}else if(0===t.avail_in&&r(e)<=r(n)&&e!==j)return a(t,W);if(o.status===mt&&0!==t.avail_in)return a(t,W);if(0!==t.avail_in||0!==o.lookahead||e!==I&&o.status!==mt){var _=o.strategy===$?m(o,e):o.strategy===q?p(o,e):E[o.level].func(o,e);if(_!==vt&&_!==kt||(o.status=mt),_===bt||_===vt)return 0===t.avail_out&&(o.last_flush=-1),L;if(_===wt&&(e===U?Z._tr_align(o):e!==F&&(Z._tr_stored_block(o,0,0,!1),e===T&&(i(o.head),0===o.lookahead&&(o.strstart=0,o.block_start=0,o.insert=0))),s(t),0===t.avail_out))return o.last_flush=-1,L}return e!==j?L:o.wrap<=0?H:(2===o.wrap?(l(o,255&t.adler),l(o,t.adler>>8&255),l(o,t.adler>>16&255),l(o,t.adler>>24&255),l(o,255&t.total_in),l(o,t.total_in>>8&255),l(o,t.total_in>>16&255),l(o,t.total_in>>24&255)):(h(o,t.adler>>>16),h(o,65535&t.adler)),s(t),o.wrap>0&&(o.wrap=-o.wrap),0!==o.pending?L:H)}function A(t){var e;return t&&t.state?(e=t.state.status)!==ft&&e!==ut&&e!==ct&&e!==_t&&e!==gt&&e!==pt&&e!==mt?a(t,M):(t.state=null,e===pt?a(t,P):L):M}function S(t,e){var n,a,r,s,o,l,h,d,f=e.length;if(!t||!t.state)return M;if(n=t.state,2===(s=n.wrap)||1===s&&n.status!==ft||n.lookahead)return M;for(1===s&&(t.adler=N(t.adler,e,f,0)),n.wrap=0,f>=n.w_size&&(0===s&&(i(n.head),n.strstart=0,n.block_start=0,n.insert=0),d=new O.Buf8(n.w_size),O.arraySet(d,e,f-n.w_size,n.w_size,0),e=d,f=n.w_size),o=t.avail_in,l=t.next_in,h=t.input,t.avail_in=f,t.next_in=0,t.input=e,u(n);n.lookahead>=ot;){a=n.strstart,r=n.lookahead-(ot-1);do{n.ins_h=(n.ins_h<=0;)t[e]=0}function r(t,e,n,a,r){this.static_tree=t,this.extra_bits=e,this.extra_base=n,this.elems=a,this.max_length=r,this.has_stree=t&&t.length}function i(t,e){this.dyn_tree=t,this.max_code=0,this.stat_desc=e}function s(t){return t<256?it[t]:it[256+(t>>>7)]}function o(t,e){t.pending_buf[t.pending++]=255&e,t.pending_buf[t.pending++]=e>>>8&255}function l(t,e,n){t.bi_valid>$-n?(t.bi_buf|=e<>$-t.bi_valid,t.bi_valid+=n-$):(t.bi_buf|=e<>>=1,n<<=1}while(--e>0);return n>>>1}function f(t){16===t.bi_valid?(o(t,t.bi_buf),t.bi_buf=0,t.bi_valid=0):t.bi_valid>=8&&(t.pending_buf[t.pending++]=255&t.bi_buf,t.bi_buf>>=8,t.bi_valid-=8)}function u(t,e){var n,a,r,i,s,o,l=e.dyn_tree,h=e.max_code,d=e.stat_desc.static_tree,f=e.stat_desc.has_stree,u=e.stat_desc.extra_bits,c=e.stat_desc.extra_base,_=e.stat_desc.max_length,g=0;for(i=0;i<=Y;i++)t.bl_count[i]=0;for(l[2*t.heap[t.heap_max]+1]=0,n=t.heap_max+1;n_&&(i=_,g++),l[2*a+1]=i,a>h||(t.bl_count[i]++,s=0,a>=c&&(s=u[a-c]),o=l[2*a],t.opt_len+=o*(i+s),f&&(t.static_len+=o*(d[2*a+1]+s)));if(0!==g){do{for(i=_-1;0===t.bl_count[i];)i--;t.bl_count[i]--,t.bl_count[i+1]+=2,t.bl_count[_]--,g-=2}while(g>0);for(i=_;0!==i;i--)for(a=t.bl_count[i];0!==a;)(r=t.heap[--n])>h||(l[2*r+1]!==i&&(t.opt_len+=(i-l[2*r+1])*l[2*r],l[2*r+1]=i),a--)}}function c(t,e,n){var a,r,i=new Array(Y+1),s=0;for(a=1;a<=Y;a++)i[a]=s=s+n[a-1]<<1;for(r=0;r<=e;r++){var o=t[2*r+1];0!==o&&(t[2*r]=d(i[o]++,o))}}function _(){var t,e,n,a,i,s=new Array(Y+1);for(n=0,a=0;a>=7;a8?o(t,t.bi_buf):t.bi_valid>0&&(t.pending_buf[t.pending++]=t.bi_buf),t.bi_buf=0,t.bi_valid=0}function m(t,e,n,a){p(t),a&&(o(t,n),o(t,~n)),N.arraySet(t.pending_buf,t.window,e,n,t.pending),t.pending+=n}function b(t,e,n,a){var r=2*e,i=2*n;return t[r]>1;n>=1;n--)w(t,i,n);r=l;do{n=t.heap[1],t.heap[1]=t.heap[t.heap_len--],w(t,i,1),a=t.heap[1],t.heap[--t.heap_max]=n,t.heap[--t.heap_max]=a,i[2*r]=i[2*n]+i[2*a],t.depth[r]=(t.depth[n]>=t.depth[a]?t.depth[n]:t.depth[a])+1,i[2*n+1]=i[2*a+1]=r,t.heap[1]=r++,w(t,i,1)}while(t.heap_len>=2);t.heap[--t.heap_max]=t.heap[1],u(t,e),c(i,h,t.bl_count)}function y(t,e,n){var a,r,i=-1,s=e[1],o=0,l=7,h=4;for(0===s&&(l=138,h=3),e[2*(n+1)+1]=65535,a=0;a<=n;a++)r=s,s=e[2*(a+1)+1],++o=3&&0===t.bl_tree[2*nt[e]+1];e--);return t.opt_len+=3*(e+1)+5+5+4,e}function B(t,e,n,a){var r;for(l(t,e-257,5),l(t,n-1,5),l(t,a-4,4),r=0;r>>=1)if(1&n&&0!==t.dyn_ltree[2*e])return D;if(0!==t.dyn_ltree[18]||0!==t.dyn_ltree[20]||0!==t.dyn_ltree[26])return I;for(e=32;e0?(t.strm.data_type===U&&(t.strm.data_type=C(t)),k(t,t.l_desc),k(t,t.d_desc),s=z(t),r=t.opt_len+3+7>>>3,(i=t.static_len+3+7>>>3)<=r&&(r=i)):r=i=n+5,n+4<=r&&-1!==e?S(t,e,n,a):t.strategy===R||i===r?(l(t,(j<<1)+(a?1:0),3),v(t,at,rt)):(l(t,(F<<1)+(a?1:0),3),B(t,t.l_desc.max_code+1,t.d_desc.max_code+1,s+1),v(t,t.dyn_ltree,t.dyn_dtree)),g(t),a&&p(t)}function Z(t,e,n){return t.pending_buf[t.d_buf+2*t.last_lit]=e>>>8&255,t.pending_buf[t.d_buf+2*t.last_lit+1]=255&e,t.pending_buf[t.l_buf+t.last_lit]=255&n,t.last_lit++,0===e?t.dyn_ltree[2*n]++:(t.matches++,e--,t.dyn_ltree[2*(st[n]+H+1)]++,t.dyn_dtree[2*s(e)]++),t.last_lit===t.lit_bufsize-1}var N=t("../utils/common"),R=4,D=0,I=1,U=2,T=0,j=1,F=2,L=29,H=256,M=H+1+L,P=30,W=19,K=2*M+1,Y=15,$=16,q=7,G=256,X=16,J=17,Q=18,V=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0],tt=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],et=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],nt=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],at=new Array(2*(M+2));a(at);var rt=new Array(2*P);a(rt);var it=new Array(512);a(it);var st=new Array(256);a(st);var ot=new Array(L);a(ot);var lt=new Array(P);a(lt);var ht,dt,ft,ut=!1;n._tr_init=A,n._tr_stored_block=S,n._tr_flush_block=O,n._tr_tally=Z,n._tr_align=E},{"../utils/common":5}],12:[function(t,e,n){"use strict";function a(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}e.exports=a},{}]},{},[3])(3)})},function(t,e,n){var a,a;!function(e){t.exports=e()}(function(){return function(){function t(e,n,r){function i(o,l){if(!n[o]){if(!e[o]){var h="function"==typeof a&&a;if(!l&&h)return a(o,!0);if(s)return s(o,!0);var d=new Error("Cannot find module '"+o+"'");throw d.code="MODULE_NOT_FOUND",d}var f=n[o]={exports:{}};e[o][0].call(f.exports,function(t){return i(e[o][1][t]||t)},f,f.exports,t,e,n,r)}return n[o].exports}for(var s="function"==typeof a&&a,o=0;o=97?e-61:e>=65?e-55:e>=48?e-48:"?"}function r(t){var e=a(t[0]),n=a(t[1]),r=a(t[2]);return[e<<2|n>>4&63,n<<4&240|r>>2&15,r<<6&192|63&a(t[3])]}e.exports=function(t){var e="",n=0;for(n=0;n=0&&e.windowBits<16&&(e.windowBits=-e.windowBits,0===e.windowBits&&(e.windowBits=-15)),!(e.windowBits>=0&&e.windowBits<16)||t&&t.windowBits||(e.windowBits+=32),e.windowBits>15&&e.windowBits<48&&0==(15&e.windowBits)&&(e.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new f,this.strm.avail_out=0;var n=s.inflateInit2(this.strm,e.windowBits);if(n!==h.Z_OK)throw new Error(d[n]);if(this.header=new u,s.inflateGetHeader(this.strm,this.header),e.dictionary&&("string"==typeof e.dictionary?e.dictionary=l.string2buf(e.dictionary):"[object ArrayBuffer]"===c.call(e.dictionary)&&(e.dictionary=new Uint8Array(e.dictionary)),e.raw&&(n=s.inflateSetDictionary(this.strm,e.dictionary))!==h.Z_OK))throw new Error(d[n])}function r(t,e){var n=new a(e);if(n.push(t,!0),n.err)throw n.msg||d[n.err];return n.result}function i(t,e){return e=e||{},e.raw=!0,r(t,e)}var s=t("./zlib/inflate"),o=t("./utils/common"),l=t("./utils/strings"),h=t("./zlib/constants"),d=t("./zlib/messages"),f=t("./zlib/zstream"),u=t("./zlib/gzheader"),c=Object.prototype.toString;a.prototype.push=function(t,e){var n,a,r,i,d,f=this.strm,u=this.options.chunkSize,_=this.options.dictionary,g=!1;if(this.ended)return!1;a=e===~~e?e:!0===e?h.Z_FINISH:h.Z_NO_FLUSH,"string"==typeof t?f.input=l.binstring2buf(t):"[object ArrayBuffer]"===c.call(t)?f.input=new Uint8Array(t):f.input=t,f.next_in=0,f.avail_in=f.input.length;do{if(0===f.avail_out&&(f.output=new o.Buf8(u),f.next_out=0,f.avail_out=u),n=s.inflate(f,h.Z_NO_FLUSH),n===h.Z_NEED_DICT&&_&&(n=s.inflateSetDictionary(this.strm,_)),n===h.Z_BUF_ERROR&&!0===g&&(n=h.Z_OK,g=!1),n!==h.Z_STREAM_END&&n!==h.Z_OK)return this.onEnd(n),this.ended=!0,!1;f.next_out&&(0!==f.avail_out&&n!==h.Z_STREAM_END&&(0!==f.avail_in||a!==h.Z_FINISH&&a!==h.Z_SYNC_FLUSH)||("string"===this.options.to?(r=l.utf8border(f.output,f.next_out),i=f.next_out-r,d=l.buf2string(f.output,r),f.next_out=i,f.avail_out=u-i,i&&o.arraySet(f.output,f.output,r,i,0),this.onData(d)):this.onData(o.shrinkBuf(f.output,f.next_out)))),0===f.avail_in&&0===f.avail_out&&(g=!0)}while((f.avail_in>0||0===f.avail_out)&&n!==h.Z_STREAM_END);return n===h.Z_STREAM_END&&(a=h.Z_FINISH),a===h.Z_FINISH?(n=s.inflateEnd(this.strm),this.onEnd(n),this.ended=!0,n===h.Z_OK):a!==h.Z_SYNC_FLUSH||(this.onEnd(h.Z_OK),f.avail_out=0,!0)},a.prototype.onData=function(t){this.chunks.push(t)},a.prototype.onEnd=function(t){t===h.Z_OK&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=o.flattenChunks(this.chunks)),this.chunks=[],this.err=t,this.msg=this.strm.msg},n.Inflate=a,n.inflate=r,n.inflateRaw=i,n.ungzip=r},{"./utils/common":5,"./utils/strings":6,"./zlib/constants":8,"./zlib/gzheader":10,"./zlib/inflate":12,"./zlib/messages":14,"./zlib/zstream":15}],5:[function(t,e,n){"use strict";function a(t,e){return Object.prototype.hasOwnProperty.call(t,e)}var r="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Int32Array;n.assign=function(t){for(var e=Array.prototype.slice.call(arguments,1);e.length;){var n=e.shift();if(n){if("object"!=typeof n)throw new TypeError(n+"must be non-object");for(var r in n)a(n,r)&&(t[r]=n[r])}}return t},n.shrinkBuf=function(t,e){return t.length===e?t:t.subarray?t.subarray(0,e):(t.length=e,t)};var i={arraySet:function(t,e,n,a,r){if(e.subarray&&t.subarray)return void t.set(e.subarray(n,n+a),r);for(var i=0;i=252?6:l>=248?5:l>=240?4:l>=224?3:l>=192?2:1;o[254]=o[254]=1,n.string2buf=function(t){var e,n,a,i,s,o=t.length,l=0;for(i=0;i>>6,e[s++]=128|63&n):n<65536?(e[s++]=224|n>>>12,e[s++]=128|n>>>6&63,e[s++]=128|63&n):(e[s++]=240|n>>>18,e[s++]=128|n>>>12&63,e[s++]=128|n>>>6&63,e[s++]=128|63&n);return e},n.buf2binstring=function(t){return a(t,t.length)},n.binstring2buf=function(t){for(var e=new r.Buf8(t.length),n=0,a=e.length;n4)h[r++]=65533,n+=s-1;else{for(i&=2===s?31:3===s?15:7;s>1&&n1?h[r++]=65533:i<65536?h[r++]=i:(i-=65536,h[r++]=55296|i>>10&1023,h[r++]=56320|1023&i)}return a(h,r)},n.utf8border=function(t,e){var n;for(e=e||t.length,e>t.length&&(e=t.length),n=e-1;n>=0&&128==(192&t[n]);)n--;return n<0?e:0===n?e:n+o[t[n]]>e?n:e}},{"./common":5}],7:[function(t,e,n){"use strict";function a(t,e,n,a){for(var r=65535&t|0,i=t>>>16&65535|0,s=0;0!==n;){s=n>2e3?2e3:n,n-=s;do{r=r+e[a++]|0,i=i+r|0}while(--s);r%=65521,i%=65521}return r|i<<16|0}e.exports=a},{}],8:[function(t,e,n){"use strict";e.exports={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8}},{}],9:[function(t,e,n){"use strict";function a(t,e,n,a){var i=r,s=a+n;t^=-1;for(var o=a;o>>8^i[255&(t^e[o])];return-1^t}var r=function(){for(var t,e=[],n=0;n<256;n++){t=n;for(var a=0;a<8;a++)t=1&t?3988292384^t>>>1:t>>>1;e[n]=t}return e}();e.exports=a},{}],10:[function(t,e,n){"use strict";function a(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1}e.exports=a},{}],11:[function(t,e,n){"use strict";e.exports=function(t,e){var n,a,r,i,s,o,l,h,d,f,u,c,_,g,p,m,b,w,v,k,y,x,z,B,C;n=t.state,a=t.next_in,B=t.input,r=a+(t.avail_in-5),i=t.next_out,C=t.output,s=i-(e-t.avail_out),o=i+(t.avail_out-257),l=n.dmax,h=n.wsize,d=n.whave,f=n.wnext,u=n.window,c=n.hold,_=n.bits,g=n.lencode,p=n.distcode,m=(1<>>24,c>>>=v,_-=v,0===(v=w>>>16&255))C[i++]=65535&w;else{if(!(16&v)){if(0==(64&v)){w=g[(65535&w)+(c&(1<>>=v,_-=v),_<15&&(c+=B[a++]<<_,_+=8,c+=B[a++]<<_,_+=8),w=p[c&b];n:for(;;){if(v=w>>>24,c>>>=v,_-=v,!(16&(v=w>>>16&255))){if(0==(64&v)){w=p[(65535&w)+(c&(1<l){t.msg="invalid distance too far back",n.mode=30;break t}if(c>>>=v,_-=v,v=i-s,y>v){if((v=y-v)>d&&n.sane){t.msg="invalid distance too far back",n.mode=30;break t}if(x=0,z=u,0===f){if(x+=h-v,v2;)C[i++]=z[x++],C[i++]=z[x++],C[i++]=z[x++],k-=3;k&&(C[i++]=z[x++],k>1&&(C[i++]=z[x++]))}else{x=i-y;do{C[i++]=C[x++],C[i++]=C[x++],C[i++]=C[x++],k-=3}while(k>2);k&&(C[i++]=C[x++],k>1&&(C[i++]=C[x++]))}break}}break}}while(a>3,a-=k,_-=k<<3,c&=(1<<_)-1,t.next_in=a,t.next_out=i,t.avail_in=a>>24&255)+(t>>>8&65280)+((65280&t)<<8)+((255&t)<<24)}function r(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new b.Buf16(320),this.work=new b.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function i(t){var e;return t&&t.state?(e=t.state,t.total_in=t.total_out=e.total=0,t.msg="",e.wrap&&(t.adler=1&e.wrap),e.mode=T,e.last=0,e.havedict=0,e.dmax=32768,e.head=null,e.hold=0,e.bits=0,e.lencode=e.lendyn=new b.Buf32(gt),e.distcode=e.distdyn=new b.Buf32(pt),e.sane=1,e.back=-1,E):N}function s(t){var e;return t&&t.state?(e=t.state,e.wsize=0,e.whave=0,e.wnext=0,i(t)):N}function o(t,e){var n,a;return t&&t.state?(a=t.state,e<0?(n=0,e=-e):(n=1+(e>>4),e<48&&(e&=15)),e&&(e<8||e>15)?N:(null!==a.window&&a.wbits!==e&&(a.window=null),a.wrap=n,a.wbits=e,s(t))):N}function l(t,e){var n,a;return t?(a=new r,t.state=a,a.window=null,n=o(t,e),n!==E&&(t.state=null),n):N}function h(t){return l(t,mt)}function d(t){if(bt){var e;for(p=new b.Buf32(512),m=new b.Buf32(32),e=0;e<144;)t.lens[e++]=8;for(;e<256;)t.lens[e++]=9;for(;e<280;)t.lens[e++]=7;for(;e<288;)t.lens[e++]=8;for(y(z,t.lens,0,288,p,0,t.work,{bits:9}),e=0;e<32;)t.lens[e++]=5;y(B,t.lens,0,32,m,0,t.work,{bits:5}),bt=!1}t.lencode=p,t.lenbits=9,t.distcode=m,t.distbits=5}function f(t,e,n,a){var r,i=t.state;return null===i.window&&(i.wsize=1<=i.wsize?(b.arraySet(i.window,e,n-i.wsize,i.wsize,0),i.wnext=0,i.whave=i.wsize):(r=i.wsize-i.wnext,r>a&&(r=a),b.arraySet(i.window,e,n-a,r,i.wnext),a-=r,a?(b.arraySet(i.window,e,n-a,a,0),i.wnext=a,i.whave=i.wsize):(i.wnext+=r,i.wnext===i.wsize&&(i.wnext=0),i.whave>>8&255,n.check=v(n.check,At,2,0),u=0,c=0,n.mode=j;break}if(n.flags=0,n.head&&(n.head.done=!1),!(1&n.wrap)||(((255&u)<<8)+(u>>8))%31){t.msg="incorrect header check",n.mode=ut;break}if((15&u)!==U){t.msg="unknown compression method",n.mode=ut;break}if(u>>>=4,c-=4,yt=8+(15&u),0===n.wbits)n.wbits=yt;else if(yt>n.wbits){t.msg="invalid window size",n.mode=ut;break}n.dmax=1<>8&1),512&n.flags&&(At[0]=255&u,At[1]=u>>>8&255,n.check=v(n.check,At,2,0)),u=0,c=0,n.mode=F;case F:for(;c<32;){if(0===l)break t;l--,u+=r[s++]<>>8&255,At[2]=u>>>16&255,At[3]=u>>>24&255,n.check=v(n.check,At,4,0)),u=0,c=0,n.mode=L;case L:for(;c<16;){if(0===l)break t;l--,u+=r[s++]<>8),512&n.flags&&(At[0]=255&u,At[1]=u>>>8&255,n.check=v(n.check,At,2,0)),u=0,c=0,n.mode=H;case H:if(1024&n.flags){for(;c<16;){if(0===l)break t;l--,u+=r[s++]<>>8&255,n.check=v(n.check,At,2,0)),u=0,c=0}else n.head&&(n.head.extra=null);n.mode=M;case M:if(1024&n.flags&&(p=n.length,p>l&&(p=l),p&&(n.head&&(yt=n.head.extra_len-n.length,n.head.extra||(n.head.extra=new Array(n.head.extra_len)),b.arraySet(n.head.extra,r,s,p,yt)),512&n.flags&&(n.check=v(n.check,r,p,s)),l-=p,s+=p,n.length-=p),n.length))break t;n.length=0,n.mode=P;case P:if(2048&n.flags){if(0===l)break t;p=0;do{yt=r[s+p++],n.head&&yt&&n.length<65536&&(n.head.name+=String.fromCharCode(yt))}while(yt&&p>9&1,n.head.done=!0),t.adler=n.check=0,n.mode=q;break;case Y:for(;c<32;){if(0===l)break t;l--,u+=r[s++]<>>=7&c,c-=7&c,n.mode=ht;break}for(;c<3;){if(0===l)break t;l--,u+=r[s++]<>>=1,c-=1,3&u){case 0:n.mode=X;break;case 1:if(d(n),n.mode=nt,e===S){u>>>=2,c-=2;break t}break;case 2:n.mode=V;break;case 3:t.msg="invalid block type",n.mode=ut}u>>>=2,c-=2;break;case X:for(u>>>=7&c,c-=7&c;c<32;){if(0===l)break t;l--,u+=r[s++]<>>16^65535)){t.msg="invalid stored block lengths",n.mode=ut;break}if(n.length=65535&u,u=0,c=0,n.mode=J,e===S)break t;case J:n.mode=Q;case Q:if(p=n.length){if(p>l&&(p=l),p>h&&(p=h),0===p)break t;b.arraySet(i,r,s,p,o),l-=p,s+=p,h-=p,o+=p,n.length-=p;break}n.mode=q;break;case V:for(;c<14;){if(0===l)break t;l--,u+=r[s++]<>>=5,c-=5,n.ndist=1+(31&u),u>>>=5,c-=5,n.ncode=4+(15&u),u>>>=4,c-=4,n.nlen>286||n.ndist>30){t.msg="too many length or distance symbols",n.mode=ut;break}n.have=0,n.mode=tt;case tt:for(;n.have>>=3,c-=3}for(;n.have<19;)n.lens[St[n.have++]]=0;if(n.lencode=n.lendyn,n.lenbits=7,zt={bits:n.lenbits},xt=y(x,n.lens,0,19,n.lencode,0,n.work,zt),n.lenbits=zt.bits,xt){t.msg="invalid code lengths set",n.mode=ut;break}n.have=0,n.mode=et;case et:for(;n.have>>24,mt=Ct>>>16&255,bt=65535&Ct,!(pt<=c);){if(0===l)break t;l--,u+=r[s++]<>>=pt,c-=pt,n.lens[n.have++]=bt;else{if(16===bt){for(Bt=pt+2;c>>=pt,c-=pt,0===n.have){t.msg="invalid bit length repeat",n.mode=ut;break}yt=n.lens[n.have-1],p=3+(3&u),u>>>=2,c-=2}else if(17===bt){for(Bt=pt+3;c>>=pt,c-=pt,yt=0,p=3+(7&u),u>>>=3,c-=3}else{for(Bt=pt+7;c>>=pt,c-=pt,yt=0,p=11+(127&u),u>>>=7,c-=7}if(n.have+p>n.nlen+n.ndist){t.msg="invalid bit length repeat",n.mode=ut;break}for(;p--;)n.lens[n.have++]=yt}}if(n.mode===ut)break;if(0===n.lens[256]){t.msg="invalid code -- missing end-of-block",n.mode=ut;break}if(n.lenbits=9,zt={bits:n.lenbits},xt=y(z,n.lens,0,n.nlen,n.lencode,0,n.work,zt),n.lenbits=zt.bits,xt){t.msg="invalid literal/lengths set",n.mode=ut;break}if(n.distbits=6,n.distcode=n.distdyn,zt={bits:n.distbits},xt=y(B,n.lens,n.nlen,n.ndist,n.distcode,0,n.work,zt),n.distbits=zt.bits,xt){t.msg="invalid distances set",n.mode=ut;break}if(n.mode=nt,e===S)break t;case nt:n.mode=at;case at:if(l>=6&&h>=258){t.next_out=o,t.avail_out=h,t.next_in=s,t.avail_in=l,n.hold=u,n.bits=c,k(t,g),o=t.next_out,i=t.output,h=t.avail_out,s=t.next_in,r=t.input,l=t.avail_in,u=n.hold,c=n.bits,n.mode===q&&(n.back=-1);break}for(n.back=0;Ct=n.lencode[u&(1<>>24,mt=Ct>>>16&255,bt=65535&Ct,!(pt<=c);){if(0===l)break t;l--,u+=r[s++]<>wt)],pt=Ct>>>24,mt=Ct>>>16&255,bt=65535&Ct,!(wt+pt<=c);){if(0===l)break t;l--,u+=r[s++]<>>=wt,c-=wt,n.back+=wt}if(u>>>=pt,c-=pt,n.back+=pt,n.length=bt,0===mt){n.mode=lt;break}if(32&mt){n.back=-1,n.mode=q;break}if(64&mt){t.msg="invalid literal/length code",n.mode=ut;break}n.extra=15&mt,n.mode=rt;case rt:if(n.extra){for(Bt=n.extra;c>>=n.extra,c-=n.extra,n.back+=n.extra}n.was=n.length,n.mode=it;case it:for(;Ct=n.distcode[u&(1<>>24,mt=Ct>>>16&255,bt=65535&Ct,!(pt<=c);){if(0===l)break t;l--,u+=r[s++]<>wt)],pt=Ct>>>24,mt=Ct>>>16&255,bt=65535&Ct,!(wt+pt<=c);){if(0===l)break t;l--,u+=r[s++]<>>=wt,c-=wt,n.back+=wt}if(u>>>=pt,c-=pt,n.back+=pt,64&mt){t.msg="invalid distance code",n.mode=ut;break}n.offset=bt,n.extra=15&mt,n.mode=st;case st:if(n.extra){for(Bt=n.extra;c>>=n.extra,c-=n.extra,n.back+=n.extra}if(n.offset>n.dmax){t.msg="invalid distance too far back",n.mode=ut;break}n.mode=ot;case ot:if(0===h)break t;if(p=g-h,n.offset>p){if((p=n.offset-p)>n.whave&&n.sane){t.msg="invalid distance too far back",n.mode=ut;break}p>n.wnext?(p-=n.wnext,m=n.wsize-p):m=n.wnext-p,p>n.length&&(p=n.length),gt=n.window}else gt=i,m=o-n.offset,p=n.length;p>h&&(p=h),h-=p,n.length-=p;do{i[o++]=gt[m++]}while(--p);0===n.length&&(n.mode=at);break;case lt:if(0===h)break t;i[o++]=n.length,h--,n.mode=at;break;case ht:if(n.wrap){for(;c<32;){if(0===l)break t;l--,u|=r[s++]<=1&&0===I[C];C--);if(A>C&&(A=C),0===C)return h[d++]=20971520,h[d++]=20971520,u.bits=1,0;for(B=1;B0&&(0===t||1!==C))return-1;for(U[1]=0,x=1;x<15;x++)U[x+1]=U[x]+I[x];for(z=0;z852||2===t&&Z>592)return 1;for(;;){w=x-E,f[z]b?(v=T[j+f[z]],k=R[D+f[z]]):(v=96,k=0),c=1<>E)+_]=w<<24|v<<16|k|0}while(0!==_);for(c=1<>=1;if(0!==c?(N&=c-1,N+=c):N=0,z++,0==--I[x]){if(x===C)break;x=e[n+f[z]]}if(x>A&&(N&p)!==g){for(0===E&&(E=A),m+=B,S=x-E,O=1<852||2===t&&Z>592)return 1;g=N&p,h[g]=A<<24|S<<16|m-d|0}}return 0!==N&&(h[m+N]=x-E<<24|64<<16|0),u.bits=A,0}},{"../utils/common":5}],14:[function(t,e,n){"use strict";e.exports={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"}},{}],15:[function(t,e,n){"use strict";function a(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}e.exports=a},{}]},{},[3])(3)})}]); \ No newline at end of file diff --git a/doc/lib/js/docsify@4.js b/doc/lib/js/docsify@4.js new file mode 100644 index 000000000..76fc75557 --- /dev/null +++ b/doc/lib/js/docsify@4.js @@ -0,0 +1 @@ +!function(){function c(i){var o=Object.create(null);return function(e){var n=f(e)?e:JSON.stringify(e);return o[n]||(o[n]=i(e))}}var a=c(function(e){return e.replace(/([A-Z])/g,function(e){return"-"+e.toLowerCase()})}),u=Object.prototype.hasOwnProperty,m=Object.assign||function(e){for(var n=arguments,i=1;i=e||n.classList.contains("hidden")?S(h,"add","sticky"):S(h,"remove","sticky"))}function ee(e,n,o,i){var t=[];null!=(n=l(n))&&(t=k(n,"a"));var a,r=decodeURI(e.toURL(e.getCurrentPath()));return t.sort(function(e,n){return n.href.length-e.href.length}).forEach(function(e){var n=decodeURI(e.getAttribute("href")),i=o?e.parentNode:e;e.title=e.title||e.innerText,0!==r.indexOf(n)||a?S(i,"remove","active"):(a=e,S(i,"add","active"))}),i&&(v.title=a?a.title||a.innerText+" - "+J:J),a}function ne(e,n){for(var i=0;ithis.end&&e>=this.next}[this.direction]}},{key:"_defaultEase",value:function(e,n,i,o){return(e/=o/2)<1?i/2*e*e+n:-i/2*(--e*(e-2)-1)+n}}]),re);function re(){var e=0c){n=n||p;break}n=p}!n||(r=fe[ve(e,n.getAttribute("data-id"))])&&r!==a&&(a&&a.classList.remove("active"),r.classList.add("active"),a=r,!pe&&h.classList.contains("sticky")&&(e=i.clientHeight,r=a.offsetTop+a.clientHeight+40,a=a.offsetTop>=t.scrollTop&&r<=t.scrollTop+e,i.scrollTop=a?t.scrollTop:+r"']/),xe=/[&<>"']/g,Se=/[<>"']|&(?!#?\w+;)/,Ae=/[<>"']|&(?!#?\w+;)/g,$e={"&":"&","<":"<",">":">",'"':""","'":"'"};var ze=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi;function Fe(e){return e.replace(ze,function(e,n){return"colon"===(n=n.toLowerCase())?":":"#"===n.charAt(0)?"x"===n.charAt(1)?String.fromCharCode(parseInt(n.substring(2),16)):String.fromCharCode(+n.substring(1)):""})}var Ee=/(^|[^\[])\^/g;var Te=/[^\w:]/g,Ce=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;var Re={},je=/^[^:]+:\/*[^/]*$/,Oe=/^([^:]+:)[\s\S]*$/,Le=/^([^:]+:\/*[^/]*)[\s\S]*$/;function qe(e,n){Re[" "+e]||(je.test(e)?Re[" "+e]=e+"/":Re[" "+e]=Pe(e,"/",!0));var i=-1===(e=Re[" "+e]).indexOf(":");return"//"===n.substring(0,2)?i?n:e.replace(Oe,"$1")+n:"/"===n.charAt(0)?i?n:e.replace(Le,"$1")+n:e+n}function Pe(e,n,i){var o=e.length;if(0===o)return"";for(var t=0;tn)i.splice(n);else for(;i.length>=1,e+=e;return i+e},We=we.defaults,Xe=Be,Qe=Ze,Je=Me,Ke=Ve;function en(e,n,i){var o=n.href,t=n.title?Je(n.title):null,n=e[1].replace(/\\([\[\]])/g,"$1");return"!"!==e[0].charAt(0)?{type:"link",raw:i,href:o,title:t,text:n}:{type:"image",raw:i,href:o,title:t,text:Je(n)}}var nn=function(){function e(e){this.options=e||We}return e.prototype.space=function(e){e=this.rules.block.newline.exec(e);if(e)return 1=i.length?e.slice(i.length):e}).join("\n")}(i,n[3]||"");return{type:"code",raw:i,lang:n[2]&&n[2].trim(),text:e}}},e.prototype.heading=function(e){var n=this.rules.block.heading.exec(e);if(n){var i=n[2].trim();return/#$/.test(i)&&(e=Xe(i,"#"),!this.options.pedantic&&e&&!/ $/.test(e)||(i=e.trim())),{type:"heading",raw:n[0],depth:n[1].length,text:i}}},e.prototype.nptable=function(e){e=this.rules.block.nptable.exec(e);if(e){var n={type:"table",header:Qe(e[1].replace(/^ *| *\| *$/g,"")),align:e[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:e[3]?e[3].replace(/\n$/,"").split("\n"):[],raw:e[0]};if(n.header.length===n.align.length){for(var i=n.align.length,o=0;o ?/gm,"");return{type:"blockquote",raw:n[0],text:e}}},e.prototype.list=function(e){e=this.rules.block.list.exec(e);if(e){for(var n,i,o,t,a,r=e[0],c=e[2],u=1s[1].length:o[1].length>s[0].length||3/i.test(e[0])&&(n=!1),!i&&/^<(pre|code|kbd|script)(\s|>)/i.test(e[0])?i=!0:i&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(e[0])&&(i=!1),{type:this.options.sanitize?"text":"html",raw:e[0],inLink:n,inRawBlock:i,text:this.options.sanitize?this.options.sanitizer?this.options.sanitizer(e[0]):Je(e[0]):e[0]}},e.prototype.link=function(e){var n=this.rules.inline.link.exec(e);if(n){e=n[2].trim();if(!this.options.pedantic&&/^$/.test(e))return;var i=Xe(e.slice(0,-1),"\\");if((e.length-i.length)%2==0)return}else{var o=Ke(n[2],"()");-1$/.test(e)?i.slice(1):i.slice(1,-1):i)&&i.replace(this.rules.inline._escapes,"$1"),title:o&&o.replace(this.rules.inline._escapes,"$1")},n[0])}},e.prototype.reflink=function(e,n){if((i=this.rules.inline.reflink.exec(e))||(i=this.rules.inline.nolink.exec(e))){var e=(i[2]||i[1]).replace(/\s+/g," ");if((e=n[e.toLowerCase()])&&e.href)return en(i,e,i[0]);var i=i[0].charAt(0);return{type:"text",raw:i,text:i}}},e.prototype.strong=function(e,n,i){void 0===i&&(i="");var o=this.rules.inline.strong.start.exec(e);if(o&&(!o[1]||o[1]&&(""===i||this.rules.inline.punctuation.exec(i)))){n=n.slice(-1*e.length);var t,a="**"===o[0]?this.rules.inline.strong.endAst:this.rules.inline.strong.endUnd;for(a.lastIndex=0;null!=(o=a.exec(n));)if(t=this.rules.inline.strong.middle.exec(n.slice(0,o.index+3)))return{type:"strong",raw:e.slice(0,t[0].length),text:e.slice(2,t[0].length-2)}}},e.prototype.em=function(e,n,i){void 0===i&&(i="");var o=this.rules.inline.em.start.exec(e);if(o&&(!o[1]||o[1]&&(""===i||this.rules.inline.punctuation.exec(i)))){n=n.slice(-1*e.length);var t,a="*"===o[0]?this.rules.inline.em.endAst:this.rules.inline.em.endUnd;for(a.lastIndex=0;null!=(o=a.exec(n));)if(t=this.rules.inline.em.middle.exec(n.slice(0,o.index+2)))return{type:"em",raw:e.slice(0,t[0].length),text:e.slice(1,t[0].length-1)}}},e.prototype.codespan=function(e){var n=this.rules.inline.code.exec(e);if(n){var i=n[2].replace(/\n/g," "),o=/[^ ]/.test(i),e=/^ /.test(i)&&/ $/.test(i);return o&&e&&(i=i.substring(1,i.length-1)),i=Je(i,!0),{type:"codespan",raw:n[0],text:i}}},e.prototype.br=function(e){e=this.rules.inline.br.exec(e);if(e)return{type:"br",raw:e[0]}},e.prototype.del=function(e){e=this.rules.inline.del.exec(e);if(e)return{type:"del",raw:e[0],text:e[2]}},e.prototype.autolink=function(e,n){e=this.rules.inline.autolink.exec(e);if(e){var i,n="@"===e[2]?"mailto:"+(i=Je(this.options.mangle?n(e[1]):e[1])):i=Je(e[1]);return{type:"link",raw:e[0],text:i,href:n,tokens:[{type:"text",raw:i,text:i}]}}},e.prototype.url=function(e,n){var i,o,t,a;if(i=this.rules.inline.url.exec(e)){if("@"===i[2])t="mailto:"+(o=Je(this.options.mangle?n(i[0]):i[0]));else{for(;a=i[0],i[0]=this.rules.inline._backpedal.exec(i[0])[0],a!==i[0];);o=Je(i[0]),t="www."===i[1]?"http://"+o:o}return{type:"link",raw:i[0],text:o,href:t,tokens:[{type:"text",raw:o,text:o}]}}},e.prototype.inlineText=function(e,n,i){e=this.rules.inline.text.exec(e);if(e){i=n?this.options.sanitize?this.options.sanitizer?this.options.sanitizer(e[0]):Je(e[0]):e[0]:Je(this.options.smartypants?i(e[0]):e[0]);return{type:"text",raw:e[0],text:i}}},e}(),Ze=De,Ve=Ne,De=Ue,Ne={newline:/^(?: *(?:\n|$))+/,code:/^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,fences:/^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,hr:/^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,heading:/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/,html:"^ {0,3}(?:<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:\\n{2,}|$)|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$))",def:/^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,nptable:Ze,table:Ze,lheading:/^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,_paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html| +\n)[^\n]+)*)/,text:/^[^\n]+/,_label:/(?!\s*\])(?:\\[\[\]]|[^\[\]])+/,_title:/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/};Ne.def=Ve(Ne.def).replace("label",Ne._label).replace("title",Ne._title).getRegex(),Ne.bullet=/(?:[*+-]|\d{1,9}[.)])/,Ne.item=/^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/,Ne.item=Ve(Ne.item,"gm").replace(/bull/g,Ne.bullet).getRegex(),Ne.listItemStart=Ve(/^( *)(bull)/).replace("bull",Ne.bullet).getRegex(),Ne.list=Ve(Ne.list).replace(/bull/g,Ne.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+Ne.def.source+")").getRegex(),Ne._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",Ne._comment=/|$)/,Ne.html=Ve(Ne.html,"i").replace("comment",Ne._comment).replace("tag",Ne._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),Ne.paragraph=Ve(Ne._paragraph).replace("hr",Ne.hr).replace("heading"," {0,3}#{1,6} ").replace("|lheading","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",Ne._tag).getRegex(),Ne.blockquote=Ve(Ne.blockquote).replace("paragraph",Ne.paragraph).getRegex(),Ne.normal=De({},Ne),Ne.gfm=De({},Ne.normal,{nptable:"^ *([^|\\n ].*\\|.*)\\n {0,3}([-:]+ *\\|[-| :]*)(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)",table:"^ *\\|(.+)\\n {0,3}\\|?( *[-:]+[-| :]*)(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)"}),Ne.gfm.nptable=Ve(Ne.gfm.nptable).replace("hr",Ne.hr).replace("heading"," {0,3}#{1,6} ").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",Ne._tag).getRegex(),Ne.gfm.table=Ve(Ne.gfm.table).replace("hr",Ne.hr).replace("heading"," {0,3}#{1,6} ").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",Ne._tag).getRegex(),Ne.pedantic=De({},Ne.normal,{html:Ve("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",Ne._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:Ze,paragraph:Ve(Ne.normal._paragraph).replace("hr",Ne.hr).replace("heading"," *#{1,6} *[^\n]").replace("lheading",Ne.lheading).replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").getRegex()});Ze={escape:/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:Ze,tag:"^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^",link:/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,nolink:/^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,reflinkSearch:"reflink|nolink(?!\\()",strong:{start:/^(?:(\*\*(?=[*punctuation]))|\*\*)(?![\s])|__/,middle:/^\*\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*\*$|^__(?![\s])((?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?)__$/,endAst:/[^punctuation\s]\*\*(?!\*)|[punctuation]\*\*(?!\*)(?:(?=[punctuation_\s]|$))/,endUnd:/[^\s]__(?!_)(?:(?=[punctuation*\s])|$)/},em:{start:/^(?:(\*(?=[punctuation]))|\*)(?![*\s])|_/,middle:/^\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*$|^_(?![_\s])(?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?_$/,endAst:/[^punctuation\s]\*(?!\*)|[punctuation]\*(?!\*)(?:(?=[punctuation_\s]|$))/,endUnd:/[^\s]_(?!_)(?:(?=[punctuation*\s])|$)/},code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:Ze,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\?@\\[\\]`^{|}~"};Ze.punctuation=Ve(Ze.punctuation).replace(/punctuation/g,Ze._punctuation).getRegex(),Ze._blockSkip="\\[[^\\]]*?\\]\\([^\\)]*?\\)|`[^`]*?`|<[^>]*?>",Ze._overlapSkip="__[^_]*?__|\\*\\*\\[^\\*\\]*?\\*\\*",Ze._comment=Ve(Ne._comment).replace("(?:--\x3e|$)","--\x3e").getRegex(),Ze.em.start=Ve(Ze.em.start).replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.em.middle=Ve(Ze.em.middle).replace(/punctuation/g,Ze._punctuation).replace(/overlapSkip/g,Ze._overlapSkip).getRegex(),Ze.em.endAst=Ve(Ze.em.endAst,"g").replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.em.endUnd=Ve(Ze.em.endUnd,"g").replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.strong.start=Ve(Ze.strong.start).replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.strong.middle=Ve(Ze.strong.middle).replace(/punctuation/g,Ze._punctuation).replace(/overlapSkip/g,Ze._overlapSkip).getRegex(),Ze.strong.endAst=Ve(Ze.strong.endAst,"g").replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.strong.endUnd=Ve(Ze.strong.endUnd,"g").replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.blockSkip=Ve(Ze._blockSkip,"g").getRegex(),Ze.overlapSkip=Ve(Ze._overlapSkip,"g").getRegex(),Ze._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g,Ze._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,Ze._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,Ze.autolink=Ve(Ze.autolink).replace("scheme",Ze._scheme).replace("email",Ze._email).getRegex(),Ze._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,Ze.tag=Ve(Ze.tag).replace("comment",Ze._comment).replace("attribute",Ze._attribute).getRegex(),Ze._label=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,Ze._href=/<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/,Ze._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,Ze.link=Ve(Ze.link).replace("label",Ze._label).replace("href",Ze._href).replace("title",Ze._title).getRegex(),Ze.reflink=Ve(Ze.reflink).replace("label",Ze._label).getRegex(),Ze.reflinkSearch=Ve(Ze.reflinkSearch,"g").replace("reflink",Ze.reflink).replace("nolink",Ze.nolink).getRegex(),Ze.normal=De({},Ze),Ze.pedantic=De({},Ze.normal,{strong:{start:/^__|\*\*/,middle:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,endAst:/\*\*(?!\*)/g,endUnd:/__(?!_)/g},em:{start:/^_|\*/,middle:/^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,endAst:/\*(?!\*)/g,endUnd:/_(?!_)/g},link:Ve(/^!?\[(label)\]\((.*?)\)/).replace("label",Ze._label).getRegex(),reflink:Ve(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",Ze._label).getRegex()}),Ze.gfm=De({},Ze.normal,{escape:Ve(Ze.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,text:/^([`~]+|[^`~])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\'+(i?e:gn(e,!0))+"\n":"
"+(i?e:gn(e,!0))+"
\n"},e.prototype.blockquote=function(e){return"
\n"+e+"
\n"},e.prototype.html=function(e){return e},e.prototype.heading=function(e,n,i,o){return this.options.headerIds?"'+e+"\n":""+e+"\n"},e.prototype.hr=function(){return this.options.xhtml?"
\n":"
\n"},e.prototype.list=function(e,n,i){var o=n?"ol":"ul";return"<"+o+(n&&1!==i?' start="'+i+'"':"")+">\n"+e+"\n"},e.prototype.listitem=function(e){return"
  • "+e+"
  • \n"},e.prototype.checkbox=function(e){return" "},e.prototype.paragraph=function(e){return"

    "+e+"

    \n"},e.prototype.table=function(e,n){return"\n\n"+e+"\n"+(n=n&&""+n+"")+"
    \n"},e.prototype.tablerow=function(e){return"\n"+e+"\n"},e.prototype.tablecell=function(e,n){var i=n.header?"th":"td";return(n.align?"<"+i+' align="'+n.align+'">':"<"+i+">")+e+"\n"},e.prototype.strong=function(e){return""+e+""},e.prototype.em=function(e){return""+e+""},e.prototype.codespan=function(e){return""+e+""},e.prototype.br=function(){return this.options.xhtml?"
    ":"
    "},e.prototype.del=function(e){return""+e+""},e.prototype.link=function(e,n,i){if(null===(e=dn(this.options.sanitize,this.options.baseUrl,e)))return i;e='"},e.prototype.image=function(e,n,i){if(null===(e=dn(this.options.sanitize,this.options.baseUrl,e)))return i;i=''+i+'":">"},e.prototype.text=function(e){return e},e}(),ln=function(){function e(){}return e.prototype.strong=function(e){return e},e.prototype.em=function(e){return e},e.prototype.codespan=function(e){return e},e.prototype.del=function(e){return e},e.prototype.html=function(e){return e},e.prototype.text=function(e){return e},e.prototype.link=function(e,n,i){return""+i},e.prototype.image=function(e,n,i){return""+i},e.prototype.br=function(){return""},e}(),vn=function(){function e(){this.seen={}}return e.prototype.serialize=function(e){return e.toLowerCase().trim().replace(/<[!\/a-z].*?>/gi,"").replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g,"").replace(/\s/g,"-")},e.prototype.getNextSafeSlug=function(e,n){var i=e,o=0;if(this.seen.hasOwnProperty(i))for(o=this.seen[e];i=e+"-"+ ++o,this.seen.hasOwnProperty(i););return n||(this.seen[e]=o,this.seen[i]=0),i},e.prototype.slug=function(e,n){void 0===n&&(n={});e=this.serialize(e);return this.getNextSafeSlug(e,n.dryrun)},e}(),hn=we.defaults,_n=Ie,mn=function(){function i(e){this.options=e||hn,this.options.renderer=this.options.renderer||new sn,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new ln,this.slugger=new vn}return i.parse=function(e,n){return new i(n).parse(e)},i.parseInline=function(e,n){return new i(n).parseInline(e)},i.prototype.parse=function(e,n){void 0===n&&(n=!0);for(var i,o,t,a,r,c,u,f,p,d,g,s,l,v,h,_="",m=e.length,b=0;bAn error occurred:

    "+wn(e.message+"",!0)+"
    ";throw e}}xn.options=xn.setOptions=function(e){return bn(xn.defaults,e),yn(xn.defaults),xn},xn.getDefaults=Me,xn.defaults=we,xn.use=function(a){var n,e=bn({},a);if(a.renderer){var i,r=xn.defaults.renderer||new sn;for(i in a.renderer)!function(o){var t=r[o];r[o]=function(){for(var e=[],n=arguments.length;n--;)e[n]=arguments[n];var i=a.renderer[o].apply(r,e);return i=!1===i?t.apply(r,e):i}}(i);e.renderer=r}if(a.tokenizer){var t,c=xn.defaults.tokenizer||new nn;for(t in a.tokenizer)!function(){var o=c[t];c[t]=function(){for(var e=[],n=arguments.length;n--;)e[n]=arguments[n];var i=a.tokenizer[t].apply(c,e);return i=!1===i?o.apply(c,e):i}}();e.tokenizer=c}a.walkTokens&&(n=xn.defaults.walkTokens,e.walkTokens=function(e){a.walkTokens(e),n&&n(e)}),xn.setOptions(e)},xn.walkTokens=function(e,n){for(var i=0,o=e;iAn error occurred:

    "+wn(e.message+"",!0)+"
    ";throw e}},xn.Parser=mn,xn.parser=mn.parse,xn.Renderer=sn,xn.TextRenderer=ln,xn.Lexer=fn,xn.lexer=fn.lex,xn.Tokenizer=nn,xn.Slugger=vn;var Sn=xn.parse=xn;function An(e,i){if(void 0===i&&(i='
      {inner}
    '),!e||!e.length)return"";var o="";return e.forEach(function(e){var n=e.title.replace(/(<([^>]+)>)/g,"");o+='
  • '+e.title+"
  • ",e.children&&(o+=An(e.children,i))}),i.replace("{inner}",o)}function $n(e,n){return'

    '+n.slice(5).trim()+"

    "}function zn(e,o){var t=[],a={};return e.forEach(function(e){var n=e.level||1,i=n-1;o?@[\]^`{|}~]/g;function Tn(e){return e.toLowerCase()}function Cn(e){if("string"!=typeof e)return"";var n=e.trim().replace(/[A-Z]+/g,Tn).replace(/<[^>]+>/g,"").replace(En,"").replace(/\s/g,"-").replace(/-+/g,"-").replace(/^(\d)/,"_$1"),e=Fn[n],e=u.call(Fn,n)?e+1:0;return n=(Fn[n]=e)?n+"-"+e:n}Cn.clear=function(){Fn={}};var Rn={baseURL:"https://github.githubassets.com/images/icons/emoji/",data:{100:"unicode/1f4af.png?v8",1234:"unicode/1f522.png?v8","+1":"unicode/1f44d.png?v8","-1":"unicode/1f44e.png?v8","1st_place_medal":"unicode/1f947.png?v8","2nd_place_medal":"unicode/1f948.png?v8","3rd_place_medal":"unicode/1f949.png?v8","8ball":"unicode/1f3b1.png?v8",a:"unicode/1f170.png?v8",ab:"unicode/1f18e.png?v8",abacus:"unicode/1f9ee.png?v8",abc:"unicode/1f524.png?v8",abcd:"unicode/1f521.png?v8",accept:"unicode/1f251.png?v8",accordion:"unicode/1fa97.png?v8",adhesive_bandage:"unicode/1fa79.png?v8",adult:"unicode/1f9d1.png?v8",aerial_tramway:"unicode/1f6a1.png?v8",afghanistan:"unicode/1f1e6-1f1eb.png?v8",airplane:"unicode/2708.png?v8",aland_islands:"unicode/1f1e6-1f1fd.png?v8",alarm_clock:"unicode/23f0.png?v8",albania:"unicode/1f1e6-1f1f1.png?v8",alembic:"unicode/2697.png?v8",algeria:"unicode/1f1e9-1f1ff.png?v8",alien:"unicode/1f47d.png?v8",ambulance:"unicode/1f691.png?v8",american_samoa:"unicode/1f1e6-1f1f8.png?v8",amphora:"unicode/1f3fa.png?v8",anatomical_heart:"unicode/1fac0.png?v8",anchor:"unicode/2693.png?v8",andorra:"unicode/1f1e6-1f1e9.png?v8",angel:"unicode/1f47c.png?v8",anger:"unicode/1f4a2.png?v8",angola:"unicode/1f1e6-1f1f4.png?v8",angry:"unicode/1f620.png?v8",anguilla:"unicode/1f1e6-1f1ee.png?v8",anguished:"unicode/1f627.png?v8",ant:"unicode/1f41c.png?v8",antarctica:"unicode/1f1e6-1f1f6.png?v8",antigua_barbuda:"unicode/1f1e6-1f1ec.png?v8",apple:"unicode/1f34e.png?v8",aquarius:"unicode/2652.png?v8",argentina:"unicode/1f1e6-1f1f7.png?v8",aries:"unicode/2648.png?v8",armenia:"unicode/1f1e6-1f1f2.png?v8",arrow_backward:"unicode/25c0.png?v8",arrow_double_down:"unicode/23ec.png?v8",arrow_double_up:"unicode/23eb.png?v8",arrow_down:"unicode/2b07.png?v8",arrow_down_small:"unicode/1f53d.png?v8",arrow_forward:"unicode/25b6.png?v8",arrow_heading_down:"unicode/2935.png?v8",arrow_heading_up:"unicode/2934.png?v8",arrow_left:"unicode/2b05.png?v8",arrow_lower_left:"unicode/2199.png?v8",arrow_lower_right:"unicode/2198.png?v8",arrow_right:"unicode/27a1.png?v8",arrow_right_hook:"unicode/21aa.png?v8",arrow_up:"unicode/2b06.png?v8",arrow_up_down:"unicode/2195.png?v8",arrow_up_small:"unicode/1f53c.png?v8",arrow_upper_left:"unicode/2196.png?v8",arrow_upper_right:"unicode/2197.png?v8",arrows_clockwise:"unicode/1f503.png?v8",arrows_counterclockwise:"unicode/1f504.png?v8",art:"unicode/1f3a8.png?v8",articulated_lorry:"unicode/1f69b.png?v8",artificial_satellite:"unicode/1f6f0.png?v8",artist:"unicode/1f9d1-1f3a8.png?v8",aruba:"unicode/1f1e6-1f1fc.png?v8",ascension_island:"unicode/1f1e6-1f1e8.png?v8",asterisk:"unicode/002a-20e3.png?v8",astonished:"unicode/1f632.png?v8",astronaut:"unicode/1f9d1-1f680.png?v8",athletic_shoe:"unicode/1f45f.png?v8",atm:"unicode/1f3e7.png?v8",atom:"atom.png?v8",atom_symbol:"unicode/269b.png?v8",australia:"unicode/1f1e6-1f1fa.png?v8",austria:"unicode/1f1e6-1f1f9.png?v8",auto_rickshaw:"unicode/1f6fa.png?v8",avocado:"unicode/1f951.png?v8",axe:"unicode/1fa93.png?v8",azerbaijan:"unicode/1f1e6-1f1ff.png?v8",b:"unicode/1f171.png?v8",baby:"unicode/1f476.png?v8",baby_bottle:"unicode/1f37c.png?v8",baby_chick:"unicode/1f424.png?v8",baby_symbol:"unicode/1f6bc.png?v8",back:"unicode/1f519.png?v8",bacon:"unicode/1f953.png?v8",badger:"unicode/1f9a1.png?v8",badminton:"unicode/1f3f8.png?v8",bagel:"unicode/1f96f.png?v8",baggage_claim:"unicode/1f6c4.png?v8",baguette_bread:"unicode/1f956.png?v8",bahamas:"unicode/1f1e7-1f1f8.png?v8",bahrain:"unicode/1f1e7-1f1ed.png?v8",balance_scale:"unicode/2696.png?v8",bald_man:"unicode/1f468-1f9b2.png?v8",bald_woman:"unicode/1f469-1f9b2.png?v8",ballet_shoes:"unicode/1fa70.png?v8",balloon:"unicode/1f388.png?v8",ballot_box:"unicode/1f5f3.png?v8",ballot_box_with_check:"unicode/2611.png?v8",bamboo:"unicode/1f38d.png?v8",banana:"unicode/1f34c.png?v8",bangbang:"unicode/203c.png?v8",bangladesh:"unicode/1f1e7-1f1e9.png?v8",banjo:"unicode/1fa95.png?v8",bank:"unicode/1f3e6.png?v8",bar_chart:"unicode/1f4ca.png?v8",barbados:"unicode/1f1e7-1f1e7.png?v8",barber:"unicode/1f488.png?v8",baseball:"unicode/26be.png?v8",basecamp:"basecamp.png?v8",basecampy:"basecampy.png?v8",basket:"unicode/1f9fa.png?v8",basketball:"unicode/1f3c0.png?v8",basketball_man:"unicode/26f9-2642.png?v8",basketball_woman:"unicode/26f9-2640.png?v8",bat:"unicode/1f987.png?v8",bath:"unicode/1f6c0.png?v8",bathtub:"unicode/1f6c1.png?v8",battery:"unicode/1f50b.png?v8",beach_umbrella:"unicode/1f3d6.png?v8",bear:"unicode/1f43b.png?v8",bearded_person:"unicode/1f9d4.png?v8",beaver:"unicode/1f9ab.png?v8",bed:"unicode/1f6cf.png?v8",bee:"unicode/1f41d.png?v8",beer:"unicode/1f37a.png?v8",beers:"unicode/1f37b.png?v8",beetle:"unicode/1fab2.png?v8",beginner:"unicode/1f530.png?v8",belarus:"unicode/1f1e7-1f1fe.png?v8",belgium:"unicode/1f1e7-1f1ea.png?v8",belize:"unicode/1f1e7-1f1ff.png?v8",bell:"unicode/1f514.png?v8",bell_pepper:"unicode/1fad1.png?v8",bellhop_bell:"unicode/1f6ce.png?v8",benin:"unicode/1f1e7-1f1ef.png?v8",bento:"unicode/1f371.png?v8",bermuda:"unicode/1f1e7-1f1f2.png?v8",beverage_box:"unicode/1f9c3.png?v8",bhutan:"unicode/1f1e7-1f1f9.png?v8",bicyclist:"unicode/1f6b4.png?v8",bike:"unicode/1f6b2.png?v8",biking_man:"unicode/1f6b4-2642.png?v8",biking_woman:"unicode/1f6b4-2640.png?v8",bikini:"unicode/1f459.png?v8",billed_cap:"unicode/1f9e2.png?v8",biohazard:"unicode/2623.png?v8",bird:"unicode/1f426.png?v8",birthday:"unicode/1f382.png?v8",bison:"unicode/1f9ac.png?v8",black_cat:"unicode/1f408-2b1b.png?v8",black_circle:"unicode/26ab.png?v8",black_flag:"unicode/1f3f4.png?v8",black_heart:"unicode/1f5a4.png?v8",black_joker:"unicode/1f0cf.png?v8",black_large_square:"unicode/2b1b.png?v8",black_medium_small_square:"unicode/25fe.png?v8",black_medium_square:"unicode/25fc.png?v8",black_nib:"unicode/2712.png?v8",black_small_square:"unicode/25aa.png?v8",black_square_button:"unicode/1f532.png?v8",blond_haired_man:"unicode/1f471-2642.png?v8",blond_haired_person:"unicode/1f471.png?v8",blond_haired_woman:"unicode/1f471-2640.png?v8",blonde_woman:"unicode/1f471-2640.png?v8",blossom:"unicode/1f33c.png?v8",blowfish:"unicode/1f421.png?v8",blue_book:"unicode/1f4d8.png?v8",blue_car:"unicode/1f699.png?v8",blue_heart:"unicode/1f499.png?v8",blue_square:"unicode/1f7e6.png?v8",blueberries:"unicode/1fad0.png?v8",blush:"unicode/1f60a.png?v8",boar:"unicode/1f417.png?v8",boat:"unicode/26f5.png?v8",bolivia:"unicode/1f1e7-1f1f4.png?v8",bomb:"unicode/1f4a3.png?v8",bone:"unicode/1f9b4.png?v8",book:"unicode/1f4d6.png?v8",bookmark:"unicode/1f516.png?v8",bookmark_tabs:"unicode/1f4d1.png?v8",books:"unicode/1f4da.png?v8",boom:"unicode/1f4a5.png?v8",boomerang:"unicode/1fa83.png?v8",boot:"unicode/1f462.png?v8",bosnia_herzegovina:"unicode/1f1e7-1f1e6.png?v8",botswana:"unicode/1f1e7-1f1fc.png?v8",bouncing_ball_man:"unicode/26f9-2642.png?v8",bouncing_ball_person:"unicode/26f9.png?v8",bouncing_ball_woman:"unicode/26f9-2640.png?v8",bouquet:"unicode/1f490.png?v8",bouvet_island:"unicode/1f1e7-1f1fb.png?v8",bow:"unicode/1f647.png?v8",bow_and_arrow:"unicode/1f3f9.png?v8",bowing_man:"unicode/1f647-2642.png?v8",bowing_woman:"unicode/1f647-2640.png?v8",bowl_with_spoon:"unicode/1f963.png?v8",bowling:"unicode/1f3b3.png?v8",bowtie:"bowtie.png?v8",boxing_glove:"unicode/1f94a.png?v8",boy:"unicode/1f466.png?v8",brain:"unicode/1f9e0.png?v8",brazil:"unicode/1f1e7-1f1f7.png?v8",bread:"unicode/1f35e.png?v8",breast_feeding:"unicode/1f931.png?v8",bricks:"unicode/1f9f1.png?v8",bride_with_veil:"unicode/1f470-2640.png?v8",bridge_at_night:"unicode/1f309.png?v8",briefcase:"unicode/1f4bc.png?v8",british_indian_ocean_territory:"unicode/1f1ee-1f1f4.png?v8",british_virgin_islands:"unicode/1f1fb-1f1ec.png?v8",broccoli:"unicode/1f966.png?v8",broken_heart:"unicode/1f494.png?v8",broom:"unicode/1f9f9.png?v8",brown_circle:"unicode/1f7e4.png?v8",brown_heart:"unicode/1f90e.png?v8",brown_square:"unicode/1f7eb.png?v8",brunei:"unicode/1f1e7-1f1f3.png?v8",bubble_tea:"unicode/1f9cb.png?v8",bucket:"unicode/1faa3.png?v8",bug:"unicode/1f41b.png?v8",building_construction:"unicode/1f3d7.png?v8",bulb:"unicode/1f4a1.png?v8",bulgaria:"unicode/1f1e7-1f1ec.png?v8",bullettrain_front:"unicode/1f685.png?v8",bullettrain_side:"unicode/1f684.png?v8",burkina_faso:"unicode/1f1e7-1f1eb.png?v8",burrito:"unicode/1f32f.png?v8",burundi:"unicode/1f1e7-1f1ee.png?v8",bus:"unicode/1f68c.png?v8",business_suit_levitating:"unicode/1f574.png?v8",busstop:"unicode/1f68f.png?v8",bust_in_silhouette:"unicode/1f464.png?v8",busts_in_silhouette:"unicode/1f465.png?v8",butter:"unicode/1f9c8.png?v8",butterfly:"unicode/1f98b.png?v8",cactus:"unicode/1f335.png?v8",cake:"unicode/1f370.png?v8",calendar:"unicode/1f4c6.png?v8",call_me_hand:"unicode/1f919.png?v8",calling:"unicode/1f4f2.png?v8",cambodia:"unicode/1f1f0-1f1ed.png?v8",camel:"unicode/1f42b.png?v8",camera:"unicode/1f4f7.png?v8",camera_flash:"unicode/1f4f8.png?v8",cameroon:"unicode/1f1e8-1f1f2.png?v8",camping:"unicode/1f3d5.png?v8",canada:"unicode/1f1e8-1f1e6.png?v8",canary_islands:"unicode/1f1ee-1f1e8.png?v8",cancer:"unicode/264b.png?v8",candle:"unicode/1f56f.png?v8",candy:"unicode/1f36c.png?v8",canned_food:"unicode/1f96b.png?v8",canoe:"unicode/1f6f6.png?v8",cape_verde:"unicode/1f1e8-1f1fb.png?v8",capital_abcd:"unicode/1f520.png?v8",capricorn:"unicode/2651.png?v8",car:"unicode/1f697.png?v8",card_file_box:"unicode/1f5c3.png?v8",card_index:"unicode/1f4c7.png?v8",card_index_dividers:"unicode/1f5c2.png?v8",caribbean_netherlands:"unicode/1f1e7-1f1f6.png?v8",carousel_horse:"unicode/1f3a0.png?v8",carpentry_saw:"unicode/1fa9a.png?v8",carrot:"unicode/1f955.png?v8",cartwheeling:"unicode/1f938.png?v8",cat:"unicode/1f431.png?v8",cat2:"unicode/1f408.png?v8",cayman_islands:"unicode/1f1f0-1f1fe.png?v8",cd:"unicode/1f4bf.png?v8",central_african_republic:"unicode/1f1e8-1f1eb.png?v8",ceuta_melilla:"unicode/1f1ea-1f1e6.png?v8",chad:"unicode/1f1f9-1f1e9.png?v8",chains:"unicode/26d3.png?v8",chair:"unicode/1fa91.png?v8",champagne:"unicode/1f37e.png?v8",chart:"unicode/1f4b9.png?v8",chart_with_downwards_trend:"unicode/1f4c9.png?v8",chart_with_upwards_trend:"unicode/1f4c8.png?v8",checkered_flag:"unicode/1f3c1.png?v8",cheese:"unicode/1f9c0.png?v8",cherries:"unicode/1f352.png?v8",cherry_blossom:"unicode/1f338.png?v8",chess_pawn:"unicode/265f.png?v8",chestnut:"unicode/1f330.png?v8",chicken:"unicode/1f414.png?v8",child:"unicode/1f9d2.png?v8",children_crossing:"unicode/1f6b8.png?v8",chile:"unicode/1f1e8-1f1f1.png?v8",chipmunk:"unicode/1f43f.png?v8",chocolate_bar:"unicode/1f36b.png?v8",chopsticks:"unicode/1f962.png?v8",christmas_island:"unicode/1f1e8-1f1fd.png?v8",christmas_tree:"unicode/1f384.png?v8",church:"unicode/26ea.png?v8",cinema:"unicode/1f3a6.png?v8",circus_tent:"unicode/1f3aa.png?v8",city_sunrise:"unicode/1f307.png?v8",city_sunset:"unicode/1f306.png?v8",cityscape:"unicode/1f3d9.png?v8",cl:"unicode/1f191.png?v8",clamp:"unicode/1f5dc.png?v8",clap:"unicode/1f44f.png?v8",clapper:"unicode/1f3ac.png?v8",classical_building:"unicode/1f3db.png?v8",climbing:"unicode/1f9d7.png?v8",climbing_man:"unicode/1f9d7-2642.png?v8",climbing_woman:"unicode/1f9d7-2640.png?v8",clinking_glasses:"unicode/1f942.png?v8",clipboard:"unicode/1f4cb.png?v8",clipperton_island:"unicode/1f1e8-1f1f5.png?v8",clock1:"unicode/1f550.png?v8",clock10:"unicode/1f559.png?v8",clock1030:"unicode/1f565.png?v8",clock11:"unicode/1f55a.png?v8",clock1130:"unicode/1f566.png?v8",clock12:"unicode/1f55b.png?v8",clock1230:"unicode/1f567.png?v8",clock130:"unicode/1f55c.png?v8",clock2:"unicode/1f551.png?v8",clock230:"unicode/1f55d.png?v8",clock3:"unicode/1f552.png?v8",clock330:"unicode/1f55e.png?v8",clock4:"unicode/1f553.png?v8",clock430:"unicode/1f55f.png?v8",clock5:"unicode/1f554.png?v8",clock530:"unicode/1f560.png?v8",clock6:"unicode/1f555.png?v8",clock630:"unicode/1f561.png?v8",clock7:"unicode/1f556.png?v8",clock730:"unicode/1f562.png?v8",clock8:"unicode/1f557.png?v8",clock830:"unicode/1f563.png?v8",clock9:"unicode/1f558.png?v8",clock930:"unicode/1f564.png?v8",closed_book:"unicode/1f4d5.png?v8",closed_lock_with_key:"unicode/1f510.png?v8",closed_umbrella:"unicode/1f302.png?v8",cloud:"unicode/2601.png?v8",cloud_with_lightning:"unicode/1f329.png?v8",cloud_with_lightning_and_rain:"unicode/26c8.png?v8",cloud_with_rain:"unicode/1f327.png?v8",cloud_with_snow:"unicode/1f328.png?v8",clown_face:"unicode/1f921.png?v8",clubs:"unicode/2663.png?v8",cn:"unicode/1f1e8-1f1f3.png?v8",coat:"unicode/1f9e5.png?v8",cockroach:"unicode/1fab3.png?v8",cocktail:"unicode/1f378.png?v8",coconut:"unicode/1f965.png?v8",cocos_islands:"unicode/1f1e8-1f1e8.png?v8",coffee:"unicode/2615.png?v8",coffin:"unicode/26b0.png?v8",coin:"unicode/1fa99.png?v8",cold_face:"unicode/1f976.png?v8",cold_sweat:"unicode/1f630.png?v8",collision:"unicode/1f4a5.png?v8",colombia:"unicode/1f1e8-1f1f4.png?v8",comet:"unicode/2604.png?v8",comoros:"unicode/1f1f0-1f1f2.png?v8",compass:"unicode/1f9ed.png?v8",computer:"unicode/1f4bb.png?v8",computer_mouse:"unicode/1f5b1.png?v8",confetti_ball:"unicode/1f38a.png?v8",confounded:"unicode/1f616.png?v8",confused:"unicode/1f615.png?v8",congo_brazzaville:"unicode/1f1e8-1f1ec.png?v8",congo_kinshasa:"unicode/1f1e8-1f1e9.png?v8",congratulations:"unicode/3297.png?v8",construction:"unicode/1f6a7.png?v8",construction_worker:"unicode/1f477.png?v8",construction_worker_man:"unicode/1f477-2642.png?v8",construction_worker_woman:"unicode/1f477-2640.png?v8",control_knobs:"unicode/1f39b.png?v8",convenience_store:"unicode/1f3ea.png?v8",cook:"unicode/1f9d1-1f373.png?v8",cook_islands:"unicode/1f1e8-1f1f0.png?v8",cookie:"unicode/1f36a.png?v8",cool:"unicode/1f192.png?v8",cop:"unicode/1f46e.png?v8",copyright:"unicode/00a9.png?v8",corn:"unicode/1f33d.png?v8",costa_rica:"unicode/1f1e8-1f1f7.png?v8",cote_divoire:"unicode/1f1e8-1f1ee.png?v8",couch_and_lamp:"unicode/1f6cb.png?v8",couple:"unicode/1f46b.png?v8",couple_with_heart:"unicode/1f491.png?v8",couple_with_heart_man_man:"unicode/1f468-2764-1f468.png?v8",couple_with_heart_woman_man:"unicode/1f469-2764-1f468.png?v8",couple_with_heart_woman_woman:"unicode/1f469-2764-1f469.png?v8",couplekiss:"unicode/1f48f.png?v8",couplekiss_man_man:"unicode/1f468-2764-1f48b-1f468.png?v8",couplekiss_man_woman:"unicode/1f469-2764-1f48b-1f468.png?v8",couplekiss_woman_woman:"unicode/1f469-2764-1f48b-1f469.png?v8",cow:"unicode/1f42e.png?v8",cow2:"unicode/1f404.png?v8",cowboy_hat_face:"unicode/1f920.png?v8",crab:"unicode/1f980.png?v8",crayon:"unicode/1f58d.png?v8",credit_card:"unicode/1f4b3.png?v8",crescent_moon:"unicode/1f319.png?v8",cricket:"unicode/1f997.png?v8",cricket_game:"unicode/1f3cf.png?v8",croatia:"unicode/1f1ed-1f1f7.png?v8",crocodile:"unicode/1f40a.png?v8",croissant:"unicode/1f950.png?v8",crossed_fingers:"unicode/1f91e.png?v8",crossed_flags:"unicode/1f38c.png?v8",crossed_swords:"unicode/2694.png?v8",crown:"unicode/1f451.png?v8",cry:"unicode/1f622.png?v8",crying_cat_face:"unicode/1f63f.png?v8",crystal_ball:"unicode/1f52e.png?v8",cuba:"unicode/1f1e8-1f1fa.png?v8",cucumber:"unicode/1f952.png?v8",cup_with_straw:"unicode/1f964.png?v8",cupcake:"unicode/1f9c1.png?v8",cupid:"unicode/1f498.png?v8",curacao:"unicode/1f1e8-1f1fc.png?v8",curling_stone:"unicode/1f94c.png?v8",curly_haired_man:"unicode/1f468-1f9b1.png?v8",curly_haired_woman:"unicode/1f469-1f9b1.png?v8",curly_loop:"unicode/27b0.png?v8",currency_exchange:"unicode/1f4b1.png?v8",curry:"unicode/1f35b.png?v8",cursing_face:"unicode/1f92c.png?v8",custard:"unicode/1f36e.png?v8",customs:"unicode/1f6c3.png?v8",cut_of_meat:"unicode/1f969.png?v8",cyclone:"unicode/1f300.png?v8",cyprus:"unicode/1f1e8-1f1fe.png?v8",czech_republic:"unicode/1f1e8-1f1ff.png?v8",dagger:"unicode/1f5e1.png?v8",dancer:"unicode/1f483.png?v8",dancers:"unicode/1f46f.png?v8",dancing_men:"unicode/1f46f-2642.png?v8",dancing_women:"unicode/1f46f-2640.png?v8",dango:"unicode/1f361.png?v8",dark_sunglasses:"unicode/1f576.png?v8",dart:"unicode/1f3af.png?v8",dash:"unicode/1f4a8.png?v8",date:"unicode/1f4c5.png?v8",de:"unicode/1f1e9-1f1ea.png?v8",deaf_man:"unicode/1f9cf-2642.png?v8",deaf_person:"unicode/1f9cf.png?v8",deaf_woman:"unicode/1f9cf-2640.png?v8",deciduous_tree:"unicode/1f333.png?v8",deer:"unicode/1f98c.png?v8",denmark:"unicode/1f1e9-1f1f0.png?v8",department_store:"unicode/1f3ec.png?v8",derelict_house:"unicode/1f3da.png?v8",desert:"unicode/1f3dc.png?v8",desert_island:"unicode/1f3dd.png?v8",desktop_computer:"unicode/1f5a5.png?v8",detective:"unicode/1f575.png?v8",diamond_shape_with_a_dot_inside:"unicode/1f4a0.png?v8",diamonds:"unicode/2666.png?v8",diego_garcia:"unicode/1f1e9-1f1ec.png?v8",disappointed:"unicode/1f61e.png?v8",disappointed_relieved:"unicode/1f625.png?v8",disguised_face:"unicode/1f978.png?v8",diving_mask:"unicode/1f93f.png?v8",diya_lamp:"unicode/1fa94.png?v8",dizzy:"unicode/1f4ab.png?v8",dizzy_face:"unicode/1f635.png?v8",djibouti:"unicode/1f1e9-1f1ef.png?v8",dna:"unicode/1f9ec.png?v8",do_not_litter:"unicode/1f6af.png?v8",dodo:"unicode/1f9a4.png?v8",dog:"unicode/1f436.png?v8",dog2:"unicode/1f415.png?v8",dollar:"unicode/1f4b5.png?v8",dolls:"unicode/1f38e.png?v8",dolphin:"unicode/1f42c.png?v8",dominica:"unicode/1f1e9-1f1f2.png?v8",dominican_republic:"unicode/1f1e9-1f1f4.png?v8",door:"unicode/1f6aa.png?v8",doughnut:"unicode/1f369.png?v8",dove:"unicode/1f54a.png?v8",dragon:"unicode/1f409.png?v8",dragon_face:"unicode/1f432.png?v8",dress:"unicode/1f457.png?v8",dromedary_camel:"unicode/1f42a.png?v8",drooling_face:"unicode/1f924.png?v8",drop_of_blood:"unicode/1fa78.png?v8",droplet:"unicode/1f4a7.png?v8",drum:"unicode/1f941.png?v8",duck:"unicode/1f986.png?v8",dumpling:"unicode/1f95f.png?v8",dvd:"unicode/1f4c0.png?v8","e-mail":"unicode/1f4e7.png?v8",eagle:"unicode/1f985.png?v8",ear:"unicode/1f442.png?v8",ear_of_rice:"unicode/1f33e.png?v8",ear_with_hearing_aid:"unicode/1f9bb.png?v8",earth_africa:"unicode/1f30d.png?v8",earth_americas:"unicode/1f30e.png?v8",earth_asia:"unicode/1f30f.png?v8",ecuador:"unicode/1f1ea-1f1e8.png?v8",egg:"unicode/1f95a.png?v8",eggplant:"unicode/1f346.png?v8",egypt:"unicode/1f1ea-1f1ec.png?v8",eight:"unicode/0038-20e3.png?v8",eight_pointed_black_star:"unicode/2734.png?v8",eight_spoked_asterisk:"unicode/2733.png?v8",eject_button:"unicode/23cf.png?v8",el_salvador:"unicode/1f1f8-1f1fb.png?v8",electric_plug:"unicode/1f50c.png?v8",electron:"electron.png?v8",elephant:"unicode/1f418.png?v8",elevator:"unicode/1f6d7.png?v8",elf:"unicode/1f9dd.png?v8",elf_man:"unicode/1f9dd-2642.png?v8",elf_woman:"unicode/1f9dd-2640.png?v8",email:"unicode/1f4e7.png?v8",end:"unicode/1f51a.png?v8",england:"unicode/1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.png?v8",envelope:"unicode/2709.png?v8",envelope_with_arrow:"unicode/1f4e9.png?v8",equatorial_guinea:"unicode/1f1ec-1f1f6.png?v8",eritrea:"unicode/1f1ea-1f1f7.png?v8",es:"unicode/1f1ea-1f1f8.png?v8",estonia:"unicode/1f1ea-1f1ea.png?v8",ethiopia:"unicode/1f1ea-1f1f9.png?v8",eu:"unicode/1f1ea-1f1fa.png?v8",euro:"unicode/1f4b6.png?v8",european_castle:"unicode/1f3f0.png?v8",european_post_office:"unicode/1f3e4.png?v8",european_union:"unicode/1f1ea-1f1fa.png?v8",evergreen_tree:"unicode/1f332.png?v8",exclamation:"unicode/2757.png?v8",exploding_head:"unicode/1f92f.png?v8",expressionless:"unicode/1f611.png?v8",eye:"unicode/1f441.png?v8",eye_speech_bubble:"unicode/1f441-1f5e8.png?v8",eyeglasses:"unicode/1f453.png?v8",eyes:"unicode/1f440.png?v8",face_exhaling:"unicode/1f62e-1f4a8.png?v8",face_in_clouds:"unicode/1f636-1f32b.png?v8",face_with_head_bandage:"unicode/1f915.png?v8",face_with_spiral_eyes:"unicode/1f635-1f4ab.png?v8",face_with_thermometer:"unicode/1f912.png?v8",facepalm:"unicode/1f926.png?v8",facepunch:"unicode/1f44a.png?v8",factory:"unicode/1f3ed.png?v8",factory_worker:"unicode/1f9d1-1f3ed.png?v8",fairy:"unicode/1f9da.png?v8",fairy_man:"unicode/1f9da-2642.png?v8",fairy_woman:"unicode/1f9da-2640.png?v8",falafel:"unicode/1f9c6.png?v8",falkland_islands:"unicode/1f1eb-1f1f0.png?v8",fallen_leaf:"unicode/1f342.png?v8",family:"unicode/1f46a.png?v8",family_man_boy:"unicode/1f468-1f466.png?v8",family_man_boy_boy:"unicode/1f468-1f466-1f466.png?v8",family_man_girl:"unicode/1f468-1f467.png?v8",family_man_girl_boy:"unicode/1f468-1f467-1f466.png?v8",family_man_girl_girl:"unicode/1f468-1f467-1f467.png?v8",family_man_man_boy:"unicode/1f468-1f468-1f466.png?v8",family_man_man_boy_boy:"unicode/1f468-1f468-1f466-1f466.png?v8",family_man_man_girl:"unicode/1f468-1f468-1f467.png?v8",family_man_man_girl_boy:"unicode/1f468-1f468-1f467-1f466.png?v8",family_man_man_girl_girl:"unicode/1f468-1f468-1f467-1f467.png?v8",family_man_woman_boy:"unicode/1f468-1f469-1f466.png?v8",family_man_woman_boy_boy:"unicode/1f468-1f469-1f466-1f466.png?v8",family_man_woman_girl:"unicode/1f468-1f469-1f467.png?v8",family_man_woman_girl_boy:"unicode/1f468-1f469-1f467-1f466.png?v8",family_man_woman_girl_girl:"unicode/1f468-1f469-1f467-1f467.png?v8",family_woman_boy:"unicode/1f469-1f466.png?v8",family_woman_boy_boy:"unicode/1f469-1f466-1f466.png?v8",family_woman_girl:"unicode/1f469-1f467.png?v8",family_woman_girl_boy:"unicode/1f469-1f467-1f466.png?v8",family_woman_girl_girl:"unicode/1f469-1f467-1f467.png?v8",family_woman_woman_boy:"unicode/1f469-1f469-1f466.png?v8",family_woman_woman_boy_boy:"unicode/1f469-1f469-1f466-1f466.png?v8",family_woman_woman_girl:"unicode/1f469-1f469-1f467.png?v8",family_woman_woman_girl_boy:"unicode/1f469-1f469-1f467-1f466.png?v8",family_woman_woman_girl_girl:"unicode/1f469-1f469-1f467-1f467.png?v8",farmer:"unicode/1f9d1-1f33e.png?v8",faroe_islands:"unicode/1f1eb-1f1f4.png?v8",fast_forward:"unicode/23e9.png?v8",fax:"unicode/1f4e0.png?v8",fearful:"unicode/1f628.png?v8",feather:"unicode/1fab6.png?v8",feelsgood:"feelsgood.png?v8",feet:"unicode/1f43e.png?v8",female_detective:"unicode/1f575-2640.png?v8",female_sign:"unicode/2640.png?v8",ferris_wheel:"unicode/1f3a1.png?v8",ferry:"unicode/26f4.png?v8",field_hockey:"unicode/1f3d1.png?v8",fiji:"unicode/1f1eb-1f1ef.png?v8",file_cabinet:"unicode/1f5c4.png?v8",file_folder:"unicode/1f4c1.png?v8",film_projector:"unicode/1f4fd.png?v8",film_strip:"unicode/1f39e.png?v8",finland:"unicode/1f1eb-1f1ee.png?v8",finnadie:"finnadie.png?v8",fire:"unicode/1f525.png?v8",fire_engine:"unicode/1f692.png?v8",fire_extinguisher:"unicode/1f9ef.png?v8",firecracker:"unicode/1f9e8.png?v8",firefighter:"unicode/1f9d1-1f692.png?v8",fireworks:"unicode/1f386.png?v8",first_quarter_moon:"unicode/1f313.png?v8",first_quarter_moon_with_face:"unicode/1f31b.png?v8",fish:"unicode/1f41f.png?v8",fish_cake:"unicode/1f365.png?v8",fishing_pole_and_fish:"unicode/1f3a3.png?v8",fist:"unicode/270a.png?v8",fist_left:"unicode/1f91b.png?v8",fist_oncoming:"unicode/1f44a.png?v8",fist_raised:"unicode/270a.png?v8",fist_right:"unicode/1f91c.png?v8",five:"unicode/0035-20e3.png?v8",flags:"unicode/1f38f.png?v8",flamingo:"unicode/1f9a9.png?v8",flashlight:"unicode/1f526.png?v8",flat_shoe:"unicode/1f97f.png?v8",flatbread:"unicode/1fad3.png?v8",fleur_de_lis:"unicode/269c.png?v8",flight_arrival:"unicode/1f6ec.png?v8",flight_departure:"unicode/1f6eb.png?v8",flipper:"unicode/1f42c.png?v8",floppy_disk:"unicode/1f4be.png?v8",flower_playing_cards:"unicode/1f3b4.png?v8",flushed:"unicode/1f633.png?v8",fly:"unicode/1fab0.png?v8",flying_disc:"unicode/1f94f.png?v8",flying_saucer:"unicode/1f6f8.png?v8",fog:"unicode/1f32b.png?v8",foggy:"unicode/1f301.png?v8",fondue:"unicode/1fad5.png?v8",foot:"unicode/1f9b6.png?v8",football:"unicode/1f3c8.png?v8",footprints:"unicode/1f463.png?v8",fork_and_knife:"unicode/1f374.png?v8",fortune_cookie:"unicode/1f960.png?v8",fountain:"unicode/26f2.png?v8",fountain_pen:"unicode/1f58b.png?v8",four:"unicode/0034-20e3.png?v8",four_leaf_clover:"unicode/1f340.png?v8",fox_face:"unicode/1f98a.png?v8",fr:"unicode/1f1eb-1f1f7.png?v8",framed_picture:"unicode/1f5bc.png?v8",free:"unicode/1f193.png?v8",french_guiana:"unicode/1f1ec-1f1eb.png?v8",french_polynesia:"unicode/1f1f5-1f1eb.png?v8",french_southern_territories:"unicode/1f1f9-1f1eb.png?v8",fried_egg:"unicode/1f373.png?v8",fried_shrimp:"unicode/1f364.png?v8",fries:"unicode/1f35f.png?v8",frog:"unicode/1f438.png?v8",frowning:"unicode/1f626.png?v8",frowning_face:"unicode/2639.png?v8",frowning_man:"unicode/1f64d-2642.png?v8",frowning_person:"unicode/1f64d.png?v8",frowning_woman:"unicode/1f64d-2640.png?v8",fu:"unicode/1f595.png?v8",fuelpump:"unicode/26fd.png?v8",full_moon:"unicode/1f315.png?v8",full_moon_with_face:"unicode/1f31d.png?v8",funeral_urn:"unicode/26b1.png?v8",gabon:"unicode/1f1ec-1f1e6.png?v8",gambia:"unicode/1f1ec-1f1f2.png?v8",game_die:"unicode/1f3b2.png?v8",garlic:"unicode/1f9c4.png?v8",gb:"unicode/1f1ec-1f1e7.png?v8",gear:"unicode/2699.png?v8",gem:"unicode/1f48e.png?v8",gemini:"unicode/264a.png?v8",genie:"unicode/1f9de.png?v8",genie_man:"unicode/1f9de-2642.png?v8",genie_woman:"unicode/1f9de-2640.png?v8",georgia:"unicode/1f1ec-1f1ea.png?v8",ghana:"unicode/1f1ec-1f1ed.png?v8",ghost:"unicode/1f47b.png?v8",gibraltar:"unicode/1f1ec-1f1ee.png?v8",gift:"unicode/1f381.png?v8",gift_heart:"unicode/1f49d.png?v8",giraffe:"unicode/1f992.png?v8",girl:"unicode/1f467.png?v8",globe_with_meridians:"unicode/1f310.png?v8",gloves:"unicode/1f9e4.png?v8",goal_net:"unicode/1f945.png?v8",goat:"unicode/1f410.png?v8",goberserk:"goberserk.png?v8",godmode:"godmode.png?v8",goggles:"unicode/1f97d.png?v8",golf:"unicode/26f3.png?v8",golfing:"unicode/1f3cc.png?v8",golfing_man:"unicode/1f3cc-2642.png?v8",golfing_woman:"unicode/1f3cc-2640.png?v8",gorilla:"unicode/1f98d.png?v8",grapes:"unicode/1f347.png?v8",greece:"unicode/1f1ec-1f1f7.png?v8",green_apple:"unicode/1f34f.png?v8",green_book:"unicode/1f4d7.png?v8",green_circle:"unicode/1f7e2.png?v8",green_heart:"unicode/1f49a.png?v8",green_salad:"unicode/1f957.png?v8",green_square:"unicode/1f7e9.png?v8",greenland:"unicode/1f1ec-1f1f1.png?v8",grenada:"unicode/1f1ec-1f1e9.png?v8",grey_exclamation:"unicode/2755.png?v8",grey_question:"unicode/2754.png?v8",grimacing:"unicode/1f62c.png?v8",grin:"unicode/1f601.png?v8",grinning:"unicode/1f600.png?v8",guadeloupe:"unicode/1f1ec-1f1f5.png?v8",guam:"unicode/1f1ec-1f1fa.png?v8",guard:"unicode/1f482.png?v8",guardsman:"unicode/1f482-2642.png?v8",guardswoman:"unicode/1f482-2640.png?v8",guatemala:"unicode/1f1ec-1f1f9.png?v8",guernsey:"unicode/1f1ec-1f1ec.png?v8",guide_dog:"unicode/1f9ae.png?v8",guinea:"unicode/1f1ec-1f1f3.png?v8",guinea_bissau:"unicode/1f1ec-1f1fc.png?v8",guitar:"unicode/1f3b8.png?v8",gun:"unicode/1f52b.png?v8",guyana:"unicode/1f1ec-1f1fe.png?v8",haircut:"unicode/1f487.png?v8",haircut_man:"unicode/1f487-2642.png?v8",haircut_woman:"unicode/1f487-2640.png?v8",haiti:"unicode/1f1ed-1f1f9.png?v8",hamburger:"unicode/1f354.png?v8",hammer:"unicode/1f528.png?v8",hammer_and_pick:"unicode/2692.png?v8",hammer_and_wrench:"unicode/1f6e0.png?v8",hamster:"unicode/1f439.png?v8",hand:"unicode/270b.png?v8",hand_over_mouth:"unicode/1f92d.png?v8",handbag:"unicode/1f45c.png?v8",handball_person:"unicode/1f93e.png?v8",handshake:"unicode/1f91d.png?v8",hankey:"unicode/1f4a9.png?v8",hash:"unicode/0023-20e3.png?v8",hatched_chick:"unicode/1f425.png?v8",hatching_chick:"unicode/1f423.png?v8",headphones:"unicode/1f3a7.png?v8",headstone:"unicode/1faa6.png?v8",health_worker:"unicode/1f9d1-2695.png?v8",hear_no_evil:"unicode/1f649.png?v8",heard_mcdonald_islands:"unicode/1f1ed-1f1f2.png?v8",heart:"unicode/2764.png?v8",heart_decoration:"unicode/1f49f.png?v8",heart_eyes:"unicode/1f60d.png?v8",heart_eyes_cat:"unicode/1f63b.png?v8",heart_on_fire:"unicode/2764-1f525.png?v8",heartbeat:"unicode/1f493.png?v8",heartpulse:"unicode/1f497.png?v8",hearts:"unicode/2665.png?v8",heavy_check_mark:"unicode/2714.png?v8",heavy_division_sign:"unicode/2797.png?v8",heavy_dollar_sign:"unicode/1f4b2.png?v8",heavy_exclamation_mark:"unicode/2757.png?v8",heavy_heart_exclamation:"unicode/2763.png?v8",heavy_minus_sign:"unicode/2796.png?v8",heavy_multiplication_x:"unicode/2716.png?v8",heavy_plus_sign:"unicode/2795.png?v8",hedgehog:"unicode/1f994.png?v8",helicopter:"unicode/1f681.png?v8",herb:"unicode/1f33f.png?v8",hibiscus:"unicode/1f33a.png?v8",high_brightness:"unicode/1f506.png?v8",high_heel:"unicode/1f460.png?v8",hiking_boot:"unicode/1f97e.png?v8",hindu_temple:"unicode/1f6d5.png?v8",hippopotamus:"unicode/1f99b.png?v8",hocho:"unicode/1f52a.png?v8",hole:"unicode/1f573.png?v8",honduras:"unicode/1f1ed-1f1f3.png?v8",honey_pot:"unicode/1f36f.png?v8",honeybee:"unicode/1f41d.png?v8",hong_kong:"unicode/1f1ed-1f1f0.png?v8",hook:"unicode/1fa9d.png?v8",horse:"unicode/1f434.png?v8",horse_racing:"unicode/1f3c7.png?v8",hospital:"unicode/1f3e5.png?v8",hot_face:"unicode/1f975.png?v8",hot_pepper:"unicode/1f336.png?v8",hotdog:"unicode/1f32d.png?v8",hotel:"unicode/1f3e8.png?v8",hotsprings:"unicode/2668.png?v8",hourglass:"unicode/231b.png?v8",hourglass_flowing_sand:"unicode/23f3.png?v8",house:"unicode/1f3e0.png?v8",house_with_garden:"unicode/1f3e1.png?v8",houses:"unicode/1f3d8.png?v8",hugs:"unicode/1f917.png?v8",hungary:"unicode/1f1ed-1f1fa.png?v8",hurtrealbad:"hurtrealbad.png?v8",hushed:"unicode/1f62f.png?v8",hut:"unicode/1f6d6.png?v8",ice_cream:"unicode/1f368.png?v8",ice_cube:"unicode/1f9ca.png?v8",ice_hockey:"unicode/1f3d2.png?v8",ice_skate:"unicode/26f8.png?v8",icecream:"unicode/1f366.png?v8",iceland:"unicode/1f1ee-1f1f8.png?v8",id:"unicode/1f194.png?v8",ideograph_advantage:"unicode/1f250.png?v8",imp:"unicode/1f47f.png?v8",inbox_tray:"unicode/1f4e5.png?v8",incoming_envelope:"unicode/1f4e8.png?v8",india:"unicode/1f1ee-1f1f3.png?v8",indonesia:"unicode/1f1ee-1f1e9.png?v8",infinity:"unicode/267e.png?v8",information_desk_person:"unicode/1f481.png?v8",information_source:"unicode/2139.png?v8",innocent:"unicode/1f607.png?v8",interrobang:"unicode/2049.png?v8",iphone:"unicode/1f4f1.png?v8",iran:"unicode/1f1ee-1f1f7.png?v8",iraq:"unicode/1f1ee-1f1f6.png?v8",ireland:"unicode/1f1ee-1f1ea.png?v8",isle_of_man:"unicode/1f1ee-1f1f2.png?v8",israel:"unicode/1f1ee-1f1f1.png?v8",it:"unicode/1f1ee-1f1f9.png?v8",izakaya_lantern:"unicode/1f3ee.png?v8",jack_o_lantern:"unicode/1f383.png?v8",jamaica:"unicode/1f1ef-1f1f2.png?v8",japan:"unicode/1f5fe.png?v8",japanese_castle:"unicode/1f3ef.png?v8",japanese_goblin:"unicode/1f47a.png?v8",japanese_ogre:"unicode/1f479.png?v8",jeans:"unicode/1f456.png?v8",jersey:"unicode/1f1ef-1f1ea.png?v8",jigsaw:"unicode/1f9e9.png?v8",jordan:"unicode/1f1ef-1f1f4.png?v8",joy:"unicode/1f602.png?v8",joy_cat:"unicode/1f639.png?v8",joystick:"unicode/1f579.png?v8",jp:"unicode/1f1ef-1f1f5.png?v8",judge:"unicode/1f9d1-2696.png?v8",juggling_person:"unicode/1f939.png?v8",kaaba:"unicode/1f54b.png?v8",kangaroo:"unicode/1f998.png?v8",kazakhstan:"unicode/1f1f0-1f1ff.png?v8",kenya:"unicode/1f1f0-1f1ea.png?v8",key:"unicode/1f511.png?v8",keyboard:"unicode/2328.png?v8",keycap_ten:"unicode/1f51f.png?v8",kick_scooter:"unicode/1f6f4.png?v8",kimono:"unicode/1f458.png?v8",kiribati:"unicode/1f1f0-1f1ee.png?v8",kiss:"unicode/1f48b.png?v8",kissing:"unicode/1f617.png?v8",kissing_cat:"unicode/1f63d.png?v8",kissing_closed_eyes:"unicode/1f61a.png?v8",kissing_heart:"unicode/1f618.png?v8",kissing_smiling_eyes:"unicode/1f619.png?v8",kite:"unicode/1fa81.png?v8",kiwi_fruit:"unicode/1f95d.png?v8",kneeling_man:"unicode/1f9ce-2642.png?v8",kneeling_person:"unicode/1f9ce.png?v8",kneeling_woman:"unicode/1f9ce-2640.png?v8",knife:"unicode/1f52a.png?v8",knot:"unicode/1faa2.png?v8",koala:"unicode/1f428.png?v8",koko:"unicode/1f201.png?v8",kosovo:"unicode/1f1fd-1f1f0.png?v8",kr:"unicode/1f1f0-1f1f7.png?v8",kuwait:"unicode/1f1f0-1f1fc.png?v8",kyrgyzstan:"unicode/1f1f0-1f1ec.png?v8",lab_coat:"unicode/1f97c.png?v8",label:"unicode/1f3f7.png?v8",lacrosse:"unicode/1f94d.png?v8",ladder:"unicode/1fa9c.png?v8",lady_beetle:"unicode/1f41e.png?v8",lantern:"unicode/1f3ee.png?v8",laos:"unicode/1f1f1-1f1e6.png?v8",large_blue_circle:"unicode/1f535.png?v8",large_blue_diamond:"unicode/1f537.png?v8",large_orange_diamond:"unicode/1f536.png?v8",last_quarter_moon:"unicode/1f317.png?v8",last_quarter_moon_with_face:"unicode/1f31c.png?v8",latin_cross:"unicode/271d.png?v8",latvia:"unicode/1f1f1-1f1fb.png?v8",laughing:"unicode/1f606.png?v8",leafy_green:"unicode/1f96c.png?v8",leaves:"unicode/1f343.png?v8",lebanon:"unicode/1f1f1-1f1e7.png?v8",ledger:"unicode/1f4d2.png?v8",left_luggage:"unicode/1f6c5.png?v8",left_right_arrow:"unicode/2194.png?v8",left_speech_bubble:"unicode/1f5e8.png?v8",leftwards_arrow_with_hook:"unicode/21a9.png?v8",leg:"unicode/1f9b5.png?v8",lemon:"unicode/1f34b.png?v8",leo:"unicode/264c.png?v8",leopard:"unicode/1f406.png?v8",lesotho:"unicode/1f1f1-1f1f8.png?v8",level_slider:"unicode/1f39a.png?v8",liberia:"unicode/1f1f1-1f1f7.png?v8",libra:"unicode/264e.png?v8",libya:"unicode/1f1f1-1f1fe.png?v8",liechtenstein:"unicode/1f1f1-1f1ee.png?v8",light_rail:"unicode/1f688.png?v8",link:"unicode/1f517.png?v8",lion:"unicode/1f981.png?v8",lips:"unicode/1f444.png?v8",lipstick:"unicode/1f484.png?v8",lithuania:"unicode/1f1f1-1f1f9.png?v8",lizard:"unicode/1f98e.png?v8",llama:"unicode/1f999.png?v8",lobster:"unicode/1f99e.png?v8",lock:"unicode/1f512.png?v8",lock_with_ink_pen:"unicode/1f50f.png?v8",lollipop:"unicode/1f36d.png?v8",long_drum:"unicode/1fa98.png?v8",loop:"unicode/27bf.png?v8",lotion_bottle:"unicode/1f9f4.png?v8",lotus_position:"unicode/1f9d8.png?v8",lotus_position_man:"unicode/1f9d8-2642.png?v8",lotus_position_woman:"unicode/1f9d8-2640.png?v8",loud_sound:"unicode/1f50a.png?v8",loudspeaker:"unicode/1f4e2.png?v8",love_hotel:"unicode/1f3e9.png?v8",love_letter:"unicode/1f48c.png?v8",love_you_gesture:"unicode/1f91f.png?v8",low_brightness:"unicode/1f505.png?v8",luggage:"unicode/1f9f3.png?v8",lungs:"unicode/1fac1.png?v8",luxembourg:"unicode/1f1f1-1f1fa.png?v8",lying_face:"unicode/1f925.png?v8",m:"unicode/24c2.png?v8",macau:"unicode/1f1f2-1f1f4.png?v8",macedonia:"unicode/1f1f2-1f1f0.png?v8",madagascar:"unicode/1f1f2-1f1ec.png?v8",mag:"unicode/1f50d.png?v8",mag_right:"unicode/1f50e.png?v8",mage:"unicode/1f9d9.png?v8",mage_man:"unicode/1f9d9-2642.png?v8",mage_woman:"unicode/1f9d9-2640.png?v8",magic_wand:"unicode/1fa84.png?v8",magnet:"unicode/1f9f2.png?v8",mahjong:"unicode/1f004.png?v8",mailbox:"unicode/1f4eb.png?v8",mailbox_closed:"unicode/1f4ea.png?v8",mailbox_with_mail:"unicode/1f4ec.png?v8",mailbox_with_no_mail:"unicode/1f4ed.png?v8",malawi:"unicode/1f1f2-1f1fc.png?v8",malaysia:"unicode/1f1f2-1f1fe.png?v8",maldives:"unicode/1f1f2-1f1fb.png?v8",male_detective:"unicode/1f575-2642.png?v8",male_sign:"unicode/2642.png?v8",mali:"unicode/1f1f2-1f1f1.png?v8",malta:"unicode/1f1f2-1f1f9.png?v8",mammoth:"unicode/1f9a3.png?v8",man:"unicode/1f468.png?v8",man_artist:"unicode/1f468-1f3a8.png?v8",man_astronaut:"unicode/1f468-1f680.png?v8",man_beard:"unicode/1f9d4-2642.png?v8",man_cartwheeling:"unicode/1f938-2642.png?v8",man_cook:"unicode/1f468-1f373.png?v8",man_dancing:"unicode/1f57a.png?v8",man_facepalming:"unicode/1f926-2642.png?v8",man_factory_worker:"unicode/1f468-1f3ed.png?v8",man_farmer:"unicode/1f468-1f33e.png?v8",man_feeding_baby:"unicode/1f468-1f37c.png?v8",man_firefighter:"unicode/1f468-1f692.png?v8",man_health_worker:"unicode/1f468-2695.png?v8",man_in_manual_wheelchair:"unicode/1f468-1f9bd.png?v8",man_in_motorized_wheelchair:"unicode/1f468-1f9bc.png?v8",man_in_tuxedo:"unicode/1f935-2642.png?v8",man_judge:"unicode/1f468-2696.png?v8",man_juggling:"unicode/1f939-2642.png?v8",man_mechanic:"unicode/1f468-1f527.png?v8",man_office_worker:"unicode/1f468-1f4bc.png?v8",man_pilot:"unicode/1f468-2708.png?v8",man_playing_handball:"unicode/1f93e-2642.png?v8",man_playing_water_polo:"unicode/1f93d-2642.png?v8",man_scientist:"unicode/1f468-1f52c.png?v8",man_shrugging:"unicode/1f937-2642.png?v8",man_singer:"unicode/1f468-1f3a4.png?v8",man_student:"unicode/1f468-1f393.png?v8",man_teacher:"unicode/1f468-1f3eb.png?v8",man_technologist:"unicode/1f468-1f4bb.png?v8",man_with_gua_pi_mao:"unicode/1f472.png?v8",man_with_probing_cane:"unicode/1f468-1f9af.png?v8",man_with_turban:"unicode/1f473-2642.png?v8",man_with_veil:"unicode/1f470-2642.png?v8",mandarin:"unicode/1f34a.png?v8",mango:"unicode/1f96d.png?v8",mans_shoe:"unicode/1f45e.png?v8",mantelpiece_clock:"unicode/1f570.png?v8",manual_wheelchair:"unicode/1f9bd.png?v8",maple_leaf:"unicode/1f341.png?v8",marshall_islands:"unicode/1f1f2-1f1ed.png?v8",martial_arts_uniform:"unicode/1f94b.png?v8",martinique:"unicode/1f1f2-1f1f6.png?v8",mask:"unicode/1f637.png?v8",massage:"unicode/1f486.png?v8",massage_man:"unicode/1f486-2642.png?v8",massage_woman:"unicode/1f486-2640.png?v8",mate:"unicode/1f9c9.png?v8",mauritania:"unicode/1f1f2-1f1f7.png?v8",mauritius:"unicode/1f1f2-1f1fa.png?v8",mayotte:"unicode/1f1fe-1f1f9.png?v8",meat_on_bone:"unicode/1f356.png?v8",mechanic:"unicode/1f9d1-1f527.png?v8",mechanical_arm:"unicode/1f9be.png?v8",mechanical_leg:"unicode/1f9bf.png?v8",medal_military:"unicode/1f396.png?v8",medal_sports:"unicode/1f3c5.png?v8",medical_symbol:"unicode/2695.png?v8",mega:"unicode/1f4e3.png?v8",melon:"unicode/1f348.png?v8",memo:"unicode/1f4dd.png?v8",men_wrestling:"unicode/1f93c-2642.png?v8",mending_heart:"unicode/2764-1fa79.png?v8",menorah:"unicode/1f54e.png?v8",mens:"unicode/1f6b9.png?v8",mermaid:"unicode/1f9dc-2640.png?v8",merman:"unicode/1f9dc-2642.png?v8",merperson:"unicode/1f9dc.png?v8",metal:"unicode/1f918.png?v8",metro:"unicode/1f687.png?v8",mexico:"unicode/1f1f2-1f1fd.png?v8",microbe:"unicode/1f9a0.png?v8",micronesia:"unicode/1f1eb-1f1f2.png?v8",microphone:"unicode/1f3a4.png?v8",microscope:"unicode/1f52c.png?v8",middle_finger:"unicode/1f595.png?v8",military_helmet:"unicode/1fa96.png?v8",milk_glass:"unicode/1f95b.png?v8",milky_way:"unicode/1f30c.png?v8",minibus:"unicode/1f690.png?v8",minidisc:"unicode/1f4bd.png?v8",mirror:"unicode/1fa9e.png?v8",mobile_phone_off:"unicode/1f4f4.png?v8",moldova:"unicode/1f1f2-1f1e9.png?v8",monaco:"unicode/1f1f2-1f1e8.png?v8",money_mouth_face:"unicode/1f911.png?v8",money_with_wings:"unicode/1f4b8.png?v8",moneybag:"unicode/1f4b0.png?v8",mongolia:"unicode/1f1f2-1f1f3.png?v8",monkey:"unicode/1f412.png?v8",monkey_face:"unicode/1f435.png?v8",monocle_face:"unicode/1f9d0.png?v8",monorail:"unicode/1f69d.png?v8",montenegro:"unicode/1f1f2-1f1ea.png?v8",montserrat:"unicode/1f1f2-1f1f8.png?v8",moon:"unicode/1f314.png?v8",moon_cake:"unicode/1f96e.png?v8",morocco:"unicode/1f1f2-1f1e6.png?v8",mortar_board:"unicode/1f393.png?v8",mosque:"unicode/1f54c.png?v8",mosquito:"unicode/1f99f.png?v8",motor_boat:"unicode/1f6e5.png?v8",motor_scooter:"unicode/1f6f5.png?v8",motorcycle:"unicode/1f3cd.png?v8",motorized_wheelchair:"unicode/1f9bc.png?v8",motorway:"unicode/1f6e3.png?v8",mount_fuji:"unicode/1f5fb.png?v8",mountain:"unicode/26f0.png?v8",mountain_bicyclist:"unicode/1f6b5.png?v8",mountain_biking_man:"unicode/1f6b5-2642.png?v8",mountain_biking_woman:"unicode/1f6b5-2640.png?v8",mountain_cableway:"unicode/1f6a0.png?v8",mountain_railway:"unicode/1f69e.png?v8",mountain_snow:"unicode/1f3d4.png?v8",mouse:"unicode/1f42d.png?v8",mouse2:"unicode/1f401.png?v8",mouse_trap:"unicode/1faa4.png?v8",movie_camera:"unicode/1f3a5.png?v8",moyai:"unicode/1f5ff.png?v8",mozambique:"unicode/1f1f2-1f1ff.png?v8",mrs_claus:"unicode/1f936.png?v8",muscle:"unicode/1f4aa.png?v8",mushroom:"unicode/1f344.png?v8",musical_keyboard:"unicode/1f3b9.png?v8",musical_note:"unicode/1f3b5.png?v8",musical_score:"unicode/1f3bc.png?v8",mute:"unicode/1f507.png?v8",mx_claus:"unicode/1f9d1-1f384.png?v8",myanmar:"unicode/1f1f2-1f1f2.png?v8",nail_care:"unicode/1f485.png?v8",name_badge:"unicode/1f4db.png?v8",namibia:"unicode/1f1f3-1f1e6.png?v8",national_park:"unicode/1f3de.png?v8",nauru:"unicode/1f1f3-1f1f7.png?v8",nauseated_face:"unicode/1f922.png?v8",nazar_amulet:"unicode/1f9ff.png?v8",neckbeard:"neckbeard.png?v8",necktie:"unicode/1f454.png?v8",negative_squared_cross_mark:"unicode/274e.png?v8",nepal:"unicode/1f1f3-1f1f5.png?v8",nerd_face:"unicode/1f913.png?v8",nesting_dolls:"unicode/1fa86.png?v8",netherlands:"unicode/1f1f3-1f1f1.png?v8",neutral_face:"unicode/1f610.png?v8",new:"unicode/1f195.png?v8",new_caledonia:"unicode/1f1f3-1f1e8.png?v8",new_moon:"unicode/1f311.png?v8",new_moon_with_face:"unicode/1f31a.png?v8",new_zealand:"unicode/1f1f3-1f1ff.png?v8",newspaper:"unicode/1f4f0.png?v8",newspaper_roll:"unicode/1f5de.png?v8",next_track_button:"unicode/23ed.png?v8",ng:"unicode/1f196.png?v8",ng_man:"unicode/1f645-2642.png?v8",ng_woman:"unicode/1f645-2640.png?v8",nicaragua:"unicode/1f1f3-1f1ee.png?v8",niger:"unicode/1f1f3-1f1ea.png?v8",nigeria:"unicode/1f1f3-1f1ec.png?v8",night_with_stars:"unicode/1f303.png?v8",nine:"unicode/0039-20e3.png?v8",ninja:"unicode/1f977.png?v8",niue:"unicode/1f1f3-1f1fa.png?v8",no_bell:"unicode/1f515.png?v8",no_bicycles:"unicode/1f6b3.png?v8",no_entry:"unicode/26d4.png?v8",no_entry_sign:"unicode/1f6ab.png?v8",no_good:"unicode/1f645.png?v8",no_good_man:"unicode/1f645-2642.png?v8",no_good_woman:"unicode/1f645-2640.png?v8",no_mobile_phones:"unicode/1f4f5.png?v8",no_mouth:"unicode/1f636.png?v8",no_pedestrians:"unicode/1f6b7.png?v8",no_smoking:"unicode/1f6ad.png?v8","non-potable_water":"unicode/1f6b1.png?v8",norfolk_island:"unicode/1f1f3-1f1eb.png?v8",north_korea:"unicode/1f1f0-1f1f5.png?v8",northern_mariana_islands:"unicode/1f1f2-1f1f5.png?v8",norway:"unicode/1f1f3-1f1f4.png?v8",nose:"unicode/1f443.png?v8",notebook:"unicode/1f4d3.png?v8",notebook_with_decorative_cover:"unicode/1f4d4.png?v8",notes:"unicode/1f3b6.png?v8",nut_and_bolt:"unicode/1f529.png?v8",o:"unicode/2b55.png?v8",o2:"unicode/1f17e.png?v8",ocean:"unicode/1f30a.png?v8",octocat:"octocat.png?v8",octopus:"unicode/1f419.png?v8",oden:"unicode/1f362.png?v8",office:"unicode/1f3e2.png?v8",office_worker:"unicode/1f9d1-1f4bc.png?v8",oil_drum:"unicode/1f6e2.png?v8",ok:"unicode/1f197.png?v8",ok_hand:"unicode/1f44c.png?v8",ok_man:"unicode/1f646-2642.png?v8",ok_person:"unicode/1f646.png?v8",ok_woman:"unicode/1f646-2640.png?v8",old_key:"unicode/1f5dd.png?v8",older_adult:"unicode/1f9d3.png?v8",older_man:"unicode/1f474.png?v8",older_woman:"unicode/1f475.png?v8",olive:"unicode/1fad2.png?v8",om:"unicode/1f549.png?v8",oman:"unicode/1f1f4-1f1f2.png?v8",on:"unicode/1f51b.png?v8",oncoming_automobile:"unicode/1f698.png?v8",oncoming_bus:"unicode/1f68d.png?v8",oncoming_police_car:"unicode/1f694.png?v8",oncoming_taxi:"unicode/1f696.png?v8",one:"unicode/0031-20e3.png?v8",one_piece_swimsuit:"unicode/1fa71.png?v8",onion:"unicode/1f9c5.png?v8",open_book:"unicode/1f4d6.png?v8",open_file_folder:"unicode/1f4c2.png?v8",open_hands:"unicode/1f450.png?v8",open_mouth:"unicode/1f62e.png?v8",open_umbrella:"unicode/2602.png?v8",ophiuchus:"unicode/26ce.png?v8",orange:"unicode/1f34a.png?v8",orange_book:"unicode/1f4d9.png?v8",orange_circle:"unicode/1f7e0.png?v8",orange_heart:"unicode/1f9e1.png?v8",orange_square:"unicode/1f7e7.png?v8",orangutan:"unicode/1f9a7.png?v8",orthodox_cross:"unicode/2626.png?v8",otter:"unicode/1f9a6.png?v8",outbox_tray:"unicode/1f4e4.png?v8",owl:"unicode/1f989.png?v8",ox:"unicode/1f402.png?v8",oyster:"unicode/1f9aa.png?v8",package:"unicode/1f4e6.png?v8",page_facing_up:"unicode/1f4c4.png?v8",page_with_curl:"unicode/1f4c3.png?v8",pager:"unicode/1f4df.png?v8",paintbrush:"unicode/1f58c.png?v8",pakistan:"unicode/1f1f5-1f1f0.png?v8",palau:"unicode/1f1f5-1f1fc.png?v8",palestinian_territories:"unicode/1f1f5-1f1f8.png?v8",palm_tree:"unicode/1f334.png?v8",palms_up_together:"unicode/1f932.png?v8",panama:"unicode/1f1f5-1f1e6.png?v8",pancakes:"unicode/1f95e.png?v8",panda_face:"unicode/1f43c.png?v8",paperclip:"unicode/1f4ce.png?v8",paperclips:"unicode/1f587.png?v8",papua_new_guinea:"unicode/1f1f5-1f1ec.png?v8",parachute:"unicode/1fa82.png?v8",paraguay:"unicode/1f1f5-1f1fe.png?v8",parasol_on_ground:"unicode/26f1.png?v8",parking:"unicode/1f17f.png?v8",parrot:"unicode/1f99c.png?v8",part_alternation_mark:"unicode/303d.png?v8",partly_sunny:"unicode/26c5.png?v8",partying_face:"unicode/1f973.png?v8",passenger_ship:"unicode/1f6f3.png?v8",passport_control:"unicode/1f6c2.png?v8",pause_button:"unicode/23f8.png?v8",paw_prints:"unicode/1f43e.png?v8",peace_symbol:"unicode/262e.png?v8",peach:"unicode/1f351.png?v8",peacock:"unicode/1f99a.png?v8",peanuts:"unicode/1f95c.png?v8",pear:"unicode/1f350.png?v8",pen:"unicode/1f58a.png?v8",pencil:"unicode/1f4dd.png?v8",pencil2:"unicode/270f.png?v8",penguin:"unicode/1f427.png?v8",pensive:"unicode/1f614.png?v8",people_holding_hands:"unicode/1f9d1-1f91d-1f9d1.png?v8",people_hugging:"unicode/1fac2.png?v8",performing_arts:"unicode/1f3ad.png?v8",persevere:"unicode/1f623.png?v8",person_bald:"unicode/1f9d1-1f9b2.png?v8",person_curly_hair:"unicode/1f9d1-1f9b1.png?v8",person_feeding_baby:"unicode/1f9d1-1f37c.png?v8",person_fencing:"unicode/1f93a.png?v8",person_in_manual_wheelchair:"unicode/1f9d1-1f9bd.png?v8",person_in_motorized_wheelchair:"unicode/1f9d1-1f9bc.png?v8",person_in_tuxedo:"unicode/1f935.png?v8",person_red_hair:"unicode/1f9d1-1f9b0.png?v8",person_white_hair:"unicode/1f9d1-1f9b3.png?v8",person_with_probing_cane:"unicode/1f9d1-1f9af.png?v8",person_with_turban:"unicode/1f473.png?v8",person_with_veil:"unicode/1f470.png?v8",peru:"unicode/1f1f5-1f1ea.png?v8",petri_dish:"unicode/1f9eb.png?v8",philippines:"unicode/1f1f5-1f1ed.png?v8",phone:"unicode/260e.png?v8",pick:"unicode/26cf.png?v8",pickup_truck:"unicode/1f6fb.png?v8",pie:"unicode/1f967.png?v8",pig:"unicode/1f437.png?v8",pig2:"unicode/1f416.png?v8",pig_nose:"unicode/1f43d.png?v8",pill:"unicode/1f48a.png?v8",pilot:"unicode/1f9d1-2708.png?v8",pinata:"unicode/1fa85.png?v8",pinched_fingers:"unicode/1f90c.png?v8",pinching_hand:"unicode/1f90f.png?v8",pineapple:"unicode/1f34d.png?v8",ping_pong:"unicode/1f3d3.png?v8",pirate_flag:"unicode/1f3f4-2620.png?v8",pisces:"unicode/2653.png?v8",pitcairn_islands:"unicode/1f1f5-1f1f3.png?v8",pizza:"unicode/1f355.png?v8",placard:"unicode/1faa7.png?v8",place_of_worship:"unicode/1f6d0.png?v8",plate_with_cutlery:"unicode/1f37d.png?v8",play_or_pause_button:"unicode/23ef.png?v8",pleading_face:"unicode/1f97a.png?v8",plunger:"unicode/1faa0.png?v8",point_down:"unicode/1f447.png?v8",point_left:"unicode/1f448.png?v8",point_right:"unicode/1f449.png?v8",point_up:"unicode/261d.png?v8",point_up_2:"unicode/1f446.png?v8",poland:"unicode/1f1f5-1f1f1.png?v8",polar_bear:"unicode/1f43b-2744.png?v8",police_car:"unicode/1f693.png?v8",police_officer:"unicode/1f46e.png?v8",policeman:"unicode/1f46e-2642.png?v8",policewoman:"unicode/1f46e-2640.png?v8",poodle:"unicode/1f429.png?v8",poop:"unicode/1f4a9.png?v8",popcorn:"unicode/1f37f.png?v8",portugal:"unicode/1f1f5-1f1f9.png?v8",post_office:"unicode/1f3e3.png?v8",postal_horn:"unicode/1f4ef.png?v8",postbox:"unicode/1f4ee.png?v8",potable_water:"unicode/1f6b0.png?v8",potato:"unicode/1f954.png?v8",potted_plant:"unicode/1fab4.png?v8",pouch:"unicode/1f45d.png?v8",poultry_leg:"unicode/1f357.png?v8",pound:"unicode/1f4b7.png?v8",pout:"unicode/1f621.png?v8",pouting_cat:"unicode/1f63e.png?v8",pouting_face:"unicode/1f64e.png?v8",pouting_man:"unicode/1f64e-2642.png?v8",pouting_woman:"unicode/1f64e-2640.png?v8",pray:"unicode/1f64f.png?v8",prayer_beads:"unicode/1f4ff.png?v8",pregnant_woman:"unicode/1f930.png?v8",pretzel:"unicode/1f968.png?v8",previous_track_button:"unicode/23ee.png?v8",prince:"unicode/1f934.png?v8",princess:"unicode/1f478.png?v8",printer:"unicode/1f5a8.png?v8",probing_cane:"unicode/1f9af.png?v8",puerto_rico:"unicode/1f1f5-1f1f7.png?v8",punch:"unicode/1f44a.png?v8",purple_circle:"unicode/1f7e3.png?v8",purple_heart:"unicode/1f49c.png?v8",purple_square:"unicode/1f7ea.png?v8",purse:"unicode/1f45b.png?v8",pushpin:"unicode/1f4cc.png?v8",put_litter_in_its_place:"unicode/1f6ae.png?v8",qatar:"unicode/1f1f6-1f1e6.png?v8",question:"unicode/2753.png?v8",rabbit:"unicode/1f430.png?v8",rabbit2:"unicode/1f407.png?v8",raccoon:"unicode/1f99d.png?v8",racehorse:"unicode/1f40e.png?v8",racing_car:"unicode/1f3ce.png?v8",radio:"unicode/1f4fb.png?v8",radio_button:"unicode/1f518.png?v8",radioactive:"unicode/2622.png?v8",rage:"unicode/1f621.png?v8",rage1:"rage1.png?v8",rage2:"rage2.png?v8",rage3:"rage3.png?v8",rage4:"rage4.png?v8",railway_car:"unicode/1f683.png?v8",railway_track:"unicode/1f6e4.png?v8",rainbow:"unicode/1f308.png?v8",rainbow_flag:"unicode/1f3f3-1f308.png?v8",raised_back_of_hand:"unicode/1f91a.png?v8",raised_eyebrow:"unicode/1f928.png?v8",raised_hand:"unicode/270b.png?v8",raised_hand_with_fingers_splayed:"unicode/1f590.png?v8",raised_hands:"unicode/1f64c.png?v8",raising_hand:"unicode/1f64b.png?v8",raising_hand_man:"unicode/1f64b-2642.png?v8",raising_hand_woman:"unicode/1f64b-2640.png?v8",ram:"unicode/1f40f.png?v8",ramen:"unicode/1f35c.png?v8",rat:"unicode/1f400.png?v8",razor:"unicode/1fa92.png?v8",receipt:"unicode/1f9fe.png?v8",record_button:"unicode/23fa.png?v8",recycle:"unicode/267b.png?v8",red_car:"unicode/1f697.png?v8",red_circle:"unicode/1f534.png?v8",red_envelope:"unicode/1f9e7.png?v8",red_haired_man:"unicode/1f468-1f9b0.png?v8",red_haired_woman:"unicode/1f469-1f9b0.png?v8",red_square:"unicode/1f7e5.png?v8",registered:"unicode/00ae.png?v8",relaxed:"unicode/263a.png?v8",relieved:"unicode/1f60c.png?v8",reminder_ribbon:"unicode/1f397.png?v8",repeat:"unicode/1f501.png?v8",repeat_one:"unicode/1f502.png?v8",rescue_worker_helmet:"unicode/26d1.png?v8",restroom:"unicode/1f6bb.png?v8",reunion:"unicode/1f1f7-1f1ea.png?v8",revolving_hearts:"unicode/1f49e.png?v8",rewind:"unicode/23ea.png?v8",rhinoceros:"unicode/1f98f.png?v8",ribbon:"unicode/1f380.png?v8",rice:"unicode/1f35a.png?v8",rice_ball:"unicode/1f359.png?v8",rice_cracker:"unicode/1f358.png?v8",rice_scene:"unicode/1f391.png?v8",right_anger_bubble:"unicode/1f5ef.png?v8",ring:"unicode/1f48d.png?v8",ringed_planet:"unicode/1fa90.png?v8",robot:"unicode/1f916.png?v8",rock:"unicode/1faa8.png?v8",rocket:"unicode/1f680.png?v8",rofl:"unicode/1f923.png?v8",roll_eyes:"unicode/1f644.png?v8",roll_of_paper:"unicode/1f9fb.png?v8",roller_coaster:"unicode/1f3a2.png?v8",roller_skate:"unicode/1f6fc.png?v8",romania:"unicode/1f1f7-1f1f4.png?v8",rooster:"unicode/1f413.png?v8",rose:"unicode/1f339.png?v8",rosette:"unicode/1f3f5.png?v8",rotating_light:"unicode/1f6a8.png?v8",round_pushpin:"unicode/1f4cd.png?v8",rowboat:"unicode/1f6a3.png?v8",rowing_man:"unicode/1f6a3-2642.png?v8",rowing_woman:"unicode/1f6a3-2640.png?v8",ru:"unicode/1f1f7-1f1fa.png?v8",rugby_football:"unicode/1f3c9.png?v8",runner:"unicode/1f3c3.png?v8",running:"unicode/1f3c3.png?v8",running_man:"unicode/1f3c3-2642.png?v8",running_shirt_with_sash:"unicode/1f3bd.png?v8",running_woman:"unicode/1f3c3-2640.png?v8",rwanda:"unicode/1f1f7-1f1fc.png?v8",sa:"unicode/1f202.png?v8",safety_pin:"unicode/1f9f7.png?v8",safety_vest:"unicode/1f9ba.png?v8",sagittarius:"unicode/2650.png?v8",sailboat:"unicode/26f5.png?v8",sake:"unicode/1f376.png?v8",salt:"unicode/1f9c2.png?v8",samoa:"unicode/1f1fc-1f1f8.png?v8",san_marino:"unicode/1f1f8-1f1f2.png?v8",sandal:"unicode/1f461.png?v8",sandwich:"unicode/1f96a.png?v8",santa:"unicode/1f385.png?v8",sao_tome_principe:"unicode/1f1f8-1f1f9.png?v8",sari:"unicode/1f97b.png?v8",sassy_man:"unicode/1f481-2642.png?v8",sassy_woman:"unicode/1f481-2640.png?v8",satellite:"unicode/1f4e1.png?v8",satisfied:"unicode/1f606.png?v8",saudi_arabia:"unicode/1f1f8-1f1e6.png?v8",sauna_man:"unicode/1f9d6-2642.png?v8",sauna_person:"unicode/1f9d6.png?v8",sauna_woman:"unicode/1f9d6-2640.png?v8",sauropod:"unicode/1f995.png?v8",saxophone:"unicode/1f3b7.png?v8",scarf:"unicode/1f9e3.png?v8",school:"unicode/1f3eb.png?v8",school_satchel:"unicode/1f392.png?v8",scientist:"unicode/1f9d1-1f52c.png?v8",scissors:"unicode/2702.png?v8",scorpion:"unicode/1f982.png?v8",scorpius:"unicode/264f.png?v8",scotland:"unicode/1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.png?v8",scream:"unicode/1f631.png?v8",scream_cat:"unicode/1f640.png?v8",screwdriver:"unicode/1fa9b.png?v8",scroll:"unicode/1f4dc.png?v8",seal:"unicode/1f9ad.png?v8",seat:"unicode/1f4ba.png?v8",secret:"unicode/3299.png?v8",see_no_evil:"unicode/1f648.png?v8",seedling:"unicode/1f331.png?v8",selfie:"unicode/1f933.png?v8",senegal:"unicode/1f1f8-1f1f3.png?v8",serbia:"unicode/1f1f7-1f1f8.png?v8",service_dog:"unicode/1f415-1f9ba.png?v8",seven:"unicode/0037-20e3.png?v8",sewing_needle:"unicode/1faa1.png?v8",seychelles:"unicode/1f1f8-1f1e8.png?v8",shallow_pan_of_food:"unicode/1f958.png?v8",shamrock:"unicode/2618.png?v8",shark:"unicode/1f988.png?v8",shaved_ice:"unicode/1f367.png?v8",sheep:"unicode/1f411.png?v8",shell:"unicode/1f41a.png?v8",shield:"unicode/1f6e1.png?v8",shinto_shrine:"unicode/26e9.png?v8",ship:"unicode/1f6a2.png?v8",shipit:"shipit.png?v8",shirt:"unicode/1f455.png?v8",shit:"unicode/1f4a9.png?v8",shoe:"unicode/1f45e.png?v8",shopping:"unicode/1f6cd.png?v8",shopping_cart:"unicode/1f6d2.png?v8",shorts:"unicode/1fa73.png?v8",shower:"unicode/1f6bf.png?v8",shrimp:"unicode/1f990.png?v8",shrug:"unicode/1f937.png?v8",shushing_face:"unicode/1f92b.png?v8",sierra_leone:"unicode/1f1f8-1f1f1.png?v8",signal_strength:"unicode/1f4f6.png?v8",singapore:"unicode/1f1f8-1f1ec.png?v8",singer:"unicode/1f9d1-1f3a4.png?v8",sint_maarten:"unicode/1f1f8-1f1fd.png?v8",six:"unicode/0036-20e3.png?v8",six_pointed_star:"unicode/1f52f.png?v8",skateboard:"unicode/1f6f9.png?v8",ski:"unicode/1f3bf.png?v8",skier:"unicode/26f7.png?v8",skull:"unicode/1f480.png?v8",skull_and_crossbones:"unicode/2620.png?v8",skunk:"unicode/1f9a8.png?v8",sled:"unicode/1f6f7.png?v8",sleeping:"unicode/1f634.png?v8",sleeping_bed:"unicode/1f6cc.png?v8",sleepy:"unicode/1f62a.png?v8",slightly_frowning_face:"unicode/1f641.png?v8",slightly_smiling_face:"unicode/1f642.png?v8",slot_machine:"unicode/1f3b0.png?v8",sloth:"unicode/1f9a5.png?v8",slovakia:"unicode/1f1f8-1f1f0.png?v8",slovenia:"unicode/1f1f8-1f1ee.png?v8",small_airplane:"unicode/1f6e9.png?v8",small_blue_diamond:"unicode/1f539.png?v8",small_orange_diamond:"unicode/1f538.png?v8",small_red_triangle:"unicode/1f53a.png?v8",small_red_triangle_down:"unicode/1f53b.png?v8",smile:"unicode/1f604.png?v8",smile_cat:"unicode/1f638.png?v8",smiley:"unicode/1f603.png?v8",smiley_cat:"unicode/1f63a.png?v8",smiling_face_with_tear:"unicode/1f972.png?v8",smiling_face_with_three_hearts:"unicode/1f970.png?v8",smiling_imp:"unicode/1f608.png?v8",smirk:"unicode/1f60f.png?v8",smirk_cat:"unicode/1f63c.png?v8",smoking:"unicode/1f6ac.png?v8",snail:"unicode/1f40c.png?v8",snake:"unicode/1f40d.png?v8",sneezing_face:"unicode/1f927.png?v8",snowboarder:"unicode/1f3c2.png?v8",snowflake:"unicode/2744.png?v8",snowman:"unicode/26c4.png?v8",snowman_with_snow:"unicode/2603.png?v8",soap:"unicode/1f9fc.png?v8",sob:"unicode/1f62d.png?v8",soccer:"unicode/26bd.png?v8",socks:"unicode/1f9e6.png?v8",softball:"unicode/1f94e.png?v8",solomon_islands:"unicode/1f1f8-1f1e7.png?v8",somalia:"unicode/1f1f8-1f1f4.png?v8",soon:"unicode/1f51c.png?v8",sos:"unicode/1f198.png?v8",sound:"unicode/1f509.png?v8",south_africa:"unicode/1f1ff-1f1e6.png?v8",south_georgia_south_sandwich_islands:"unicode/1f1ec-1f1f8.png?v8",south_sudan:"unicode/1f1f8-1f1f8.png?v8",space_invader:"unicode/1f47e.png?v8",spades:"unicode/2660.png?v8",spaghetti:"unicode/1f35d.png?v8",sparkle:"unicode/2747.png?v8",sparkler:"unicode/1f387.png?v8",sparkles:"unicode/2728.png?v8",sparkling_heart:"unicode/1f496.png?v8",speak_no_evil:"unicode/1f64a.png?v8",speaker:"unicode/1f508.png?v8",speaking_head:"unicode/1f5e3.png?v8",speech_balloon:"unicode/1f4ac.png?v8",speedboat:"unicode/1f6a4.png?v8",spider:"unicode/1f577.png?v8",spider_web:"unicode/1f578.png?v8",spiral_calendar:"unicode/1f5d3.png?v8",spiral_notepad:"unicode/1f5d2.png?v8",sponge:"unicode/1f9fd.png?v8",spoon:"unicode/1f944.png?v8",squid:"unicode/1f991.png?v8",sri_lanka:"unicode/1f1f1-1f1f0.png?v8",st_barthelemy:"unicode/1f1e7-1f1f1.png?v8",st_helena:"unicode/1f1f8-1f1ed.png?v8",st_kitts_nevis:"unicode/1f1f0-1f1f3.png?v8",st_lucia:"unicode/1f1f1-1f1e8.png?v8",st_martin:"unicode/1f1f2-1f1eb.png?v8",st_pierre_miquelon:"unicode/1f1f5-1f1f2.png?v8",st_vincent_grenadines:"unicode/1f1fb-1f1e8.png?v8",stadium:"unicode/1f3df.png?v8",standing_man:"unicode/1f9cd-2642.png?v8",standing_person:"unicode/1f9cd.png?v8",standing_woman:"unicode/1f9cd-2640.png?v8",star:"unicode/2b50.png?v8",star2:"unicode/1f31f.png?v8",star_and_crescent:"unicode/262a.png?v8",star_of_david:"unicode/2721.png?v8",star_struck:"unicode/1f929.png?v8",stars:"unicode/1f320.png?v8",station:"unicode/1f689.png?v8",statue_of_liberty:"unicode/1f5fd.png?v8",steam_locomotive:"unicode/1f682.png?v8",stethoscope:"unicode/1fa7a.png?v8",stew:"unicode/1f372.png?v8",stop_button:"unicode/23f9.png?v8",stop_sign:"unicode/1f6d1.png?v8",stopwatch:"unicode/23f1.png?v8",straight_ruler:"unicode/1f4cf.png?v8",strawberry:"unicode/1f353.png?v8",stuck_out_tongue:"unicode/1f61b.png?v8",stuck_out_tongue_closed_eyes:"unicode/1f61d.png?v8",stuck_out_tongue_winking_eye:"unicode/1f61c.png?v8",student:"unicode/1f9d1-1f393.png?v8",studio_microphone:"unicode/1f399.png?v8",stuffed_flatbread:"unicode/1f959.png?v8",sudan:"unicode/1f1f8-1f1e9.png?v8",sun_behind_large_cloud:"unicode/1f325.png?v8",sun_behind_rain_cloud:"unicode/1f326.png?v8",sun_behind_small_cloud:"unicode/1f324.png?v8",sun_with_face:"unicode/1f31e.png?v8",sunflower:"unicode/1f33b.png?v8",sunglasses:"unicode/1f60e.png?v8",sunny:"unicode/2600.png?v8",sunrise:"unicode/1f305.png?v8",sunrise_over_mountains:"unicode/1f304.png?v8",superhero:"unicode/1f9b8.png?v8",superhero_man:"unicode/1f9b8-2642.png?v8",superhero_woman:"unicode/1f9b8-2640.png?v8",supervillain:"unicode/1f9b9.png?v8",supervillain_man:"unicode/1f9b9-2642.png?v8",supervillain_woman:"unicode/1f9b9-2640.png?v8",surfer:"unicode/1f3c4.png?v8",surfing_man:"unicode/1f3c4-2642.png?v8",surfing_woman:"unicode/1f3c4-2640.png?v8",suriname:"unicode/1f1f8-1f1f7.png?v8",sushi:"unicode/1f363.png?v8",suspect:"suspect.png?v8",suspension_railway:"unicode/1f69f.png?v8",svalbard_jan_mayen:"unicode/1f1f8-1f1ef.png?v8",swan:"unicode/1f9a2.png?v8",swaziland:"unicode/1f1f8-1f1ff.png?v8",sweat:"unicode/1f613.png?v8",sweat_drops:"unicode/1f4a6.png?v8",sweat_smile:"unicode/1f605.png?v8",sweden:"unicode/1f1f8-1f1ea.png?v8",sweet_potato:"unicode/1f360.png?v8",swim_brief:"unicode/1fa72.png?v8",swimmer:"unicode/1f3ca.png?v8",swimming_man:"unicode/1f3ca-2642.png?v8",swimming_woman:"unicode/1f3ca-2640.png?v8",switzerland:"unicode/1f1e8-1f1ed.png?v8",symbols:"unicode/1f523.png?v8",synagogue:"unicode/1f54d.png?v8",syria:"unicode/1f1f8-1f1fe.png?v8",syringe:"unicode/1f489.png?v8","t-rex":"unicode/1f996.png?v8",taco:"unicode/1f32e.png?v8",tada:"unicode/1f389.png?v8",taiwan:"unicode/1f1f9-1f1fc.png?v8",tajikistan:"unicode/1f1f9-1f1ef.png?v8",takeout_box:"unicode/1f961.png?v8",tamale:"unicode/1fad4.png?v8",tanabata_tree:"unicode/1f38b.png?v8",tangerine:"unicode/1f34a.png?v8",tanzania:"unicode/1f1f9-1f1ff.png?v8",taurus:"unicode/2649.png?v8",taxi:"unicode/1f695.png?v8",tea:"unicode/1f375.png?v8",teacher:"unicode/1f9d1-1f3eb.png?v8",teapot:"unicode/1fad6.png?v8",technologist:"unicode/1f9d1-1f4bb.png?v8",teddy_bear:"unicode/1f9f8.png?v8",telephone:"unicode/260e.png?v8",telephone_receiver:"unicode/1f4de.png?v8",telescope:"unicode/1f52d.png?v8",tennis:"unicode/1f3be.png?v8",tent:"unicode/26fa.png?v8",test_tube:"unicode/1f9ea.png?v8",thailand:"unicode/1f1f9-1f1ed.png?v8",thermometer:"unicode/1f321.png?v8",thinking:"unicode/1f914.png?v8",thong_sandal:"unicode/1fa74.png?v8",thought_balloon:"unicode/1f4ad.png?v8",thread:"unicode/1f9f5.png?v8",three:"unicode/0033-20e3.png?v8",thumbsdown:"unicode/1f44e.png?v8",thumbsup:"unicode/1f44d.png?v8",ticket:"unicode/1f3ab.png?v8",tickets:"unicode/1f39f.png?v8",tiger:"unicode/1f42f.png?v8",tiger2:"unicode/1f405.png?v8",timer_clock:"unicode/23f2.png?v8",timor_leste:"unicode/1f1f9-1f1f1.png?v8",tipping_hand_man:"unicode/1f481-2642.png?v8",tipping_hand_person:"unicode/1f481.png?v8",tipping_hand_woman:"unicode/1f481-2640.png?v8",tired_face:"unicode/1f62b.png?v8",tm:"unicode/2122.png?v8",togo:"unicode/1f1f9-1f1ec.png?v8",toilet:"unicode/1f6bd.png?v8",tokelau:"unicode/1f1f9-1f1f0.png?v8",tokyo_tower:"unicode/1f5fc.png?v8",tomato:"unicode/1f345.png?v8",tonga:"unicode/1f1f9-1f1f4.png?v8",tongue:"unicode/1f445.png?v8",toolbox:"unicode/1f9f0.png?v8",tooth:"unicode/1f9b7.png?v8",toothbrush:"unicode/1faa5.png?v8",top:"unicode/1f51d.png?v8",tophat:"unicode/1f3a9.png?v8",tornado:"unicode/1f32a.png?v8",tr:"unicode/1f1f9-1f1f7.png?v8",trackball:"unicode/1f5b2.png?v8",tractor:"unicode/1f69c.png?v8",traffic_light:"unicode/1f6a5.png?v8",train:"unicode/1f68b.png?v8",train2:"unicode/1f686.png?v8",tram:"unicode/1f68a.png?v8",transgender_flag:"unicode/1f3f3-26a7.png?v8",transgender_symbol:"unicode/26a7.png?v8",triangular_flag_on_post:"unicode/1f6a9.png?v8",triangular_ruler:"unicode/1f4d0.png?v8",trident:"unicode/1f531.png?v8",trinidad_tobago:"unicode/1f1f9-1f1f9.png?v8",tristan_da_cunha:"unicode/1f1f9-1f1e6.png?v8",triumph:"unicode/1f624.png?v8",trolleybus:"unicode/1f68e.png?v8",trollface:"trollface.png?v8",trophy:"unicode/1f3c6.png?v8",tropical_drink:"unicode/1f379.png?v8",tropical_fish:"unicode/1f420.png?v8",truck:"unicode/1f69a.png?v8",trumpet:"unicode/1f3ba.png?v8",tshirt:"unicode/1f455.png?v8",tulip:"unicode/1f337.png?v8",tumbler_glass:"unicode/1f943.png?v8",tunisia:"unicode/1f1f9-1f1f3.png?v8",turkey:"unicode/1f983.png?v8",turkmenistan:"unicode/1f1f9-1f1f2.png?v8",turks_caicos_islands:"unicode/1f1f9-1f1e8.png?v8",turtle:"unicode/1f422.png?v8",tuvalu:"unicode/1f1f9-1f1fb.png?v8",tv:"unicode/1f4fa.png?v8",twisted_rightwards_arrows:"unicode/1f500.png?v8",two:"unicode/0032-20e3.png?v8",two_hearts:"unicode/1f495.png?v8",two_men_holding_hands:"unicode/1f46c.png?v8",two_women_holding_hands:"unicode/1f46d.png?v8",u5272:"unicode/1f239.png?v8",u5408:"unicode/1f234.png?v8",u55b6:"unicode/1f23a.png?v8",u6307:"unicode/1f22f.png?v8",u6708:"unicode/1f237.png?v8",u6709:"unicode/1f236.png?v8",u6e80:"unicode/1f235.png?v8",u7121:"unicode/1f21a.png?v8",u7533:"unicode/1f238.png?v8",u7981:"unicode/1f232.png?v8",u7a7a:"unicode/1f233.png?v8",uganda:"unicode/1f1fa-1f1ec.png?v8",uk:"unicode/1f1ec-1f1e7.png?v8",ukraine:"unicode/1f1fa-1f1e6.png?v8",umbrella:"unicode/2614.png?v8",unamused:"unicode/1f612.png?v8",underage:"unicode/1f51e.png?v8",unicorn:"unicode/1f984.png?v8",united_arab_emirates:"unicode/1f1e6-1f1ea.png?v8",united_nations:"unicode/1f1fa-1f1f3.png?v8",unlock:"unicode/1f513.png?v8",up:"unicode/1f199.png?v8",upside_down_face:"unicode/1f643.png?v8",uruguay:"unicode/1f1fa-1f1fe.png?v8",us:"unicode/1f1fa-1f1f8.png?v8",us_outlying_islands:"unicode/1f1fa-1f1f2.png?v8",us_virgin_islands:"unicode/1f1fb-1f1ee.png?v8",uzbekistan:"unicode/1f1fa-1f1ff.png?v8",v:"unicode/270c.png?v8",vampire:"unicode/1f9db.png?v8",vampire_man:"unicode/1f9db-2642.png?v8",vampire_woman:"unicode/1f9db-2640.png?v8",vanuatu:"unicode/1f1fb-1f1fa.png?v8",vatican_city:"unicode/1f1fb-1f1e6.png?v8",venezuela:"unicode/1f1fb-1f1ea.png?v8",vertical_traffic_light:"unicode/1f6a6.png?v8",vhs:"unicode/1f4fc.png?v8",vibration_mode:"unicode/1f4f3.png?v8",video_camera:"unicode/1f4f9.png?v8",video_game:"unicode/1f3ae.png?v8",vietnam:"unicode/1f1fb-1f1f3.png?v8",violin:"unicode/1f3bb.png?v8",virgo:"unicode/264d.png?v8",volcano:"unicode/1f30b.png?v8",volleyball:"unicode/1f3d0.png?v8",vomiting_face:"unicode/1f92e.png?v8",vs:"unicode/1f19a.png?v8",vulcan_salute:"unicode/1f596.png?v8",waffle:"unicode/1f9c7.png?v8",wales:"unicode/1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.png?v8",walking:"unicode/1f6b6.png?v8",walking_man:"unicode/1f6b6-2642.png?v8",walking_woman:"unicode/1f6b6-2640.png?v8",wallis_futuna:"unicode/1f1fc-1f1eb.png?v8",waning_crescent_moon:"unicode/1f318.png?v8",waning_gibbous_moon:"unicode/1f316.png?v8",warning:"unicode/26a0.png?v8",wastebasket:"unicode/1f5d1.png?v8",watch:"unicode/231a.png?v8",water_buffalo:"unicode/1f403.png?v8",water_polo:"unicode/1f93d.png?v8",watermelon:"unicode/1f349.png?v8",wave:"unicode/1f44b.png?v8",wavy_dash:"unicode/3030.png?v8",waxing_crescent_moon:"unicode/1f312.png?v8",waxing_gibbous_moon:"unicode/1f314.png?v8",wc:"unicode/1f6be.png?v8",weary:"unicode/1f629.png?v8",wedding:"unicode/1f492.png?v8",weight_lifting:"unicode/1f3cb.png?v8",weight_lifting_man:"unicode/1f3cb-2642.png?v8",weight_lifting_woman:"unicode/1f3cb-2640.png?v8",western_sahara:"unicode/1f1ea-1f1ed.png?v8",whale:"unicode/1f433.png?v8",whale2:"unicode/1f40b.png?v8",wheel_of_dharma:"unicode/2638.png?v8",wheelchair:"unicode/267f.png?v8",white_check_mark:"unicode/2705.png?v8",white_circle:"unicode/26aa.png?v8",white_flag:"unicode/1f3f3.png?v8",white_flower:"unicode/1f4ae.png?v8",white_haired_man:"unicode/1f468-1f9b3.png?v8",white_haired_woman:"unicode/1f469-1f9b3.png?v8",white_heart:"unicode/1f90d.png?v8",white_large_square:"unicode/2b1c.png?v8",white_medium_small_square:"unicode/25fd.png?v8",white_medium_square:"unicode/25fb.png?v8",white_small_square:"unicode/25ab.png?v8",white_square_button:"unicode/1f533.png?v8",wilted_flower:"unicode/1f940.png?v8",wind_chime:"unicode/1f390.png?v8",wind_face:"unicode/1f32c.png?v8",window:"unicode/1fa9f.png?v8",wine_glass:"unicode/1f377.png?v8",wink:"unicode/1f609.png?v8",wolf:"unicode/1f43a.png?v8",woman:"unicode/1f469.png?v8",woman_artist:"unicode/1f469-1f3a8.png?v8",woman_astronaut:"unicode/1f469-1f680.png?v8",woman_beard:"unicode/1f9d4-2640.png?v8",woman_cartwheeling:"unicode/1f938-2640.png?v8",woman_cook:"unicode/1f469-1f373.png?v8",woman_dancing:"unicode/1f483.png?v8",woman_facepalming:"unicode/1f926-2640.png?v8",woman_factory_worker:"unicode/1f469-1f3ed.png?v8",woman_farmer:"unicode/1f469-1f33e.png?v8",woman_feeding_baby:"unicode/1f469-1f37c.png?v8",woman_firefighter:"unicode/1f469-1f692.png?v8",woman_health_worker:"unicode/1f469-2695.png?v8",woman_in_manual_wheelchair:"unicode/1f469-1f9bd.png?v8",woman_in_motorized_wheelchair:"unicode/1f469-1f9bc.png?v8",woman_in_tuxedo:"unicode/1f935-2640.png?v8",woman_judge:"unicode/1f469-2696.png?v8",woman_juggling:"unicode/1f939-2640.png?v8",woman_mechanic:"unicode/1f469-1f527.png?v8",woman_office_worker:"unicode/1f469-1f4bc.png?v8",woman_pilot:"unicode/1f469-2708.png?v8",woman_playing_handball:"unicode/1f93e-2640.png?v8",woman_playing_water_polo:"unicode/1f93d-2640.png?v8",woman_scientist:"unicode/1f469-1f52c.png?v8",woman_shrugging:"unicode/1f937-2640.png?v8",woman_singer:"unicode/1f469-1f3a4.png?v8",woman_student:"unicode/1f469-1f393.png?v8",woman_teacher:"unicode/1f469-1f3eb.png?v8",woman_technologist:"unicode/1f469-1f4bb.png?v8",woman_with_headscarf:"unicode/1f9d5.png?v8",woman_with_probing_cane:"unicode/1f469-1f9af.png?v8",woman_with_turban:"unicode/1f473-2640.png?v8",woman_with_veil:"unicode/1f470-2640.png?v8",womans_clothes:"unicode/1f45a.png?v8",womans_hat:"unicode/1f452.png?v8",women_wrestling:"unicode/1f93c-2640.png?v8",womens:"unicode/1f6ba.png?v8",wood:"unicode/1fab5.png?v8",woozy_face:"unicode/1f974.png?v8",world_map:"unicode/1f5fa.png?v8",worm:"unicode/1fab1.png?v8",worried:"unicode/1f61f.png?v8",wrench:"unicode/1f527.png?v8",wrestling:"unicode/1f93c.png?v8",writing_hand:"unicode/270d.png?v8",x:"unicode/274c.png?v8",yarn:"unicode/1f9f6.png?v8",yawning_face:"unicode/1f971.png?v8",yellow_circle:"unicode/1f7e1.png?v8",yellow_heart:"unicode/1f49b.png?v8",yellow_square:"unicode/1f7e8.png?v8",yemen:"unicode/1f1fe-1f1ea.png?v8",yen:"unicode/1f4b4.png?v8",yin_yang:"unicode/262f.png?v8",yo_yo:"unicode/1fa80.png?v8",yum:"unicode/1f60b.png?v8",zambia:"unicode/1f1ff-1f1f2.png?v8",zany_face:"unicode/1f92a.png?v8",zap:"unicode/26a1.png?v8",zebra:"unicode/1f993.png?v8",zero:"unicode/0030-20e3.png?v8",zimbabwe:"unicode/1f1ff-1f1fc.png?v8",zipper_mouth_face:"unicode/1f910.png?v8",zombie:"unicode/1f9df.png?v8",zombie_man:"unicode/1f9df-2642.png?v8",zombie_woman:"unicode/1f9df-2640.png?v8",zzz:"unicode/1f4a4.png?v8"}};function jn(e,t){return e.replace(/<(code|pre|script|template)[^>]*?>[\s\S]+?<\/(code|pre|script|template)>/g,function(e){return e.replace(/:/g,"__colon__")}).replace(//g,function(e){return e.replace(/:/g,"__colon__")}).replace(/([a-z]{2,}:)?\/\/[^\s'">)]+/gi,function(e){return e.replace(/:/g,"__colon__")}).replace(/:([a-z0-9_\-+]+?):/g,function(e,n){return i=e,o=n,e=t,n=Rn.data[o],i,i=n?e&&/unicode/.test(n)?''+n.replace("unicode/","").replace(/\.png.*/,"").split("-").map(function(e){return"&#x"+e+";"}).join("‍").concat("︎")+"":''+o+'':i;var i,o}).replace(/__colon__/g,":")}function On(e){var o={};return{str:e=(e=void 0===e?"":e)&&e.replace(/^('|")/,"").replace(/('|")$/,"").replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g,function(e,n,i){return-1===n.indexOf(":")?(o[n]=i&&i.replace(/"/g,"")||!0,""):e}).trim(),config:o}}function Ln(e){return(e=void 0===e?"":e).replace(/(<\/?a.*?>)/gi,"")}var qn,Pn=be(function(e){var u,f,p,d,n,g=function(u){var i=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,n=0,e={},T={manual:u.Prism&&u.Prism.manual,disableWorkerMessageHandler:u.Prism&&u.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof C?new C(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=r.reach);m+=_.value.length,_=_.next){var b=_.value;if(i.length>n.length)return;if(!(b instanceof C)){var k,w=1;if(l){if(!(k=R(h,m,n,s))||k.index>=n.length)break;var y=k.index,x=k.index+k[0].length,S=m;for(S+=_.value.length;S<=y;)_=_.next,S+=_.value.length;if(S-=_.value.length,m=S,_.value instanceof C)continue;for(var A=_;A!==i.tail&&(Sr.reach&&(r.reach=E);b=_.prev;z&&(b=j(i,b,z),m+=z.length),O(i,b,w);$=new C(c,g?T.tokenize($,g):$,v,$);_=j(i,b,$),F&&j(i,_,F),1r.reach&&(r.reach=E.reach))}}}}}(e,t,n,t.head,0),function(e){var n=[],i=e.head.next;for(;i!==e.tail;)n.push(i.value),i=i.next;return n}(t)},hooks:{all:{},add:function(e,n){var i=T.hooks.all;i[e]=i[e]||[],i[e].push(n)},run:function(e,n){var i=T.hooks.all[e];if(i&&i.length)for(var o,t=0;o=i[t++];)o(n)}},Token:C};function C(e,n,i,o){this.type=e,this.content=n,this.alias=i,this.length=0|(o||"").length}function R(e,n,i,o){e.lastIndex=n;i=e.exec(i);return i&&o&&i[1]&&(o=i[1].length,i.index+=o,i[0]=i[0].slice(o)),i}function a(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function j(e,n,i){var o=n.next,i={value:i,prev:n,next:o};return n.next=i,o.prev=i,e.length++,i}function O(e,n,i){for(var o=n.next,t=0;t"+t.content+""},!u.document)return u.addEventListener&&(T.disableWorkerMessageHandler||u.addEventListener("message",function(e){var n=JSON.parse(e.data),i=n.language,e=n.code,n=n.immediateClose;u.postMessage(T.highlight(e,T.languages[i],i)),n&&u.close()},!1)),T;var o=T.util.currentScript();function t(){T.manual||T.highlightAll()}return o&&(T.filename=o.src,o.hasAttribute("data-manual")&&(T.manual=!0)),T.manual||("loading"===(e=document.readyState)||"interactive"===e&&o&&o.defer?document.addEventListener("DOMContentLoaded",t):window.requestAnimationFrame?window.requestAnimationFrame(t):window.setTimeout(t,16)),T}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});e.exports&&(e.exports=g),void 0!==me&&(me.Prism=g),g.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},g.languages.markup.tag.inside["attr-value"].inside.entity=g.languages.markup.entity,g.languages.markup.doctype.inside["internal-subset"].inside=g.languages.markup,g.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),Object.defineProperty(g.languages.markup.tag,"addInlined",{value:function(e,n){var i={};i["language-"+n]={pattern:/(^$)/i,lookbehind:!0,inside:g.languages[n]},i.cdata=/^$/i;i={"included-cdata":{pattern://i,inside:i}};i["language-"+n]={pattern:/[\s\S]+/,inside:g.languages[n]};n={};n[e]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,function(){return e}),"i"),lookbehind:!0,greedy:!0,inside:i},g.languages.insertBefore("markup","cdata",n)}}),Object.defineProperty(g.languages.markup.tag,"addAttribute",{value:function(e,n){g.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[n,"language-"+n],inside:g.languages[n]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),g.languages.html=g.languages.markup,g.languages.mathml=g.languages.markup,g.languages.svg=g.languages.markup,g.languages.xml=g.languages.extend("markup",{}),g.languages.ssml=g.languages.xml,g.languages.atom=g.languages.xml,g.languages.rss=g.languages.xml,function(e){var n=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-](?:[^;{\s]|\s+(?![\s{]))*(?:;|(?=\s*\{))/,inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+n.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+n.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+n.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:n,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;e=e.languages.markup;e&&(e.tag.addInlined("style","css"),e.tag.addAttribute("style","css"))}(g),g.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},g.languages.javascript=g.languages.extend("clike",{"class-name":[g.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),g.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,g.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:g.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:g.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:g.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:g.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:g.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),g.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:g.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),g.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),g.languages.markup&&(g.languages.markup.tag.addInlined("script","javascript"),g.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),g.languages.js=g.languages.javascript,void 0!==g&&"undefined"!=typeof document&&(Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector),u={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"},d="pre[data-src]:not(["+(f="data-src-status")+'="loaded"]):not(['+f+'="'+(p="loading")+'"])',g.hooks.add("before-highlightall",function(e){e.selector+=", "+d}),g.hooks.add("before-sanity-check",function(e){var t,n,i,o,a,r,c=e.element;c.matches(d)&&(e.code="",c.setAttribute(f,p),(t=c.appendChild(document.createElement("CODE"))).textContent="Loading…",i=c.getAttribute("data-src"),"none"===(e=e.language)&&(n=(/\.(\w+)$/.exec(i)||[,"none"])[1],e=u[n]||n),g.util.setLanguage(t,e),g.util.setLanguage(c,e),(n=g.plugins.autoloader)&&n.loadLanguages(e),i=i,o=function(e){c.setAttribute(f,"loaded");var n,i,o=function(e){if(i=/^\s*(\d+)\s*(?:(,)\s*(?:(\d+)\s*)?)?$/.exec(e||"")){var n=Number(i[1]),e=i[2],i=i[3];return e?i?[n,Number(i)]:[n,void 0]:[n,n]}}(c.getAttribute("data-range"));o&&(n=e.split(/\r\n?|\n/g),i=o[0],o=null==o[1]?n.length:o[1],i<0&&(i+=n.length),i=Math.max(0,Math.min(i-1,n.length)),o<0&&(o+=n.length),o=Math.max(0,Math.min(o,n.length)),e=n.slice(i,o).join("\n"),c.hasAttribute("data-start")||c.setAttribute("data-start",String(i+1))),t.textContent=e,g.highlightElement(t)},a=function(e){c.setAttribute(f,"failed"),t.textContent=e},(r=new XMLHttpRequest).open("GET",i,!0),r.onreadystatechange=function(){4==r.readyState&&(r.status<400&&r.responseText?o(r.responseText):400<=r.status?a("✖ Error "+r.status+" while fetching file: "+r.statusText):a("✖ Error: File does not exist or is empty"))},r.send(null))}),n=!(g.plugins.fileHighlight={highlight:function(e){for(var n,i=(e||document).querySelectorAll(d),o=0;n=i[o++];)g.highlightElement(n)}}),g.fileHighlight=function(){n||(console.warn("Prism.fileHighlight is deprecated. Use `Prism.plugins.fileHighlight.highlight` instead."),n=!0),g.plugins.fileHighlight.highlight.apply(this,arguments)})});function Mn(e,n){return"___"+e.toUpperCase()+n+"___"}qn=Prism,Object.defineProperties(qn.languages["markup-templating"]={},{buildPlaceholders:{value:function(o,t,e,a){var r;o.language===t&&(r=o.tokenStack=[],o.code=o.code.replace(e,function(e){if("function"==typeof a&&!a(e))return e;for(var n,i=r.length;-1!==o.code.indexOf(n=Mn(t,i));)++i;return r[i]=e,n}),o.grammar=qn.languages.markup)}},tokenizePlaceholders:{value:function(f,p){var d,g;f.language===p&&f.tokenStack&&(f.grammar=qn.languages[p],d=0,g=Object.keys(f.tokenStack),function e(n){for(var i=0;i=g.length);i++){var o,t,a,r,c,u=n[i];"string"==typeof u||u.content&&"string"==typeof u.content?(t=g[d],a=f.tokenStack[t],o="string"==typeof u?u:u.content,c=Mn(p,t),-1<(r=o.indexOf(c))&&(++d,t=o.substring(0,r),a=new qn.Token(p,qn.tokenize(a,f.grammar),"language-"+p,a),r=o.substring(r+c.length),c=[],t&&c.push.apply(c,e([t])),c.push(a),r&&c.push.apply(c,e([r])),"string"==typeof u?n.splice.apply(n,[i,1].concat(c)):u.content=c)):u.content&&e(u.content)}return n}(f.tokens))}}});function In(t,e){var a=this;this.config=t,this.router=e,this.cacheTree={},this.toc=[],this.cacheTOC={},this.linkTarget=t.externalLinkTarget||"_blank",this.linkRel="_blank"===this.linkTarget?t.externalLinkRel||"noopener":"",this.contentBase=e.getBasePath();var n=this._initRenderer();this.heading=n.heading;var r=o(e=t.markdown||{})?e(Sn,n):(Sn.setOptions(m(e,{renderer:m(n,e.renderer)})),Sn);this._marked=r,this.compile=function(i){var o=!0,e=c(function(e){o=!1;var n="";return i&&(n=f(i)?r(i):r.parser(i),n=t.noEmoji?n:jn(n,t.nativeEmoji),Cn.clear(),n)})(i),n=a.router.parse().file;return o?a.toc=a.cacheTOC[n]:a.cacheTOC[n]=[].concat(a.toc),e}}var Nn={},Hn={markdown:function(e){return{url:e}},mermaid:function(e){return{url:e}},iframe:function(e,n){return{html:'"}},video:function(e,n){return{html:'"}},audio:function(e,n){return{html:'"}},code:function(e,n){var i=e.match(/\.(\w+)$/);return{url:e,lang:i="md"===(i=n||i&&i[1])?"markdown":i}}};In.prototype.compileEmbed=function(e,n){var i,o,t=On(n),a=t.str,t=t.config;if(n=a,t.include)return T(e)||(e=q(this.contentBase,R(this.router.getCurrentPath()),e)),t.type&&(o=Hn[t.type])?(i=o.call(this,e,n)).type=t.type:(o="code",/\.(md|markdown)/.test(e)?o="markdown":/\.mmd/.test(e)?o="mermaid":/\.html?/.test(e)?o="iframe":/\.(mp4|ogg)/.test(e)?o="video":/\.mp3/.test(e)&&(o="audio"),(i=Hn[o].call(this,e,n)).type=o),i.fragment=t.fragment,i},In.prototype._matchNotCompileLink=function(e){for(var n=this.config.noCompileLinks||[],i=0;i/g.test(o)&&(o=o.replace("\x3c!-- {docsify-ignore} --\x3e",""),e.title=Ln(o),e.ignoreSubHeading=!0),/{docsify-ignore}/g.test(o)&&(o=o.replace("{docsify-ignore}",""),e.title=Ln(o),e.ignoreSubHeading=!0),//g.test(o)&&(o=o.replace("\x3c!-- {docsify-ignore-all} --\x3e",""),e.title=Ln(o),e.ignoreAllSubs=!0),/{docsify-ignore-all}/g.test(o)&&(o=o.replace("{docsify-ignore-all}",""),e.title=Ln(o),e.ignoreAllSubs=!0);i=Cn(t.id||o),t=a.toURL(a.getCurrentPath(),{id:i});return e.slug=t,g.toc.push(e),"'+o+""},t.code={renderer:e}.renderer.code=function(e,n){var i=Pn.languages[n=void 0===n?"markup":n]||Pn.languages.markup;return'
    '+Pn.highlight(e.replace(/@DOCSIFY_QM@/g,"`"),i,n)+"
    "},t.link=(i=(n={renderer:e,router:a,linkTarget:n,linkRel:i,compilerClass:g}).renderer,c=n.router,u=n.linkTarget,n.linkRel,f=n.compilerClass,i.link=function(e,n,i){var o=[],t=On(n=void 0===n?"":n),a=t.str,t=t.config;return u=t.target||u,r="_blank"===u?f.config.externalLinkRel||"noopener":"",n=a,T(e)||f._matchNotCompileLink(e)||t.ignore?(T(e)||"./"!==e.slice(0,2)||(e=document.URL.replace(/\/(?!.*\/).*/,"/").replace("#/./","")+e),o.push(0===e.indexOf("mailto:")?"":'target="'+u+'"'),o.push(0!==e.indexOf("mailto:")&&""!==r?' rel="'+r+'"':"")):(e===f.config.homepage&&(e="README"),e=c.toURL(e,null,c.getCurrentPath())),t.crossorgin&&"_self"===u&&"history"===f.config.routerMode&&-1===f.config.crossOriginLinks.indexOf(e)&&f.config.crossOriginLinks.push(e),t.disabled&&(o.push("disabled"),e="javascript:void(0)"),t.class&&o.push('class="'+t.class+'"'),t.id&&o.push('id="'+t.id+'"'),n&&o.push('title="'+n+'"'),'"+i+""}),t.paragraph={renderer:e}.renderer.paragraph=function(e){e=/^!>/.test(e)?$n("tip",e):/^\?>/.test(e)?$n("warn",e):"

    "+e+"

    ";return e},t.image=(o=(i={renderer:e,contentBase:o,router:a}).renderer,p=i.contentBase,d=i.router,o.image=function(e,n,i){var o=e,t=[],a=On(n),r=a.str,a=a.config;return n=r,a["no-zoom"]&&t.push("data-no-zoom"),n&&t.push('title="'+n+'"'),a.size&&(n=(r=a.size.split("x"))[0],(r=r[1])?t.push('width="'+n+'" height="'+r+'"'):t.push('width="'+n+'"')),a.class&&t.push('class="'+a.class+'"'),a.id&&t.push('id="'+a.id+'"'),T(e)||(o=q(p,R(d.getCurrentPath()),e)),0":''+i+'"}),t.list={renderer:e}.renderer.list=function(e,n,i){n=n?"ol":"ul";return"<"+n+" "+[/
  • /.test(e.split('class="task-list"')[0])?'class="task-list"':"",i&&1"+e+""},t.listitem={renderer:e}.renderer.listitem=function(e){return/^(]*>)/.test(e)?'
  • ":"
  • "+e+"
  • "},e.origin=t,e},In.prototype.sidebar=function(e,n){var i=this.toc,o=this.router.getCurrentPath(),t="";if(e)t=this.compile(e);else{for(var a=0;a{inner}");this.cacheTree[o]=n}return t},In.prototype.subSidebar=function(e){if(e){var n=this.router.getCurrentPath(),i=this.cacheTree,o=this.toc;o[0]&&o[0].ignoreAllSubs&&o.splice(0),o[0]&&1===o[0].level&&o.shift();for(var t=0;t\n'+e+"\n"}]).links={}:(n=[{type:"html",text:e}]).links={}),a({token:t,embedToken:n}),++u>=c&&a({})}}(n);n.embed.url?X(n.embed.url).then(o):o(n.embed.html)}}({compile:i,embedTokens:c,fetch:n},function(e){var n,i=e.embedToken,e=e.token;e?(n=e.index,p.forEach(function(e){n>e.start&&(n+=e.length)}),m(f,i.links),r=r.slice(0,n).concat(i,r.slice(n+1)),p.push({start:n,length:i.length-1})):(Bn[t]=r.concat(),r.links=Bn[t].links=f,o(r))})}function Yn(e,n,i){var o,t,a,r;return n="function"==typeof i?i(n):"string"==typeof i?(a=[],r=0,(o=i).replace(V,function(n,e,i){a.push(o.substring(r,i-1)),r=i+=n.length+1,a.push(t&&t[n]||function(e){return("00"+("string"==typeof Y[n]?e[Y[n]]():Y[n](e))).slice(-n.length)})}),r!==o.length&&a.push(o.substring(r)),function(e){for(var n="",i=0,o=e||new Date;i404 - Not found","Vue"in window)for(var a=0,r=k(".markdown-section > *").filter(n);ascript").filter(function(e){return!/template/.test(e.type)})[0])||(e=e.innerText.trim())&&new Function(e)()),"Vue"in window){var u,f,p=[],d=Object.keys(i.vueComponents||{});2===t&&d.length&&d.forEach(function(e){window.Vue.options.components[e]||window.Vue.component(e,i.vueComponents[e])}),!Un&&i.vueGlobalOptions&&"function"==typeof i.vueGlobalOptions.data&&(Un=i.vueGlobalOptions.data()),p.push.apply(p,Object.keys(i.vueMounts||{}).map(function(e){return[b(o,e),i.vueMounts[e]]}).filter(function(e){var n=e[0];e[1];return n})),(i.vueGlobalOptions||d.length)&&(u=/{{2}[^{}]*}{2}/,f=/<[^>/]+\s([@:]|v-)[\w-:.[\]]+[=>\s]/,p.push.apply(p,k(".markdown-section > *").filter(function(i){return!p.some(function(e){var n=e[0];e[1];return n===i})}).filter(function(e){return e.tagName.toLowerCase()in(i.vueComponents||{})||e.querySelector(d.join(",")||null)||u.test(e.outerHTML)||f.test(e.outerHTML)}).map(function(e){var n=m({},i.vueGlobalOptions||{});return Un&&(n.data=function(){return Un}),[e,n]})));for(var g=0,s=p;g([^<]*?)

    $'))&&("color"===n[2]?o.style.background=n[1]+(n[3]||""):(e=n[1],S(o,"add","has-mask"),T(n[1])||(e=q(this.router.getBasePath(),n[1])),o.style.backgroundImage="url("+e+")",o.style.backgroundSize="cover",o.style.backgroundPosition="center center"),i=i.replace(n[0],"")),this._renderTo(".cover-main",i),K()):S(o,"remove","show")},n.prototype._updateRender=function(){var e,n,i,o;e=this,n=l(".app-name-link"),i=e.config.nameLink,o=e.route.path,n&&(f(e.config.nameLink)?n.setAttribute("href",i):"object"==typeof i&&(e=Object.keys(i).filter(function(e){return-1':"")),e.coverpage&&(f+=(o=", 100%, 85%",'
    \x3c!--cover--\x3e
    ')),e.logo&&(o=/^data:image/.test(e.logo),n=/(?:http[s]?:)?\/\//.test(e.logo),i=/^\./.test(e.logo),o||n||i||(e.logo=q(this.router.getBasePath(),e.logo))),f+=(i=(n=e).name||"","
    "+('')+'
    \x3c!--main--\x3e
    '),this._renderTo(u,f,!0)):this.rendered=!0,e.mergeNavbar&&s?p=b(".sidebar"):(c.classList.add("app-nav"),e.repo||c.classList.add("no-badge")),e.loadNavbar&&y(p,c),e.themeColor&&(v.head.appendChild(w("div","").firstElementChild),a=e.themeColor,window.CSS&&window.CSS.supports&&window.CSS.supports("(--v:red)")||(e=k("style:not(.inserted),link"),[].forEach.call(e,function(e){"STYLE"===e.nodeName?Q(e,a):"LINK"===e.nodeName&&(e=e.getAttribute("href"),/\.css$/.test(e)&&X(e).then(function(e){e=w("style",e);_.appendChild(e),Q(e,a)}))}))),this._updateRender(),S(h,"ready")},n}(function(e){function n(){e.apply(this,arguments)}return e&&(n.__proto__=e),((n.prototype=Object.create(e&&e.prototype)).constructor=n).prototype.routes=function(){return this.config.routes||{}},n.prototype.matchVirtualRoute=function(t){var a=this.routes(),r=Object.keys(a),c=function(){return null};function u(){var e=r.shift();if(!e)return c(null);var n=A(o=(i="^",0===(o=e).indexOf(i)?o:"^"+o),"$")?o:o+"$",i=t.match(n);if(!i)return u();var o=a[e];if("string"==typeof o)return c(o);if("function"!=typeof o)return u();n=o,e=Xn(),o=e[0];return(0,e[1])(function(e){return"string"==typeof e?c(e):!1===e?c(null):u()}),n.length<=2?o(n(t,i)):n(t,i,o)}return{then:function(e){c=e,u()}}},n}(function(i){function e(){for(var e=[],n=arguments.length;n--;)e[n]=arguments[n];i.apply(this,e),this.route={}}return i&&(e.__proto__=i),((e.prototype=Object.create(i&&i.prototype)).constructor=e).prototype.updateRender=function(){this.router.normalize(),this.route=this.router.parse(),h.setAttribute("data-page",this.route.file)},e.prototype.initRouter=function(){var n=this,e=this.config,e=new("history"===(e.routerMode||"hash")&&t?D:H)(e);this.router=e,this.updateRender(),U=this.route,e.onchange(function(e){n.updateRender(),n._updateRender(),U.path!==n.route.path?(n.$fetch(d,n.$resetEvents.bind(n,e.source)),U=n.route):n.$resetEvents(e.source)})},e}(function(e){function n(){e.apply(this,arguments)}return e&&(n.__proto__=e),((n.prototype=Object.create(e&&e.prototype)).constructor=n).prototype.initLifecycle=function(){var i=this;this._hooks={},this._lifecycle={},["init","mounted","beforeEach","afterEach","doneEach","ready"].forEach(function(e){var n=i._hooks[e]=[];i._lifecycle[e]=function(e){return n.push(e)}})},n.prototype.callHook=function(e,t,a){void 0===a&&(a=d);var r=this._hooks[e],c=this.config.catchPluginErrors,u=function(n){var e=r[n];if(n>=r.length)a(t);else if("function"==typeof e){var i="Docsify plugin error";if(2===e.length)try{e(t,function(e){t=e,u(n+1)})}catch(e){if(!c)throw e;console.error(i,e),u(n+1)}else try{var o=e(t);t=void 0===o?t:o,u(n+1)}catch(e){if(!c)throw e;console.error(i,e),u(n+1)}}else u(n+1)};u(0)},n}(we))))))));function Kn(e,n,i){return Qn&&Qn.abort&&Qn.abort(),Qn=X(e,!0,i)}window.Docsify={util:Me,dom:n,get:X,slugify:Cn,version:"4.13.0"},window.DocsifyCompiler=In,window.marked=Sn,window.Prism=Pn,e(function(e){return new Jn})}(); diff --git a/doc/lib/js/search.min.js b/doc/lib/js/search.min.js new file mode 100644 index 000000000..be7a9f555 --- /dev/null +++ b/doc/lib/js/search.min.js @@ -0,0 +1 @@ +!function(){function u(e){return e.replace(//,"").replace(/{docsify-ignore}/,"").replace(//,"").replace(/{docsify-ignore-all}/,"").trim()}var f={},m={EXPIRE_KEY:"docsify.search.expires",INDEX_KEY:"docsify.search.index"};function g(e){var n={"&":"&","<":"<",">":">",'"':""","'":"'"};return String(e).replace(/[&<>"']/g,function(e){return n[e]})}function y(e){return e.text||"table"!==e.type||(e.cells.unshift(e.header),e.text=e.cells.map(function(e){return e.join(" | ")}).join(" |\n ")),e.text}function v(e){return e.text||"list"!==e.type||(e.text=e.raw),e.text}function b(o,e,s,c){void 0===e&&(e="");var d,e=window.marked.lexer(e),l=window.Docsify.slugify,p={},h="";return e.forEach(function(e,n){var t,a,i,r;"heading"===e.type&&e.depth<=c?(t=(a=(i=e.text,r={},{str:i=(i=void 0===i?"":i)&&i.replace(/^('|")/,"").replace(/('|")$/,"").replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g,function(e,n,t){return-1===n.indexOf(":")?(r[n]=t&&t.replace(/"/g,"")||!0,""):e}).trim(),config:r})).str,i=a.config,a=u(e.text),d=i.id?s.toURL(o,{id:l(i.id)}):s.toURL(o,{id:l(g(a))}),t&&(h=u(t)),p[d]={slug:d,title:h,body:""}):(0===n&&(d=s.toURL(o),p[d]={slug:d,title:"/"!==o?o.slice(1):"Home Page",body:e.text||""}),d&&(p[d]?p[d].body?(e.text=y(e),e.text=v(e),p[d].body+="\n"+(e.text||"")):(e.text=y(e),e.text=v(e),p[d].body=p[d].body?p[d].body+e.text:e.text):p[d]={slug:d,title:"",body:""}))}),l.clear(),p}function p(e){return e&&e.normalize?e.normalize("NFD").replace(/[\u0300-\u036f]/g,""):e}function o(e){var n=[],t=[];Object.keys(f).forEach(function(n){t=t.concat(Object.keys(f[n]).map(function(e){return f[n][e]}))});var a=(e=e.trim()).split(/[\s\-,\\/]+/);1!==a.length&&(a=[].concat(e,a));for(var i=0;il.length&&(t=l.length),a=c&&"..."+c.substring(n,t).replace(a,function(e){return''+e+""})+"...",o+=a)}),0\n\n

    '+e.title+"

    \n

    "+e.content+"

    \n
    \n"}),t.classList.add("show"),a.classList.add("show"),t.innerHTML=r||'

    '+c+"

    ",s.hideOtherSidebarContent&&(i&&i.classList.add("hide"),n&&n.classList.add("hide"))}function l(e){s=e}function h(e,n){var t,a,i=n.router.parse().query.s;l(e),Docsify.dom.style("\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0.6em 7px;\n font-size: inherit;\n border: 1px solid transparent;\n}\n\n.search input:focus {\n box-shadow: 0 0 5px var(--theme-color, #42b983);\n border: 1px solid var(--theme-color, #42b983);\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n.search input::-ms-clear {\n display: none;\n height: 0;\n width: 0;\n}\n\n.search .clear-button {\n cursor: pointer;\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}"),function(e){void 0===e&&(e="");var n=Docsify.dom.create("div",'
    \n \n
    \n \n \n \n \n \n
    \n
    \n
    \n '),e=Docsify.dom.find("aside");Docsify.dom.toggleClass(n,"search"),Docsify.dom.before(e,n)}(i),n=Docsify.dom.find("div.search"),a=Docsify.dom.find(n,"input"),e=Docsify.dom.find(n,".input-wrap"),Docsify.dom.on(n,"click",function(e){return-1===["A","H2","P","EM"].indexOf(e.target.tagName)&&e.stopPropagation()}),Docsify.dom.on(a,"input",function(n){clearTimeout(t),t=setTimeout(function(e){return d(n.target.value.trim())},100)}),Docsify.dom.on(e,"click",function(e){"INPUT"!==e.target.tagName&&(a.value="",d())}),i&&setTimeout(function(e){return d(i)},500)}function x(e,n){var t,a,i,r,o;l(e),t=e.placeholder,a=n.route.path,(r=Docsify.dom.getNode('.search input[type="search"]'))&&("string"==typeof t?r.placeholder=t:(i=Object.keys(t).filter(function(e){return-1u.scrollOffset&&setTimeout(a,150))}),window.addEventListener("resize",a);var f={open:i,close:a,toggle:o,update:function(){var e=0 run.sh && \ + echo 'echo ${WVP_IP}' >> run.sh && \ + echo 'echo ${WVP_CONFIG}' >> run.sh && \ + echo 'cd /opt/assist' >> run.sh && \ + echo 'nohup java ${ASSIST_JVM_CONFIG} -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/assist/heapdump/ -jar *.jar --spring.config.location=/opt/assist/config/application.yml --userSettings.record=/opt/media/www/record/ --media.record-assist-port=18081 ${ASSIST_CONFIG} &' >> run.sh && \ + echo 'nohup /opt/media/MediaServer -d -m 3 &' >> run.sh && \ + echo 'cd /opt/wvp' >> run.sh && \ + echo 'java ${WVP_JVM_CONFIG} -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/wvp/heapdump/ -jar *.jar --spring.config.location=/opt/wvp/config/application.yml --media.record-assist-port=18081 ${WVP_CONFIG}' >> run.sh && \ + chmod +x run.sh + +FROM ubuntu:20.04 + +EXPOSE 18080/tcp +EXPOSE 5060/tcp +EXPOSE 5060/udp +EXPOSE 6379/tcp +EXPOSE 18081/tcp +EXPOSE 80/tcp +EXPOSE 1935/tcp +EXPOSE 554/tcp +EXPOSE 554/udp +EXPOSE 30000-30500/tcp +EXPOSE 30000-30500/udp + +ENV LC_ALL zh_CN.UTF-8 + +RUN export DEBIAN_FRONTEND=noninteractive &&\ + apt-get update && \ + apt-get install -y --no-install-recommends openjdk-11-jre ca-certificates ffmpeg language-pack-zh-hans && \ + apt-get autoremove -y && \ + apt-get clean -y && \ + rm -rf /var/lib/apt/lists/*dic + +COPY --from=build /opt /opt +WORKDIR /opt/wvp +CMD ["sh", "run.sh"] diff --git a/libs/jdbc-aarch/kingbase8-8.6.0.jar b/libs/jdbc-aarch/kingbase8-8.6.0.jar new file mode 100644 index 000000000..489bf53d9 Binary files /dev/null and b/libs/jdbc-aarch/kingbase8-8.6.0.jar differ diff --git a/libs/jdbc-aarch/kingbase8-8.6.0.jre7.jar b/libs/jdbc-aarch/kingbase8-8.6.0.jre7.jar new file mode 100644 index 000000000..28102eb91 Binary files /dev/null and b/libs/jdbc-aarch/kingbase8-8.6.0.jre7.jar differ diff --git a/libs/jdbc-aarch/postgresql-42.2.9.jar b/libs/jdbc-aarch/postgresql-42.2.9.jar new file mode 100644 index 000000000..62d0b6daf Binary files /dev/null and b/libs/jdbc-aarch/postgresql-42.2.9.jar differ diff --git a/libs/jdbc-aarch/postgresql-42.2.9.jre7.jar b/libs/jdbc-aarch/postgresql-42.2.9.jre7.jar new file mode 100644 index 000000000..f6077b618 Binary files /dev/null and b/libs/jdbc-aarch/postgresql-42.2.9.jre7.jar differ diff --git a/pom.xml b/pom.xml index c2f5d82f0..2ecfad302 100644 --- a/pom.xml +++ b/pom.xml @@ -6,12 +6,15 @@ org.springframework.boot spring-boot-starter-parent - 2.3.5.RELEASE + 2.7.2 com.genersoft - wvp + wvp-pro + 2.6.9 web video platform + 国标28181视频平台 + ${project.packaging} @@ -43,9 +46,10 @@ UTF-8 + MMddHHmm + 3.1.1 - 5.2.0 ${project.build.directory}/generated-snippets ${project.basedir}/docs/asciidoc ${project.build.directory}/asciidoc @@ -53,6 +57,42 @@ ${project.build.directory}/asciidoc/pdf + + + jar + + true + + + jar + + + + war + + war + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-jetty + + + + + javax.servlet + javax.servlet-api + 3.1.0 + provided + + + + + org.springframework.boot @@ -62,58 +102,79 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.boot + spring-boot-configuration-processor + true + org.mybatis.spring.boot mybatis-spring-boot-starter - 2.1.4 + 2.2.2 + + + com.zaxxer + HikariCP + + + + + org.springframework.boot + spring-boot-starter-security - - com.alibaba - druid - 1.2.3 + org.springframework.boot + spring-boot-starter-jdbc mysql mysql-connector-java - 8.0.22 + 8.0.30 - + - org.xerial - sqlite-jdbc - 3.32.3.2 + org.postgresql + postgresql + 42.5.1 + + + + + + + com.kingbase + kingbase8 + 8.6.0 + system + ${basedir}/libs/jdbc-aarch/kingbase8-8.6.0.jar com.github.pagehelper pagehelper-spring-boot-starter - 1.2.10 + 1.4.6 - - - - - - - - io.springfox - springfox-swagger2 - 2.9.2 + org.springdoc + springdoc-openapi-ui + 1.6.10 + - io.springfox - springfox-swagger-ui - 2.6.1 + com.github.xiaoymin + knife4j-springdoc-ui + 3.0.3 + + javax.validation validation-api @@ -132,10 +193,11 @@ 1.3.0-91 + - log4j - log4j - 1.2.17 + org.slf4j + log4j-over-slf4j + 1.7.36 @@ -145,49 +207,156 @@ 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 + + + + + io.github.rburgst + okhttp-digest + 2.7 + + + + + + + + + + + + org.bitbucket.b_c + jose4j + 0.9.3 + + + + + org.mitre.dsmiley.httpproxy + smiley-http-proxy-servlet + 1.12.1 + + + + + com.alibaba + easyexcel + 3.1.1 + + + + + com.github.oshi + oshi-core + 6.2.2 + + + + org.springframework.session + spring-session-core + + + + + + + + + + + + + com.google.guava + guava + 31.1-jre + + + + + org.springframework.boot + spring-boot-starter-test + + + + + + - wvp-2.5.8 + ${project.artifactId}-${project.version}-${maven.build.timestamp} org.springframework.boot spring-boot-maven-plugin + 2.7.2 + + true + org.apache.maven.plugins maven-compiler-plugin + 3.8.1 1.8 1.8 + + 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/2.6.6-2.6.7更新.sql b/sql/2.6.6-2.6.7更新.sql new file mode 100755 index 000000000..285d57346 --- /dev/null +++ b/sql/2.6.6-2.6.7更新.sql @@ -0,0 +1,13 @@ +alter table device + add asMessageChannel int default 0; + +alter table parent_platform + add asMessageChannel int default 0; + +alter table device + add mediaServerId varchar(50) default null; + +ALTER TABLE device + ADD COLUMN `switchPrimarySubStream` bit(1) NOT NULL DEFAULT b'0' COMMENT '开启主子码流切换的开关(0-不开启,1-开启)现在已知支持设备为 大华、TP——LINK全系设备' AFTER `keepalive_interval_time` + + diff --git a/sql/2.6.8升级2.6.9.sql b/sql/2.6.8升级2.6.9.sql new file mode 100644 index 000000000..7b16c3572 --- /dev/null +++ b/sql/2.6.8升级2.6.9.sql @@ -0,0 +1,502 @@ +alter table device + change deviceId device_id varchar(50) not null; + +alter table device + change streamMode stream_mode varchar(50) null; + +alter table device + change registerTime register_time varchar(50) null; + +alter table device + change keepaliveTime keepalive_time varchar(50) null; + +alter table device + change createTime create_time varchar(50) not null; + +alter table device + change updateTime update_time varchar(50) not null; + +alter table device + change subscribeCycleForCatalog subscribe_cycle_for_catalog bool default false; + +alter table device + change subscribeCycleForMobilePosition subscribe_cycle_for_mobile_position bool default false; + +alter table device + change mobilePositionSubmissionInterval mobile_position_submission_interval int default 5 not null; + +alter table device + change subscribeCycleForAlarm subscribe_cycle_for_alarm bool default false; + +alter table device + change hostAddress host_address varchar(50) null; + +alter table device + change ssrcCheck ssrc_check bool default false; + +alter table device + change geoCoordSys geo_coord_sys varchar(50) not null; + +alter table device + drop column treeType; + +alter table device + change mediaServerId media_server_id varchar(50) default 'auto' null; + +alter table device + change sdpIp sdp_ip varchar(50) null; + +alter table device + change localIp local_ip varchar(50) null; + +alter table device + change asMessageChannel as_message_channel bool default false; + +alter table device + change keepaliveIntervalTime keepalive_interval_time int null; + +alter table device + change online on_line varchar(50) null; + +alter table device + add COLUMN switch_primary_sub_stream bool default false comment '开启主子码流切换的开关(0-不开启,1-开启)现在已知支持设备为 大华、TP——LINK全系设备' + + +alter table device_alarm + change deviceId device_id varchar(50) not null; + +alter table device_alarm + change channelId channel_id varchar(50) not null; + +alter table device_alarm + change alarmPriority alarm_priority varchar(50) not null; + +alter table device_alarm + change alarmMethod alarm_method varchar(50) null; + +alter table device_alarm + change alarmTime alarm_time varchar(50) not null; + +alter table device_alarm + change alarmDescription alarm_description varchar(255) null; + +alter table device_alarm + change alarmType alarm_type varchar(50) null; + +alter table device_alarm + change createTime create_time varchar(50) null; + +alter table device_channel + change channelId channel_id varchar(50) not null; + +alter table device_channel + change civilCode civil_code varchar(50) null; + +alter table device_channel + change parentId parent_id varchar(50) null; + +alter table device_channel + change safetyWay safety_way int null; + +alter table device_channel + change registerWay register_way int null; + +alter table device_channel + change certNum cert_num varchar(50) null; + +alter table device_channel + change errCode err_code int null; + +alter table device_channel + change endTime end_time varchar(50) null; + +alter table device_channel + change ipAddress ip_address varchar(50) null; + +alter table device_channel + change PTZType ptz_type int null; + +alter table device_channel + change status status bool default false; + +alter table device_channel + change streamId stream_id varchar(50) null; + +alter table device_channel + change deviceId device_id varchar(50) not null; + + +alter table device_channel + change hasAudio has_audio bool default false; + +alter table device_channel + change createTime create_time varchar(50) not null; + +alter table device_channel + change updateTime update_time varchar(50) not null; + +alter table device_channel + change subCount sub_count int default 0 null; + +alter table device_channel + change longitudeGcj02 longitude_gcj02 double null; + +alter table device_channel + change latitudeGcj02 latitude_gcj02 double null; + +alter table device_channel + change longitudeWgs84 longitude_wgs84 double null; + +alter table device_channel + change latitudeWgs84 latitude_wgs84 double null; + +alter table device_channel + change businessGroupId business_group_id varchar(50) null; + +alter table device_channel + change gpsTime gps_time varchar(50) null; + +alter table device_mobile_position + change deviceId device_id varchar(50) not null; + +alter table device_mobile_position + change channelId channel_id varchar(50) not null; + +alter table device_mobile_position + change deviceName device_name varchar(255) null; + +alter table device_mobile_position + change reportSource report_source varchar(50) null; + +alter table device_mobile_position + change longitudeGcj02 longitude_gcj02 double null; + +alter table device_mobile_position + change latitudeGcj02 latitude_gcj02 double null; + +alter table device_mobile_position + change longitudeWgs84 longitude_wgs84 double null; + +alter table device_mobile_position + change latitudeWgs84 latitude_wgs84 double null; + +alter table device_mobile_position + change createTime create_time varchar(50) null; + +alter table gb_stream + change gbStreamId gb_stream_id int auto_increment; + +alter table gb_stream + change gbId gb_id varchar(50) not null; + +alter table gb_stream + change streamType stream_type varchar(50) null; + +alter table gb_stream + change mediaServerId media_server_id varchar(50) null; + +alter table gb_stream + change createTime create_time varchar(50) null; + +alter table log + change createTime create_time varchar(50) not null; + +alter table media_server + change hookIp hook_ip varchar(50) not null; + +alter table media_server + add send_rtp_port_range varchar(50) not null; + +alter table media_server + add column send_rtp_port_range varchar(50) default null; + +alter table media_server + change sdpIp sdp_ip varchar(50) not null; + +alter table media_server + change streamIp stream_ip varchar(50) not null; + +alter table media_server + change httpPort http_port int not null; + +alter table media_server + change httpSSlPort http_ssl_port int not null; + +alter table media_server + change rtmpPort rtmp_port int not null; + +alter table media_server + change rtmpSSlPort rtmp_ssl_port int not null; + +alter table media_server + change rtpProxyPort rtp_proxy_port int not null; + +alter table media_server + change rtspPort rtsp_port int not null; + +alter table media_server + change rtspSSLPort rtsp_ssl_port int not null; + +alter table media_server + change autoConfig auto_config bool default true; + +alter table media_server + change rtpEnable rtp_enable bool default false; + +alter table media_server + change rtpPortRange rtp_port_range varchar(50) not null; + +alter table media_server + change recordAssistPort record_assist_port int not null; + +alter table media_server + change defaultServer default_server bool default false; + +alter table media_server + change createTime create_time varchar(50) not null; + +alter table media_server + change updateTime update_time varchar(50) not null; + +alter table media_server + change hookAliveInterval hook_alive_interval int not null; + +alter table parent_platform + change serverGBId server_gb_id varchar(50) not null; + +alter table parent_platform + change serverGBDomain server_gb_domain varchar(50) null; + +alter table parent_platform + change serverIP server_ip varchar(50) null; + +alter table parent_platform + change serverPort server_port int null; + +alter table parent_platform + change deviceGBId device_gb_id varchar(50) not null; + +alter table parent_platform + change deviceIp device_ip varchar(50) null; + +alter table parent_platform + change devicePort device_port varchar(50) null; + +alter table parent_platform + change keepTimeout keep_timeout varchar(50) null; + +alter table parent_platform + change characterSet character_set varchar(50) null; + +alter table parent_platform + change catalogId catalog_id varchar(50) not null; + +alter table parent_platform + change startOfflinePush start_offline_push bool default false; + +alter table parent_platform + change administrativeDivision administrative_division varchar(50) not null; + +alter table parent_platform + change catalogGroup catalog_group int default 1 null; + +alter table parent_platform + change createTime create_time varchar(50) null; + +alter table parent_platform + change updateTime update_time varchar(50) null; + +alter table parent_platform + drop column treeType; + +alter table parent_platform + change asMessageChannel as_message_channel bool default false; + +alter table parent_platform + change enable enable bool default false; + +alter table parent_platform + change ptz ptz bool default false; + +alter table parent_platform + change rtcp rtcp bool default false; + +alter table parent_platform + change status status bool default false; + +alter table parent_platform + change status status bool default false; + +alter table platform_catalog + change platformId platform_id varchar(50) not null; + +alter table platform_catalog + change parentId parent_id varchar(50) null; + +alter table platform_catalog + change civilCode civil_code varchar(50) null; + +alter table platform_catalog + change businessGroupId business_group_id varchar(50) null; + +alter table platform_gb_channel + change platformId platform_id varchar(50) not null; + +alter table platform_gb_channel + change catalogId catalog_id varchar(50) not null; + +alter table platform_gb_channel + change deviceChannelId device_channel_id int not null; + +alter table platform_gb_stream + change platformId platform_id varchar(50) not null; + +alter table platform_gb_stream + change catalogId catalog_id varchar(50) not null; + +alter table platform_gb_stream + change gbStreamId gb_stream_id int not null; + +alter table stream_proxy + change mediaServerId media_server_id varchar(50) null; + +alter table stream_proxy + change createTime create_time varchar(50) not null; + +alter table stream_proxy + change updateTime update_time varchar(50) null; + +alter table stream_proxy + change enable_remove_none_reader enable_remove_none_reader bool default false; + +alter table stream_proxy + change enable_disable_none_reader enable_disable_none_reader bool default false; + +alter table stream_proxy + change enable_audio enable_audio bool default false; + +alter table stream_proxy + change enable_mp4 enable_mp4 bool default false; + +alter table stream_proxy + change enable enable bool default false; + +alter table stream_push + change totalReaderCount total_reader_count varchar(50) null; + +alter table stream_push + change originType origin_type int null; + +alter table stream_push + change originTypeStr origin_type_str varchar(50) null; + +alter table stream_push + change createTime create_time varchar(50) null; + +alter table stream_push + change aliveSecond alive_second int null; + +alter table stream_push + change mediaServerId media_server_id varchar(50) null; + +alter table stream_push + change status status bool default false; + +alter table stream_push + change pushTime push_time varchar(50) null; + +alter table stream_push + change updateTime update_time varchar(50) null; + +alter table stream_push + change pushIng push_ing bool default false; + +alter table stream_push + change status status bool default false; + +alter table stream_push + change self self bool default false; + +alter table stream_push + drop column serverId; + + +alter table user + change roleId role_id int not null; + +alter table user + change createTime create_time varchar(50) not null; + +alter table user + change updateTime update_time varchar(50) not null; + +alter table user + change pushKey push_key varchar(50) null; + +alter table user_role + change createTime create_time varchar(50) not null; + +alter table user_role + change updateTime update_time varchar(50) not null; + +rename table device to wvp_device; +rename table device_alarm to wvp_device_alarm; +rename table device_channel to wvp_device_channel; +rename table device_mobile_position to wvp_device_mobile_position; +rename table gb_stream to wvp_gb_stream; +rename table log to wvp_log; +rename table media_server to wvp_media_server; +rename table parent_platform to wvp_platform; +rename table platform_catalog to wvp_platform_catalog; +rename table platform_gb_channel to wvp_platform_gb_channel; +rename table platform_gb_stream to wvp_platform_gb_stream; +rename table stream_proxy to wvp_stream_proxy; +rename table stream_push to wvp_stream_push; +rename table user to wvp_user; +rename table user_role to wvp_user_role; + +alter table wvp_device add column broadcast_push_after_ack bool default false; +alter table wvp_device_channel add column custom_name varchar(255) null ; +alter table wvp_device_channel add column custom_longitude double null ; +alter table wvp_device_channel add column custom_latitude double null ; +alter table wvp_device_channel add column custom_ptz_type int null ; + +create table wvp_resources_tree ( + id serial primary key , + is_catalog bool default true, + device_channel_id integer , + gb_stream_id integer, + name character varying(255), + parentId integer, + path character varying(255) +); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sql/clean.sql b/sql/clean.sql new file mode 100644 index 000000000..b49fa1c15 --- /dev/null +++ b/sql/clean.sql @@ -0,0 +1,13 @@ +delete from wvp-device; +delete from wvp-device_alarm; +delete from wvp-device_channel; +delete from wvp-device_mobile_position; +delete from wvp-gb_stream; +delete from wvp-log; +delete from wvp-media_server; +delete from wvp-parent_platform; +delete from wvp-platform_catalog; +delete from wvp-platform_gb_channel; +delete from wvp-platform_gb_stream; +delete from wvp-stream_proxy; +delete from wvp-stream_push; \ No newline at end of file diff --git a/sql/初始化.sql b/sql/初始化.sql new file mode 100644 index 000000000..f2952ba13 --- /dev/null +++ b/sql/初始化.sql @@ -0,0 +1,302 @@ +/*建表*/ +create table wvp_device ( + id serial primary key , + device_id character varying(50) not null , + name character varying(255), + manufacturer character varying(255), + model character varying(255), + firmware character varying(255), + transport character varying(50), + stream_mode character varying(50), + on_line bool default false, + register_time character varying(50), + keepalive_time character varying(50), + ip character varying(50), + create_time character varying(50), + update_time character varying(50), + port integer, + expires integer, + subscribe_cycle_for_catalog integer DEFAULT 0, + subscribe_cycle_for_mobile_position integer DEFAULT 0, + mobile_position_submission_interval integer DEFAULT 5, + subscribe_cycle_for_alarm integer DEFAULT 0, + host_address character varying(50), + charset character varying(50), + ssrc_check bool default false, + geo_coord_sys character varying(50), + media_server_id character varying(50), + custom_name character varying(255), + sdp_ip character varying(50), + local_ip character varying(50), + password character varying(255), + as_message_channel bool default false, + keepalive_interval_time integer, + switch_primary_sub_stream bool default false, + broadcast_push_after_ack bool default false, + constraint uk_device_device unique (device_id) +); + +create table wvp_device_alarm ( + id serial primary key , + device_id character varying(50) not null, + channel_id character varying(50) not null, + alarm_priority character varying(50), + alarm_method character varying(50), + alarm_time character varying(50), + alarm_description character varying(255), + longitude double precision, + latitude double precision, + alarm_type character varying(50), + create_time character varying(50) not null +); + +create table wvp_device_channel ( + id serial primary key , + channel_id character varying(50) not null, + name character varying(255), + custom_name character varying(255), + manufacture character varying(50), + model character varying(50), + owner character varying(50), + civil_code character varying(50), + block character varying(50), + address character varying(50), + parent_id character varying(50), + safety_way integer, + register_way integer, + cert_num character varying(50), + certifiable integer, + err_code integer, + end_time character varying(50), + secrecy character varying(50), + ip_address character varying(50), + port integer, + password character varying(255), + ptz_type integer, + custom_ptz_type integer, + status bool default false, + longitude double precision, + custom_longitude double precision, + latitude double precision, + custom_latitude double precision, + stream_id character varying(50), + device_id character varying(50) not null, + parental character varying(50), + has_audio bool default false, + create_time character varying(50) not null, + update_time character varying(50) not null, + sub_count integer, + longitude_gcj02 double precision, + latitude_gcj02 double precision, + longitude_wgs84 double precision, + latitude_wgs84 double precision, + business_group_id character varying(50), + gps_time character varying(50), + constraint uk_wvp_device_channel_unique_device_channel unique (device_id, channel_id) +); + +create table wvp_device_mobile_position ( + id serial primary key, + device_id character varying(50) not null, + channel_id character varying(50) not null, + device_name character varying(255), + time character varying(50), + longitude double precision, + latitude double precision, + altitude double precision, + speed double precision, + direction double precision, + report_source character varying(50), + longitude_gcj02 double precision, + latitude_gcj02 double precision, + longitude_wgs84 double precision, + latitude_wgs84 double precision, + create_time character varying(50) +); + +create table wvp_gb_stream ( + gb_stream_id serial primary key, + app character varying(255) not null, + stream character varying(255) not null, + gb_id character varying(50) not null, + name character varying(255), + longitude double precision, + latitude double precision, + stream_type character varying(50), + media_server_id character varying(50), + create_time character varying(50), + constraint uk_gb_stream_unique_gb_id unique (gb_id), + constraint uk_gb_stream_unique_app_stream unique (app, stream) +); + +create table wvp_log ( + id serial primary key , + name character varying(50), + type character varying(50), + uri character varying(200), + address character varying(50), + result character varying(50), + timing bigint, + username character varying(50), + create_time character varying(50) +); + +create table wvp_media_server ( + id character varying(255) primary key , + ip character varying(50), + hook_ip character varying(50), + sdp_ip character varying(50), + stream_ip character varying(50), + http_port integer, + http_ssl_port integer, + rtmp_port integer, + rtmp_ssl_port integer, + rtp_proxy_port integer, + rtsp_port integer, + rtsp_ssl_port integer, + auto_config bool default false, + secret character varying(50), + rtp_enable bool default false, + rtp_port_range character varying(50), + send_rtp_port_range character varying(50), + record_assist_port integer, + default_server bool default false, + create_time character varying(50), + update_time character varying(50), + hook_alive_interval integer, + constraint uk_media_server_unique_ip_http_port unique (ip, http_port) +); + +create table wvp_platform ( + id serial primary key , + enable bool default false, + name character varying(255), + server_gb_id character varying(50), + server_gb_domain character varying(50), + server_ip character varying(50), + server_port integer, + device_gb_id character varying(50), + device_ip character varying(50), + device_port character varying(50), + username character varying(255), + password character varying(50), + expires character varying(50), + keep_timeout character varying(50), + transport character varying(50), + character_set character varying(50), + catalog_id character varying(50), + ptz bool default false, + rtcp bool default false, + status bool default false, + start_offline_push bool default false, + administrative_division character varying(50), + catalog_group integer, + create_time character varying(50), + update_time character varying(50), + as_message_channel bool default false, + constraint uk_platform_unique_server_gb_id unique (server_gb_id) +); + +create table wvp_platform_catalog ( + id character varying(50), + platform_id character varying(50), + name character varying(255), + parent_id character varying(50), + civil_code character varying(50), + business_group_id character varying(50), + constraint uk_platform_catalog_id_platform_id unique (id, platform_id) +); + +create table wvp_platform_gb_channel ( + id serial primary key , + platform_id character varying(50), + catalog_id character varying(50), + device_channel_id integer, + constraint uk_platform_gb_channel_platform_id_catalog_id_device_channel_id unique (platform_id, catalog_id, device_channel_id) +); + +create table wvp_platform_gb_stream ( + id serial primary key, + platform_id character varying(50), + catalog_id character varying(50), + gb_stream_id integer, + constraint uk_platform_gb_stream_platform_id_catalog_id_gb_stream_id unique (platform_id, catalog_id, gb_stream_id) +); + +create table wvp_stream_proxy ( + id serial primary key, + type character varying(50), + app character varying(255), + stream character varying(255), + url character varying(255), + src_url character varying(255), + dst_url character varying(255), + timeout_ms integer, + ffmpeg_cmd_key character varying(255), + rtp_type character varying(50), + media_server_id character varying(50), + enable_audio bool default false, + enable_mp4 bool default false, + enable bool default false, + status boolean, + enable_remove_none_reader bool default false, + create_time character varying(50), + name character varying(255), + update_time character varying(50), + enable_disable_none_reader bool default false, + constraint uk_stream_proxy_app_stream unique (app, stream) +); + +create table wvp_stream_push ( + id serial primary key, + app character varying(255), + stream character varying(255), + total_reader_count character varying(50), + origin_type integer, + origin_type_str character varying(50), + create_time character varying(50), + alive_second integer, + media_server_id character varying(50), + push_time character varying(50), + status bool default false, + update_time character varying(50), + push_ing bool default false, + self bool default false, + constraint uk_stream_push_app_stream unique (app, stream) +); + +create table wvp_user ( + id serial primary key, + username character varying(255), + password character varying(255), + role_id integer, + create_time character varying(50), + update_time character varying(50), + push_key character varying(50), + constraint uk_user_username unique (username) +); + +create table wvp_user_role ( + id serial primary key, + name character varying(50), + authority character varying(50), + create_time character varying(50), + update_time character varying(50) +); +create table wvp_resources_tree ( + id serial primary key , + is_catalog bool default true, + device_channel_id integer , + gb_stream_id integer, + name character varying(255), + parentId integer, + path character varying(255) +); + + +/*初始数据*/ +INSERT INTO wvp_user VALUES (1, 'admin','21232f297a57a5a743894a0e4a801fc3',1,'2021-04-13 14:14:57','2021-04-13 14:14:57','3e80d1762a324d5b0ff636e0bd16f1e3'); +INSERT INTO wvp_user_role VALUES (1, 'admin','0','2021-04-13 14:14:57','2021-04-13 14:14:57'); + + + diff --git a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java index 4a92e0931..886d6fa17 100644 --- a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java +++ b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java @@ -1,13 +1,63 @@ package com.genersoft.iot.vmp; -import java.util.logging.LogManager; - +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.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.ServletComponentScan; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.scheduling.annotation.EnableScheduling; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.SessionCookieConfig; +import javax.servlet.SessionTrackingMode; +import java.util.Collections; + +/** + * 启动类 + */ +@ServletComponentScan("com.genersoft.iot.vmp.conf") @SpringBootApplication -public class VManageBootstrap extends LogManager { +@EnableScheduling +public class VManageBootstrap extends SpringBootServletInitializer { + + private final static Logger logger = LoggerFactory.getLogger(VManageBootstrap.class); + + private static String[] args; + private static ConfigurableApplicationContext context; public static void main(String[] args) { - SpringApplication.run(VManageBootstrap.class, 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() { + context.close(); + VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args); + } + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(VManageBootstrap.class); + } + + @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/common/ApiSaveConstant.java b/src/main/java/com/genersoft/iot/vmp/common/ApiSaveConstant.java new file mode 100644 index 000000000..ed1c2b9b5 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/common/ApiSaveConstant.java @@ -0,0 +1,198 @@ +package com.genersoft.iot.vmp.common; + +/** + * 为API重命名, 方便向数据库记录数据的时候展示 + * @author lin + */ +public class ApiSaveConstant { + + public static String getVal(String key) { + String[] keyItemArray = key.split("/"); + if (keyItemArray.length <= 1 || !"api".equals(keyItemArray[1])) { + return null; + } + if (keyItemArray.length >= 4) { + switch (keyItemArray[2]) { + case "alarm": + if ("delete".equals(keyItemArray[3])) { + return "删除报警"; + } + break; + case "device": + switch (keyItemArray[3]) { + case "config": + if (keyItemArray.length >= 5 && "basicParam".equals(keyItemArray[4])) { + return "[设备配置] 基本配置设置命令"; + } + break; + case "control": + switch (keyItemArray[4]) { + case "teleboot": + return "[设备控制] 远程启动"; + case "record": + return "[设备控制] 录像控制"; + case "guard": + return "[设备控制] 布防/撤防命令"; + case "reset_alarm": + return "[设备控制] 报警复位"; + case "i_frame": + return "[设备控制] 强制关键帧"; + case "home_position": + return "[设备控制] 看守位控制"; + default: + return ""; + } + case "query": + if (keyItemArray.length <= 5) { + return null; + } + switch (keyItemArray[4]) { + case "devices": + if (keyItemArray.length < 7) { + return null; + } + switch (keyItemArray[6]) { + case "sync": + return "[设备查询] 同步设备通道"; + case "delete": + return "[设备查询] 移除设备"; + default: + return ""; + } + case "channel": + return "[设备查询] 更新通道信息"; + case "transport": + return "[设备查询] 修改数据流传输模式"; + default: + return ""; + } + default: + return ""; + } + + break; + case "gbStream": + switch (keyItemArray[3]) { + case "del": + return "移除通道与国标的关联"; + case "add": + return "添加通道与国标的关联"; + default: + return ""; + } + case "media": + break; + case "position": + if ("subscribe".equals(keyItemArray[3])) { + return "订阅位置信息"; + } + break; + case "platform": + switch (keyItemArray[3]) { + case "save": + return "添加上级平台"; + case "delete": + return "移除上级平台"; + case "update_channel_for_gb": + return "向上级平台添加国标通道"; + case "del_channel_for_gb": + return "从上级平台移除国标通道"; + default: + return ""; + } + case "platform_gb_stream": + break; + case "play": + switch (keyItemArray[3]) { + case "start": + return "开始点播"; + case "stop": + return "停止点播"; + case "convert": + return "转码"; + case "convertStop": + return "结束转码"; + case "broadcast": + return "语音广播"; + default: + return ""; + } + case "download": + switch (keyItemArray[3]) { + case "start": + return "开始历史媒体下载"; + case "stop": + return "停止历史媒体下载"; + default: + return ""; + } + case "playback": + switch (keyItemArray[3]) { + case "start": + return "开始视频回放"; + case "stop": + return "停止视频回放"; + default: + return ""; + } + case "ptz": + switch (keyItemArray[3]) { + case "control": + return "云台控制"; + case "front_end_command": + return "通用前端控制命令"; + default: + return ""; + } + case "gb_record": + break; + case "onvif": + break; + case "server": + if ("restart".equals(keyItemArray[3])) { + return "重启流媒体服务"; + } + break; + case "proxy": + switch (keyItemArray[3]) { + case "save": + return "保存代理"; + case "del": + return "移除代理"; + case "start": + return "启用代理"; + case "stop": + return "停用代理"; + default: + return ""; + } + case "push": + switch (keyItemArray[3]) { + case "save_to_gb": + return "将推流添加到国标"; + case "remove_form_gb": + return "将推流移出到国标"; + default: + return ""; + } + case "user": + switch (keyItemArray[3]) { + case "login": + return "登录"; + case "changePassword": + return "修改密码"; + case "add": + return "添加用户"; + case "delete": + return "删除用户"; + default: + return ""; + } + default: + return ""; + } + } + return null; + } +} + diff --git a/src/main/java/com/genersoft/iot/vmp/common/CivilCodePo.java b/src/main/java/com/genersoft/iot/vmp/common/CivilCodePo.java new file mode 100644 index 000000000..452c12efe --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/common/CivilCodePo.java @@ -0,0 +1,42 @@ +package com.genersoft.iot.vmp.common; + +public class CivilCodePo { + + private String code; + + private String name; + + private String parentCode; + + public static CivilCodePo getInstance(String[] infoArray) { + CivilCodePo civilCodePo = new CivilCodePo(); + civilCodePo.setCode(infoArray[0]); + civilCodePo.setName(infoArray[1]); + civilCodePo.setParentCode(infoArray[2]); + return civilCodePo; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getParentCode() { + return parentCode; + } + + public void setParentCode(String parentCode) { + this.parentCode = parentCode; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/common/CommonCallback.java b/src/main/java/com/genersoft/iot/vmp/common/CommonCallback.java new file mode 100644 index 000000000..819fe0dd5 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/common/CommonCallback.java @@ -0,0 +1,5 @@ +package com.genersoft.iot.vmp.common; + +public interface CommonCallback{ + public void run(T t); +} diff --git a/src/main/java/com/genersoft/iot/vmp/common/GeneralCallback.java b/src/main/java/com/genersoft/iot/vmp/common/GeneralCallback.java new file mode 100644 index 000000000..df07fac5e --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/common/GeneralCallback.java @@ -0,0 +1,5 @@ +package com.genersoft.iot.vmp.common; + +public interface GeneralCallback{ + void run(int code, String msg, T data); +} diff --git a/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java b/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java new file mode 100644 index 000000000..dabdb4f07 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java @@ -0,0 +1,144 @@ +package com.genersoft.iot.vmp.common; + +import com.genersoft.iot.vmp.service.bean.SSRCInfo; +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * 记录每次发送invite消息的状态 + */ +public class InviteInfo { + + private String deviceId; + + private String channelId; + + private String stream; + + private SSRCInfo ssrcInfo; + + private String receiveIp; + + private Integer receivePort; + + private String streamMode; + + private InviteSessionType type; + + private InviteSessionStatus status; + + private StreamInfo streamInfo; + + + public static InviteInfo getInviteInfo(String deviceId, String channelId, String stream, SSRCInfo ssrcInfo, + String receiveIp, Integer receivePort, String streamMode, + InviteSessionType type, InviteSessionStatus status) { + InviteInfo inviteInfo = new InviteInfo(); + inviteInfo.setDeviceId(deviceId); + inviteInfo.setChannelId(channelId); + inviteInfo.setStream(stream); + inviteInfo.setSsrcInfo(ssrcInfo); + inviteInfo.setReceiveIp(receiveIp); + inviteInfo.setReceivePort(receivePort); + inviteInfo.setStreamMode(streamMode); + inviteInfo.setType(type); + inviteInfo.setStatus(status); + return inviteInfo; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public InviteSessionType getType() { + return type; + } + + public void setType(InviteSessionType type) { + this.type = type; + } + + public InviteSessionStatus getStatus() { + return status; + } + + public void setStatus(InviteSessionStatus status) { + this.status = status; + } + + public StreamInfo getStreamInfo() { + return streamInfo; + } + + public void setStreamInfo(StreamInfo streamInfo) { + this.streamInfo = streamInfo; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public SSRCInfo getSsrcInfo() { + return ssrcInfo; + } + + public void setSsrcInfo(SSRCInfo ssrcInfo) { + this.ssrcInfo = ssrcInfo; + } + + public String getReceiveIp() { + return receiveIp; + } + + public void setReceiveIp(String receiveIp) { + this.receiveIp = receiveIp; + } + + public Integer getReceivePort() { + return receivePort; + } + + public void setReceivePort(Integer receivePort) { + this.receivePort = receivePort; + } + + public String getStreamMode() { + return streamMode; + } + + public void setStreamMode(String streamMode) { + this.streamMode = streamMode; + } + + + /*=========================设备主子码流逻辑START====================*/ + @Schema(description = "是否为子码流(true-是,false-主码流)") + private boolean subStream; + + public boolean isSubStream() { + return subStream; + } + + public void setSubStream(boolean subStream) { + this.subStream = subStream; + } + + + + +} diff --git a/src/main/java/com/genersoft/iot/vmp/common/InviteSessionStatus.java b/src/main/java/com/genersoft/iot/vmp/common/InviteSessionStatus.java new file mode 100644 index 000000000..04cc7c918 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/common/InviteSessionStatus.java @@ -0,0 +1,11 @@ +package com.genersoft.iot.vmp.common; + +/** + * 标识invite消息发出后的各个状态, + * 收到ok钱停止invite发送cancel, + * 收到200ok后发送BYE停止invite + */ +public enum InviteSessionStatus { + ready, + ok, +} diff --git a/src/main/java/com/genersoft/iot/vmp/common/InviteSessionType.java b/src/main/java/com/genersoft/iot/vmp/common/InviteSessionType.java new file mode 100644 index 000000000..5a6eb85b7 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/common/InviteSessionType.java @@ -0,0 +1,7 @@ +package com.genersoft.iot.vmp.common; + +public enum InviteSessionType { + PLAY, + PLAYBACK, + DOWNLOAD +} 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 0082a27d4..cde235bc7 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java @@ -1,23 +1,338 @@ 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; +import java.util.Objects; - private String streamId; +@Schema(description = "流信息") +public class StreamInfo implements Serializable, Cloneable{ + + @Schema(description = "应用名") + private String app; + @Schema(description = "流ID") + private String stream; + @Schema(description = "设备编号") private String deviceID; + @Schema(description = "通道编号") private String channelId; - private String flv; - private String ws_flv; - private String fmp4; - private String ws_fmp4; - private String hls; - private String ws_hls; - private String ts; - private String ws_ts; - private String rtmp; - private String rtsp; - private JSONArray tracks; + + @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) { + if (callIdParam != null) { + callIdParam = Objects.equals(callIdParam, "") ? callIdParam : callIdParam.replace("?", "&"); + } + 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; + public String localTag; + public String remoteTag; + public String branch; + } + + private TransactionInfo transactionInfo; + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } public String getDeviceID() { return deviceID; @@ -35,99 +350,197 @@ 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 JSONArray getTracks() { - return tracks; - } - - public void setTracks(JSONArray 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 StreamURL getRtsp() { + return rtsp; + } + + public StreamURL getRtsps() { + return rtsps; + } + + public StreamURL getRtc() { + return rtc; + } + + public StreamURL getRtcs() { + return 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; + } + + public boolean isPause() { + return pause; + } + + public void setPause(boolean pause) { + this.pause = pause; + } + + public TransactionInfo getTransactionInfo() { + return transactionInfo; + } + + public void setTransactionInfo(TransactionInfo transactionInfo) { + this.transactionInfo = transactionInfo; + } + + @Override + public StreamInfo clone() { + StreamInfo instance = null; + try{ + instance = (StreamInfo)super.clone(); + }catch(CloneNotSupportedException e) { + e.printStackTrace(); + } + return instance; + } + + + /*=========================设备主子码流逻辑START====================*/ + @Schema(description = "是否为子码流(true-是,false-主码流)") + private boolean subStream; + + public boolean isSubStream() { + return subStream; + } + + public void setSubStream(boolean subStream) { + this.subStream = subStream; + } + + } 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 000000000..eecf469ff --- /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 000000000..48485da85 --- /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 new file mode 100644 index 000000000..f2c8454c5 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/common/VersionPo.java @@ -0,0 +1,149 @@ +package com.genersoft.iot.vmp.common; + +import com.alibaba.fastjson2.annotation.JSONField; + +public class VersionPo { + /** + * git的全版本号 + */ + @JSONField(name="GIT_Revision") + private String GIT_Revision; + /** + * maven版本 + */ + @JSONField(name = "Create_By") + private String Create_By; + /** + * git的分支 + */ + @JSONField(name = "GIT_BRANCH") + private String GIT_BRANCH; + /** + * git的url + */ + @JSONField(name = "GIT_URL") + private String GIT_URL; + /** + * 构建日期 + */ + @JSONField(name = "BUILD_DATE") + private String BUILD_DATE; + /** + * 构建日期 + */ + @JSONField(name = "GIT_DATE") + private String GIT_DATE; + /** + * 项目名称 配合pom使用 + */ + @JSONField(name = "artifactId") + private String artifactId; + /** + * git局部版本号 + */ + @JSONField(name = "GIT_Revision_SHORT") + private String GIT_Revision_SHORT; + /** + * 项目的版本如2.0.1.0 配合pom使用 + */ + @JSONField(name = "version") + private String version; + /** + * 子系统名称 + */ + @JSONField(name = "project") + private String project; + /** + * jdk版本 + */ + @JSONField(name="Build_Jdk") + private String Build_Jdk; + + public void setGIT_Revision(String GIT_Revision) { + this.GIT_Revision = GIT_Revision; + } + + public void setCreate_By(String create_By) { + Create_By = create_By; + } + + public void setGIT_BRANCH(String GIT_BRANCH) { + this.GIT_BRANCH = GIT_BRANCH; + } + + public void setGIT_URL(String GIT_URL) { + this.GIT_URL = GIT_URL; + } + + public void setBUILD_DATE(String BUILD_DATE) { + this.BUILD_DATE = BUILD_DATE; + } + + public void setArtifactId(String artifactId) { + this.artifactId = artifactId; + } + + public void setGIT_Revision_SHORT(String GIT_Revision_SHORT) { + this.GIT_Revision_SHORT = GIT_Revision_SHORT; + } + + public void setVersion(String version) { + this.version = version; + } + + public void setProject(String project) { + this.project = project; + } + + public void setBuild_Jdk(String build_Jdk) { + Build_Jdk = build_Jdk; + } + + public String getGIT_Revision() { + return GIT_Revision; + } + + public String getCreate_By() { + return Create_By; + } + + public String getGIT_BRANCH() { + return GIT_BRANCH; + } + + public String getGIT_URL() { + return GIT_URL; + } + + public String getBUILD_DATE() { + return BUILD_DATE; + } + + public String getArtifactId() { + return artifactId; + } + + public String getGIT_Revision_SHORT() { + return GIT_Revision_SHORT; + } + + public String getVersion() { + return version; + } + + public String getProject() { + return project; + } + + 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 846d3d014..2c7c2c3bc 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java +++ b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java @@ -1,30 +1,173 @@ package com.genersoft.iot.vmp.common; /** - * @Description: 定义常量 + * @description: 定义常量 * @author: swwheihei * @date: 2019年5月30日 下午3:04:04 * */ public class VideoManagerConstants { - public static final String MEDIA_SERVER_PREFIX = "VMP_media_server"; + public static final String WVP_SERVER_PREFIX = "VMP_SIGNALLING_SERVER_INFO_"; - public static final String DEVICE_PREFIX = "VMP_device_"; + public static final String WVP_SERVER_STREAM_PREFIX = "VMP_SIGNALLING_STREAM_"; - public static final String CACHEKEY_PREFIX = "VMP_channel_"; + public static final String MEDIA_SERVER_PREFIX = "VMP_MEDIA_SERVER_"; - public static final String KEEPLIVEKEY_PREFIX = "VMP_keeplive_"; + public static final String MEDIA_SERVERS_ONLINE_PREFIX = "VMP_MEDIA_ONLINE_SERVERS_"; - public static final String PLAYER_PREFIX = "VMP_player_"; + public static final String DEVICE_PREFIX = "VMP_DEVICE_"; - public static final String PLAY_BLACK_PREFIX = "VMP_playback_"; + // 设备同步完成 + 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 INVITE_PREFIX = "VMP_INVITE"; + public static final String PLAYER_PREFIX = "VMP_INVITE_PLAY_"; + public static final String PLAY_BLACK_PREFIX = "VMP_INVITE_PLAYBACK_"; + public static final String DOWNLOAD_PREFIX = "VMP_INVITE_DOWNLOAD_"; + + public static final String PLATFORM_KEEPALIVE_PREFIX = "VMP_PLATFORM_KEEPALIVE_"; + + public static final String PLATFORM_CATCH_PREFIX = "VMP_PLATFORM_CATCH_"; + + public static final String PLATFORM_REGISTER_PREFIX = "VMP_PLATFORM_REGISTER_"; + + public static final String PLATFORM_REGISTER_INFO_PREFIX = "VMP_PLATFORM_REGISTER_INFO_"; + + public static final String PLATFORM_SEND_RTP_INFO_PREFIX = "VMP_PLATFORM_SEND_RTP_INFO_"; public static final String EVENT_ONLINE_REGISTER = "1"; - - public static final String EVENT_ONLINE_KEEPLIVE = "2"; - + + public static final String EVENT_ONLINE_MESSAGE = "3"; + public static final String EVENT_OUTLINE_UNREGISTER = "1"; public static final String EVENT_OUTLINE_TIMEOUT = "2"; + + public static final String MEDIA_SSRC_USED_PREFIX = "VMP_MEDIA_USED_SSRC_"; + + public static final String MEDIA_TRANSACTION_USED_PREFIX = "VMP_MEDIA_TRANSACTION_"; + + public static final String 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_START_PLAY_NOTIFY = "VM_MSG_STREAM_START_PLAY_NOTIFY"; + + /** + * redis 消息通知上级平台停止观看流 + */ + public static final String VM_MSG_STREAM_STOP_PLAY_NOTIFY = "VM_MSG_STREAM_STOP_PLAY_NOTIFY"; + + /** + * redis 消息接收关闭一个推流 + */ + public static final String VM_MSG_STREAM_PUSH_CLOSE_REQUESTED = "VM_MSG_STREAM_PUSH_CLOSE_REQUESTED"; + + + /** + * redis 消息通知平台通知设备推流结果 + */ + public static final String VM_MSG_STREAM_PUSH_RESPONSE = "VM_MSG_STREAM_PUSH_RESPONSE"; + + /** + * redis 通知平台关闭推流 + */ + public static final String VM_MSG_STREAM_PUSH_CLOSE = "VM_MSG_STREAM_PUSH_CLOSE"; + + /** + * 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_"; + public static final String WVP_OTHER_SEND_RTP_INFO = "VMP_OTHER_SEND_RTP_INFO_"; + + /** + * Redis Const + * 设备录像信息结果前缀 + */ + public static final String REDIS_RECORD_INFO_RES_PRE = "GB_RECORD_INFO_RES_"; + /** + * Redis Const + * 设备录像信息结果前缀 + */ + public static final String REDIS_RECORD_INFO_RES_COUNT_PRE = "GB_RECORD_INFO_RES_COUNT:"; + } diff --git a/src/main/java/com/genersoft/iot/vmp/common/enums/DeviceControlType.java b/src/main/java/com/genersoft/iot/vmp/common/enums/DeviceControlType.java new file mode 100644 index 000000000..02202d89c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/common/enums/DeviceControlType.java @@ -0,0 +1,77 @@ +package com.genersoft.iot.vmp.common.enums; + +import org.dom4j.Element; +import org.springframework.util.ObjectUtils; + + +/** + * @author gaofuwang + * @date 2023/01/18/ 10:09:00 + * @since 1.0 + */ +public enum DeviceControlType { + + /** + * 云台控制 + * 上下左右,预置位,扫描,辅助功能,巡航 + */ + PTZ("PTZCmd","云台控制"), + /** + * 远程启动 + */ + TELE_BOOT("TeleBoot","远程启动"), + /** + * 录像控制 + */ + RECORD("RecordCmd","录像控制"), + /** + * 布防撤防 + */ + GUARD("GuardCmd","布防撤防"), + /** + * 告警控制 + */ + ALARM("AlarmCmd","告警控制"), + /** + * 强制关键帧 + */ + I_FRAME("IFameCmd","强制关键帧"), + /** + * 拉框放大 + */ + DRAG_ZOOM_IN("DragZoomIn","拉框放大"), + /** + * 拉框缩小 + */ + DRAG_ZOOM_OUT("DragZoomOut","拉框缩小"), + /** + * 看守位 + */ + HOME_POSITION("HomePosition","看守位"); + + private final String val; + + private final String desc; + + DeviceControlType(String val, String desc) { + this.val = val; + this.desc = desc; + } + + public String getVal() { + return val; + } + + public String getDesc() { + return desc; + } + + public static DeviceControlType typeOf(Element rootElement) { + for (DeviceControlType item : DeviceControlType.values()) { + if (!ObjectUtils.isEmpty(rootElement.element(item.val)) || !ObjectUtils.isEmpty(rootElement.elements(item.val))) { + return item; + } + } + return null; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java b/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java new file mode 100644 index 000000000..4d885e86c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java @@ -0,0 +1,121 @@ +package com.genersoft.iot.vmp.conf; + +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.stereotype.Component; +import org.springframework.util.ObjectUtils; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @author lin + */ +@WebFilter(filterName = "ApiAccessFilter", urlPatterns = "/api/*", asyncSupported=true) +@Component +public class ApiAccessFilter extends OncePerRequestFilter { + + private final static Logger logger = LoggerFactory.getLogger(ApiAccessFilter.class); + + + @Autowired + private UserSetting userSetting; + + @Autowired + private ILogService logService; + + + @Override + protected void doFilterInternal(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException { + String username = null; + if (SecurityUtils.getUserInfo() == null) { + username = servletRequest.getParameter("username"); + }else { + username = SecurityUtils.getUserInfo().getUsername(); + } + long start = System.currentTimeMillis(); // 请求进入时间 + String uriName = ApiSaveConstant.getVal(servletRequest.getRequestURI()); + + filterChain.doFilter(servletRequest, servletResponse); + + if (uriName != null && userSetting != null && userSetting.getLogInDatebase() != 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(DateUtil.getNow()); + logService.add(logDto); + + + } + } + + /** + * 获取IP地址 + * + * @param request 请求 + * @return request发起客户端的IP地址 + */ + private String getIP(HttpServletRequest request) { + if (request == null) { + return "0.0.0.0"; + } + + String Xip = request.getHeader("X-Real-IP"); + String XFor = request.getHeader("X-Forwarded-For"); + + String UNKNOWN_IP = "unknown"; + if (StringUtils.isNotEmpty(XFor) && !UNKNOWN_IP.equalsIgnoreCase(XFor)) { + //多次反向代理后会有多个ip值,第一个ip才是真实ip + int index = XFor.indexOf(","); + if (index != -1) { + return XFor.substring(0, index); + } else { + return XFor; + } + } + + XFor = Xip; + if (StringUtils.isNotEmpty(XFor) && !UNKNOWN_IP.equalsIgnoreCase(XFor)) { + return XFor; + } + + if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) { + XFor = request.getHeader("Proxy-Client-IP"); + } + if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) { + XFor = request.getHeader("WL-Proxy-Client-IP"); + } + if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) { + XFor = request.getHeader("HTTP_CLIENT_IP"); + } + if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) { + XFor = request.getHeader("HTTP_X_FORWARDED_FOR"); + } + if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) { + XFor = request.getRemoteAddr(); + } + return XFor; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ApplicationCheckRunner.java b/src/main/java/com/genersoft/iot/vmp/conf/ApplicationCheckRunner.java deleted file mode 100644 index faa9ef12f..000000000 --- a/src/main/java/com/genersoft/iot/vmp/conf/ApplicationCheckRunner.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.genersoft.iot.vmp.conf; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.CommandLineRunner; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; - - -/** - * 对配置文件进行校验 - */ -@Component -@Order(value=2) -public class ApplicationCheckRunner implements CommandLineRunner { - - private Logger logger = LoggerFactory.getLogger("ApplicationCheckRunner"); - - @Value("${sip.ip}") - private String sipIp; - - @Value("${media.ip}") - private String mediaIp; - - @Value("${media.wanIp}") - private String mediaWanIp; - - @Value("${media.hookIp}") - private String mediaHookIp; - - @Value("${media.port}") - private int mediaPort; - - @Value("${media.secret}") - private String mediaSecret; - - @Value("${media.streamNoneReaderDelayMS}") - private String streamNoneReaderDelayMS; - - @Value("${sip.ip}") - private String sipIP; - - @Value("${server.port}") - private String serverPort; - - @Value("${media.autoConfig}") - private boolean autoConfig; - - - @Override - public void run(String... args) throws Exception { - if (sipIP.equals("localhost") || sipIP.equals("127.0.0.1")) { - logger.error("sip.ip不能使用 {} ,请使用类似192.168.1.44这样的来自网卡的IP!!!", sipIP ); - System.exit(1); - } - - if (mediaIp.equals("localhost") || mediaIp.equals("127.0.0.1")) { - logger.warn("mediaIp.ip使用 {} ,将无法收到网络内其他设备的推流!!!", mediaIp ); - } - - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/CivilCodeFileConf.java b/src/main/java/com/genersoft/iot/vmp/conf/CivilCodeFileConf.java new file mode 100644 index 000000000..39e0a70a4 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/CivilCodeFileConf.java @@ -0,0 +1,101 @@ +package com.genersoft.iot.vmp.conf; + +import com.genersoft.iot.vmp.common.CivilCodePo; +import org.ehcache.impl.internal.concurrent.ConcurrentHashMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; +import org.springframework.core.annotation.Order; +import org.springframework.core.io.ClassPathResource; +import org.springframework.util.ObjectUtils; + +import java.io.*; +import java.nio.file.Files; +import java.util.Map; + +/** + * 启动时读取行政区划表 + */ +@Configuration +@Order(value=14) +public class CivilCodeFileConf implements CommandLineRunner { + + private final static Logger logger = LoggerFactory.getLogger(CivilCodeFileConf.class); + + private final Map civilCodeMap= new ConcurrentHashMap<>(); + + @Autowired + @Lazy + private UserSetting userSetting; + + @Override + public void run(String... args) throws Exception { + if (ObjectUtils.isEmpty(userSetting.getCivilCodeFile())) { + logger.warn("[行政区划] 文件未设置,可能造成目录刷新结果不完整"); + return; + } + InputStream inputStream; + if (userSetting.getCivilCodeFile().startsWith("classpath:")){ + String filePath = userSetting.getCivilCodeFile().substring("classpath:".length()); + ClassPathResource civilCodeFile = new ClassPathResource(filePath); + if (!civilCodeFile.exists()) { + logger.warn("[行政区划] 文件<{}>不存在,可能造成目录刷新结果不完整", userSetting.getCivilCodeFile()); + return; + } + inputStream = civilCodeFile.getInputStream(); + + }else { + File civilCodeFile = new File(userSetting.getCivilCodeFile()); + if (!civilCodeFile.exists()) { + logger.warn("[行政区划] 文件<{}>不存在,可能造成目录刷新结果不完整", userSetting.getCivilCodeFile()); + return; + } + inputStream = Files.newInputStream(civilCodeFile.toPath()); + } + + BufferedReader inputStreamReader = new BufferedReader(new InputStreamReader(inputStream)); + int index = -1; + String line; + while ((line = inputStreamReader.readLine()) != null) { + index ++; + if (index == 0) { + continue; + } + String[] infoArray = line.split(","); + CivilCodePo civilCodePo = CivilCodePo.getInstance(infoArray); + civilCodeMap.put(civilCodePo.getCode(), civilCodePo); + } + inputStreamReader.close(); + inputStream.close(); + if (civilCodeMap.size() == 0) { + logger.warn("[行政区划] 文件内容为空,可能造成目录刷新结果不完整"); + }else { + logger.info("[行政区划] 加载成功,共加载数据{}条", civilCodeMap.size()); + } + } + + public CivilCodePo getParentCode(String code) { + if (code.length() > 8) { + return null; + } + if (code.length() == 8) { + String parentCode = code.substring(0, 6); + return civilCodeMap.get(parentCode); + }else { + CivilCodePo civilCodePo = civilCodeMap.get(code); + if (civilCodePo == null){ + return null; + } + String parentCode = civilCodePo.getParentCode(); + if (parentCode == null) { + return null; + } + return civilCodeMap.get(parentCode); + } + + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java b/src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java new file mode 100644 index 000000000..041d73884 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java @@ -0,0 +1,141 @@ +package com.genersoft.iot.vmp.conf; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.stereotype.Component; + +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 { + + private final Logger logger = LoggerFactory.getLogger(DynamicTask.class); + + private ThreadPoolTaskScheduler threadPoolTaskScheduler; + + private final Map> futureMap = new ConcurrentHashMap<>(); + private final Map runnableMap = new ConcurrentHashMap<>(); + + @PostConstruct + public void DynamicTask() { + threadPoolTaskScheduler = new ThreadPoolTaskScheduler(); + threadPoolTaskScheduler.setPoolSize(300); + threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true); + threadPoolTaskScheduler.setAwaitTerminationSeconds(10); + threadPoolTaskScheduler.initialize(); + } + + /** + * 循环执行的任务 + * @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表示执行的间隔 + 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 000000000..0333e0dc3 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/GlobalExceptionHandler.java @@ -0,0 +1,80 @@ +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.HttpRequestMethodNotSupportedException; +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()); + } + + /** + * 默认异常处理 + * @param e 异常 + * @return 统一返回结果 + */ + @ExceptionHandler(IllegalStateException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public WVPResult exceptionHandler(IllegalStateException e) { + return WVPResult.fail(ErrorCode.ERROR400); + } + + /** + * 默认异常处理 + * @param e 异常 + * @return 统一返回结果 + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public WVPResult exceptionHandler(HttpRequestMethodNotSupportedException e) { + return WVPResult.fail(ErrorCode.ERROR400); + } + + + /** + * 自定义异常处理, 处理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 000000000..c8b84fe9f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/GlobalResponseAdvice.java @@ -0,0 +1,66 @@ +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.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; + +/** + * 全局统一返回结果 + * @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 fast() { + 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 new file mode 100644 index 000000000..97b841a3a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java @@ -0,0 +1,237 @@ +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.core.annotation.Order; +import org.springframework.util.ObjectUtils; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.regex.Pattern; + + +@Configuration("mediaConfig") +@Order(0) +public class MediaConfig{ + + 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:}") + private String hookIp; + + @Value("${sip.ip}") + private String sipIp; + + @Value("${sip.domain}") + private String sipDomain; + + @Value("${media.sdp-ip:${media.ip}}") + private String sdpIp; + + @Value("${media.stream-ip:${media.ip}}") + private String streamIp; + + @Value("${media.http-port}") + private Integer httpPort; + + @Value("${media.http-ssl-port:0}") + private Integer httpSSlPort = 0; + + @Value("${media.rtmp-port:0}") + private Integer rtmpPort = 0; + + @Value("${media.rtmp-ssl-port:0}") + private Integer rtmpSSlPort = 0; + + @Value("${media.rtp-proxy-port:0}") + private Integer rtpProxyPort = 0; + + @Value("${media.rtsp-port:0}") + private Integer rtspPort = 0; + + @Value("${media.rtsp-ssl-port:0}") + private Integer rtspSSLPort = 0; + + @Value("${media.auto-config:true}") + private boolean autoConfig = true; + + @Value("${media.secret}") + private String secret; + + @Value("${media.rtp.enable}") + private boolean rtpEnable; + + @Value("${media.rtp.port-range}") + private String rtpPortRange; + + @Value("${media.rtp.send-port-range}") + private String rtpSendPortRange; + + @Value("${media.record-assist-port:0}") + private Integer recordAssistPort = 0; + + public String getId() { + return id; + } + + public String getIp() { + return ip; + } + + public String getHookIp() { + if (ObjectUtils.isEmpty(hookIp)){ + return sipIp.split(",")[0]; + }else { + return hookIp; + } + + } + + public String getSipIp() { + if (sipIp == null) { + return this.ip; + }else { + return sipIp; + } + } + + public int getHttpPort() { + return httpPort; + } + + public int getHttpSSlPort() { + return httpSSlPort; + } + + public int getRtmpPort() { + return rtmpPort; + } + + public int getRtmpSSlPort() { + return rtmpSSlPort; + } + + public int getRtpProxyPort() { + if (rtpProxyPort == null) { + return 0; + }else { + return rtpProxyPort; + } + + } + + public int getRtspPort() { + return rtspPort; + } + + public int getRtspSSLPort() { + return rtspSSLPort; + } + + public boolean isAutoConfig() { + return autoConfig; + } + + public String getSecret() { + return secret; + } + + public boolean isRtpEnable() { + return rtpEnable; + } + + public String getRtpPortRange() { + return rtpPortRange; + } + + public int getRecordAssistPort() { + return recordAssistPort; + } + + public String getSdpIp() { + if (ObjectUtils.isEmpty(sdpIp)){ + return ip; + }else { + 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 (ObjectUtils.isEmpty(streamIp)){ + return ip; + }else { + return streamIp; + } + } + + public String getSipDomain() { + return sipDomain; + } + + public MediaServerItem getMediaSerItem(){ + MediaServerItem mediaServerItem = new MediaServerItem(); + mediaServerItem.setId(id); + mediaServerItem.setIp(ip); + mediaServerItem.setDefaultServer(true); + mediaServerItem.setHookIp(getHookIp()); + mediaServerItem.setSdpIp(getSdpIp()); + mediaServerItem.setStreamIp(getStreamIp()); + mediaServerItem.setHttpPort(httpPort); + mediaServerItem.setHttpSSlPort(httpSSlPort); + mediaServerItem.setRtmpPort(rtmpPort); + mediaServerItem.setRtmpSSlPort(rtmpSSlPort); + mediaServerItem.setRtpProxyPort(getRtpProxyPort()); + mediaServerItem.setRtspPort(rtspPort); + mediaServerItem.setRtspSSLPort(rtspSSLPort); + mediaServerItem.setAutoConfig(autoConfig); + mediaServerItem.setSecret(secret); + mediaServerItem.setRtpEnable(rtpEnable); + mediaServerItem.setRtpPortRange(rtpPortRange); + mediaServerItem.setSendRtpPortRange(rtpSendPortRange); + mediaServerItem.setRecordAssistPort(recordAssistPort); + mediaServerItem.setHookAliveInterval(30.00f); + + mediaServerItem.setCreateTime(DateUtil.getNow()); + mediaServerItem.setUpdateTime(DateUtil.getNow()); + + return mediaServerItem; + } + + public String getRtpSendPortRange() { + return rtpSendPortRange; + } + + public void setRtpSendPortRange(String rtpSendPortRange) { + this.rtpSendPortRange = rtpSendPortRange; + } + + 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 new file mode 100644 index 000000000..56573fe58 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/MediaStatusTimerTask.java @@ -0,0 +1,15 @@ +package com.genersoft.iot.vmp.conf; + +import org.springframework.scheduling.annotation.Scheduled; + +/** + * 定时向zlm同步媒体流状态 + */ +public class MediaStatusTimerTask { + + + @Scheduled(fixedRate = 2 * 1000) //每3秒执行一次 + public void execute(){ + + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/MybatisConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/MybatisConfig.java new file mode 100644 index 000000000..03ef09945 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/MybatisConfig.java @@ -0,0 +1,36 @@ +package com.genersoft.iot.vmp.conf; + +import org.apache.ibatis.logging.stdout.StdOutImpl; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; + +import javax.sql.DataSource; + +/** + * 配置mybatis + */ +@Configuration +@Order(value=1) +public class MybatisConfig { + + @Autowired + private UserSetting userSetting; + + @Bean + public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { + final SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean(); + sqlSessionFactory.setDataSource(dataSource); + org.apache.ibatis.session.Configuration config = new org.apache.ibatis.session.Configuration(); + if (userSetting.getSqlLog()){ + config.setLogImpl(StdOutImpl.class); + } + config.setMapUnderscoreToCamelCase(true); + sqlSessionFactory.setConfiguration(config); + return sqlSessionFactory.getObject(); + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java new file mode 100644 index 000000000..f3fd1d80a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java @@ -0,0 +1,278 @@ +package com.genersoft.iot.vmp.conf; + +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.service.IMediaServerService; +import org.apache.http.HttpHost; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.springframework.core.annotation.Order; +import org.mitre.dsmiley.httpproxy.ProxyServlet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.ObjectUtils; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.net.ConnectException; + +/** + * @author lin + */ +@SuppressWarnings(value = {"rawtypes", "unchecked"}) +@Configuration +@Order(1) +public class ProxyServletConfig { + + private final static Logger logger = LoggerFactory.getLogger(ProxyServletConfig.class); + + @Autowired + private IMediaServerService mediaServerService; + + @Value("${server.port}") + private int serverPort; + + @Bean + public ServletRegistrationBean zlmServletRegistrationBean(){ + ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new ZlmProxyServlet(),"/zlm/*"); + servletRegistrationBean.setName("zlm_Proxy"); + servletRegistrationBean.addInitParameter("targetUri", "http://127.0.0.1:6080"); + servletRegistrationBean.addUrlMappings(); + if (logger.isDebugEnabled()) { + servletRegistrationBean.addInitParameter("log", "true"); + } + return servletRegistrationBean; + } + + class ZlmProxyServlet extends ProxyServlet{ + @Override + protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) { + String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString); + MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI()); + if (mediaInfo != null) { + if (!ObjectUtils.isEmpty(queryStr)) { + queryStr += "&secret=" + mediaInfo.getSecret(); + }else { + queryStr = "secret=" + mediaInfo.getSecret(); + } + } + return queryStr; + } + + /** + * 异常处理 + */ + @Override + protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResonse, Exception e){ + try { + super.handleRequestException(proxyRequest, proxyResonse, e); + } catch (ServletException servletException) { + logger.error("zlm 代理失败: ", e); + } catch (IOException ioException) { + if (ioException instanceof ConnectException) { + logger.error("zlm 连接失败"); + } else { + logger.error("zlm 代理失败: ", e); + } + } catch (RuntimeException exception){ + logger.error("zlm 代理失败: ", e); + } + } + + /** + * 对于为按照格式请求的可以直接返回404 + */ + @Override + protected String getTargetUri(HttpServletRequest servletRequest) { + String requestURI = servletRequest.getRequestURI(); + MediaServerItem mediaInfo = getMediaInfoByUri(requestURI); + + String uri = null; + if (mediaInfo != null) { +// String realRequestURI = requestURI.substring(requestURI.indexOf(mediaInfo.getId())+ mediaInfo.getId().length()); + uri = String.format("http://%s:%s", mediaInfo.getIp(), mediaInfo.getHttpPort()); + }else { + uri = "http://127.0.0.1:" + serverPort +"/index/hook/null"; // 只是一个能返回404的请求而已, 其他的也可以 + } + return uri; + } + + /** + * 动态替换请求目标 + */ + @Override + protected HttpHost getTargetHost(HttpServletRequest servletRequest) { + String requestURI = servletRequest.getRequestURI(); + MediaServerItem mediaInfo = getMediaInfoByUri(requestURI); + HttpHost host; + if (mediaInfo != null) { + host = new HttpHost(mediaInfo.getIp(), mediaInfo.getHttpPort()); + }else { + host = new HttpHost("127.0.0.1", serverPort); + } + return host; + + } + + /** + * 根据uri获取流媒体信息 + */ + MediaServerItem getMediaInfoByUri(String uri){ + String[] split = uri.split("/"); + String mediaServerId = split[2]; + if ("default".equalsIgnoreCase(mediaServerId)) { + return mediaServerService.getDefaultMediaServer(); + }else { + return mediaServerService.getOne(mediaServerId); + } + } + + /** + * 去掉url中的标志信息 + */ + @Override + protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) { + String requestURI = servletRequest.getRequestURI(); + MediaServerItem mediaInfo = getMediaInfoByUri(requestURI); + String url = super.rewriteUrlFromRequest(servletRequest); + if (mediaInfo == null) { + logger.error("[ZLM服务访问代理],错误:处理url信息时未找到流媒体信息=>{}", requestURI); + return url; + } + if (!ObjectUtils.isEmpty(mediaInfo.getId())) { + url = url.replace(mediaInfo.getId() + "/", ""); + } + return url.replace("default/", ""); + } + } + + @Bean + public ServletRegistrationBean recordServletRegistrationBean(){ + ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new RecordProxyServlet(),"/record_proxy/*"); + servletRegistrationBean.setName("record_proxy"); + servletRegistrationBean.addInitParameter("targetUri", "http://127.0.0.1:18081"); + servletRegistrationBean.addUrlMappings(); + if (logger.isDebugEnabled()) { + servletRegistrationBean.addInitParameter("log", "true"); + } + return servletRegistrationBean; + } + + class RecordProxyServlet extends ProxyServlet{ + + @Override + protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) { + String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString); + MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI()); + if (mediaInfo == null) { + return null; + } + String remoteHost = String.format("http://%s:%s", mediaInfo.getStreamIp(), mediaInfo.getRecordAssistPort()); + if (!ObjectUtils.isEmpty(queryStr)) { + queryStr += "&remoteHost=" + remoteHost; + }else { + queryStr = "remoteHost=" + remoteHost; + } + return queryStr; + } + + /** + * 异常处理 + */ + @Override + protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResponse, Exception e){ + try { + super.handleRequestException(proxyRequest, proxyResponse, e); + } catch (ServletException servletException) { + logger.error("录像服务 代理失败: ", e); + } catch (IOException ioException) { + if (ioException instanceof ConnectException) { + logger.error("录像服务 连接失败"); +// }else if (ioException instanceof ClientAbortException) { +// /** +// * TODO 使用这个代理库实现代理在遇到代理视频文件时,如果是206结果,会遇到报错蛋市目前功能正常, +// * TODO 暂时去除异常处理。后续使用其他代理框架修改测试 +// */ + + }else { + logger.error("录像服务 代理失败: ", e); + } + } catch (RuntimeException exception){ + logger.error("录像服务 代理失败: ", e); + } + } + + /** + * 对于为按照格式请求的可以直接返回404 + */ + @Override + protected String getTargetUri(HttpServletRequest servletRequest) { + String requestURI = servletRequest.getRequestURI(); + MediaServerItem mediaInfo = getMediaInfoByUri(requestURI); + + String uri = null; + if (mediaInfo != null) { +// String realRequestURI = requestURI.substring(requestURI.indexOf(mediaInfo.getId())+ mediaInfo.getId().length()); + uri = String.format("http://%s:%s", mediaInfo.getIp(), mediaInfo.getRecordAssistPort()); + }else { + uri = "http://127.0.0.1:" + serverPort +"/index/hook/null"; // 只是一个能返回404的请求而已, 其他的也可以 + } + return uri; + } + + /** + * 动态替换请求目标 + */ + @Override + protected HttpHost getTargetHost(HttpServletRequest servletRequest) { + String requestURI = servletRequest.getRequestURI(); + MediaServerItem mediaInfo = getMediaInfoByUri(requestURI); + HttpHost host; + if (mediaInfo != null) { + host = new HttpHost(mediaInfo.getIp(), mediaInfo.getRecordAssistPort()); + }else { + host = new HttpHost("127.0.0.1", serverPort); + } + return host; + + } + + /** + * 根据uri获取流媒体信息 + */ + MediaServerItem getMediaInfoByUri(String uri){ + String[] split = uri.split("/"); + String mediaServerId = split[2]; + if ("default".equalsIgnoreCase(mediaServerId)) { + return mediaServerService.getDefaultMediaServer(); + }else { + return mediaServerService.getOne(mediaServerId); + } + + } + + /** + * 去掉url中的标志信息 + */ + @Override + protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) { + String requestURI = servletRequest.getRequestURI(); + MediaServerItem mediaInfo = getMediaInfoByUri(requestURI); + String url = super.rewriteUrlFromRequest(servletRequest); + if (mediaInfo == null) { + logger.error("[录像服务访问代理],错误:处理url信息时未找到流媒体信息=>{}", requestURI); + return url; + } + 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 47d2a02b8..000000000 --- a/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.genersoft.iot.vmp.conf; - -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; - -/** - * @Description:Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置 - * @author: swwheihei - * @date: 2019年5月30日 上午10:58:25 - * - */ -@Configuration -public class RedisConfig extends CachingConfigurerSupport { - - @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 - * @param listenerAdapter - * @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/ScheduleConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/ScheduleConfig.java new file mode 100644 index 000000000..432fafbb0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/ScheduleConfig.java @@ -0,0 +1,30 @@ +package com.genersoft.iot.vmp.conf; + +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.SchedulingConfigurer; +import org.springframework.scheduling.config.ScheduledTaskRegistrar; + +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * "@Scheduled"是Spring框架提供的一种定时任务执行机制,默认情况下它是单线程的,在同时执行多个定时任务时可能会出现阻塞和性能问题。 + * 为了解决这种单线程瓶颈问题,可以将定时任务的执行机制改为支持多线程 + */ +@Configuration +public class ScheduleConfig implements SchedulingConfigurer { + + public static final int cpuNum = Runtime.getRuntime().availableProcessors(); + + private static final int corePoolSize = cpuNum; + + private static final String threadNamePrefix = "scheduled-task-pool-%d"; + + @Override + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { + taskRegistrar.setScheduler(new ScheduledThreadPoolExecutor(corePoolSize, + new BasicThreadFactory.Builder().namingPattern(threadNamePrefix).daemon(true).build(), + new ThreadPoolExecutor.CallerRunsPolicy())); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ServiceInfo.java b/src/main/java/com/genersoft/iot/vmp/conf/ServiceInfo.java new file mode 100644 index 000000000..55fbcf48f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/ServiceInfo.java @@ -0,0 +1,30 @@ +package com.genersoft.iot.vmp.conf; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.web.context.WebServerInitializedEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +@Component +public class ServiceInfo implements ApplicationListener { + + private final Logger logger = LoggerFactory.getLogger(ServiceInfo.class); + + private static int serverPort; + + public static int getServerPort() { + return serverPort; + } + + @Override + public void onApplicationEvent(WebServerInitializedEvent event) { + // 项目启动获取启动的端口号 + ServiceInfo.serverPort = event.getWebServer().getPort(); + logger.info("项目启动获取启动的端口号: " + ServiceInfo.serverPort); + } + + public void setServerPort(int serverPort) { + ServiceInfo.serverPort = serverPort; + } +} 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 52d808906..6a6c54f90 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java @@ -1,71 +1,110 @@ package com.genersoft.iot.vmp.conf; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; -@Configuration("sipConfig") +@Component +@ConfigurationProperties(prefix = "sip", ignoreInvalidFields = true) +@Order(0) public class SipConfig { - @Value("${sip.ip}") - String sipIp; - @Value("${sip.port}") - Integer sipPort; - @Value("${sip.domain}") - String sipDomain; - @Value("${sip.id}") - String sipId; - @Value("${sip.password}") - String sipPassword; + private String ip; + + private String showIp; + + private Integer port; + + private String domain; + + private String id; + + private String password; - @Value("${sip.ptz.speed:50}") - Integer speed; + Integer ptzSpeed = 50; - public String getSipIp() { - return sipIp; + Integer registerTimeInterval = 120; + + private boolean alarm; + + public void setIp(String ip) { + this.ip = ip; } - public void setSipIp(String sipIp) { - this.sipIp = sipIp; + public void setPort(Integer port) { + this.port = port; } - public Integer getSipPort() { - return sipPort; + public void setDomain(String domain) { + this.domain = domain; } - public void setSipPort(Integer sipPort) { - this.sipPort = sipPort; + public void setId(String id) { + this.id = id; } - public String getSipDomain() { - return sipDomain; + public void setPassword(String password) { + this.password = password; } - public void setSipDomain(String sipDomain) { - this.sipDomain = sipDomain; + public void setPtzSpeed(Integer ptzSpeed) { + this.ptzSpeed = ptzSpeed; } - public String getSipId() { - return sipId; + + public void setRegisterTimeInterval(Integer registerTimeInterval) { + this.registerTimeInterval = registerTimeInterval; } - public void setSipId(String sipId) { - this.sipId = sipId; + public String getIp() { + return ip; } - public String getSipPassword() { - return sipPassword; + + public Integer getPort() { + return port; } - public void setSipPassword(String sipPassword) { - this.sipPassword = sipPassword; + + public String getDomain() { + return domain; } - public Integer getSpeed() { - return speed; + + public String getId() { + return id; } - public void setSpeed(Integer speed) { - this.speed = speed; + public String getPassword() { + return password; + } + + + public Integer getPtzSpeed() { + return ptzSpeed; + } + + public Integer getRegisterTimeInterval() { + return registerTimeInterval; + } + + public boolean isAlarm() { + return alarm; + } + + public void setAlarm(boolean alarm) { + this.alarm = alarm; + } + + public String getShowIp() { + if (this.showIp == null) { + return this.ip; + } + return showIp; + } + + public void setShowIp(String showIp) { + this.showIp = showIp; } } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java b/src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java new file mode 100644 index 000000000..eb1b157a3 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java @@ -0,0 +1,62 @@ +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.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.boot.CommandLineRunner; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 系统启动时控制上级平台重新注册 + * @author lin + */ +@Component +@Order(value=13) +public class SipPlatformRunner implements CommandLineRunner { + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IPlatformService platformService; + + @Autowired + private ISIPCommanderForPlatform sipCommanderForPlatform; + + + @Override + public void run(String... args) throws Exception { + // 获取所有启用的平台 + List parentPlatforms = storager.queryEnableParentPlatformList(true); + + for (ParentPlatform parentPlatform : parentPlatforms) { + + ParentPlatformCatch parentPlatformCatchOld = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); + + // 更新缓存 + ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch(); + parentPlatformCatch.setParentPlatform(parentPlatform); + parentPlatformCatch.setId(parentPlatform.getServerGBId()); + redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); + if (parentPlatformCatchOld != null) { + // 取消订阅 + sipCommanderForPlatform.unregister(parentPlatform, parentPlatformCatchOld.getSipTransactionInfo(), null, (eventResult)->{ + platformService.login(parentPlatform); + }); + } + + // 设置所有平台离线 + platformService.offline(parentPlatform, false); + } + } +} 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 000000000..587518f17 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/SpringDocConfig.java @@ -0,0 +1,87 @@ +package com.genersoft.iot.vmp.conf; + +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 org.springframework.core.annotation.Order; +import org.springdoc.core.GroupedOpenApi; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author lin + */ +@Configuration +@Order(1) +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/SystemInfoTimerTask.java b/src/main/java/com/genersoft/iot/vmp/conf/SystemInfoTimerTask.java new file mode 100644 index 000000000..9bc86261d --- /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 new file mode 100644 index 000000000..c7532e38e --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/ThreadPoolTaskConfig.java @@ -0,0 +1,70 @@ +package com.genersoft.iot.vmp.conf; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.ThreadPoolExecutor; + +/** + * ThreadPoolTask 配置类 + * @author lin + */ +@Configuration +@Order(1) +@EnableAsync(proxyTargetClass = true) +public class ThreadPoolTaskConfig { + + public static final int cpuNum = Runtime.getRuntime().availableProcessors(); + + /** + * 默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务, + * 当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中; + * 当队列满了,就继续创建线程,当线程数量大于等于maxPoolSize后,开始使用拒绝策略拒绝 + */ + + /** + * 核心线程数(默认线程数) + */ + private static final int corePoolSize = cpuNum; + /** + * 最大线程数 + */ + private static final int maxPoolSize = cpuNum*2; + /** + * 允许线程空闲时间(单位:默认为秒) + */ + private static final int keepAliveTime = 30; + + /** + * 缓冲队列大小 + */ + private static final int queueCapacity = 10000; + /** + * 线程池名前缀 + */ + private static final String threadNamePrefix = "wvp-"; + + /** + * + * @return + */ + @Bean("taskExecutor") // bean的名称,默认为首字母小写的方法名 + public ThreadPoolTaskExecutor taskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(corePoolSize); + executor.setMaxPoolSize(maxPoolSize); + executor.setQueueCapacity(queueCapacity); + executor.setKeepAliveSeconds(keepAliveTime); + executor.setThreadNamePrefix(threadNamePrefix); + + // 线程池对拒绝任务的处理策略 + // CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + // 初始化 + executor.initialize(); + return executor; + } +} 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 000000000..55bf371e3 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java @@ -0,0 +1,328 @@ +package com.genersoft.iot.vmp.conf; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * 配置文件 user-settings 映射的配置信息 + */ +@Component +@ConfigurationProperties(prefix = "user-settings", ignoreInvalidFields = true) +@Order(0) +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 Boolean sqlLog = Boolean.FALSE; + private Boolean sendToPlatformsWhenIdLost = Boolean.FALSE; + + private Boolean refuseChannelStatusChannelFormNotify = Boolean.FALSE; + + private Boolean deviceStatusNotify = Boolean.FALSE; + private Boolean useCustomSsrcForParentInvite = Boolean.TRUE; + + private String serverId = "000000"; + + private String recordPath = null; + + private String thirdPartyGBIdReg = "[\\s\\S]*"; + + private String civilCodeFile = "classpath:civilCode.csv"; + + private List interfaceAuthenticationExcludes = new ArrayList<>(); + + private List allowedOrigins = new ArrayList<>(); + + private int maxNotifyCountQueue = 10000; + + private int registerAgainAfterTime = 60; + + private boolean registerKeepIntDialog = false; + + 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; + } + + public List getAllowedOrigins() { + return allowedOrigins; + } + + public void setAllowedOrigins(List allowedOrigins) { + this.allowedOrigins = allowedOrigins; + } + + public Boolean getSendToPlatformsWhenIdLost() { + return sendToPlatformsWhenIdLost; + } + + public void setSendToPlatformsWhenIdLost(Boolean sendToPlatformsWhenIdLost) { + this.sendToPlatformsWhenIdLost = sendToPlatformsWhenIdLost; + } + + public Boolean getRefuseChannelStatusChannelFormNotify() { + return refuseChannelStatusChannelFormNotify; + } + + public void setRefuseChannelStatusChannelFormNotify(Boolean refuseChannelStatusChannelFormNotify) { + this.refuseChannelStatusChannelFormNotify = refuseChannelStatusChannelFormNotify; + } + + public String getRecordPath() { + return recordPath; + } + + public void setRecordPath(String recordPath) { + this.recordPath = recordPath; + } + + public int getMaxNotifyCountQueue() { + return maxNotifyCountQueue; + } + + public void setMaxNotifyCountQueue(int maxNotifyCountQueue) { + this.maxNotifyCountQueue = maxNotifyCountQueue; + } + + public Boolean getDeviceStatusNotify() { + return deviceStatusNotify; + } + + public void setDeviceStatusNotify(Boolean deviceStatusNotify) { + this.deviceStatusNotify = deviceStatusNotify; + } + + public Boolean getUseCustomSsrcForParentInvite() { + return useCustomSsrcForParentInvite; + } + + public void setUseCustomSsrcForParentInvite(Boolean useCustomSsrcForParentInvite) { + this.useCustomSsrcForParentInvite = useCustomSsrcForParentInvite; + } + + public Boolean getSqlLog() { + return sqlLog; + } + + public void setSqlLog(Boolean sqlLog) { + this.sqlLog = sqlLog; + } + + public String getCivilCodeFile() { + return civilCodeFile; + } + + public void setCivilCodeFile(String civilCodeFile) { + this.civilCodeFile = civilCodeFile; + } + + public int getRegisterAgainAfterTime() { + return registerAgainAfterTime; + } + + public void setRegisterAgainAfterTime(int registerAgainAfterTime) { + this.registerAgainAfterTime = registerAgainAfterTime; + } + + public boolean isRegisterKeepIntDialog() { + return registerKeepIntDialog; + } + + public void setRegisterKeepIntDialog(boolean registerKeepIntDialog) { + this.registerKeepIntDialog = registerKeepIntDialog; + } +} 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 da587f4f5..000000000 --- a/src/main/java/com/genersoft/iot/vmp/conf/UserSetup.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.genersoft.iot.vmp.conf; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Configuration; - -@Configuration("userSetup") -public class UserSetup { - @Value("${userSettings.savePositionHistory}") - boolean savePositionHistory; - - public boolean getSavePositionHistory() { - return savePositionHistory; - } - - public void setSavePositionHistory(boolean savePositionHistory) { - this.savePositionHistory = savePositionHistory; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/VManagerConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/VManagerConfig.java deleted file mode 100644 index ff9d6439a..000000000 --- a/src/main/java/com/genersoft/iot/vmp/conf/VManagerConfig.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.genersoft.iot.vmp.conf; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Configuration; - -/** - * @Description: 获取数据库配置 - * @author: swwheihei - * @date: 2020年5月6日 下午2:46:00 - */ -@Configuration("vmConfig") -public class VManagerConfig { - - @Value("${spring.application.database:redis}") - private String database; - - - public String getDatabase() { - return database; - } - - public void setDatabase(String database) { - this.database = database; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/VersionConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/VersionConfig.java new file mode 100644 index 000000000..3d5bb5e7e --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/VersionConfig.java @@ -0,0 +1,39 @@ +package com.genersoft.iot.vmp.conf; + +import org.springframework.core.annotation.Order; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties(prefix = "version") +@Order(0) +public class VersionConfig { + + private String version; + private String artifactId; + private String description; + + public void setVersion(String version) { + this.version = version; + } + + public void setArtifactId(String artifactId) { + this.artifactId = artifactId; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getVersion() { + return version; + } + + public String getArtifactId() { + return artifactId; + } + + public String getDescription() { + return description; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/VersionInfo.java b/src/main/java/com/genersoft/iot/vmp/conf/VersionInfo.java new file mode 100644 index 000000000..eb408ab8b --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/VersionInfo.java @@ -0,0 +1,26 @@ +package com.genersoft.iot.vmp.conf; + +import com.genersoft.iot.vmp.common.VersionPo; +import com.genersoft.iot.vmp.utils.GitUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class VersionInfo { + + @Autowired + GitUtil gitUtil; + + public VersionPo getVersion() { + VersionPo versionPo = new VersionPo(); + versionPo.setGIT_Revision(gitUtil.getGitCommitId()); + versionPo.setGIT_BRANCH(gitUtil.getBranch()); + versionPo.setGIT_URL(gitUtil.getGitUrl()); + versionPo.setBUILD_DATE(gitUtil.getBuildDate()); + versionPo.setGIT_Revision_SHORT(gitUtil.getCommitIdShort()); + 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 new file mode 100644 index 000000000..77d83ee22 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java @@ -0,0 +1,33 @@ +package com.genersoft.iot.vmp.conf; + +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; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +public class WVPTimerTask { + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IMediaServerService mediaServerService; + + @Value("${server.port}") + private int serverPort; + + @Autowired + private SipConfig sipConfig; + + @Scheduled(fixedRate = 2 * 1000) //每3秒执行一次 + public void execute(){ + JSONObject jsonObject = new JSONObject(); + jsonObject.put("ip", sipConfig.getIp()); + jsonObject.put("port", serverPort); + redisCatchStorage.updateWVPInfo(jsonObject, 3); + } +} 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 000000000..d2e120690 --- /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 000000000..5566d4bb6 --- /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 000000000..e1a738abe --- /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/RedisMsgListenConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/redis/RedisMsgListenConfig.java new file mode 100644 index 000000000..c14ebcdd0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/redis/RedisMsgListenConfig.java @@ -0,0 +1,76 @@ +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.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.listener.PatternTopic; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; + + +/** + * @description:Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置 + * @author: swwheihei + * @date: 2019年5月30日 上午10:58:25 + * + */ +@Configuration +@Order(value=1) +public class RedisMsgListenConfig { + + @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; + + @Autowired + private RedisCloseStreamMsgListener redisCloseStreamMsgListener; + + @Autowired + private RedisPushStreamCloseResponseListener redisPushStreamCloseResponseListener; + + + /** + * 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)); + container.addMessageListener(redisCloseStreamMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE)); + container.addMessageListener(redisPushStreamCloseResponseListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE_REQUESTED)); + return container; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/redis/RedisTemplateConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/redis/RedisTemplateConfig.java new file mode 100644 index 000000000..df3345eef --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/redis/RedisTemplateConfig.java @@ -0,0 +1,28 @@ +package com.genersoft.iot.vmp.conf.redis; + +import com.alibaba.fastjson2.support.spring.data.redis.GenericFastJsonRedisSerializer; +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.serializer.StringRedisSerializer; + +@Configuration +public class RedisTemplateConfig { + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + // 使用fastJson序列化 + GenericFastJsonRedisSerializer fastJsonRedisSerializer = new GenericFastJsonRedisSerializer(); + // value值的序列化采用fastJsonRedisSerializer + redisTemplate.setValueSerializer(fastJsonRedisSerializer); + redisTemplate.setHashValueSerializer(fastJsonRedisSerializer); + + // key的序列化采用StringRedisSerializer + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setHashKeySerializer(new StringRedisSerializer()); + redisTemplate.setConnectionFactory(redisConnectionFactory); + return redisTemplate; + } +} 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 new file mode 100644 index 000000000..ab85c458c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java @@ -0,0 +1,48 @@ +package com.genersoft.iot.vmp.conf.security; + +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.conf.security.dto.JwtUser; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import org.springframework.http.MediaType; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +/** + * 处理匿名用户访问逻辑 + * @author lin + */ +@Component +public class AnonymousAuthenticationEntryPoint implements AuthenticationEntryPoint { + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) { + String jwt = request.getHeader(JwtUtils.getHeader()); + JwtUser jwtUser = JwtUtils.verifyToken(jwt); + String username = jwtUser.getUserName(); + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, jwtUser.getPassword() ); + SecurityContextHolder.getContext().setAuthentication(token); + JSONObject jsonObject = new JSONObject(); + 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); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setCharacterEncoding(StandardCharsets.UTF_8.name()); + try { + response.getWriter().print(jsonObject.toJSONString()); + } catch (IOException ioException) { + ioException.printStackTrace(); + } + } +} 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 new file mode 100644 index 000000000..0cda4a5cd --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java @@ -0,0 +1,51 @@ +package com.genersoft.iot.vmp.conf.security; + +import com.alibaba.excel.util.StringUtils; +import com.genersoft.iot.vmp.conf.security.dto.LoginUser; +import com.genersoft.iot.vmp.service.IUserService; +import com.genersoft.iot.vmp.storager.dao.dto.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; + +/** + * 用户登录认证逻辑 + */ +@Component +public class DefaultUserDetailsServiceImpl implements UserDetailsService { + + private final static Logger logger = LoggerFactory.getLogger(DefaultUserDetailsServiceImpl.class); + + @Autowired + private IUserService userService; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + if (StringUtils.isBlank(username)) { + logger.info("登录用户:{} 不存在", username); + throw new UsernameNotFoundException("登录用户:" + username + " 不存在"); + } + + // 查出密码 + User user = userService.getUserByUsername(username); + if (user == null) { + logger.info("登录用户:{} 不存在", username); + throw new UsernameNotFoundException("登录用户:" + username + " 不存在"); + } + String password = SecurityUtils.encryptPassword(user.getPassword()); + user.setPassword(password); + return new LoginUser(user, LocalDateTime.now()); + } + + + + + + +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java new file mode 100644 index 000000000..f35b5bd86 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java @@ -0,0 +1,91 @@ +package com.genersoft.iot.vmp.conf.security; + +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.conf.security.dto.JwtUser; +import com.genersoft.iot.vmp.storager.dao.dto.Role; +import com.genersoft.iot.vmp.storager.dao.dto.User; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; + +/** + * jwt token 过滤器 + */ + +@Component +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + + @Autowired + private UserSetting userSetting; + + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { + + // 忽略登录请求的token验证 + String requestURI = request.getRequestURI(); + if (requestURI.equalsIgnoreCase("/api/user/login")) { + chain.doFilter(request, response); + return; + } + if (!userSetting.isInterfaceAuthentication()) { + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(null, null, new ArrayList<>() ); + SecurityContextHolder.getContext().setAuthentication(token); + chain.doFilter(request, response); + return; + } + String jwt = request.getHeader(JwtUtils.getHeader()); + // 这里如果没有jwt,继续往后走,因为后面还有鉴权管理器等去判断是否拥有身份凭证,所以是可以放行的 + // 没有jwt相当于匿名访问,若有一些接口是需要权限的,则不能访问这些接口 + if (StringUtils.isBlank(jwt)) { + jwt = request.getParameter(JwtUtils.getHeader()); + if (StringUtils.isBlank(jwt)) { + chain.doFilter(request, response); + return; + } + } + + JwtUser jwtUser = JwtUtils.verifyToken(jwt); + String username = jwtUser.getUserName(); + // TODO 处理各个状态 + switch (jwtUser.getStatus()){ + case EXPIRED: + response.setStatus(400); + chain.doFilter(request, response); + // 异常 + return; + case EXCEPTION: + // 过期 + response.setStatus(400); + chain.doFilter(request, response); + return; + case EXPIRING_SOON: + // 即将过期 +// return; + default: + } + + // 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录 + User user = new User(); + user.setUsername(jwtUser.getUserName()); + user.setPassword(jwtUser.getPassword()); + Role role = new Role(); + role.setId(jwtUser.getRoleId()); + user.setRole(role); + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user, jwtUser.getPassword(), new ArrayList<>() ); + SecurityContextHolder.getContext().setAuthentication(token); + chain.doFilter(request, response); + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java new file mode 100644 index 000000000..c9c7b680f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java @@ -0,0 +1,141 @@ +package com.genersoft.iot.vmp.conf.security; + +import com.genersoft.iot.vmp.conf.security.dto.JwtUser; +import org.jose4j.json.JsonUtil; +import org.jose4j.jwk.RsaJsonWebKey; +import org.jose4j.jws.AlgorithmIdentifiers; +import org.jose4j.jws.JsonWebSignature; +import org.jose4j.jwt.JwtClaims; +import org.jose4j.jwt.NumericDate; +import org.jose4j.jwt.consumer.ErrorCodes; +import org.jose4j.jwt.consumer.InvalidJwtException; +import org.jose4j.jwt.consumer.JwtConsumer; +import org.jose4j.jwt.consumer.JwtConsumerBuilder; +import org.jose4j.lang.JoseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.security.PrivateKey; +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +public class JwtUtils { + + private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class); + + private static final String HEADER = "access-token"; + private static final String AUDIENCE = "Audience"; + + private static final long EXPIRED_THRESHOLD = 10 * 60; + + private static final String keyId = "3e79646c4dbc408383a9eed09f2b85ae"; + private static final String privateKeyStr = "{\"kty\":\"RSA\",\"kid\":\"3e79646c4dbc408383a9eed09f2b85ae\",\"alg\":\"RS256\",\"n\":\"gndmVdiOTSJ5et2HIeTM5f1m61x5ojLUi5HDfvr-jRrESQ5kbKuySGHVwR4QhwinpY1wQqBnwc80tx7cb_6SSqsTOoGln6T_l3k2Pb54ClVnGWiW_u1kmX78V2TZOsVmZmwtdZCMi-2zWIyAdIEXE-gncIehoAgEoq2VAhaCURbJWro_EwzzQwNmCTkDodLAx4npXRd_qSu0Ayp0txym9OFovBXBULRvk4DPiy3i_bPUmCDxzC46pTtFOe9p82uybTehZfULZtXXqRm85FL9n5zkrsTllPNAyEGhgb0RK9sE5nK1m_wNNysDyfLC4EFf1VXTrKm14XNVjc2vqLb7Mw\",\"e\":\"AQAB\",\"d\":\"ed7U_k3rJ4yTk70JtRSIfjKGiEb67BO1TabcymnljKO7RU8nage84zZYuSu_XpQsHk6P1f0Gzxkicghm_Er-FrfVn2pp70Xu52z3yRd6BJUgWLDFk97ngScIyw5OiULKU9SrZk2frDpftNCSUcIgb50F8m0QAnBa_CdPsQKbuuhLv8V8tBAV7F_lAwvSBgu56wRo3hPz5dWH8YeXM7XBfQ9viFMNEKd21sP_j5C7ueUnXT66nBxe3ZJEU3iuMYM6D6dB_KW2GfZC6WmTgvGhhxJD0h7aYmfjkD99MDleB7SkpbvoODOqiQ5Epb7Nyh6kv5u4KUv2CJYtATLZkUeMkQ\",\"p\":\"uBUjWPWtlGksmOqsqCNWksfqJvMcnP_8TDYN7e4-WnHL4N-9HjRuPDnp6kHvCIEi9SEfxm7gNxlRcWegvNQr3IZCz7TnCTexXc5NOklB9OavWFla6u-s3Thn6Tz45-EUjpJr0VJMxhO-KxGmuTwUXBBp4vN6K2qV6rQNFmgkWzk\",\"q\":\"tW_i7cCec56bHkhITL_79dXHz_PLC_f7xlynmlZJGU_d6mqOKmLBNBbTMLnYW8uAFiFzWxDeDHh1o5uF0mSQR-Z1Fg35OftnpbWpy0Cbc2la5WgXQjOwtG1eLYIY2BD3-wQ1VYDBCvowr4FDi-sngxwLqvwmrJ0xjhi99O-Gzcs\",\"dp\":\"q1d5jE85Hz_6M-eTh_lEluEf0NtPEc-vvhw-QO4V-cecNpbrCBdTWBmr4dE3NdpFeJc5ZVFEv-SACyei1MBEh0ItI_pFZi4BmMfy2ELh8ptaMMkTOESYyVy8U7veDq9RnBcr5i1Nqr0rsBkA77-9T6gzdvycBZdzLYAkAmwzEvk\",\"dq\":\"q29A2K08Crs-jmp2Bi8Q_8QzvIX6wSBbwZ4ir24AO-5_HNP56IrPS0yV2GCB0pqCOGb6_Hz_koDvhtuYoqdqvMVAtMoXR3YJBUaVXPt65p4RyNmFwIPe31zHs_BNUTsXVRMw4c16mci03-Af1sEm4HdLfxAp6sfM3xr5wcnhcek\",\"qi\":\"rHPgVTyHUHuYzcxfouyBfb1XAY8nshwn0ddo81o1BccD4Z7zo5It6SefDHjxCAbcmbiCcXBSooLcY-NF5FMv3fg19UE21VyLQltHcVjRRp2tRs4OHcM8yaXIU2x6N6Z6BP2tOksHb9MOBY1wAQzFOAKg_G4Sxev6-_6ud6RISuc\"}"; + private static final String publicKeyStr = "{\"kty\":\"RSA\",\"kid\":\"3e79646c4dbc408383a9eed09f2b85ae\",\"alg\":\"RS256\",\"n\":\"gndmVdiOTSJ5et2HIeTM5f1m61x5ojLUi5HDfvr-jRrESQ5kbKuySGHVwR4QhwinpY1wQqBnwc80tx7cb_6SSqsTOoGln6T_l3k2Pb54ClVnGWiW_u1kmX78V2TZOsVmZmwtdZCMi-2zWIyAdIEXE-gncIehoAgEoq2VAhaCURbJWro_EwzzQwNmCTkDodLAx4npXRd_qSu0Ayp0txym9OFovBXBULRvk4DPiy3i_bPUmCDxzC46pTtFOe9p82uybTehZfULZtXXqRm85FL9n5zkrsTllPNAyEGhgb0RK9sE5nK1m_wNNysDyfLC4EFf1VXTrKm14XNVjc2vqLb7Mw\",\"e\":\"AQAB\"}"; + + /** + * token过期时间(分钟) + */ + public static final long expirationTime = 30; + + public static String createToken(String username, String password, Integer roleId) { + try { + /** + * “iss” (issuer) 发行人 + * + * “sub” (subject) 主题 + * + * “aud” (audience) 接收方 用户 + * + * “exp” (expiration time) 到期时间 + * + * “nbf” (not before) 在此之前不可用 + * + * “iat” (issued at) jwt的签发时间 + */ + //Payload + JwtClaims claims = new JwtClaims(); + claims.setGeneratedJwtId(); + claims.setIssuedAtToNow(); + // 令牌将过期的时间 分钟 + claims.setExpirationTimeMinutesInTheFuture(expirationTime); + claims.setNotBeforeMinutesInThePast(0); + claims.setSubject("login"); + claims.setAudience(AUDIENCE); + //添加自定义参数,必须是字符串类型 + claims.setClaim("username", username); + claims.setClaim("password", password); + claims.setClaim("roleId", roleId); + + //jws + JsonWebSignature jws = new JsonWebSignature(); + //签名算法RS256 + jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256); + jws.setKeyIdHeaderValue(keyId); + jws.setPayload(claims.toJson()); + + PrivateKey privateKey = new RsaJsonWebKey(JsonUtil.parseJson(privateKeyStr)).getPrivateKey(); + jws.setKey(privateKey); + + //get token + String idToken = jws.getCompactSerialization(); + return idToken; + } catch (JoseException e) { + logger.error("[Token生成失败]: {}", e.getMessage()); + } + + return null; + } + + public static String getHeader() { + return HEADER; + } + + + public static JwtUser verifyToken(String token) { + + JwtUser jwtUser = new JwtUser(); + + try { + JwtConsumer consumer = new JwtConsumerBuilder() + .setRequireExpirationTime() + .setMaxFutureValidityInMinutes(5256000) + .setAllowedClockSkewInSeconds(30) + .setRequireSubject() + //.setExpectedIssuer("") + .setExpectedAudience(AUDIENCE) + .setVerificationKey(new RsaJsonWebKey(JsonUtil.parseJson(publicKeyStr)).getPublicKey()) + .build(); + + JwtClaims claims = consumer.processToClaims(token); + NumericDate expirationTime = claims.getExpirationTime(); + // 判断是否即将过期, 默认剩余时间小于5分钟未即将过期 + // 剩余时间 (秒) + long timeRemaining = LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8)) - expirationTime.getValue(); + if (timeRemaining < 5 * 60) { + jwtUser.setStatus(JwtUser.TokenStatus.EXPIRING_SOON); + }else { + jwtUser.setStatus(JwtUser.TokenStatus.NORMAL); + } + + String username = (String) claims.getClaimValue("username"); + String password = (String) claims.getClaimValue("password"); + Long roleId = (Long) claims.getClaimValue("roleId"); + jwtUser.setUserName(username); + jwtUser.setPassword(password); + jwtUser.setRoleId(roleId.intValue()); + + return jwtUser; + } catch (InvalidJwtException e) { + if (e.hasErrorCode(ErrorCodes.EXPIRED)) { + jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED); + }else { + jwtUser.setStatus(JwtUser.TokenStatus.EXCEPTION); + } + return jwtUser; + }catch (Exception e) { + logger.error("[Token解析失败]: {}", e.getMessage()); + jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED); + return jwtUser; + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/LogoutHandler.java b/src/main/java/com/genersoft/iot/vmp/conf/security/LogoutHandler.java new file mode 100644 index 000000000..790eab842 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/LogoutHandler.java @@ -0,0 +1,27 @@ +package com.genersoft.iot.vmp.conf.security; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * 退出登录成功 + */ +@Component +public class LogoutHandler implements LogoutSuccessHandler { + + private final static Logger logger = LoggerFactory.getLogger(LogoutHandler.class); + + @Override + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { + String username = request.getParameter("username"); + logger.info("[退出登录成功] - [{}]", username); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java b/src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java new file mode 100644 index 000000000..f012f7efb --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java @@ -0,0 +1,84 @@ +package com.genersoft.iot.vmp.conf.security; + +import com.genersoft.iot.vmp.conf.security.dto.LoginUser; +import com.genersoft.iot.vmp.storager.dao.dto.User; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import javax.security.sasl.AuthenticationException; +import java.time.LocalDateTime; + +public class SecurityUtils { + + /** + * 描述根据账号密码进行调用security进行认证授权 主动调 + * 用AuthenticationManager的authenticate方法实现 + * 授权成功后将用户信息存入SecurityContext当中 + * @param username 用户名 + * @param password 密码 + * @param authenticationManager 认证授权管理器, + * @see AuthenticationManager + * @return UserInfo 用户信息 + */ + public static LoginUser login(String username, String password, AuthenticationManager authenticationManager) throws AuthenticationException { + //使用security框架自带的验证token生成器 也可以自定义。 + UsernamePasswordAuthenticationToken token =new UsernamePasswordAuthenticationToken(username,password); + //认证 如果失败,这里会自动异常后返回,所以这里不需要判断返回值是否为空,确定是否登录成功 + Authentication authenticate = authenticationManager.authenticate(token); + LoginUser user = (LoginUser) authenticate.getPrincipal(); + + SecurityContextHolder.getContext().setAuthentication(token); + + return user; + } + + /** + * 获取当前登录的所有认证信息 + * @return + */ + public static Authentication getAuthentication(){ + SecurityContext context = SecurityContextHolder.getContext(); + return context.getAuthentication(); + } + + /** + * 获取当前登录用户信息 + * @return + */ + public static LoginUser getUserInfo(){ + Authentication authentication = getAuthentication(); + if(authentication!=null){ + Object principal = authentication.getPrincipal(); + if(principal!=null && !"anonymousUser".equals(principal.toString())){ + + User user = (User) principal; + return new LoginUser(user, LocalDateTime.now()); + } + } + return null; + } + + /** + * 获取当前登录用户ID + * @return + */ + public static int getUserId(){ + LoginUser user = getUserInfo(); + return user.getId(); + } + + /** + * 生成BCryptPasswordEncoder密码 + * + * @param password 密码 + * @return 加密字符串 + */ + public static String encryptPassword(String password) { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.encode(password); + } +} 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 new file mode 100644 index 000000000..6a247359b --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java @@ -0,0 +1,158 @@ +package com.genersoft.iot.vmp.conf.security; + +import com.genersoft.iot.vmp.conf.UserSetting; +import org.springframework.core.annotation.Order; +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.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.CorsUtils; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * 配置Spring Security + * @author lin + */ +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +@Order(1) +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + + private final static Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class); + + @Autowired + private UserSetting userSetting; + + @Autowired + private DefaultUserDetailsServiceImpl userDetailsService; + /** + * 登出成功的处理 + */ + @Autowired + private LogoutHandler logoutHandler; + /** + * 未登录的处理 + */ + @Autowired + private AnonymousAuthenticationEntryPoint anonymousAuthenticationEntryPoint; + @Autowired + private JwtAuthenticationFilter jwtAuthenticationFilter; + + + /** + * 描述: 静态资源放行,这里的放行,是不走 Spring Security 过滤器链 + **/ + @Override + public void configure(WebSecurity web) { + if (userSetting.isInterfaceAuthentication()) { + ArrayList matchers = new ArrayList<>(); + matchers.add("/"); + matchers.add("/#/**"); + matchers.add("/static/**"); + matchers.add("/index.html"); + matchers.add("/doc.html"); + matchers.add("/webjars/**"); + matchers.add("/swagger-resources/**"); + matchers.add("/v3/api-docs/**"); + matchers.add("/js/**"); + matchers.add("/api/device/query/snap/**"); + matchers.add("/record_proxy/*/**"); + matchers.addAll(userSetting.getInterfaceAuthenticationExcludes()); + // 可以直接访问的静态数据 + web.ignoring().antMatchers(matchers.toArray(new String[0])); + } + } + + /** + * 配置认证方式 + * @param auth + * @throws Exception + */ + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + // 设置不隐藏 未找到用户异常 + provider.setHideUserNotFoundExceptions(true); + // 用户认证service - 查询数据库的逻辑 + provider.setUserDetailsService(userDetailsService); + // 设置密码加密算法 + provider.setPasswordEncoder(passwordEncoder()); + auth.authenticationProvider(provider); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.headers().contentTypeOptions().disable() + .and().cors().configurationSource(configurationSource()) + .and().csrf().disable() + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + + // 配置拦截规则 + .and() + .authorizeRequests() + .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() + .antMatchers(userSetting.getInterfaceAuthenticationExcludes().toArray(new String[0])).permitAll() + .antMatchers("/api/user/login","/index/hook/**","/zlm_Proxy/FhTuMYqB2HeCuNOb/record/t/1/2023-03-25/16:35:07-16:35:16-9353.mp4").permitAll() + .anyRequest().authenticated() + // 异常处理器 + .and() + .exceptionHandling() + .authenticationEntryPoint(anonymousAuthenticationEntryPoint) + .and().logout().logoutUrl("/api/user/logout").permitAll() + .logoutSuccessHandler(logoutHandler) + ; + http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + + } + + CorsConfigurationSource configurationSource(){ + // 配置跨域 + CorsConfiguration corsConfiguration = new CorsConfiguration(); + corsConfiguration.setAllowedHeaders(Arrays.asList("*")); + corsConfiguration.setAllowedMethods(Arrays.asList("*")); + corsConfiguration.setMaxAge(3600L); + corsConfiguration.setAllowCredentials(true); + corsConfiguration.setAllowedOrigins(userSetting.getAllowedOrigins()); + corsConfiguration.setExposedHeaders(Arrays.asList(JwtUtils.getHeader())); + + UrlBasedCorsConfigurationSource url = new UrlBasedCorsConfigurationSource(); + url.registerCorsConfiguration("/**",corsConfiguration); + return url; + } + + /** + * 描述: 密码加密算法 BCrypt 推荐使用 + **/ + @Bean + public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + /** + * 描述: 注入AuthenticationManager管理器 + **/ + @Override + @Bean + public AuthenticationManager authenticationManager() throws Exception { + return super.authenticationManager(); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/dto/JwtUser.java b/src/main/java/com/genersoft/iot/vmp/conf/security/dto/JwtUser.java new file mode 100644 index 000000000..8921a3083 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/dto/JwtUser.java @@ -0,0 +1,63 @@ +package com.genersoft.iot.vmp.conf.security.dto; + +public class JwtUser { + + public enum TokenStatus{ + /** + * 正常的使用状态 + */ + NORMAL, + /** + * 过期而失效 + */ + EXPIRED, + /** + * 即将过期 + */ + EXPIRING_SOON, + /** + * 异常 + */ + EXCEPTION + } + + private String userName; + + private String password; + + private int roleId; + + private TokenStatus status; + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public TokenStatus getStatus() { + return status; + } + + public void setStatus(TokenStatus status) { + this.status = status; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public int getRoleId() { + return roleId; + } + + public void setRoleId(int roleId) { + this.roleId = roleId; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/dto/LoginUser.java b/src/main/java/com/genersoft/iot/vmp/conf/security/dto/LoginUser.java new file mode 100644 index 000000000..61bd6fa6f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/dto/LoginUser.java @@ -0,0 +1,111 @@ +package com.genersoft.iot.vmp.conf.security.dto; + +import com.genersoft.iot.vmp.storager.dao.dto.Role; +import com.genersoft.iot.vmp.storager.dao.dto.User; +import org.springframework.security.core.CredentialsContainer; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.SpringSecurityCoreVersion; +import org.springframework.security.core.userdetails.UserDetails; + +import java.time.LocalDateTime; +import java.util.Collection; + +public class LoginUser implements UserDetails, CredentialsContainer { + + private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; + + /** + * 用户 + */ + private User user; + + private String accessToken; + + + /** + * 登录时间 + */ + private LocalDateTime loginTime; + + public LoginUser(User user, LocalDateTime loginTime) { + this.user = user; + this.loginTime = loginTime; + } + + + @Override + public Collection getAuthorities() { + return null; + } + + @Override + public String getPassword() { + return user.getPassword(); + } + + @Override + public String getUsername() { + return user.getUsername(); + } + + /** + * 账户是否未过期,过期无法验证 + */ + @Override + public boolean isAccountNonExpired() { + return true; + } + + /** + * 指定用户是否解锁,锁定的用户无法进行身份验证 + *

    + * 密码锁定 + *

    + */ + @Override + public boolean isAccountNonLocked() { + return true; + } + + /** + * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证 + */ + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + /** + * 用户是否被启用或禁用。禁用的用户无法进行身份验证。 + */ + @Override + public boolean isEnabled() { + return true; + } + + /** + * 认证完成后,擦除密码 + */ + @Override + public void eraseCredentials() { + user.setPassword(null); + } + + + public int getId() { + return user.getId(); + } + + public Role getRole() { + return user.getRole(); + } + + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } +} 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 c5dc1e5cc..90b5291e8 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java @@ -1,32 +1,27 @@ package com.genersoft.iot.vmp.gb28181; -import java.text.ParseException; -import java.util.Properties; -import java.util.TooManyListenersException; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import javax.sip.*; -import javax.sip.header.CallIdHeader; -import javax.sip.message.Response; - -import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; +import com.genersoft.iot.vmp.conf.SipConfig; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.GbStringMsgParserFactory; +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 com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorFactory; -import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; - -import gov.nist.javax.sip.SipStackImpl; +import javax.sip.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; @Component -public class SipLayer implements SipListener { +@Order(value=10) +public class SipLayer implements CommandLineRunner { private final static Logger logger = LoggerFactory.getLogger(SipLayer.class); @@ -34,202 +29,113 @@ public class SipLayer implements SipListener { private SipConfig sipConfig; @Autowired - private SIPProcessorFactory processorFactory; + private ISIPProcessorObserver sipProcessorObserver; @Autowired - private SipSubscribe sipSubscribe; + private UserSetting userSetting; - private SipStack sipStack; + 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()); + } - /** - * 消息处理器线程池 - */ - private ThreadPoolExecutor processThreadPool; - - @Bean("initSipServer") - private ThreadPoolExecutor initSipServer() { - - int processThreadNum = Runtime.getRuntime().availableProcessors() * 10; - LinkedBlockingQueue processQueue = new LinkedBlockingQueue(10000); - processThreadPool = new ThreadPoolExecutor(processThreadNum,processThreadNum, - 0L,TimeUnit.MILLISECONDS,processQueue, - new ThreadPoolExecutor.CallerRunsPolicy()); - return processThreadPool; - } - - @Bean("sipFactory") - @DependsOn("initSipServer") - private SipFactory createSipFactory() { - sipFactory = SipFactory.getInstance(); - sipFactory.setPathName("gov.nist"); - return sipFactory; - } - - @Bean("sipStack") - @DependsOn({"initSipServer", "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.getSipIp()); - 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; + SipFactory.getInstance().setPathName("gov.nist"); + if (monitorIps.size() > 0) { + for (String monitorIp : monitorIps) { + addListeningPoint(monitorIp, sipConfig.getPort()); + } + if (udpSipProviderMap.size() + tcpSipProviderMap.size() == 0) { + System.exit(1); + } + } } - @Bean("tcpSipProvider") - @DependsOn("sipStack") - private SipProvider startTcpListener() { - ListeningPoint tcpListeningPoint = null; - SipProvider tcpSipProvider = null; + private void addListeningPoint(String monitorIp, int port){ + SipStackImpl sipStack; try { - tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(), "TCP"); - tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint); - tcpSipProvider.addSipListener(this); - logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getSipPort() + "}"); - } catch (TransportNotSupportedException | InvalidArgumentException | TooManyListenersException | ObjectInUseException e) { - logger.error(String.format("创建SIP服务失败: %s", e.getMessage())); - } - return tcpSipProvider; - } - - @Bean("udpSipProvider") - @DependsOn("sipStack") - private SipProvider startUdpListener() throws Exception { - ListeningPoint udpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(), "UDP"); - SipProvider udpSipProvider = sipStack.createSipProvider(udpListeningPoint); - udpSipProvider.addSipListener(this); - logger.info("Sip Server UDP 启动成功 port {" + sipConfig.getSipPort() + "}"); - return udpSipProvider; - } - - /** - * SIP服务端接收消息的方法 Content 里面是GBK编码 This method is called by the SIP stack when a - * new request arrives. - */ - @Override - public void processRequest(RequestEvent evt) { - logger.debug(evt.getRequest().toString()); - // 由于jainsip是单线程程序,为提高性能并发处理 - processThreadPool.execute(() -> { - if (processorFactory != null) { - processorFactory.createRequestProcessor(evt).process(); - } - }); - } - - @Override - public void processResponse(ResponseEvent evt) { - Response response = evt.getResponse(); - logger.debug(evt.getResponse().toString()); - int status = response.getStatusCode(); - if ((status >= 200) && (status < 300)) { // Success! - ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt); - try { - processor.process(evt, this, sipConfig); - } catch (ParseException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - if (evt.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0 ) { - CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME); - if (callIdHeader != null) { - SipSubscribe.Event subscribe = sipSubscribe.getOkSubscribe(callIdHeader.getCallId()); - if (subscribe != null) { - subscribe.response(evt); - } - } - } - } else if ((status >= 100) && (status < 200)) { - // 增加其它无需回复的响应,如101、180等 - } else { - logger.warn("接收到失败的response响应!status:" + status + ",message:" + response.getReasonPhrase()/* .getContent().toString()*/); - if (evt.getResponse() != null && sipSubscribe.getErrorSubscribesSize() > 0 ) { - CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME); - if (callIdHeader != null) { - SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()); - if (subscribe != null) { - subscribe.response(evt); - } - } - } + sipStack = (SipStackImpl)SipFactory.getInstance().createSipStack(DefaultProperties.getProperties(monitorIp, userSetting.getSipLog())); + sipStack.setMessageParserFactory(new GbStringMsgParserFactory()); + } 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); + 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); + } + try { + ListeningPoint udpListeningPoint = sipStack.createListeningPoint(monitorIp, port, "UDP"); + + SipProviderImpl udpSipProvider = (SipProviderImpl)sipStack.createSipProvider(udpListeningPoint); + udpSipProvider.addSipListener(sipProcessorObserver); + + 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); + } } - /** - *

    - * Title: processTimeout - *

    - *

    - * Description: - *

    - * - * @param timeoutEvent - */ - @Override - public void processTimeout(TimeoutEvent timeoutEvent) { - // TODO Auto-generated method stub - + public SipProviderImpl getUdpSipProvider(String ip) { + if (ObjectUtils.isEmpty(ip)) { + return null; + } + return udpSipProviderMap.get(ip); } - /** - *

    - * Title: processIOException - *

    - *

    - * Description: - *

    - * - * @param exceptionEvent - */ - @Override - public void processIOException(IOExceptionEvent exceptionEvent) { - // TODO Auto-generated method stub - + public SipProviderImpl getUdpSipProvider() { + if (udpSipProviderMap.size() != 1) { + return null; + } + return udpSipProviderMap.values().stream().findFirst().get(); } - /** - *

    - * Title: processTransactionTerminated - *

    - *

    - * Description: - *

    - * - * @param transactionTerminatedEvent - */ - @Override - public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) { - // TODO Auto-generated method stub - + public SipProviderImpl getTcpSipProvider() { + if (tcpSipProviderMap.size() != 1) { + return null; + } + return tcpSipProviderMap.values().stream().findFirst().get(); } - /** - *

    - * Title: processDialogTerminated - *

    - *

    - * Description: - *

    - * - * @param dialogTerminatedEvent - */ - @Override - public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) { - // TODO Auto-generated method stub - + 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 4605ede8f..05ecb078c 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,8 +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 java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.Instant; +import java.util.Random; /** * Implements the HTTP digest authentication method server side functionality. @@ -49,6 +49,8 @@ import gov.nist.core.InternalErrorHandler; public class DigestServerAuthenticationHelper { + private Logger logger = LoggerFactory.getLogger(DigestServerAuthenticationHelper.class); + private MessageDigest messageDigest; public static final String DEFAULT_ALGORITHM = "MD5"; @@ -86,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); } @@ -105,13 +102,10 @@ public class DigestServerAuthenticationHelper { WWWAuthenticateHeader proxyAuthenticate = headerFactory .createWWWAuthenticateHeader(DEFAULT_SCHEME); proxyAuthenticate.setParameter("realm", realm); + proxyAuthenticate.setParameter("qop", "auth"); proxyAuthenticate.setParameter("nonce", generateNonce()); - - proxyAuthenticate.setParameter("opaque", ""); - proxyAuthenticate.setParameter("stale", "FALSE"); proxyAuthenticate.setParameter("algorithm", DEFAULT_ALGORITHM); -// proxyAuthenticate.setParameter("qop", "auth"); response.setHeader(proxyAuthenticate); } catch (Exception ex) { InternalErrorHandler.handleException(ex); @@ -128,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(); @@ -175,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 || authHeader.getRealm() == null) { + return false; + } String realm = authHeader.getRealm().trim(); String username = authHeader.getUsername().trim(); @@ -193,32 +191,34 @@ public class DigestServerAuthenticationHelper { // 客户端随机数,这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。 // 这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护 - //String cNonce = authHeader.getCNonce(); + String cnonce = authHeader.getCNonce(); // nonce计数器,是一个16进制的数值,表示同一nonce下客户端发送出请求的数量 int nc = authHeader.getNonceCount(); - String ncStr = new DecimalFormat("00000000").format(nc); -// String ncStr = new DecimalFormat("00000000").format(Integer.parseInt(nc + "", 16)); + String ncStr = String.format("%08x", nc).toUpperCase(); + // String ncStr = new DecimalFormat("00000000").format(nc); + // 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); - System.out.println("A1: " + A1); - System.out.println("A2: " + A2); - + logger.debug("A1: " + A1); + logger.debug("A2: " + A2); mdbytes = messageDigest.digest(A2.getBytes()); String HA2 = toHexString(mdbytes); - System.out.println("HA1: " + HA1); - System.out.println("HA2: " + HA2); - String cnonce = authHeader.getCNonce(); - System.out.println("nonce: " + nonce); - System.out.println("nc: " + ncStr); - System.out.println("cnonce: " + cnonce); - System.out.println("qop: " + qop); + logger.debug("HA1: " + HA1); + logger.debug("HA2: " + HA2); + // String cnonce = authHeader.getCNonce(); + logger.debug("nonce: " + nonce); + logger.debug("nc: " + ncStr); + logger.debug("cnonce: " + cnonce); + 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; } @@ -228,13 +228,14 @@ public class DigestServerAuthenticationHelper { KD += ":" + qop; } KD += ":" + HA2; - System.out.println("KD: " + KD); + logger.debug("KD: " + KD); mdbytes = messageDigest.digest(KD.getBytes()); String mdString = toHexString(mdbytes); - System.out.println("mdString: " + mdString); + logger.debug("mdString: " + mdString); String response = authHeader.getResponse(); - System.out.println("response: " + response); + logger.debug("response: " + response); return mdString.equals(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 6fe63cc57..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/auth/RegisterLogicHandler.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.auth; - -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 { - - @Autowired - private SIPCommander cmder; - - public void onRegister(Device device) { - // TODO 后续处理,只有第一次注册时调用查询设备信息,如需更新调用更新API接口 - 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 000000000..7f50f4d37 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/AlarmChannelMessage.java @@ -0,0 +1,57 @@ +package com.genersoft.iot.vmp.gb28181.bean; + + +/** + * 通过redis分发报警消息 + */ +public class AlarmChannelMessage { + /** + * 国标编号 + */ + private String gbId; + /** + * 报警编号 + */ + private int alarmSn; + /** + * 告警类型 + */ + private int alarmType; + + /** + * 报警描述 + */ + 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 int getAlarmType() { + return alarmType; + } + + public void setAlarmType(int alarmType) { + this.alarmType = alarmType; + } + + 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 000000000..965d7f2af --- /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 000000000..320bbdd03 --- /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/CmdType.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CmdType.java new file mode 100644 index 000000000..ed5cad6ce --- /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 9393106ee..1c7baac9c 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,39 +53,144 @@ 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; /** * 在线 */ - private int online; + @Schema(description = "是否在线,true为在线,false为离线") + private boolean onLine; /** * 注册时间 */ - private Long registerTimeMillis; + @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; + + /** + * 字符集, 支持 UTF-8 与 GB2312 + */ + @Schema(description = "符集, 支持 UTF-8 与 GB2312") + private String charset ; + + /** + * 目录订阅周期,0为不订阅 + */ + @Schema(description = "目录订阅周期,o为不订阅") + 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; + + @Schema(description = "密码") + private String password; + + @Schema(description = "收流IP") + private String sdpIp; + + @Schema(description = "SIP交互IP(设备访问平台的IP)") + private String localIp; + + @Schema(description = "是否作为消息通道") + private boolean asMessageChannel; + + @Schema(description = "设备注册的事务信息") + private SipTransactionInfo sipTransactionInfo; + + + + public String getDeviceId() { return deviceId; } @@ -125,6 +243,20 @@ public class Device { return streamMode; } + public Integer getStreamModeForParam() { + if (streamMode == null) { + return 0; + } + if (streamMode.equalsIgnoreCase("UDP")) { + return 0; + }else if (streamMode.equalsIgnoreCase("TCP-PASSIVE")) { + return 1; + }else if (streamMode.equalsIgnoreCase("TCP-ACTIVE")) { + return 2; + } + return 0; + } + public void setStreamMode(String streamMode) { this.streamMode = streamMode; } @@ -153,12 +285,12 @@ public class Device { this.hostAddress = hostAddress; } - public int getOnline() { - return online; + public boolean isOnLine() { + return onLine; } - public void setOnline(int online) { - this.online = online; + public void setOnLine(boolean onLine) { + this.onLine = onLine; } public int getChannelCount() { @@ -169,11 +301,171 @@ public class Device { this.channelCount = channelCount; } - public Long getRegisterTimeMillis() { - return registerTimeMillis; + public String getRegisterTime() { + return registerTime; } - public void setRegisterTimeMillis(Long registerTimeMillis) { - this.registerTimeMillis = registerTimeMillis; + public void setRegisterTime(String registerTime) { + this.registerTime = registerTime; } + + public String getKeepaliveTime() { + return keepaliveTime; + } + + public void setKeepaliveTime(String keepaliveTime) { + this.keepaliveTime = keepaliveTime; + } + + public int getExpires() { + return expires; + } + + public void setExpires(int expires) { + this.expires = expires; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + public String getCharset() { + return charset; + } + + public void setCharset(String charset) { + this.charset = charset; + } + + public int getSubscribeCycleForCatalog() { + return subscribeCycleForCatalog; + } + + public void setSubscribeCycleForCatalog(int subscribeCycleForCatalog) { + this.subscribeCycleForCatalog = subscribeCycleForCatalog; + } + + public int getSubscribeCycleForMobilePosition() { + return subscribeCycleForMobilePosition; + } + + public void setSubscribeCycleForMobilePosition(int subscribeCycleForMobilePosition) { + this.subscribeCycleForMobilePosition = subscribeCycleForMobilePosition; + } + + public int getMobilePositionSubmissionInterval() { + return mobilePositionSubmissionInterval; + } + + public void setMobilePositionSubmissionInterval(int mobilePositionSubmissionInterval) { + this.mobilePositionSubmissionInterval = mobilePositionSubmissionInterval; + } + + public int getSubscribeCycleForAlarm() { + return subscribeCycleForAlarm; + } + + public void setSubscribeCycleForAlarm(int subscribeCycleForAlarm) { + this.subscribeCycleForAlarm = subscribeCycleForAlarm; + } + + public boolean isSsrcCheck() { + return ssrcCheck; + } + + public void setSsrcCheck(boolean ssrcCheck) { + this.ssrcCheck = ssrcCheck; + } + + public String getGeoCoordSys() { + return geoCoordSys; + } + + public void setGeoCoordSys(String geoCoordSys) { + this.geoCoordSys = geoCoordSys; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getSdpIp() { + return sdpIp; + } + + public void setSdpIp(String sdpIp) { + this.sdpIp = sdpIp; + } + + public String getLocalIp() { + return localIp; + } + + public void setLocalIp(String localIp) { + this.localIp = localIp; + } + + public int getKeepaliveIntervalTime() { + return keepaliveIntervalTime; + } + + public void setKeepaliveIntervalTime(int keepaliveIntervalTime) { + this.keepaliveIntervalTime = keepaliveIntervalTime; + } + + public boolean isAsMessageChannel() { + return asMessageChannel; + } + + public void setAsMessageChannel(boolean asMessageChannel) { + this.asMessageChannel = asMessageChannel; + } + + public SipTransactionInfo getSipTransactionInfo() { + return sipTransactionInfo; + } + + public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) { + this.sipTransactionInfo = sipTransactionInfo; + } + + /*======================设备主子码流逻辑START=========================*/ + @Schema(description = "开启主子码流切换的开关(false-不开启,true-开启)") + private boolean switchPrimarySubStream; + + public boolean isSwitchPrimarySubStream() { + return switchPrimarySubStream; + } + + public void setSwitchPrimarySubStream(boolean switchPrimarySubStream) { + this.switchPrimarySubStream = switchPrimarySubStream; + } + + /*======================设备主子码流逻辑END=========================*/ + + } 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 35cb6bf4a..bfc97b555 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,49 +1,109 @@ 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; /** - * 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级 警情- + * 通道Id */ + @Schema(description = "通道的国标编号") + private String channelId; + + /** + * 报警级别, 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; + } + + public void setId(String id) { + this.id = id; + } public String getDeviceId() { return deviceId; @@ -108,4 +168,20 @@ public class DeviceAlarm { public void setAlarmType(String alarmType) { this.alarmType = alarmType; } + + public String getChannelId() { + return channelId; + } + + 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 000000000..d1fb6db61 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarmMethod.java @@ -0,0 +1,54 @@ +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; + } + + /** + * 查询是否匹配类型 + * @param code + * @return + */ + public static DeviceAlarmMethod typeOf(int code) { + for (DeviceAlarmMethod item : DeviceAlarmMethod.values()) { + if (code==item.getVal()) { + return item; + } + } + return null; + } +} 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 ca6ef60f1..f7ef12d32 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,123 +1,166 @@ 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; /** * 在线/离线 @@ -128,33 +171,89 @@ public class DeviceChannel { * OFF * 遇到过NVR下的IPC下发信令可以推流, 但是 Status 响应 OFF */ - private int status; + @Schema(description = "在线/离线, 1在线,0离线") + private boolean 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; } @@ -356,11 +455,11 @@ public class DeviceChannel { this.PTZTypeText = PTZTypeText; } - public int getStatus() { + public boolean isStatus() { return status; } - public void setStatus(int status) { + public void setStatus(boolean status) { this.status = status; } @@ -380,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; } @@ -403,4 +534,44 @@ public class DeviceChannel { public void setStreamId(String streamId) { this.streamId = streamId; } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public int getChannelType() { + return channelType; + } + + public void setChannelType(int channelType) { + this.channelType = channelType; + } + + public String getBusinessGroupId() { + return businessGroupId; + } + + public void setBusinessGroupId(String businessGroupId) { + this.businessGroupId = businessGroupId; + } + + public String getGpsTime() { + return gpsTime; + } + + public void setGpsTime(String gpsTime) { + this.gpsTime = gpsTime; + } } 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 000000000..c61bb088b --- /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 000000000..c782c3c16 --- /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/DragZoomRequest.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DragZoomRequest.java new file mode 100644 index 000000000..86fdb4d29 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DragZoomRequest.java @@ -0,0 +1,143 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import com.genersoft.iot.vmp.gb28181.utils.MessageElement; + +/** + * 设备信息查询响应 + * + * @author Y.G + * @version 1.0 + * @date 2022/6/28 14:55 + */ +public class DragZoomRequest { + /** + * 序列号 + */ + @MessageElement("SN") + private String sn; + + @MessageElement("DeviceID") + private String deviceId; + + @MessageElement(value = "DragZoomIn") + private DragZoom dragZoomIn; + + @MessageElement(value = "DragZoomOut") + private DragZoom dragZoomOut; + + /** + * 基本参数 + */ + public static class DragZoom { + /** + * 播放窗口长度像素值 + */ + @MessageElement("Length") + protected Integer length; + /** + * 播放窗口宽度像素值 + */ + @MessageElement("Width") + protected Integer width; + /** + * 拉框中心的横轴坐标像素值 + */ + @MessageElement("MidPointX") + protected Integer midPointX; + /** + * 拉框中心的纵轴坐标像素值 + */ + @MessageElement("MidPointY") + protected Integer midPointY; + /** + * 拉框长度像素值 + */ + @MessageElement("LengthX") + protected Integer lengthX; + /** + * 拉框宽度像素值 + */ + @MessageElement("LengthY") + protected Integer lengthY; + + public Integer getLength() { + return length; + } + + public void setLength(Integer length) { + this.length = length; + } + + public Integer getWidth() { + return width; + } + + public void setWidth(Integer width) { + this.width = width; + } + + public Integer getMidPointX() { + return midPointX; + } + + public void setMidPointX(Integer midPointX) { + this.midPointX = midPointX; + } + + public Integer getMidPointY() { + return midPointY; + } + + public void setMidPointY(Integer midPointY) { + this.midPointY = midPointY; + } + + public Integer getLengthX() { + return lengthX; + } + + public void setLengthX(Integer lengthX) { + this.lengthX = lengthX; + } + + public Integer getLengthY() { + return lengthY; + } + + public void setLengthY(Integer lengthY) { + this.lengthY = lengthY; + } + } + + public String getSn() { + return sn; + } + + public void setSn(String sn) { + this.sn = sn; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public DragZoom getDragZoomIn() { + return dragZoomIn; + } + + public void setDragZoomIn(DragZoom dragZoomIn) { + this.dragZoomIn = dragZoomIn; + } + + public DragZoom getDragZoomOut() { + return dragZoomOut; + } + + public void setDragZoomOut(DragZoom dragZoomOut) { + this.dragZoomOut = dragZoomOut; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GBStringMsgParser.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GBStringMsgParser.java new file mode 100644 index 000000000..2abc3569a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GBStringMsgParser.java @@ -0,0 +1,457 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import gov.nist.core.CommonLogger; +import gov.nist.core.Host; +import gov.nist.core.HostNameParser; +import gov.nist.core.StackLogger; +import gov.nist.javax.sip.SIPConstants; +import gov.nist.javax.sip.address.AddressImpl; +import gov.nist.javax.sip.address.GenericURI; +import gov.nist.javax.sip.address.SipUri; +import gov.nist.javax.sip.address.TelephoneNumber; +import gov.nist.javax.sip.header.*; +import gov.nist.javax.sip.message.SIPMessage; +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; +import gov.nist.javax.sip.parser.*; + +import java.io.UnsupportedEncodingException; +import java.text.ParseException; + +public class GBStringMsgParser implements MessageParser { + + protected static boolean computeContentLengthFromMessage = false; + + private static StackLogger logger = CommonLogger.getLogger(StringMsgParser.class); + + /** + * @since v0.9 + */ + public GBStringMsgParser() { + super(); + } + + /** + * Parse a buffer containing a single SIP Message where the body is an array + * of un-interpreted bytes. This is intended for parsing the message from a + * memory buffer when the buffer. Incorporates a bug fix for a bug that was + * noted by Will Sullin of Callcast + * + * @param msgBuffer + * a byte buffer containing the messages to be parsed. This can + * consist of multiple SIP Messages concatenated together. + * @return a SIPMessage[] structure (request or response) containing the + * parsed SIP message. + * @exception ParseException + * is thrown when an illegal message has been encountered + * (and the rest of the buffer is discarded). + * @see ParseExceptionListener + */ + public SIPMessage parseSIPMessage(byte[] msgBuffer, boolean readBody, boolean strict, ParseExceptionListener parseExceptionListener) throws ParseException { + if (msgBuffer == null || msgBuffer.length == 0) + return null; + + int i = 0; + + // Squeeze out any leading control character. + try { + while (msgBuffer[i] < 0x20) + i++; + } + catch (ArrayIndexOutOfBoundsException e) { + // Array contains only control char, return null. + if (logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) { + logger.logDebug("handled only control char so returning null"); + } + return null; + } + + // Iterate thru the request/status line and headers. + String currentLine = null; + String currentHeader = null; + boolean isFirstLine = true; + SIPMessage message = null; + do + { + int lineStart = i; + + // Find the length of the line. + try { + while (msgBuffer[i] != '\r' && msgBuffer[i] != '\n') + i++; + } + catch (ArrayIndexOutOfBoundsException e) { + // End of the message. + break; + } + int lineLength = i - lineStart; + + // Make it a String. + try { + currentLine = new String(msgBuffer, lineStart, lineLength, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new ParseException("Bad message encoding!", 0); + } + + currentLine = trimEndOfLine(currentLine); + + if (currentLine.length() == 0) { + // Last header line, process the previous buffered header. + if (currentHeader != null && message != null) { + processHeader(currentHeader, message, parseExceptionListener, msgBuffer); + } + + } + else { + if (isFirstLine) { + message = processFirstLine(currentLine, parseExceptionListener, msgBuffer); + } else { + char firstChar = currentLine.charAt(0); + if (firstChar == '\t' || firstChar == ' ') { + if (currentHeader == null) + throw new ParseException("Bad header continuation.", 0); + + // This is a continuation, append it to the previous line. + currentHeader += currentLine.substring(1); + } + else { + if (currentHeader != null && message != null) { + processHeader(currentHeader, message, parseExceptionListener, msgBuffer); + } + currentHeader = currentLine; + } + } + } + + if (msgBuffer[i] == '\r' && msgBuffer.length > i+1 && msgBuffer[i+1] == '\n') + i++; + + i++; + + isFirstLine = false; + } while (currentLine.length() > 0); // End do - while + + if (message == null) throw new ParseException("Bad message", 0); + message.setSize(i); + + // Check for content legth header + if (readBody && message.getContentLength() != null ) { + if ( message.getContentLength().getContentLength() != 0) { + int bodyLength = msgBuffer.length - i; + + byte[] body = new byte[bodyLength]; + System.arraycopy(msgBuffer, i, body, 0, bodyLength); + message.setMessageContent(body,!strict,computeContentLengthFromMessage,message.getContentLength().getContentLength()); + } else if (message.getCSeqHeader().getMethod().equalsIgnoreCase("MESSAGE")) { + int bodyLength = msgBuffer.length - i; + + byte[] body = new byte[bodyLength]; + System.arraycopy(msgBuffer, i, body, 0, bodyLength); + message.setMessageContent(body,!strict,computeContentLengthFromMessage,bodyLength); + }else if (!computeContentLengthFromMessage && strict) { + String last4Chars = new String(msgBuffer, msgBuffer.length - 4, 4); + if(!"\r\n\r\n".equals(last4Chars)) { + throw new ParseException("Extraneous characters at the end of the message ",i); + } + } + } + + return message; + } + + protected static String trimEndOfLine(String line) { + if (line == null) + return line; + + int i = line.length() - 1; + while (i >= 0 && line.charAt(i) <= 0x20) + i--; + + if (i == line.length() - 1) + return line; + + if (i == -1) + return ""; + + return line.substring(0, i+1); + } + + protected SIPMessage processFirstLine(String firstLine, ParseExceptionListener parseExceptionListener, byte[] msgBuffer) throws ParseException { + SIPMessage message; + if (!firstLine.startsWith(SIPConstants.SIP_VERSION_STRING)) { + message = new SIPRequest(); + try { + RequestLine requestLine = new RequestLineParser(firstLine + "\n") + .parse(); + ((SIPRequest) message).setRequestLine(requestLine); + } catch (ParseException ex) { + if (parseExceptionListener != null) + try { + parseExceptionListener.handleException(ex, message, + RequestLine.class, firstLine, new String(msgBuffer, "UTF-8")); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + else + throw ex; + + } + } else { + message = new SIPResponse(); + try { + StatusLine sl = new StatusLineParser(firstLine + "\n").parse(); + ((SIPResponse) message).setStatusLine(sl); + } catch (ParseException ex) { + if (parseExceptionListener != null) { + try { + parseExceptionListener.handleException(ex, message, + StatusLine.class, firstLine, new String(msgBuffer, "UTF-8")); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } else + throw ex; + + } + } + return message; + } + + protected void processHeader(String header, SIPMessage message, ParseExceptionListener parseExceptionListener, byte[] rawMessage) throws ParseException { + if (header == null || header.length() == 0) + return; + + HeaderParser headerParser = null; + try { + headerParser = ParserFactory.createParser(header + "\n"); + } catch (ParseException ex) { + // https://java.net/jira/browse/JSIP-456 + if (parseExceptionListener != null) { + parseExceptionListener.handleException(ex, message, null, + header, null); + return; + } else { + throw ex; + } + } + + try { + SIPHeader sipHeader = headerParser.parse(); + message.attachHeader(sipHeader, false); + } catch (ParseException ex) { + if (parseExceptionListener != null) { + String headerName = Lexer.getHeaderName(header); + Class headerClass = NameMap.getClassFromName(headerName); + if (headerClass == null) { + headerClass = ExtensionHeaderImpl.class; + + } + try { + parseExceptionListener.handleException(ex, message, + headerClass, header, new String(rawMessage, "UTF-8")); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + } + } + } + + /** + * Parse an address (nameaddr or address spec) and return and address + * structure. + * + * @param address + * is a String containing the address to be parsed. + * @return a parsed address structure. + * @since v1.0 + * @exception ParseException + * when the address is badly formatted. + */ + public AddressImpl parseAddress(String address) throws ParseException { + AddressParser addressParser = new AddressParser(address); + return addressParser.address(true); + } + + /** + * Parse a host:port and return a parsed structure. + * + * @param hostport + * is a String containing the host:port to be parsed + * @return a parsed address structure. + * @since v1.0 + * @exception throws + * a ParseException when the address is badly formatted. + * + public HostPort parseHostPort(String hostport) throws ParseException { + Lexer lexer = new Lexer("charLexer", hostport); + return new HostNameParser(lexer).hostPort(); + + } + */ + + /** + * Parse a host name and return a parsed structure. + * + * @param host + * is a String containing the host name to be parsed + * @return a parsed address structure. + * @since v1.0 + * @exception ParseException + * a ParseException when the hostname is badly formatted. + */ + public Host parseHost(String host) throws ParseException { + Lexer lexer = new Lexer("charLexer", host); + return new HostNameParser(lexer).host(); + + } + + /** + * Parse a telephone number return a parsed structure. + * + * @param telephone_number + * is a String containing the telephone # to be parsed + * @return a parsed address structure. + * @since v1.0 + * @exception ParseException + * a ParseException when the address is badly formatted. + */ + public TelephoneNumber parseTelephoneNumber(String telephone_number) + throws ParseException { + // Bug fix contributed by Will Scullin + return new URLParser(telephone_number).parseTelephoneNumber(true); + + } + + /** + * Parse a SIP url from a string and return a URI structure for it. + * + * @param url + * a String containing the URI structure to be parsed. + * @return A parsed URI structure + * @exception ParseException + * if there was an error parsing the message. + */ + + public SipUri parseSIPUrl(String url) throws ParseException { + try { + return new URLParser(url).sipURL(true); + } catch (ClassCastException ex) { + throw new ParseException(url + " Not a SIP URL ", 0); + } + } + + /** + * Parse a uri from a string and return a URI structure for it. + * + * @param url + * a String containing the URI structure to be parsed. + * @return A parsed URI structure + * @exception ParseException + * if there was an error parsing the message. + */ + + public GenericURI parseUrl(String url) throws ParseException { + return new URLParser(url).parse(); + } + + /** + * Parse an individual SIP message header from a string. + * + * @param header + * String containing the SIP header. + * @return a SIPHeader structure. + * @exception ParseException + * if there was an error parsing the message. + */ + public static SIPHeader parseSIPHeader(String header) throws ParseException { + int start = 0; + int end = header.length() - 1; + try { + // Squeeze out any leading control character. + while (header.charAt(start) <= 0x20) + start++; + + // Squeeze out any trailing control character. + while (header.charAt(end) <= 0x20) + end--; + } + catch (ArrayIndexOutOfBoundsException e) { + // Array contains only control char. + throw new ParseException("Empty header.", 0); + } + + StringBuilder buffer = new StringBuilder(end + 1); + int i = start; + int lineStart = start; + boolean endOfLine = false; + while (i <= end) { + char c = header.charAt(i); + if (c == '\r' || c == '\n') { + if (!endOfLine) { + buffer.append(header.substring(lineStart, i)); + endOfLine = true; + } + } + else { + if (endOfLine) { + endOfLine = false; + if (c == ' ' || c == '\t') { + buffer.append(' '); + lineStart = i + 1; + } + else { + lineStart = i; + } + } + } + + i++; + } + buffer.append(header.substring(lineStart, i)); + buffer.append('\n'); + + HeaderParser hp = ParserFactory.createParser(buffer.toString()); + if (hp == null) + throw new ParseException("could not create parser", 0); + return hp.parse(); + } + + /** + * Parse the SIP Request Line + * + * @param requestLine + * a String containing the request line to be parsed. + * @return a RequestLine structure that has the parsed RequestLine + * @exception ParseException + * if there was an error parsing the requestLine. + */ + + public RequestLine parseSIPRequestLine(String requestLine) + throws ParseException { + requestLine += "\n"; + return new RequestLineParser(requestLine).parse(); + } + + /** + * Parse the SIP Response message status line + * + * @param statusLine + * a String containing the Status line to be parsed. + * @return StatusLine class corresponding to message + * @exception ParseException + * if there was an error parsing + * @see StatusLine + */ + + public StatusLine parseSIPStatusLine(String statusLine) + throws ParseException { + statusLine += "\n"; + return new StatusLineParser(statusLine).parse(); + } + + public static void setComputeContentLengthFromMessage( + boolean computeContentLengthFromMessage) { + GBStringMsgParser.computeContentLengthFromMessage = computeContentLengthFromMessage; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Gb28181Sdp.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Gb28181Sdp.java new file mode 100644 index 000000000..4b9e26a0b --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Gb28181Sdp.java @@ -0,0 +1,46 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import javax.sdp.SessionDescription; + +/** + * 28181 的SDP解析器 + */ +public class Gb28181Sdp { + private SessionDescription baseSdb; + private String ssrc; + + private String mediaDescription; + + public static Gb28181Sdp getInstance(SessionDescription baseSdb, String ssrc, String mediaDescription) { + Gb28181Sdp gb28181Sdp = new Gb28181Sdp(); + gb28181Sdp.setBaseSdb(baseSdb); + gb28181Sdp.setSsrc(ssrc); + gb28181Sdp.setMediaDescription(mediaDescription); + return gb28181Sdp; + } + + + public SessionDescription getBaseSdb() { + return baseSdb; + } + + public void setBaseSdb(SessionDescription baseSdb) { + this.baseSdb = baseSdb; + } + + public String getSsrc() { + return ssrc; + } + + public void setSsrc(String ssrc) { + this.ssrc = ssrc; + } + + public String getMediaDescription() { + return mediaDescription; + } + + public void setMediaDescription(String mediaDescription) { + this.mediaDescription = mediaDescription; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/WvpSipDate.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbSipDate.java similarity index 97% rename from src/main/java/com/genersoft/iot/vmp/gb28181/bean/WvpSipDate.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbSipDate.java index f2a256b9a..0631c5d06 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/WvpSipDate.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbSipDate.java @@ -8,7 +8,7 @@ import java.util.*; /** * 重写jain sip的SIPDate解决与国标时间格式不一致的问题 */ -public class WvpSipDate extends SIPDate { +public class GbSipDate extends SIPDate { /** * @@ -17,7 +17,7 @@ public class WvpSipDate extends SIPDate { private Calendar javaCal; - public WvpSipDate(long timeMillis) { + public GbSipDate(long timeMillis) { this.javaCal = new GregorianCalendar(TimeZone.getDefault(), Locale.getDefault()); Date date = new Date(timeMillis); this.javaCal.setTime(date); 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 new file mode 100644 index 000000000..67058f390 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbStream.java @@ -0,0 +1,125 @@ +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 int 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; + } + + 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 getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public double getLongitude() { + return longitude; + } + + public void setLongitude(double longitude) { + this.longitude = longitude; + } + + public double getLatitude() { + return latitude; + } + + public void setLatitude(double latitude) { + this.latitude = latitude; + } + + public String getStreamType() { + return streamType; + } + + public void setStreamType(String streamType) { + this.streamType = streamType; + } + + public boolean isStatus() { + return status; + } + + public void setStatus(boolean status) { + this.status = status; + } + + public String getMediaServerId() { + return mediaServerId; + } + + 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/GbStringMsgParserFactory.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbStringMsgParserFactory.java new file mode 100644 index 000000000..3a9a1d196 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbStringMsgParserFactory.java @@ -0,0 +1,21 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import gov.nist.javax.sip.parser.MessageParser; +import gov.nist.javax.sip.parser.MessageParserFactory; +import gov.nist.javax.sip.stack.SIPTransactionStack; + +public class GbStringMsgParserFactory implements MessageParserFactory { + + /** + * msg parser is completely stateless, reuse isntance for the whole stack + * fixes https://github.com/RestComm/jain-sip/issues/92 + */ + private static GBStringMsgParser msgParser = new GBStringMsgParser(); + /* + * (non-Javadoc) + * @see gov.nist.javax.sip.parser.MessageParserFactory#createMessageParser(gov.nist.javax.sip.stack.SIPTransactionStack) + */ + public MessageParser createMessageParser(SIPTransactionStack stack) { + return msgParser; + } +} 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 000000000..97da8630c --- /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/HomePositionRequest.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/HomePositionRequest.java new file mode 100644 index 000000000..2c20713bb --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/HomePositionRequest.java @@ -0,0 +1,94 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import com.genersoft.iot.vmp.gb28181.utils.MessageElement; + +/** + * 设备信息查询响应 + * + * @author Y.G + * @version 1.0 + * @date 2022/6/28 14:55 + */ +public class HomePositionRequest { + /** + * 序列号 + */ + @MessageElement("SN") + private String sn; + + @MessageElement("DeviceID") + private String deviceId; + + @MessageElement(value = "HomePosition") + private HomePosition homePosition; + + + /** + * 基本参数 + */ + public static class HomePosition { + /** + * 播放窗口长度像素值 + */ + @MessageElement("Enabled") + protected String enabled; + /** + * 播放窗口宽度像素值 + */ + @MessageElement("ResetTime") + protected String resetTime; + /** + * 拉框中心的横轴坐标像素值 + */ + @MessageElement("PresetIndex") + protected String presetIndex; + + public String getEnabled() { + return enabled; + } + + public void setEnabled(String enabled) { + this.enabled = enabled; + } + + public String getResetTime() { + return resetTime; + } + + public void setResetTime(String resetTime) { + this.resetTime = resetTime; + } + + public String getPresetIndex() { + return presetIndex; + } + + public void setPresetIndex(String presetIndex) { + this.presetIndex = presetIndex; + } + } + + public String getSn() { + return sn; + } + + public void setSn(String sn) { + this.sn = sn; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public HomePosition getHomePosition() { + return homePosition; + } + + public void setHomePosition(HomePosition homePosition) { + this.homePosition = homePosition; + } +} 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 000000000..42a051984 --- /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 000000000..134930b33 --- /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 000000000..677dcd71c --- /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,DOWNLOAD,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 47535a6d1..19000930c 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 @@ -1,7 +1,7 @@ package com.genersoft.iot.vmp.gb28181.bean; /** - * @Description: 移动位置bean + * @description: 移动位置bean * @author: lawrencehj * @date: 2021年1月23日 */ @@ -12,6 +12,11 @@ public class MobilePosition { */ private String deviceId; + /** + * 通道Id + */ + private String channelId; + /** * 设备名称 */ @@ -52,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() { @@ -140,27 +155,51 @@ public class MobilePosition { this.reportSource = reportSource; } - public String getGeodeticSystem() { - return GeodeticSystem; + public String getChannelId() { + return channelId; } - public void setGeodeticSystem(String geodeticSystem) { - GeodeticSystem = geodeticSystem; + public void setChannelId(String channelId) { + this.channelId = channelId; } - public String getCnLng() { - return cnLng; + public double getLongitudeGcj02() { + return longitudeGcj02; } - public void setCnLng(String cnLng) { - this.cnLng = cnLng; + public void setLongitudeGcj02(double longitudeGcj02) { + this.longitudeGcj02 = longitudeGcj02; } - public String getCnLat() { - return cnLat; + public double getLatitudeGcj02() { + return latitudeGcj02; } - public void setCnLat(String cnLat) { - this.cnLat = cnLat; + 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 new file mode 100644 index 000000000..fbc95ed96 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java @@ -0,0 +1,428 @@ +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; + + /** + * 注册周期 (秒) + */ + @Schema(description = "注册周期 (秒)") + private int expires; + + /** + * 心跳周期(秒) + */ + @Schema(description = "心跳周期(秒)") + private int keepTimeout; + + /** + * 传输协议 + * UDP/TCP + */ + @Schema(description = "传输协议") + private String transport; + + /** + * 字符集 + */ + @Schema(description = "字符集") + private String characterSet; + + /** + * 允许云台控制 + */ + @Schema(description = "允许云台控制") + private boolean ptz; + + /** + * RTCP流保活 + */ + @Schema(description = "RTCP流保活") + private boolean rtcp; + + /** + * 在线状态 + */ + @Schema(description = "在线状态") + private boolean status; + + /** + * 在线状态 + */ + @Schema(description = "在线状态") + private int channelCount; + + /** + * 默认目录Id,自动添加的通道多放在这个目录下 + */ + @Schema(description = "默认目录Id,自动添加的通道多放在这个目录下") + private String catalogId; + + /** + * 已被订阅目录信息 + */ + @Schema(description = "已被订阅目录信息") + private boolean catalogSubscribe; + + /** + * 已被订阅报警信息 + */ + @Schema(description = "已被订阅报警信息") + private boolean alarmSubscribe; + + /** + * 已被订阅移动位置信息 + */ + @Schema(description = "已被订阅移动位置信息") + private boolean mobilePositionSubscribe; + + /** + * 点播未推流的设备时是否使用redis通知拉起 + */ + @Schema(description = "点播未推流的设备时是否使用redis通知拉起") + private boolean startOfflinePush; + + /** + * 目录分组-每次向上级发送通道信息时单个包携带的通道数量,取值1,2,4,8 + */ + @Schema(description = "目录分组-每次向上级发送通道信息时单个包携带的通道数量,取值1,2,4,8") + private int catalogGroup; + + /** + * 行政区划 + */ + @Schema(description = "行政区划") + private String administrativeDivision; + + /** + * 更新时间 + */ + @Schema(description = "更新时间") + private String updateTime; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + private String createTime; + + @Schema(description = "是否作为消息通道") + private boolean asMessageChannel; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public boolean isEnable() { + return enable; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getServerGBId() { + return serverGBId; + } + + public void setServerGBId(String serverGBId) { + this.serverGBId = serverGBId; + } + + public String getServerGBDomain() { + return serverGBDomain; + } + + public void setServerGBDomain(String serverGBDomain) { + this.serverGBDomain = serverGBDomain; + } + + public String getServerIP() { + return serverIP; + } + + public void setServerIP(String serverIP) { + this.serverIP = serverIP; + } + + public int getServerPort() { + return serverPort; + } + + public void setServerPort(int serverPort) { + this.serverPort = serverPort; + } + + public String getDeviceGBId() { + return deviceGBId; + } + + public void setDeviceGBId(String deviceGBId) { + this.deviceGBId = deviceGBId; + } + + public String getDeviceIp() { + return deviceIp; + } + + public void setDeviceIp(String deviceIp) { + this.deviceIp = deviceIp; + } + + public String getDevicePort() { + return devicePort; + } + + public void setDevicePort(String devicePort) { + this.devicePort = devicePort; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public int getExpires() { + return expires; + } + + public void setExpires(int expires) { + this.expires = expires; + } + + public int getKeepTimeout() { + return keepTimeout; + } + + public void setKeepTimeout(int keepTimeout) { + this.keepTimeout = keepTimeout; + } + + public String getTransport() { + return transport; + } + + public void setTransport(String transport) { + this.transport = transport; + } + + public String getCharacterSet() { + return characterSet; + } + + public void setCharacterSet(String characterSet) { + this.characterSet = characterSet; + } + + public boolean isPtz() { + return ptz; + } + + public void setPtz(boolean ptz) { + this.ptz = ptz; + } + + public boolean isRtcp() { + return rtcp; + } + + public void setRtcp(boolean rtcp) { + this.rtcp = rtcp; + } + + public boolean isStatus() { + return status; + } + + public void setStatus(boolean status) { + this.status = status; + } + + public int getChannelCount() { + return channelCount; + } + + public void setChannelCount(int channelCount) { + this.channelCount = channelCount; + } + + public String getCatalogId() { + return catalogId; + } + + public void setCatalogId(String catalogId) { + this.catalogId = catalogId; + } + + public boolean isCatalogSubscribe() { + return catalogSubscribe; + } + + public void setCatalogSubscribe(boolean catalogSubscribe) { + this.catalogSubscribe = catalogSubscribe; + } + + public boolean isAlarmSubscribe() { + return alarmSubscribe; + } + + public void setAlarmSubscribe(boolean alarmSubscribe) { + this.alarmSubscribe = alarmSubscribe; + } + + public boolean isMobilePositionSubscribe() { + return mobilePositionSubscribe; + } + + public void setMobilePositionSubscribe(boolean mobilePositionSubscribe) { + this.mobilePositionSubscribe = mobilePositionSubscribe; + } + + public boolean isStartOfflinePush() { + return startOfflinePush; + } + + public void setStartOfflinePush(boolean startOfflinePush) { + this.startOfflinePush = startOfflinePush; + } + + public int getCatalogGroup() { + return catalogGroup; + } + + public void setCatalogGroup(int catalogGroup) { + this.catalogGroup = catalogGroup; + } + + public String getAdministrativeDivision() { + return administrativeDivision; + } + + public void setAdministrativeDivision(String administrativeDivision) { + this.administrativeDivision = administrativeDivision; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public boolean isAsMessageChannel() { + return asMessageChannel; + } + + public void setAsMessageChannel(boolean asMessageChannel) { + this.asMessageChannel = asMessageChannel; + } +} 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 new file mode 100644 index 000000000..6ff2fe32e --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatformCatch.java @@ -0,0 +1,68 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +public class ParentPlatformCatch { + + private String id; + + /** + * 心跳未回复次数 + */ + private int keepAliveReply; + + // 注册未回复次数 + private int registerAliveReply; + + private String callId; + + private ParentPlatform parentPlatform; + + private SipTransactionInfo sipTransactionInfo; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public int getKeepAliveReply() { + return keepAliveReply; + } + + public void setKeepAliveReply(int keepAliveReply) { + this.keepAliveReply = keepAliveReply; + } + + public int getRegisterAliveReply() { + return registerAliveReply; + } + + public void setRegisterAliveReply(int registerAliveReply) { + this.registerAliveReply = registerAliveReply; + } + + public ParentPlatform getParentPlatform() { + return parentPlatform; + } + + public void setParentPlatform(ParentPlatform parentPlatform) { + this.parentPlatform = parentPlatform; + } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } + + public SipTransactionInfo getSipTransactionInfo() { + return sipTransactionInfo; + } + + public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) { + this.sipTransactionInfo = sipTransactionInfo; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformCatalog.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformCatalog.java new file mode 100644 index 000000000..38ba2f03c --- /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 new file mode 100644 index 000000000..f7402b6f9 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformGbStream.java @@ -0,0 +1,39 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import io.swagger.v3.oas.annotations.media.Schema; + +public class PlatformGbStream { + + @Schema(description = "ID") + private int gbStreamId; + + @Schema(description = "平台ID") + private String platformId; + + @Schema(description = "目录ID") + private String catalogId; + + public Integer getGbStreamId() { + return gbStreamId; + } + + public void setGbStreamId(Integer gbStreamId) { + this.gbStreamId = gbStreamId; + } + + 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/PlatformRegister.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformRegister.java new file mode 100644 index 000000000..4a45862c4 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformRegister.java @@ -0,0 +1,15 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +public class PlatformRegister { + + // 未回复次数 + private int reply; + + public int getReply() { + return reply; + } + + public void setReply(int reply) { + this.reply = reply; + } +} 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 000000000..d1971a2e2 --- /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 4ec182afc..7ff528306 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,23 +1,39 @@ package com.genersoft.iot.vmp.gb28181.bean; -//import gov.nist.javax.sip.header.SIPDate; +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.Instant; import java.util.List; /** - * @Description:设备录像信息bean + * @description:设备录像信息bean * @author: swwheihei * @date: 2020年5月8日 下午2:05:56 */ +@Schema(description = "设备录像查询结果信息") public class RecordInfo { + @Schema(description = "设备编号") private String deviceId; - + + @Schema(description = "通道编号") + private String channelId; + + @Schema(description = "命令序列号") + private String sn; + + @Schema(description = "设备名称") private String name; - + + @Schema(description = "列表总数") private int sumNum; - + + private int count; + + private Instant lastTime; + + @Schema(description = "") private List recordList; public String getDeviceId() { @@ -52,4 +68,35 @@ public class RecordInfo { this.recordList = recordList; } + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public String getSn() { + return sn; + } + + public void setSn(String sn) { + this.sn = sn; + } + + public Instant getLastTime() { + return lastTime; + } + + public void setLastTime(Instant lastTime) { + this.lastTime = lastTime; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } } 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 1533b664f..07e559c81 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,35 +1,49 @@ package com.genersoft.iot.vmp.gb28181.bean; +import com.genersoft.iot.vmp.utils.DateUtil; +import io.swagger.v3.oas.annotations.media.Schema; 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 + * @description:设备录像bean * @author: swwheihei * @date: 2020年5月8日 下午2:06:54 */ +@Schema(description = "设备录像详情") public class RecordItem implements Comparable{ + @Schema(description = "设备编号") private String deviceId; - + + @Schema(description = "名称") private String name; - + + @Schema(description = "文件路径名 (可选)") private String filePath; - + + @Schema(description = "录像文件大小,单位:Byte(可选)") + private String fileSize; + + @Schema(description = "录像地址(可选)") private String address; - + + @Schema(description = "录像开始时间(可选)") private String startTime; - + + @Schema(description = "录像结束时间(可选)") private String endTime; - + + @Schema(description = "保密属性(必选)缺省为0;0:不涉密,1:涉密") private int secrecy; - + + @Schema(description = "录像产生类型(可选)time或alarm 或 manua") private String type; - + + @Schema(description = "录像触发者ID(可选)") private String recorderId; public String getDeviceId() { @@ -104,20 +118,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 000000000..4107593fe --- /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 000000000..39225b57a --- /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 new file mode 100644 index 000000000..c1fe2c1fe --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpItem.java @@ -0,0 +1,295 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +public class SendRtpItem { + + /** + * 推流ip + */ + private String ip; + + /** + * 推流端口 + */ + private int port; + + /** + * 推流标识 + */ + private String ssrc; + + /** + * 平台id + */ + private String platformId; + + /** + * 对应设备id + */ + private String deviceId; + + /** + * 直播流的应用名 + */ + private String app; + + /** + * 通道id + */ + private String channelId; + + /** + * 推流状态 + * 0 等待设备推流上来 + * 1 等待上级平台回复ack + * 2 推流中 + */ + private int status = 0; + + + /** + * 设备推流的streamId + */ + private String streamId; + + /** + * 是否为tcp + */ + private boolean tcp; + + /** + * 是否为tcp主动模式 + */ + private boolean tcpActive; + + /** + * 自己推流使用的端口 + */ + private int localPort; + + /** + * 使用的流媒体 + */ + private String mediaServerId; + + /** + * 使用的服务的ID + */ + private String serverId; + + /** + * invite 的 callId + */ + private String CallId; + + /** + * invite 的 fromTag + */ + private String fromTag; + + /** + * invite 的 toTag + */ + private String toTag; + + /** + * 发送时,rtp的pt(uint8_t),不传时默认为96 + */ + private int pt = 96; + + /** + * 发送时,rtp的负载类型。为true时,负载为ps;为false时,为es; + */ + private boolean usePs = true; + + /** + * 当usePs 为false时,有效。为1时,发送音频;为0时,发送视频;不传时默认为0 + */ + private boolean onlyAudio = false; + + /** + * 是否开启rtcp保活 + */ + private boolean rtcp = false; + + + /** + * 播放类型 + */ + private InviteStreamType playType; + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getSsrc() { + return ssrc; + } + + public void setSsrc(String ssrc) { + this.ssrc = ssrc; + } + + public String getPlatformId() { + return platformId; + } + + public void setPlatformId(String platformId) { + this.platformId = platformId; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStreamId() { + return streamId; + } + + public void setStreamId(String streamId) { + this.streamId = streamId; + } + + public boolean isTcp() { + return tcp; + } + + public void setTcp(boolean tcp) { + this.tcp = tcp; + } + + public int getLocalPort() { + return localPort; + } + + public void setLocalPort(int localPort) { + this.localPort = localPort; + } + + public boolean isTcpActive() { + return tcpActive; + } + + public void setTcpActive(boolean tcpActive) { + this.tcpActive = tcpActive; + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + public String getCallId() { + return CallId; + } + + public void setCallId(String callId) { + 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 000000000..302539b6a --- /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 000000000..c04a6959a --- /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 new file mode 100644 index 000000000..6ed8d1441 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java @@ -0,0 +1,81 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import com.genersoft.iot.vmp.common.InviteSessionType; + +public class SsrcTransaction { + + private String deviceId; + private String channelId; + private String callId; + private String stream; + private String mediaServerId; + private String ssrc; + + private SipTransactionInfo sipTransactionInfo; + + private InviteSessionType type; + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + public String getSsrc() { + return ssrc; + } + + public void setSsrc(String ssrc) { + this.ssrc = ssrc; + } + + public InviteSessionType getType() { + return type; + } + + public void setType(InviteSessionType type) { + this.type = type; + } + + public SipTransactionInfo getSipTransactionInfo() { + return sipTransactionInfo; + } + + public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) { + this.sipTransactionInfo = sipTransactionInfo; + } +} 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 000000000..ba905b50b --- /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 000000000..07176f210 --- /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 000000000..373b971cc --- /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/conf/DefaultProperties.java b/src/main/java/com/genersoft/iot/vmp/gb28181/conf/DefaultProperties.java new file mode 100644 index 000000000..18ccaf410 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/conf/DefaultProperties.java @@ -0,0 +1,61 @@ +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 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 + // 接收所有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"); + // 是否自动计算content length的实际长度,默认不计算 + properties.setProperty("gov.nist.javax.sip.COMPUTE_CONTENT_LENGTH_FROM_MESSAGE_BODY", "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) { + properties.setProperty("gov.nist.javax.sip.STACK_LOGGER", "com.genersoft.iot.vmp.gb28181.conf.StackLoggerImpl"); + properties.setProperty("gov.nist.javax.sip.SERVER_LOGGER", "com.genersoft.iot.vmp.gb28181.conf.ServerLoggerImpl"); + properties.setProperty("gov.nist.javax.sip.LOG_MESSAGE_CONTENT", "true"); + logger.info("[SIP日志]已开启"); + }else { + logger.info("[SIP日志]已关闭"); + } + return properties; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/conf/ServerLoggerImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/conf/ServerLoggerImpl.java new file mode 100644 index 000000000..19e1906c3 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/conf/ServerLoggerImpl.java @@ -0,0 +1,90 @@ +package com.genersoft.iot.vmp.gb28181.conf; + +import gov.nist.core.ServerLogger; +import gov.nist.core.StackLogger; +import gov.nist.javax.sip.message.SIPMessage; +import gov.nist.javax.sip.stack.SIPTransactionStack; + +import javax.sip.SipStack; +import java.util.Properties; + +public class ServerLoggerImpl implements ServerLogger { + + private boolean showLog = true; + + private SIPTransactionStack sipStack; + + protected StackLogger stackLogger; + + @Override + public void closeLogFile() { + + } + + @Override + public void logMessage(SIPMessage message, String from, String to, boolean sender, long time) { + if (!showLog) { + return; + } + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(sender? "发送:目标--->" + from:"接收:来自--->" + to) + .append("\r\n") + .append(message); + this.stackLogger.logInfo(stringBuilder.toString()); + + } + + @Override + public void logMessage(SIPMessage message, String from, String to, String status, boolean sender, long time) { + if (!showLog) { + return; + } + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(sender? "发送: 目标->" + from :"接收:来自->" + to) + .append("\r\n") + .append(message); + this.stackLogger.logInfo(stringBuilder.toString()); + } + + @Override + public void logMessage(SIPMessage message, String from, String to, String status, boolean sender) { + if (!showLog) { + return; + } + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(sender? "发送: 目标->" + from :"接收:来自->" + to) + .append("\r\n") + .append(message); + this.stackLogger.logInfo(stringBuilder.toString()); + } + + @Override + public void logException(Exception ex) { + if (!showLog) { + return; + } + this.stackLogger.logException(ex); + } + + @Override + public void setStackProperties(Properties stackProperties) { + if (!showLog) { + return; + } + String TRACE_LEVEL = stackProperties.getProperty("gov.nist.javax.sip.TRACE_LEVEL"); + if (TRACE_LEVEL != null) { + showLog = true; + } + } + + @Override + public void setSipStack(SipStack sipStack) { + if (!showLog) { + return; + } + if(sipStack instanceof SIPTransactionStack) { + this.sipStack = (SIPTransactionStack)sipStack; + this.stackLogger = this.sipStack.getStackLogger(); + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/conf/StackLoggerImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/conf/StackLoggerImpl.java new file mode 100644 index 000000000..5e67bdba1 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/conf/StackLoggerImpl.java @@ -0,0 +1,109 @@ +package com.genersoft.iot.vmp.gb28181.conf; + +import gov.nist.core.StackLogger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.util.Properties; + +@Component +public class StackLoggerImpl implements StackLogger { + + private final static Logger logger = LoggerFactory.getLogger(StackLoggerImpl.class); + + @Override + public void logStackTrace() { + + } + + @Override + public void logStackTrace(int traceLevel) { + System.out.println("traceLevel: " + traceLevel); + } + + @Override + public int getLineCount() { + return 0; + } + + @Override + public void logException(Throwable ex) { + + } + + @Override + public void logDebug(String message) { +// logger.debug(message); + } + + @Override + public void logDebug(String message, Exception ex) { +// logger.debug(message); + } + + @Override + public void logTrace(String message) { + logger.trace(message); + } + + @Override + public void logFatalError(String message) { +// logger.error(message); + } + + @Override + public void logError(String message) { +// logger.error(message); + } + + @Override + public boolean isLoggingEnabled() { + return true; + } + + @Override + public boolean isLoggingEnabled(int logLevel) { + return true; + } + + @Override + public void logError(String message, Exception ex) { +// logger.error(message); + } + + @Override + public void logWarning(String message) { + logger.warn(message); + } + + @Override + public void logInfo(String message) { + logger.info(message); + } + + @Override + public void disableLogging() { + + } + + @Override + public void enableLogging() { + + } + + @Override + public void setBuildTimeStamp(String buildTimeStamp) { + + } + + @Override + public void setStackProperties(Properties stackProperties) { + + } + + @Override + public String getLoggerName() { + return null; + } +} 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 60998ea35..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/DeviceOffLineDetector.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.event; - -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; - - public boolean isOnline(String deviceId) { - String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + 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 50f6bc0c0..26ababd4c 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,16 +1,25 @@ package com.genersoft.iot.vmp.gb28181.event; +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.offline.OfflineEvent; -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事件通知推送器,支持推送在线事件、离线事件 + * @description:Event事件通知推送器,支持推送在线事件、离线事件 * @author: swwheihei * @date: 2020年5月6日 上午11:30:50 */ @@ -20,20 +29,6 @@ public class EventPublisher { @Autowired private ApplicationEventPublisher applicationEventPublisher; - public void onlineEventPublish(String deviceId, String from) { - OnlineEvent onEvent = new OnlineEvent(this); - onEvent.setDeviceId(deviceId); - 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 deviceAlarm @@ -43,4 +38,81 @@ public class EventPublisher { alarmEvent.setAlarmInfo(deviceAlarm); applicationEventPublisher.publishEvent(alarmEvent); } + + public void zlmOfflineEventPublish(String mediaServerId){ + ZLMOfflineEvent outEvent = new ZLMOfflineEvent(this); + outEvent.setMediaServerId(mediaServerId); + applicationEventPublisher.publishEvent(outEvent); + } + + public void zlmOnlineEventPublish(String mediaServerId) { + ZLMOnlineEvent outEvent = new ZLMOnlineEvent(this); + 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 e00a59fcd..75751ad2e 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,38 +1,176 @@ 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.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.time.Instant; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +/** + * @author lin + */ @Component public class SipSubscribe { + private final Logger logger = LoggerFactory.getLogger(SipSubscribe.class); + private Map errorSubscribes = new ConcurrentHashMap<>(); private Map okSubscribes = new ConcurrentHashMap<>(); - public interface Event { - void response(ResponseEvent event); + private Map okTimeSubscribes = new ConcurrentHashMap<>(); + + private Map errorTimeSubscribes = new ConcurrentHashMap<>(); + + // @Scheduled(cron="*/5 * * * * ?") //每五秒执行一次 + // @Scheduled(fixedRate= 100 * 60 * 60 ) + @Scheduled(cron="0 0/5 * * * ?") //每5分钟执行一次 + public void execute(){ + logger.info("[定时任务] 清理过期的SIP订阅信息"); + + Instant instant = Instant.now().minusMillis(TimeUnit.MINUTES.toMillis(5)); + + for (String key : okTimeSubscribes.keySet()) { + if (okTimeSubscribes.get(key).isBefore(instant)){ + okSubscribes.remove(key); + okTimeSubscribes.remove(key); + } + } + 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 enum EventResultType{ + // 超时 + timeout, + // 回复 + response, + // 事务已结束 + transactionTerminated, + // 会话已结束 + dialogTerminated, + // 设备未找到 + deviceNotFoundEvent, + // 设备未找到 + cmdSendFailEvent + } + + public static class EventResult{ + public int statusCode; + public EventResultType type; + public String msg; + public String callId; + 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.type = EventResultType.response; + if (response != null) { + this.msg = response.getReasonPhrase(); + this.statusCode = response.getStatusCode(); + } + this.callId = ((CallIdHeader)response.getHeader(CallIdHeader.NAME)).getCallId(); + + }else if (event instanceof TimeoutEvent) { + TimeoutEvent timeoutEvent = (TimeoutEvent)event; + this.type = EventResultType.timeout; + this.msg = "消息超时未回复"; + this.statusCode = -1024; + 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 = EventResultType.transactionTerminated; + this.msg = "事务已结束"; + this.statusCode = -1024; + 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 = EventResultType.dialogTerminated; + this.msg = "会话已结束"; + this.statusCode = -1024; + this.callId = dialogTerminatedEvent.getDialog().getCallId().getCallId(); + }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); + errorTimeSubscribes.put(key, Instant.now()); } public void addOkSubscribe(String key, SipSubscribe.Event event) { okSubscribes.put(key, event); + okTimeSubscribes.put(key, Instant.now()); } public SipSubscribe.Event getErrorSubscribe(String key) { return errorSubscribes.get(key); } + public void removeErrorSubscribe(String key) { + if(key == null){ + return; + } + errorSubscribes.remove(key); + errorTimeSubscribes.remove(key); + } + public SipSubscribe.Event getOkSubscribe(String key) { return okSubscribes.get(key); } + public void removeOkSubscribe(String key) { + if(key == null){ + return; + } + okSubscribes.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 2b563269c..9ee647731 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 000000000..c4d3baba8 --- /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 000000000..2008f22e3 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/device/RequestTimeoutEventImpl.java @@ -0,0 +1,39 @@ +package com.genersoft.iot.vmp.gb28181.event.device; + +import com.genersoft.iot.vmp.gb28181.bean.Device; +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.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/KeepliveTimeoutListener.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepliveTimeoutListener.java deleted file mode 100644 index 4b5e571ed..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepliveTimeoutListener.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.event.offline; - -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 { - - @Autowired - private EventPublisher publisher; - - public KeepliveTimeoutListener(RedisMessageListenerContainer listenerContainer) { - super(listenerContainer); - } - - /** - * 监听失效的key,key格式为keeplive_deviceId - * @param message - * @param pattern - */ - @Override - public void onMessage(Message message, byte[] pattern) { - // 获取失效的key - String expiredKey = message.toString(); - if(!expiredKey.startsWith(VideoManagerConstants.KEEPLIVEKEY_PREFIX)){ - System.out.println("收到redis过期监听,但开头不是"+VideoManagerConstants.KEEPLIVEKEY_PREFIX+",忽略"); - return; - } - - String deviceId = expiredKey.substring(VideoManagerConstants.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 aa45efe61..000000000 --- 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 4adc8fba9..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEventListener.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.event.offline; - -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.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; - - @Override - public void onApplicationEvent(OfflineEvent event) { - - if (logger.isDebugEnabled()) { - logger.debug("设备离线事件触发,deviceId:" + event.getDeviceId() + ",from:" + event.getFrom()); - } - - String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + 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()); - } -} 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 e077cb98d..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEvent.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.event.online; - -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 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/online/OnlineEventListener.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEventListener.java deleted file mode 100644 index b9bb5b12d..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEventListener.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.event.online; - -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.request.impl.RegisterRequestProcessor} - * 2、设备未知原因离线,心跳超时,{@link com.genersoft.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor} - * @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; - - @Override - public void onApplicationEvent(OnlineEvent event) { - - if (logger.isDebugEnabled()) { - logger.debug("设备上线事件触发,deviceId:" + event.getDeviceId() + ",from:" + event.getFrom()); - } - - String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + event.getDeviceId(); - boolean needUpdateStorager = false; - - switch (event.getFrom()) { - // 注册时触发的在线事件,先在redis中增加超时超时监听 - case VideoManagerConstants.EVENT_ONLINE_REGISTER: - // TODO 超时时间暂时写死为180秒 - redis.set(key, event.getDeviceId(), 180); - needUpdateStorager = true; - break; - // 设备主动发送心跳触发的离线事件 - case VideoManagerConstants.EVENT_ONLINE_KEEPLIVE: - boolean exist = redis.hasKey(key); - // 先判断是否还存在,当设备先心跳超时后又发送心跳时,redis没有监听,需要增加 - if (!exist) { - needUpdateStorager = true; - redis.set(key, event.getDeviceId(), 180); - } else { - redis.expire(key, 180); - } - break; - } - - if (needUpdateStorager) { - // 处理离线监听 - storager.online(event.getDeviceId()); - } - } -} 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 000000000..cfd2985c5 --- /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 000000000..cb4682304 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java @@ -0,0 +1,67 @@ +package com.genersoft.iot.vmp.gb28181.event.record; + +import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; +import com.genersoft.iot.vmp.utils.redis.RedisUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import 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); + + private Map handlerMap = new ConcurrentHashMap<>(); + public interface RecordEndEventHandler{ + void handler(RecordInfo recordInfo); + } + + @Override + public void onApplicationEvent(RecordEndEvent event) { + String deviceId = event.getRecordInfo().getDeviceId(); + String channelId = event.getRecordInfo().getChannelId(); + int count = event.getRecordInfo().getCount(); + int sumNum = event.getRecordInfo().getSumNum(); + logger.info("录像查询完成事件触发,deviceId:{}, channelId: {}, 录像数量{}/{}条", event.getRecordInfo().getDeviceId(), + event.getRecordInfo().getChannelId(), count,sumNum); + if (handlerMap.size() > 0) { + RecordEndEventHandler handler = handlerMap.get(deviceId + channelId); + if (handler !=null){ + handler.handler(event.getRecordInfo()); + if (count ==sumNum){ + handlerMap.remove(deviceId + channelId); + } + } + } + } + + /** + * 添加 + * @param device + * @param channelId + * @param recordEndEventHandler + */ + public void addEndEventHandler(String device, String channelId, RecordEndEventHandler recordEndEventHandler) { + handlerMap.put(device + channelId, recordEndEventHandler); + } + /** + * 添加 + * @param device + * @param channelId + */ + public void delEndEventHandler(String device, String channelId) { + handlerMap.remove(device + channelId); + } + +} 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 000000000..5c064f756 --- /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 000000000..89ecb1867 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java @@ -0,0 +1,197 @@ +package com.genersoft.iot.vmp.gb28181.event.subscribe.catalog; + +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.genersoft.iot.vmp.service.IGbStreamService; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 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; + + @Autowired + private UserSetting userSetting; + + @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()) { + if (gbStream.getStreamType().equals("push") && !userSetting.isUsePushingAsStatus()) { + continue; + } + DeviceChannel deviceChannelByStream = gbStreamService.getDeviceChannelListByStream(gbStream, gbStream.getCatalogId(), parentPlatform); + deviceChannelList.add(deviceChannelByStream); + } + } + if (deviceChannelList.size() > 0) { + logger.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), event.getPlatformId(), deviceChannelList.size()); + 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 000000000..38cdf7c45 --- /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 000000000..3f24dbee4 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/RecordDataCatch.java @@ -0,0 +1,92 @@ +package com.genersoft.iot.vmp.gb28181.session; + +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.event.record.RecordEndEventListener; +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; + @Autowired + private RecordEndEventListener recordEndEventListener; + + + public int put(String deviceId,String channelId, 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.setChannelId(channelId); + 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); + recordEndEventListener.delEndEventHandler(recordInfo.getDeviceId(),recordInfo.getChannelId()); + 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/SSRCFactory.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/SSRCFactory.java new file mode 100644 index 000000000..657bb2fa9 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/SSRCFactory.java @@ -0,0 +1,124 @@ +package com.genersoft.iot.vmp.gb28181.session; + +import com.genersoft.iot.vmp.conf.SipConfig; +import com.genersoft.iot.vmp.conf.UserSetting; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * ssrc使用 + */ +@Component +public class SSRCFactory { + + /** + * 播流最大并发个数 + */ + private static final Integer MAX_STREAM_COUNT = 10000; + + /** + * 播流最大并发个数 + */ + private static final String SSRC_INFO_KEY = "VMP_SSRC_INFO_"; + + @Autowired + private StringRedisTemplate redisTemplate; + + @Autowired + private SipConfig sipConfig; + + @Autowired + private UserSetting userSetting; + + + public void initMediaServerSSRC(String mediaServerId, Set usedSet) { + String ssrcPrefix = sipConfig.getDomain().substring(3, 8); + String redisKey = SSRC_INFO_KEY + userSetting.getServerId() + "_" + mediaServerId; + List ssrcList = new ArrayList<>(); + for (int i = 1; i < MAX_STREAM_COUNT; i++) { + String ssrc = String.format("%s%04d", ssrcPrefix, i); + + if (null == usedSet || !usedSet.contains(ssrc)) { + ssrcList.add(ssrc); + + } + } + if (redisTemplate.opsForSet().size(redisKey) != null) { + redisTemplate.delete(redisKey); + } + redisTemplate.opsForSet().add(redisKey, ssrcList.toArray(new String[0])); + } + + + /** + * 获取视频预览的SSRC值,第一位固定为0 + * + * @return ssrc + */ + public String getPlaySsrc(String mediaServerId) { + return "0" + getSN(mediaServerId); + } + + /** + * 获取录像回放的SSRC值,第一位固定为1 + */ + public String getPlayBackSsrc(String mediaServerId) { + return "1" + getSN(mediaServerId); + } + + /** + * 释放ssrc,主要用完的ssrc一定要释放,否则会耗尽 + * + * @param ssrc 需要重置的ssrc + */ + public void releaseSsrc(String mediaServerId, String ssrc) { + if (ssrc == null) { + return; + } + String sn = ssrc.substring(1); + String redisKey = SSRC_INFO_KEY + userSetting.getServerId() + "_" + mediaServerId; + redisTemplate.opsForSet().add(redisKey, sn); + } + + /** + * 获取后四位数SN,随机数 + */ + private String getSN(String mediaServerId) { + String sn = null; + String redisKey = SSRC_INFO_KEY + userSetting.getServerId() + "_" + mediaServerId; + Long size = redisTemplate.opsForSet().size(redisKey); + if (size == null || size == 0) { + throw new RuntimeException("ssrc已经用完"); + } else { + // 在集合中移除并返回一个随机成员。 + sn = (String) redisTemplate.opsForSet().pop(redisKey); + redisTemplate.opsForSet().remove(redisKey, sn); + } + return sn; + } + + /** + * 重置一个流媒体服务的所有ssrc + * + * @param mediaServerId 流媒体服务ID + */ + public void reset(String mediaServerId) { + this.initMediaServerSSRC(mediaServerId, null); + } + + /** + * 是否已经存在了某个MediaServer的SSRC信息 + * + * @param mediaServerId 流媒体服务ID + */ + public boolean hasMediaServerSSRC(String mediaServerId) { + String redisKey = SSRC_INFO_KEY + userSetting.getServerId() + "_" + mediaServerId; + return redisTemplate.opsForSet().members(redisKey) != null; + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcUtil.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcUtil.java deleted file mode 100644 index d6b75f853..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcUtil.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.session; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.utils.SpringBeanFactory; - -/** - * @Description:SIP信令中的SSRC工具类。SSRC值由10位十进制整数组成的字符串,第一位为0代表实况,为1则代表回放;第二位至第六位由监控域ID的第4位到第8位组成;最后4位为不重复的4个整数 - * @author: swwheihei - * @date: 2020年5月10日 上午11:57:57 - */ -public class SsrcUtil { - - private static String ssrcPrefix; - - private static List isUsed; - - private static List notUsed; - - private static void init() { - SipConfig sipConfig = (SipConfig) SpringBeanFactory.getBean("sipConfig"); - ssrcPrefix = sipConfig.getSipDomain().substring(3, 8); - isUsed = new ArrayList(); - notUsed = new ArrayList(); - for (int i = 1; i < 10000; i++) { - if (i < 10) { - notUsed.add("000" + i); - } else if (i < 100) { - notUsed.add("00" + i); - } else if (i < 1000) { - notUsed.add("0" + i); - } else { - notUsed.add(String.valueOf(i)); - } - } - } - - /** - * 获取视频预览的SSRC值,第一位固定为0 - * - */ - public static String getPlaySsrc() { - return "0" + getSsrcPrefix() + getSN(); - } - - /** - * 获取录像回放的SSRC值,第一位固定为1 - * - */ - public static String getPlayBackSsrc() { - return "1" + getSsrcPrefix() + getSN(); - } - - /** - * 释放ssrc,主要用完的ssrc一定要释放,否则会耗尽 - * - */ - public static void releaseSsrc(String ssrc) { - String sn = ssrc.substring(6); - isUsed.remove(sn); - notUsed.add(sn); - } - - /** - * 获取后四位数SN,随机数 - * - */ - private static String getSN() { - String sn = null; - int index = 0; - if (notUsed.size() == 0) { - throw new RuntimeException("ssrc已经用完"); - } else if (notUsed.size() == 1) { - sn = notUsed.get(0); - } else { - index = new Random().nextInt(notUsed.size() - 1); - sn = notUsed.get(index); - } - notUsed.remove(index); - isUsed.add(sn); - return sn; - } - - private static String getSsrcPrefix() { - if (ssrcPrefix == null) { - init(); - } - return ssrcPrefix; - } -} 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 65e1e5f61..a5da0186b 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,42 +1,139 @@ package com.genersoft.iot.vmp.gb28181.session; -import java.util.concurrent.ConcurrentHashMap; - -import javax.sip.ClientTransaction; - +import com.genersoft.iot.vmp.common.InviteSessionType; +import com.genersoft.iot.vmp.common.VideoManagerConstants; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo; +import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; +import com.genersoft.iot.vmp.utils.JsonUtil; +import com.genersoft.iot.vmp.utils.redis.RedisUtil; +import gov.nist.javax.sip.message.SIPResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; -/** - * @Description:视频流session管理器,管理视频预览、预览回放的通信句柄 - * @author: swwheihei - * @date: 2020年5月13日 下午4:03:02 +import java.util.ArrayList; +import java.util.List; + +/** + * 视频流session管理器,管理视频预览、预览回放的通信句柄 */ @Component public class VideoStreamSessionManager { - private ConcurrentHashMap sessionMap = new ConcurrentHashMap<>(); - private ConcurrentHashMap ssrcMap = new ConcurrentHashMap<>(); + @Autowired + private UserSetting userSetting; - public String createPlaySsrc(){ - return SsrcUtil.getPlaySsrc(); + @Autowired + private RedisTemplate redisTemplate; + + /** + * 添加一个点播/回放的事务信息 + * 后续可以通过流Id/callID + * @param deviceId 设备ID + * @param channelId 通道ID + * @param callId 一次请求的CallID + * @param stream 流名称 + * @param mediaServerId 所使用的流媒体ID + * @param response 回复 + */ + public void put(String deviceId, String channelId, String callId, String stream, String ssrc, String mediaServerId, SIPResponse response, InviteSessionType type){ + SsrcTransaction ssrcTransaction = new SsrcTransaction(); + ssrcTransaction.setDeviceId(deviceId); + ssrcTransaction.setChannelId(channelId); + ssrcTransaction.setStream(stream); + ssrcTransaction.setSipTransactionInfo(new SipTransactionInfo(response)); + ssrcTransaction.setCallId(callId); + ssrcTransaction.setSsrc(ssrc); + ssrcTransaction.setMediaServerId(mediaServerId); + ssrcTransaction.setType(type); + + redisTemplate.opsForValue().set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + + "_" + deviceId + "_" + channelId + "_" + callId + "_" + stream, ssrcTransaction); + } + + public SsrcTransaction getSsrcTransaction(String deviceId, String channelId, String callId, String stream){ + + if (ObjectUtils.isEmpty(deviceId)) { + deviceId ="*"; + } + if (ObjectUtils.isEmpty(channelId)) { + channelId ="*"; + } + if (ObjectUtils.isEmpty(callId)) { + callId ="*"; + } + if (ObjectUtils.isEmpty(stream)) { + stream ="*"; + } + String key = VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_" + deviceId + "_" + channelId + "_" + callId+ "_" + stream; + List scanResult = RedisUtil.scan(redisTemplate, key); + if (scanResult.size() == 0) { + return null; + } + return (SsrcTransaction)redisTemplate.opsForValue().get(scanResult.get(0)); + } + + public List getSsrcTransactionForAll(String deviceId, String channelId, String callId, String stream){ + if (ObjectUtils.isEmpty(deviceId)) { + deviceId ="*"; + } + if (ObjectUtils.isEmpty(channelId)) { + channelId ="*"; + } + if (ObjectUtils.isEmpty(callId)) { + callId ="*"; + } + if (ObjectUtils.isEmpty(stream)) { + stream ="*"; + } + String key = VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_" + deviceId + "_" + channelId + "_" + callId+ "_" + stream; + List scanResult = RedisUtil.scan(redisTemplate, key); + if (scanResult.size() == 0) { + return null; + } + List result = new ArrayList<>(); + for (Object keyObj : scanResult) { + result.add((SsrcTransaction)redisTemplate.opsForValue().get(keyObj)); + } + return result; + } + + public String getMediaServerId(String deviceId, String channelId, String stream){ + SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream); + if (ssrcTransaction == null) { + return null; + } + return ssrcTransaction.getMediaServerId(); + } + + public String getSSRC(String deviceId, String channelId, String stream){ + SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream); + if (ssrcTransaction == null) { + return null; + } + return ssrcTransaction.getSsrc(); } - public String createPlayBackSsrc(){ - return SsrcUtil.getPlayBackSsrc(); + public void remove(String deviceId, String channelId, String stream) { + SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream); + if (ssrcTransaction == null) { + return; + } + redisTemplate.delete(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_" + + deviceId + "_" + channelId + "_" + ssrcTransaction.getCallId() + "_" + ssrcTransaction.getStream()); } - - public void put(String streamId,String ssrc,ClientTransaction transaction){ - sessionMap.put(streamId, transaction); - ssrcMap.put(streamId, ssrc); - } - - public ClientTransaction get(String streamId){ - return sessionMap.get(streamId); - } - - public void remove(String streamId) { - sessionMap.remove(streamId); - SsrcUtil.releaseSsrc(ssrcMap.get(streamId)); - ssrcMap.remove(streamId); + + + public List getAllSsrc() { + List ssrcTransactionKeys = RedisUtil.scan(redisTemplate, String.format("%s_*_*_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX+ userSetting.getServerId())); + List result= new ArrayList<>(); + for (Object ssrcTransactionKey : ssrcTransactionKeys) { + String key = (String) ssrcTransactionKey; + SsrcTransaction ssrcTransaction = JsonUtil.redisJsonToObject(redisTemplate, key, SsrcTransaction.class); + result.add(ssrcTransaction); + } + return result; } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/task/ISubscribeTask.java b/src/main/java/com/genersoft/iot/vmp/gb28181/task/ISubscribeTask.java new file mode 100644 index 000000000..a4e711d9e --- /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 000000000..17c23a00d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java @@ -0,0 +1,120 @@ +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.session.SSRCFactory; +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=14) +public class SipRunner implements CommandLineRunner { + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private SSRCFactory ssrcFactory; + + @Autowired + private UserSetting userSetting; + + @Autowired + private IDeviceService deviceService; + + @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, null); + } + } + // 重置cseq计数 + redisCatchStorage.resetAllCSEQ(); + // 清理redis + // 清理数据库不存在但是redis中存在的数据 + List devicesInDb = deviceService.getAll(); + if (devicesInDb.size() == 0) { + redisCatchStorage.removeAllDevice(); + }else { + List devicesInRedis = redisCatchStorage.getAllDevices(); + if (devicesInRedis.size() > 0) { + Map deviceMapInDb = new HashMap<>(); + devicesInDb.parallelStream().forEach(device -> { + deviceMapInDb.put(device.getDeviceId(), device); + }); + devicesInRedis.parallelStream().forEach(device -> { + if (deviceMapInDb.get(device.getDeviceId()) == null) { + redisCatchStorage.removeDevice(device.getDeviceId()); + } + }); + } + } + + + // 查找国标推流 + 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) { + ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc()); + 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 000000000..39dff931d --- /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 000000000..2e792c1c9 --- /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 000000000..0abd3cae3 --- /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/ISIPProcessorObserver.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/ISIPProcessorObserver.java new file mode 100644 index 000000000..2480f37e2 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/ISIPProcessorObserver.java @@ -0,0 +1,6 @@ +package com.genersoft.iot.vmp.gb28181.transmit; + +import javax.sip.SipListener; + +public interface ISIPProcessorObserver extends SipListener { +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java deleted file mode 100644 index 77e6bd957..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java +++ /dev/null @@ -1,196 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit; - -import javax.sip.RequestEvent; -import javax.sip.ResponseEvent; -import javax.sip.SipProvider; -import javax.sip.header.CSeqHeader; -import javax.sip.message.Request; -import javax.sip.message.Response; - -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.stereotype.Component; - -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.gb28181.auth.RegisterLogicHandler; -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.cmd.impl.SIPCommander; -import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.AckRequestProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.ByeRequestProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.CancelRequestProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.InviteRequestProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.NotifyRequestProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.OtherRequestProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.RegisterRequestProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.SubscribeRequestProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.response.impl.ByeResponseProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.response.impl.CancelResponseProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.response.impl.InviteResponseProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.response.impl.OtherResponseProcessor; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.utils.SpringBeanFactory; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; - -/** - * @Description: SIP信令处理分配 - * @author: swwheihei - * @date: 2020年5月3日 下午4:24:37 - */ -@Component -public class SIPProcessorFactory { - - // private final static Logger logger = LoggerFactory.getLogger(SIPProcessorFactory.class); - - @Autowired - private SipConfig sipConfig; - - @Autowired - private RegisterLogicHandler handler; - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - @Autowired - private EventPublisher publisher; - - @Autowired - private SIPCommander cmder; - - @Autowired - private RedisUtil redis; - - @Autowired - private DeferredResultHolder deferredResultHolder; - - @Autowired - private DeviceOffLineDetector offLineDetector; - - @Autowired - private InviteResponseProcessor inviteResponseProcessor; - - @Autowired - private ByeResponseProcessor byeResponseProcessor; - - @Autowired - private CancelResponseProcessor cancelResponseProcessor; - - @Autowired - private OtherResponseProcessor otherResponseProcessor; - - - // 注:这里使用注解会导致循环依赖注入,暂用springBean - private SipProvider tcpSipProvider; - - // 注:这里使用注解会导致循环依赖注入,暂用springBean - private SipProvider udpSipProvider; - - public ISIPRequestProcessor createRequestProcessor(RequestEvent evt) { - Request request = evt.getRequest(); - String method = request.getMethod(); -// logger.info("接收到消息:"+request.getMethod()); -// sipSubscribe.getSubscribe(evt.getServerTransaction().getBranchId()).response(evt); - if (Request.INVITE.equals(method)) { - InviteRequestProcessor processor = new InviteRequestProcessor(); - processor.setRequestEvent(evt); - processor.setTcpSipProvider(getTcpSipProvider()); - processor.setUdpSipProvider(getUdpSipProvider()); - return processor; - } else if (Request.REGISTER.equals(method)) { - RegisterRequestProcessor processor = new RegisterRequestProcessor(); - processor.setRequestEvent(evt); - processor.setTcpSipProvider(getTcpSipProvider()); - processor.setUdpSipProvider(getUdpSipProvider()); - processor.setHandler(handler); - processor.setPublisher(publisher); - processor.setSipConfig(sipConfig); - processor.setVideoManagerStorager(storager); - return processor; - } else if (Request.SUBSCRIBE.equals(method)) { - SubscribeRequestProcessor processor = new SubscribeRequestProcessor(); - processor.setRequestEvent(evt); - return processor; - } else if (Request.ACK.equals(method)) { - AckRequestProcessor processor = new AckRequestProcessor(); - processor.setRequestEvent(evt); - return processor; - } else if (Request.BYE.equals(method)) { - ByeRequestProcessor processor = new ByeRequestProcessor(); - processor.setRequestEvent(evt); - return processor; - } else if (Request.CANCEL.equals(method)) { - CancelRequestProcessor processor = new CancelRequestProcessor(); - processor.setRequestEvent(evt); - return processor; - } else if (Request.MESSAGE.equals(method)) { - MessageRequestProcessor processor = new MessageRequestProcessor(); - processor.setRequestEvent(evt); - processor.setTcpSipProvider(getTcpSipProvider()); - processor.setUdpSipProvider(getUdpSipProvider()); - processor.setPublisher(publisher); - processor.setRedis(redis); - processor.setDeferredResultHolder(deferredResultHolder); - processor.setOffLineDetector(offLineDetector); - processor.setCmder(cmder); - processor.setStorager(storager); - processor.setRedisCatchStorage(redisCatchStorage); - return processor; - } else if (Request.NOTIFY.equalsIgnoreCase(method)) { - NotifyRequestProcessor processor = new NotifyRequestProcessor(); - processor.setRequestEvent(evt); - processor.setTcpSipProvider(getTcpSipProvider()); - processor.setUdpSipProvider(getUdpSipProvider()); - processor.setPublisher(publisher); - processor.setRedis(redis); - processor.setDeferredResultHolder(deferredResultHolder); - processor.setOffLineDetector(offLineDetector); - processor.setCmder(cmder); - processor.setStorager(storager); - processor.setRedisCatchStorage(redisCatchStorage); - return processor; - } else { - OtherRequestProcessor processor = new OtherRequestProcessor(); - processor.setRequestEvent(evt); - return processor; - } - } - - public ISIPResponseProcessor createResponseProcessor(ResponseEvent evt) { - Response response = evt.getResponse(); - CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME); - String method = cseqHeader.getMethod(); - if(Request.INVITE.equals(method)){ - return inviteResponseProcessor; - } else if (Request.BYE.equals(method)) { - return byeResponseProcessor; - } else if (Request.CANCEL.equals(method)) { - return cancelResponseProcessor; - } else { - return otherResponseProcessor; - } - } - - private SipProvider getTcpSipProvider() { - if (tcpSipProvider == null) { - tcpSipProvider = (SipProvider) SpringBeanFactory.getBean("tcpSipProvider"); - } - return tcpSipProvider; - } - - private SipProvider getUdpSipProvider() { - if (udpSipProvider == null) { - udpSipProvider = (SipProvider) SpringBeanFactory.getBean("udpSipProvider"); - } - return udpSipProvider; - } - -} 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 new file mode 100644 index 000000000..52356c1df --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java @@ -0,0 +1,205 @@ +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; +import com.genersoft.iot.vmp.gb28181.transmit.event.timeout.ITimeoutProcessor; +import gov.nist.javax.sip.message.SIPRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import javax.sip.*; +import javax.sip.header.*; +import javax.sip.message.Request; +import javax.sip.message.Response; +import java.net.InetAddress; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @description: SIP信令处理类观察者 + * @author: panlinlin + * @date: 2021年11月5日 下午15:32 + */ +@Component +public class SIPProcessorObserver implements ISIPProcessorObserver { + + private final static Logger logger = LoggerFactory.getLogger(SIPProcessorObserver.class); + + private static Map requestProcessorMap = new ConcurrentHashMap<>(); + private static Map responseProcessorMap = new ConcurrentHashMap<>(); + private static ITimeoutProcessor timeoutProcessor; + + @Autowired + private SipSubscribe sipSubscribe; + + @Autowired + private EventPublisher eventPublisher; + + /** + * 添加 request订阅 + * @param method 方法名 + * @param processor 处理程序 + */ + public void addRequestProcessor(String method, ISIPRequestProcessor processor) { + requestProcessorMap.put(method, processor); + } + + /** + * 添加 response订阅 + * @param method 方法名 + * @param processor 处理程序 + */ + public void addResponseProcessor(String method, ISIPResponseProcessor processor) { + responseProcessorMap.put(method, processor); + } + + /** + * 添加 超时事件订阅 + * @param processor 处理程序 + */ + public void addTimeoutProcessor(ITimeoutProcessor processor) { + timeoutProcessor = processor; + } + + /** + * 分发RequestEvent事件 + * @param requestEvent RequestEvent事件 + */ + @Override + @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); + + } + + /** + * 分发ResponseEvent事件 + * @param responseEvent responseEvent事件 + */ + @Override + @Async("taskExecutor") + public void processResponse(ResponseEvent responseEvent) { + Response response = responseEvent.getResponse(); + int status = response.getStatusCode(); + + // Success + if (((status >= Response.OK) && (status < Response.MULTIPLE_CHOICES)) || status == Response.UNAUTHORIZED) { + CSeqHeader cseqHeader = (CSeqHeader) responseEvent.getResponse().getHeader(CSeqHeader.NAME); + String method = cseqHeader.getMethod(); + ISIPResponseProcessor sipRequestProcessor = responseProcessorMap.get(method); + if (sipRequestProcessor != null) { + sipRequestProcessor.process(responseEvent); + } + if (status != Response.UNAUTHORIZED && responseEvent.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0 ) { + CallIdHeader callIdHeader = (CallIdHeader)responseEvent.getResponse().getHeader(CallIdHeader.NAME); + if (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 >= Response.TRYING) && (status < Response.OK)) { + // 增加其它无需回复的响应,如101、180等 + } else { + 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) { + SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()); + if (subscribe != null) { + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(responseEvent); + subscribe.response(eventResult); + sipSubscribe.removeErrorSubscribe(callIdHeader.getCallId()); + } + } + } + if (responseEvent.getDialog() != null) { + responseEvent.getDialog().delete(); + } + } + + + } + + /** + * 向超时订阅发送消息 + * @param timeoutEvent timeoutEvent事件 + */ + @Override + public void processTimeout(TimeoutEvent timeoutEvent) { + logger.info("[消息发送超时]"); + ClientTransaction clientTransaction = timeoutEvent.getClientTransaction(); + + if (clientTransaction != null) { + logger.info("[发送错误订阅] clientTransaction != null"); + Request request = clientTransaction.getRequest(); + if (request != null) { + logger.info("[发送错误订阅] request != null"); + CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); + if (callIdHeader != null) { + logger.info("[发送错误订阅]"); + SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()); + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(timeoutEvent); + if (subscribe != null){ + subscribe.response(eventResult); + } + sipSubscribe.removeOkSubscribe(callIdHeader.getCallId()); + sipSubscribe.removeErrorSubscribe(callIdHeader.getCallId()); + } + } + } + eventPublisher.requestTimeOut(timeoutEvent); + } + + @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 + public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) { + CallIdHeader callId = dialogTerminatedEvent.getDialog().getCallId(); + } + + +} 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 000000000..e8066b755 --- /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 { + ViaHeader viaHeader = (ViaHeader)message.getHeader(ViaHeader.NAME); + String transport = "UDP"; + if (viaHeader == null) { + logger.warn("[消息头缺失]: ViaHeader, 使用默认的UDP方式处理数据"); + }else { + transport = viaHeader.getTransport(); + } + if (message.getHeader(UserAgentHeader.NAME) == null) { + try { + message.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + } catch (ParseException e) { + logger.error("添加UserAgentHeader失败", e); + } + } + + CallIdHeader callIdHeader = (CallIdHeader) message.getHeader(CallIdHeader.NAME); + // 添加错误订阅 + if (errorEvent != null) { + sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> { + 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 770edf061..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/CheckForAllRecordsThread.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit.callback; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; -import com.genersoft.iot.vmp.gb28181.bean.RecordItem; -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; - -import org.slf4j.Logger; - -@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(); - String deviceId = recordInfo.getDeviceId(); - msg.setDeviceId(deviceId); - msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO); - msg.setData(recordInfo); - deferredResultHolder.invokeResult(msg); - logger.info("处理完成,返回结果"); - MessageRequestProcessor.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 6fcc2e022..8a6218281 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,17 @@ package com.genersoft.iot.vmp.gb28181.transmit.callback; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; +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; + /** - * @Description: 异步请求处理 + * @description: 异步请求处理 * @author: swwheihei * @date: 2020年5月8日 下午7:59:05 */ @@ -31,31 +33,128 @@ public class DeferredResultHolder { public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO"; - public static final String CALLBACK_CMD_PlAY = "CALLBACK_PLAY"; + public static final String CALLBACK_CMD_PLAY = "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_PROXY = "CALLBACK_PROXY"; public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP"; - public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION"; + public static final String UPLOAD_FILE_CHANNEL = "UPLOAD_FILE_CHANNEL"; + + public static final String CALLBACK_CMD_MOBILE_POSITION = "CALLBACK_CMD_MOBILE_POSITION"; public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY"; public static final String CALLBACK_CMD_ALARM = "CALLBACK_ALARM"; - private Map map = new ConcurrentHashMap(); - - public void put(String key, DeferredResult result) { - map.put(key, result); + public static final String CALLBACK_CMD_BROADCAST = "CALLBACK_BROADCAST"; + + public static final String CALLBACK_CMD_SNAP= "CALLBACK_SNAP"; + + private Map> map = new ConcurrentHashMap<>(); + + + 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) { - return map.get(key); + 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 (id == null) { + return deferredResultMap != null; + }else { + return deferredResultMap != null && deferredResultMap.get(id) != null; + } + } + + /** + * 释放单个请求 + * @param msg + */ public void invokeResult(RequestMessage msg) { - DeferredResult result = map.get(msg.getId()); + Map deferredResultMap = map.get(msg.getKey()); + if (deferredResultMap == null) { + return; + } + 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()); + } } + + /** + * 释放所有的请求 + * @param msg + */ + public void invokeAllResult(RequestMessage msg) { + Map deferredResultMap = map.get(msg.getKey()); + if (deferredResultMap == null) { + return; + } + synchronized (this) { + deferredResultMap = map.get(msg.getKey()); + if (deferredResultMap == null) { + return; + } + 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/callback/RequestMessage.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java index 223092fbc..42ae577ad 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java @@ -1,7 +1,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.callback; /** - * @Description: 请求信息定义 + * @description: 请求信息定义 * @author: swwheihei * @date: 2020年5月8日 下午1:09:18 */ @@ -9,12 +9,10 @@ public class RequestMessage { private String id; - private String deviceId; - - private String type; - + private String key; + private Object data; - + public String getId() { return id; } @@ -23,22 +21,12 @@ public class RequestMessage { this.id = id; } - public String getDeviceId() { - return deviceId; + public void setKey(String key) { + this.key = key; } - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - this.id = type + deviceId; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - this.id = type + deviceId; + public String getKey() { + return key; } public Object getData() { 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 6fa4eca98..d68405632 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,21 @@ 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.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:设备能力接口,用于定义设备的控制、查询能力 + * @description:设备能力接口,用于定义设备的控制、查询能力 * @author: swwheihei * @date: 2020年5月3日 下午9:16:34 */ @@ -18,9 +28,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; /** * 云台方向放控制 @@ -31,7 +40,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; /** * 云台缩放控制,使用配置文件中的默认镜头缩放速度 @@ -40,7 +49,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; /** * 云台缩放控制 @@ -48,9 +57,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; /** * 云台控制,支持方向与缩放控制 @@ -63,7 +71,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指令、预置位指令、巡航指令、扫描指令和辅助开关指令 @@ -75,16 +83,23 @@ 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; + /** + * 前端控制指令(用于转发上级指令) + * @param device 控制设备 + * @param channelId 预览通道 + * @param 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(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; + /** * 请求回放视频流 * @@ -93,23 +108,72 @@ public interface ISIPCommander { * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss */ - void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); - - /** - * 视频流停止 - * - * @param ssrc ssrc - */ - void streamByeCmd(String ssrc, SipSubscribe.Event okEvent); - void streamByeCmd(String ssrc); + void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInf, Device device, String channelId, String startTime, String endTime,ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; /** + * 请求历史媒体下载 + * + * @param device 视频设备 + * @param channelId 预览通道 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss + * @param downloadSpeed 下载倍速参数 + */ + void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + String startTime, String endTime, int downloadSpeed, ZlmHttpHookSubscribe.Event hookEvent, + SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException; + + /** + * 视频流停止 + */ + 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) throws InvalidArgumentException, ParseException, SipException; + + /** + * 回放恢复 + */ + void playResumeCmd(Device device, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException; + + /** + * 回放拖动播放 + */ + void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime) throws InvalidArgumentException, ParseException, SipException; + + /** + * 回放倍速播放 + */ + 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) throws InvalidArgumentException, SipException, ParseException; + void audioBroadcastCmd(Device device) throws InvalidArgumentException, SipException, ParseException; /** * 音视频录像控制 @@ -118,22 +182,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, SipSubscribe.Event okEvent) 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, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException; /** * 报警复位命令 @@ -142,7 +205,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, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException; /** * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧 @@ -150,26 +213,27 @@ public interface ISIPCommander { * @param device 视频设备 * @param channelId 预览通道 */ - boolean iFrameCmd(Device device, String channelId); - + void iFrameCmd(Device device, String channelId) throws InvalidArgumentException, SipException, ParseException; + /** * 看守位控制命令 - * - * @param device 视频设备 - * @param enabled 看守位使能:1 = 开启,0 = 关闭 - * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s) - * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255 + * + * @param device 视频设备 + * @param channelId 通道id,非通道则是设备本身 + * @param enabled 看守位使能:1 = 开启,0 = 关闭 + * @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,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException; + /** * 设备配置命令 * * @param device 视频设备 */ - boolean deviceConfigCmd(Device device); + void deviceConfigCmd(Device device); - /** + /** * 设备配置命令:basicParam * * @param device 视频设备 @@ -179,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; /** * 查询设备信息 @@ -194,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; /** * 查询录像信息 @@ -209,8 +273,9 @@ public interface ISIPCommander { * @param device 视频设备 * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss + * @param sn */ - boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime); + 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; /** * 查询报警信息 @@ -224,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; /** * 查询设备配置 @@ -234,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; /** * 订阅、取消订阅报警信息 @@ -266,19 +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 = 命令发送成功 + */ + 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; /** - * 释放rtpserver - * @param device - * @param channelId + * 向设备发送报警NOTIFY消息, 用于互联结构下,此时将设备当成一个平级平台看待 + * @param device 设备 + * @param deviceAlarm 报警信息信息 + * @return */ - void closeRTPServer(Device device, String channelId); + 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 new file mode 100644 index 000000000..0332aabe6 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java @@ -0,0 +1,126 @@ +package com.genersoft.iot.vmp.gb28181.transmit.cmd; + +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 { + + /** + * 向上级平台注册 + * @param parentPlatform + * @return + */ + void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException; + + void register(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException; + void register(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, WWWAuthenticateHeader www, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean isRegister) throws SipException, InvalidArgumentException, ParseException; + + /** + * 向上级平台注销 + * @param parentPlatform + * @return + */ + void unregister(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException; + + + /** + * 向上级平发送心跳信息 + * @param parentPlatform + * @return callId(作为接受回复的判定) + */ + String keepalive(ParentPlatform parentPlatform,SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException; + + + /** + * 向上级回复通道信息 + * @param channel 通道信息 + * @param parentPlatform 平台信息 + * @param sn + * @param fromTag + * @param size + * @return + */ + 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 SN + * @param fromTag FROM头的tag信息 + * @return + */ + void deviceInfoResponse(ParentPlatform parentPlatform,Device device, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException; + + /** + * 向上级回复DeviceStatus查询信息 + * @param parentPlatform 平台信息 + * @param sn + * @param fromTag + * @return + */ + void deviceStatusResponse(ParentPlatform parentPlatform,String channelId, String sn, String fromTag,boolean 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 new file mode 100644 index 000000000..fe130a549 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java @@ -0,0 +1,315 @@ +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; + +import javax.sip.InvalidArgumentException; +import javax.sip.PeerUnavailableException; +import javax.sip.SipFactory; +import javax.sip.address.Address; +import javax.sip.address.SipURI; +import javax.sip.header.*; +import javax.sip.message.Request; +import javax.validation.constraints.NotNull; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.UUID; + +/** + * @description: 平台命令request创造器 TODO 冗余代码太多待优化 + * @author: panll + * @date: 2020年5月6日 上午9:29:02 + */ +@Component +public class SIPRequestHeaderPlarformProvider { + + @Autowired + private SipConfig sipConfig; + + @Autowired + private SipLayer sipLayer; + + @Autowired + private GitUtil gitUtil; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, long CSeq, String fromTag, String toTag, CallIdHeader callIdHeader, int expires) throws ParseException, InvalidArgumentException, PeerUnavailableException { + Request request = null; + String sipAddress = parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort(); + //请求行 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), + parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort()); + //via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), + Integer.parseInt(parentPlatform.getDevicePort()), parentPlatform.getTransport(), SipUtils.getNewViaTag()); + viaHeader.setRPort(); + viaHeaders.add(viaHeader); + //from + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag); + //to + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain()); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress,toTag); + + //Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + + //ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(CSeq, Request.REGISTER); + request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.REGISTER, callIdHeader, + cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); + + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory() + .createSipURI(parentPlatform.getDeviceGBId(), sipAddress)); + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); + + ExpiresHeader expiresHeader = SipFactory.getInstance().createHeaderFactory().createExpiresHeader(expires); + request.addHeader(expiresHeader); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + return request; + } + + public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, String fromTag, String toTag, + WWWAuthenticateHeader www , CallIdHeader callIdHeader, int expires) throws ParseException, PeerUnavailableException, InvalidArgumentException { + + + Request registerRequest = createRegisterRequest(parentPlatform, redisCatchStorage.getCSEQ(), fromTag, toTag, callIdHeader, expires); + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort()); + if (www == null) { + AuthorizationHeader authorizationHeader = SipFactory.getInstance().createHeaderFactory().createAuthorizationHeader("Digest"); + String username = parentPlatform.getUsername(); + if ( username == null || username == "" ) + { + authorizationHeader.setUsername(parentPlatform.getDeviceGBId()); + } else { + authorizationHeader.setUsername(username); + } + authorizationHeader.setURI(requestURI); + authorizationHeader.setAlgorithm("MD5"); + registerRequest.addHeader(authorizationHeader); + return registerRequest; + } + String realm = www.getRealm(); + String nonce = www.getNonce(); + String scheme = www.getScheme(); + + // 参考 https://blog.csdn.net/y673533511/article/details/88388138 + // qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略 + String qop = www.getQop(); + + String cNonce = null; + String nc = "00000001"; + if (qop != null) { + if ("auth".equalsIgnoreCase(qop)) { + // 客户端随机数,这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。 + // 这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护 + cNonce = UUID.randomUUID().toString(); + + }else if ("auth-int".equalsIgnoreCase(qop)){ + // TODO + } + } + String HA1 = DigestUtils.md5DigestAsHex((parentPlatform.getDeviceGBId() + ":" + realm + ":" + parentPlatform.getPassword()).getBytes()); + String HA2=DigestUtils.md5DigestAsHex((Request.REGISTER + ":" + requestURI.toString()).getBytes()); + + StringBuffer reStr = new StringBuffer(200); + reStr.append(HA1); + reStr.append(":"); + reStr.append(nonce); + reStr.append(":"); + if (qop != null) { + reStr.append(nc); + reStr.append(":"); + reStr.append(cNonce); + reStr.append(":"); + reStr.append(qop); + reStr.append(":"); + } + reStr.append(HA2); + + String RESPONSE = DigestUtils.md5DigestAsHex(reStr.toString().getBytes()); + + AuthorizationHeader authorizationHeader = SipFactory.getInstance().createHeaderFactory().createAuthorizationHeader(scheme); + authorizationHeader.setUsername(parentPlatform.getDeviceGBId()); + authorizationHeader.setRealm(realm); + authorizationHeader.setNonce(nonce); + authorizationHeader.setURI(requestURI); + authorizationHeader.setResponse(RESPONSE); + authorizationHeader.setAlgorithm("MD5"); + if (qop != null) { + authorizationHeader.setQop(qop); + authorizationHeader.setCNonce(cNonce); + authorizationHeader.setNonceCount(1); + } + registerRequest.addHeader(authorizationHeader); + + return registerRequest; + } + + public Request createMessageRequest(ParentPlatform parentPlatform, String content, SendRtpItem sendRtpItem) throws PeerUnavailableException, ParseException, InvalidArgumentException { + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId()); + return createMessageRequest(parentPlatform, content, sendRtpItem.getToTag(), SipUtils.getNewViaTag(), sendRtpItem.getFromTag(), callIdHeader); + } + + public Request createMessageRequest(ParentPlatform parentPlatform, String content, String fromTag, String viaTag, CallIdHeader callIdHeader) throws PeerUnavailableException, ParseException, InvalidArgumentException { + 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.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), serverAddress); + // via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), Integer.parseInt(parentPlatform.getDevicePort()), + parentPlatform.getTransport(), viaTag); + viaHeader.setRPort(); + viaHeaders.add(viaHeader); + // from + // SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), parentPlatform.getDeviceIp() + ":" + parentPlatform.getDeviceIp()); + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag); + // to + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), serverAddress); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, toTag); + + // Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + // ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE); + MessageFactoryImpl messageFactory = (MessageFactoryImpl) SipFactory.getInstance().createMessageFactory(); + // 设置编码, 防止中文乱码 + messageFactory.setDefaultContentEncodingCharset(parentPlatform.getCharacterSet()); + request = messageFactory.createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader, + toHeader, viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().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 = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP()+ ":" + parentPlatform.getServerPort()); + // via + ArrayList viaHeaders = new ArrayList<>(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), Integer.parseInt(parentPlatform.getDevicePort()), + parentPlatform.getTransport(), SipUtils.getNewViaTag()); + viaHeader.setRPort(); + viaHeaders.add(viaHeader); + // from + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), + parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, subscribeInfo.getResponse().getToTag()); + // to + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerGBDomain()); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, subscribeInfo.getRequest().getFromTag()); + + // Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + // ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.NOTIFY); + MessageFactoryImpl messageFactory = (MessageFactoryImpl) SipFactory.getInstance().createMessageFactory(); + // 设置编码, 防止中文乱码 + messageFactory.setDefaultContentEncodingCharset("gb2312"); + + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(subscribeInfo.getRequest().getCallIdHeader().getCallId()); + + request = (SIPRequest) messageFactory.createRequest(requestURI, Request.NOTIFY, callIdHeader, cSeqHeader, fromHeader, + toHeader, viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + EventHeader event = SipFactory.getInstance().createHeaderFactory().createEventHeader(subscribeInfo.getEventType()); + if (subscribeInfo.getEventId() != null) { + event.setEventId(subscribeInfo.getEventId()); + } + + request.addHeader(event); + + SubscriptionStateHeader active = SipFactory.getInstance().createHeaderFactory().createSubscriptionStateHeader("active"); + request.setHeader(active); + + String sipAddress = parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort(); + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory() + .createSipURI(parentPlatform.getDeviceGBId(), sipAddress)); + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); + + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().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 = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerIP()+ ":" + platform.getServerPort()); + // via + ArrayList viaHeaders = new ArrayList<>(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(platform.getDeviceIp(), Integer.parseInt(platform.getDevicePort()), + platform.getTransport(), SipUtils.getNewViaTag()); + viaHeader.setRPort(); + viaHeaders.add(viaHeader); + // from + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sendRtpItem.getChannelId(), + platform.getDeviceIp() + ":" + platform.getDevicePort()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, sendRtpItem.getToTag()); + // to + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerGBDomain()); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, sendRtpItem.getFromTag()); + + // Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + // ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE); + + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId()); + + request = (SIPRequest) SipFactory.getInstance().createMessageFactory().createRequest(requestURI, Request.BYE, callIdHeader, cSeqHeader, fromHeader, + toHeader, viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + String sipAddress = platform.getDeviceIp() + ":" + platform.getDevicePort(); + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory() + .createSipURI(platform.getDeviceGBId(), sipAddress)); + + request.addHeader(SipFactory.getInstance().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 11e3e16e4..899643700 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,26 +1,31 @@ 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.InvalidArgumentException; import javax.sip.PeerUnavailableException; +import javax.sip.SipException; import javax.sip.SipFactory; -import javax.sip.SipProvider; import javax.sip.address.Address; import javax.sip.address.SipURI; import javax.sip.header.*; import javax.sip.message.Request; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -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 冗余代码太多待优化 + * @description:摄像头命令request创造器 TODO 冗余代码太多待优化 * @author: swwheihei * @date: 2020年5月6日 上午9:29:02 */ @@ -31,183 +36,283 @@ public class SIPRequestHeaderProvider { private SipConfig sipConfig; @Autowired - private SipFactory sipFactory; - + private SipLayer sipLayer; + @Autowired - @Qualifier(value="tcpSipProvider") - private SipProvider tcpSipProvider; - + private GitUtil gitUtil; + @Autowired - @Qualifier(value="udpSipProvider") - private SipProvider udpSipProvider; + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private VideoStreamSessionManager streamSession; - public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag) throws ParseException, InvalidArgumentException, PeerUnavailableException { + 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 = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); // via ArrayList viaHeaders = new ArrayList(); - ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), - device.getTransport(), viaTag); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag); viaHeader.setRPort(); viaHeaders.add(viaHeader); // from - SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), - sipConfig.getSipIp() + ":" + sipConfig.getSipPort()); - Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); - FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag); // to - SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getSipDomain()); - Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); - ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag); - // callid - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - // Forwards - MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); - // ceq - CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.MESSAGE); + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, toTag); - request = sipFactory.createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader, + // Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + // ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE); + + request = SipFactory.getInstance().createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards); - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml"); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml"); request.setContent(content, contentTypeHeader); return request; } - public Request createInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, String ssrc) throws ParseException, InvalidArgumentException, PeerUnavailableException { + 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 = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress()); //via ArrayList viaHeaders = new ArrayList(); - ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag); + HeaderFactory headerFactory = SipFactory.getInstance().createHeaderFactory(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag); viaHeader.setRPort(); viaHeaders.add(viaHeader); //from - SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),sipConfig.getSipDomain()); - Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); - FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack //to - SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getSipDomain()); - Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); - ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null); - - //callid - CallIdHeader callIdHeader = null; - if(device.getTransport().equals("TCP")) { - callIdHeader = tcpSipProvider.getNewCallId(); - } - if(device.getTransport().equals("UDP")) { - callIdHeader = udpSipProvider.getNewCallId(); - } + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress()); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress,null); //Forwards - MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); + MaxForwardsHeader maxForwards = SipFactory.getInstance().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.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort())); - // Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort())); - request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INVITE); + request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort())); + // Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort())); + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); // Subject - SubjectHeader subjectHeader = sipFactory.createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getSipId(), 0)); + SubjectHeader subjectHeader = SipFactory.getInstance().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 = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); request.setContent(content, contentTypeHeader); return request; } - public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag) throws ParseException, InvalidArgumentException, PeerUnavailableException { + 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(device.getDeviceId(), device.getHostAddress()); + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress()); // via ArrayList viaHeaders = new ArrayList(); - ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag); viaHeader.setRPort(); viaHeaders.add(viaHeader); //from - SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),sipConfig.getSipDomain()); - Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); - FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack //to - SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getSipDomain()); - Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); - ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null); - - //callid - CallIdHeader callIdHeader = null; - if(device.getTransport().equals("TCP")) { - callIdHeader = tcpSipProvider.getNewCallId(); - } - if(device.getTransport().equals("UDP")) { - callIdHeader = udpSipProvider.getNewCallId(); - } + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress()); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress,null); //Forwards - MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); + MaxForwardsHeader maxForwards = SipFactory.getInstance().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 = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INVITE); + request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); - Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort())); - // Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort())); - request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); - - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort())); + // Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort())); + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + // Subject + SubjectHeader subjectHeader = SipFactory.getInstance().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0)); + request.addHeader(subjectHeader); + + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); request.setContent(content, contentTypeHeader); return request; } - public Request createSubscribeRequest(Device device, String content, String viaTag, String fromTag, String toTag, Integer expires, String event) throws ParseException, InvalidArgumentException, PeerUnavailableException { + public Request createByteRequest(Device device, String channelId, SipTransactionInfo transactionInfo) throws ParseException, InvalidArgumentException, PeerUnavailableException { Request request = null; - // sipuri - SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); + //请求行 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress()); // via ArrayList viaHeaders = new ArrayList(); - ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), - device.getTransport(), viaTag); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag()); + viaHeaders.add(viaHeader); + //from + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag()); + //to + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId,device.getHostAddress()); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, transactionInfo.getToTag()); + + //Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + + //ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE); + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId()); + request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort())); + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + return request; + } + + 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.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); + // via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), + device.getTransport(), SipUtils.getNewViaTag()); viaHeader.setRPort(); viaHeaders.add(viaHeader); // from - SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), - sipConfig.getSipIp() + ":" + sipConfig.getSipPort()); - Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); - FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, requestOld == null ? SipUtils.getNewFromTag() :requestOld.getFromTag()); // to - SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getSipDomain()); - Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); - ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag); - // callid - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - // Forwards - MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); - // ceq - CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.SUBSCRIBE); + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, requestOld == null ? null :requestOld.getToTag()); - request = sipFactory.createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader, + // Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + + // ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.SUBSCRIBE); + + request = SipFactory.getInstance().createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards); - - Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort())); - request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); + + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort())); + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); // Expires - ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(expires); + ExpiresHeader expireHeader = SipFactory.getInstance().createHeaderFactory().createExpiresHeader(expires); request.addHeader(expireHeader); // Event - EventHeader eventHeader = sipFactory.createHeaderFactory().createEventHeader(event); + EventHeader eventHeader = SipFactory.getInstance().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 = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml"); request.setContent(content, contentTypeHeader); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + return request; + } + + 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 = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress()); + // via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag()); + viaHeaders.add(viaHeader); + //from + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag()); + //to + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId,device.getHostAddress()); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, transactionInfo.getToTag()); + + //Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + + //ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INFO); + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId()); + request = (SIPRequest)SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.INFO, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort())); + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + if (content != null) { + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", + "MANSRTSP"); + request.setContent(content, contentTypeHeader); + } + return request; + } + + public Request createAckRequest(String localIp, SipURI sipURI, SIPResponse sipResponse) throws ParseException, InvalidArgumentException, PeerUnavailableException { + + + // via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(localIp, sipConfig.getPort(), sipResponse.getTopmostViaHeader().getTransport(), SipUtils.getNewViaTag()); + viaHeaders.add(viaHeader); + + //Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + + //ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(sipResponse.getCSeqHeader().getSeqNumber(), Request.ACK); + + Request request = SipFactory.getInstance().createMessageFactory().createRequest(sipURI, Request.ACK, sipResponse.getCallIdHeader(), cSeqHeader, sipResponse.getFromHeader(), sipResponse.getToHeader(), viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), localIp + ":"+sipConfig.getPort())); + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + 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 6561763bf..782dc06a3 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,1243 +1,1427 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl; -import java.text.ParseException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.sip.*; -import javax.sip.address.SipURI; -import javax.sip.header.CallIdHeader; -import javax.sip.header.ViaHeader; -import javax.sip.message.Request; - -import com.alibaba.fastjson.JSONObject; +import com.genersoft.iot.vmp.common.InviteSessionType; import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.conf.MediaServerConfig; +import com.genersoft.iot.vmp.conf.SipConfig; +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.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; +import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; -import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; -import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +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.NumericUtil; +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.media.zlm.dto.hook.HookParam; +import com.genersoft.iot.vmp.service.IMediaServerService; +import com.genersoft.iot.vmp.service.bean.SSRCInfo; +import com.genersoft.iot.vmp.utils.DateUtil; +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider; -import com.genersoft.iot.vmp.gb28181.utils.DateUtil; -import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; -import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; +import javax.sip.InvalidArgumentException; +import javax.sip.ResponseEvent; +import javax.sip.SipException; +import javax.sip.SipFactory; +import javax.sip.header.CallIdHeader; +import javax.sip.message.Request; +import java.text.ParseException; -/** - * @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; - - @Autowired - private SIPRequestHeaderProvider headerProvider; - - @Autowired - private VideoStreamSessionManager streamSession; + private final Logger logger = LoggerFactory.getLogger(SIPCommander.class); - @Autowired - private IVideoManagerStorager storager; + @Autowired + private SipConfig sipConfig; - @Autowired - private IRedisCatchStorage redisCatchStorage; - - @Autowired - @Qualifier(value="tcpSipProvider") - private SipProvider tcpSipProvider; - - @Autowired - @Qualifier(value="udpSipProvider") - private SipProvider udpSipProvider; + @Autowired + private SipLayer sipLayer; - @Autowired - private ZLMRTPServerFactory zlmrtpServerFactory; + @Autowired + private SIPSender sipSender; + + @Autowired + private SIPRequestHeaderProvider headerProvider; - @Value("${media.rtp.enable}") - private boolean rtpEnable; + @Autowired + private VideoStreamSessionManager streamSession; - @Value("${media.seniorSdp}") - private boolean seniorSdp; + @Autowired + private UserSetting userSetting; - @Value("${media.autoApplyPlay}") - private boolean autoApplyPlay; - - @Autowired - private ZLMHttpHookSubscribe subscribe; - - @Autowired - private SipSubscribe sipSubscribe; + @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.getSpeed(), 0); - } + @Autowired + private IMediaServerService mediaServerService; - /** - * 云台方向放控制 - * - * @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); - } - /** - * 云台缩放控制,使用配置文件中的默认镜头缩放速度 - * - * @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.getSpeed()); - } + /** + * 云台方向放控制,使用配置文件中的默认镜头移动速度 + * + * @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 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:下移 + * @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 cmdCode 指令码 - * @param parameter1 数据1 - * @param parameter2 数据2 - * @param combineCode2 组合码2 - */ + /** + * 云台缩放控制,使用配置文件中的默认镜头缩放速度 + * + * @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()); - Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "ViaPtzBranch", "FromPtz" + tm, null); - - 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); - System.out.println("控制字符串:" + 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()); - Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "ViaPtzBranch", "FromPtz" + tm, null); - 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(Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent) { - try { - - String ssrc = streamSession.createPlaySsrc(); - String streamId = null; - if (rtpEnable) { - streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId); - }else { - streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); - } - String streamMode = device.getStreamMode().toUpperCase(); - MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); - if (mediaInfo == null) { - logger.warn("点播时发现ZLM尚未连接..."); - return; - } - String mediaPort = null; - // 使用动态udp端口 - if (rtpEnable) { - mediaPort = zlmrtpServerFactory.createRTPServer(streamId) + ""; - }else { - mediaPort = mediaInfo.getRtpProxyPort(); - } - - // 添加订阅 - JSONObject subscribeKey = new JSONObject(); - subscribeKey.put("app", "rtp"); - subscribeKey.put("id", streamId); - - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, subscribeKey, event); - // - StringBuffer content = new StringBuffer(200); - content.append("v=0\r\n"); -// content.append("o="+channelId+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n"); - content.append("o="+"00000"+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n"); - content.append("s=Play\r\n"); - content.append("c=IN IP4 "+mediaInfo.getWanIp()+"\r\n"); - content.append("t=0 0\r\n"); - - if (seniorSdp) { - if("TCP-PASSIVE".equals(streamMode)) { - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); - }else if("UDP".equals(streamMode)) { - content.append("m=video "+ mediaPort +" 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 "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n"); - }else if("UDP".equals(streamMode)) { - content.append("m=video "+ mediaPort +" 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=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主动模式 - } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 - content.append("a=setup:active\r\n"); - content.append("a=connection:new\r\n"); - } - } - } - - content.append("y="+ssrc+"\r\n");//ssrc - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "FromInvt" + tm, null, ssrc); - - ClientTransaction transaction = transmitRequest(device, request, errorEvent); - streamSession.put(streamId,ssrc, transaction); - - - - } 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(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event - , SipSubscribe.Event errorEvent) { - try { - MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); - String ssrc = streamSession.createPlayBackSsrc(); - String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); - // 添加订阅 - JSONObject subscribeKey = new JSONObject(); - subscribeKey.put("app", "rtp"); - subscribeKey.put("id", streamId); - - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, subscribeKey, event); - - StringBuffer content = new StringBuffer(200); - content.append("v=0\r\n"); - content.append("o="+sipConfig.getSipId()+" 0 0 IN IP4 "+sipConfig.getSipIp()+"\r\n"); - content.append("s=Playback\r\n"); - content.append("u="+channelId+":0\r\n"); - content.append("c=IN IP4 "+mediaInfo.getWanIp()+"\r\n"); - content.append("t="+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime)+" " - +DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) +"\r\n"); - String mediaPort = null; - // 使用动态udp端口 - if (rtpEnable) { - mediaPort = zlmrtpServerFactory.createRTPServer(streamId) + ""; - }else { - mediaPort = mediaInfo.getRtpProxyPort(); - } - String streamMode = device.getStreamMode().toUpperCase(); - - if (seniorSdp) { - if("TCP-PASSIVE".equals(streamMode)) { - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); - }else if("UDP".equals(streamMode)) { - content.append("m=video "+ mediaPort +" 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 "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n"); - }else if("UDP".equals(streamMode)) { - content.append("m=video "+ mediaPort +" 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="+ssrc+"\r\n");//ssrc - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null); - - ClientTransaction transaction = transmitRequest(device, request, errorEvent); - streamSession.put(streamId, ssrc, transaction); - - } catch ( SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - } - } - - - - /** - * 视频流停止 - * - */ - @Override - public void streamByeCmd(String ssrc) { - streamByeCmd(ssrc, null); - } - @Override - public void streamByeCmd(String streamId, SipSubscribe.Event okEvent) { - - try { - ClientTransaction transaction = streamSession.get(streamId); - // 服务重启后 - if (transaction == null) { - StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); - if (streamInfo != null) { - - } - return; - } - - Dialog dialog = transaction.getDialog(); - if (dialog == null) { - return; - } - Request byeRequest = dialog.createRequest(Request.BYE); - SipURI byeURI = (SipURI) byeRequest.getRequestURI(); - String vh = transaction.getRequest().getHeader(ViaHeader.NAME).toString(); - Pattern p = Pattern.compile("(\\d+\\.\\d+\\.\\d+\\.\\d+)\\:(\\d+)"); - Matcher matcher = p.matcher(vh); - if (matcher.find()) { - String ip = matcher.group(1); - byeURI.setHost(ip); - String port = matcher.group(2); - byeURI.setPort(Integer.parseInt(port)); - } - 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); - - streamSession.remove(streamId); - zlmrtpServerFactory.closeRTPServer(streamId); - } catch (TransactionDoesNotExistException e) { - e.printStackTrace(); - } catch (SipException e) { - e.printStackTrace(); - } catch (ParseException e) { - e.printStackTrace(); - } - } - - /** - * 语音广播 - * - * @param device 视频设备 - * @param channelId 预览通道 - */ - @Override - public boolean audioBroadcastCmd(Device device, String channelId) { - // TODO Auto-generated method stub - return false; - } - - /** - * 音视频录像控制 - * - * @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 (XmlUtil.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()); - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromRecord" + tm, null); - 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()); - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromBoot" + tm, null); - 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()); - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromGuard" + tm, null); - 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 (!XmlUtil.isEmpty(alarmMethod) || !XmlUtil.isEmpty(alarmType)) { - cmdXml.append("\r\n"); - } - if (!XmlUtil.isEmpty(alarmMethod)) { - cmdXml.append("" + alarmMethod + "\r\n"); - } - if (!XmlUtil.isEmpty(alarmType)) { - cmdXml.append("" + alarmType + "\r\n"); - } - if (!XmlUtil.isEmpty(alarmMethod) || !XmlUtil.isEmpty(alarmType)) { - cmdXml.append("\r\n"); - } - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromAlarm" + tm, null); - 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 (XmlUtil.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()); - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromBoot" + tm, null); - 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 (XmlUtil.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()); - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromGuard" + tm, null); - 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 (XmlUtil.isEmpty(channelId)) { - cmdXml.append("" + device.getDeviceId() + "\r\n"); - } else { - cmdXml.append("" + channelId + "\r\n"); - } - cmdXml.append("\r\n"); - if (!XmlUtil.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()); - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null); - 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()); - Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, "FromStatus" + tm, null); - - 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()); - Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "ViaDeviceInfoBranch", "FromDev" + tm, null); - - 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()); - Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "ViaCatalogBranch", "FromCat" + tm, null); - - 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) { - - try { - StringBuffer recordInfoXml = new StringBuffer(200); - recordInfoXml.append("\r\n"); - recordInfoXml.append("\r\n"); - recordInfoXml.append("RecordInfo\r\n"); - recordInfoXml.append("" + (int)((Math.random()*9+1)*100000) + "\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()); - Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(), "ViaRecordInfoBranch", "fromRec" + tm, null); - - transmitRequest(device, request); - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - return true; - } - - /** - * 查询报警信息 - * - * @param device 视频设备 - * @param startPriority 报警起始级别(可选) - * @param endPriority 报警终止级别(可选) - * @param alarmMethods 报警方式条件(可选) - * @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 (!XmlUtil.isEmpty(startPriority)) { - cmdXml.append("" + startPriority + "\r\n"); - } - if (!XmlUtil.isEmpty(endPriority)) { - cmdXml.append("" + endPriority + "\r\n"); - } - if (!XmlUtil.isEmpty(alarmMethod)) { - cmdXml.append("" + alarmMethod + "\r\n"); - } - if (!XmlUtil.isEmpty(alarmType)) { - cmdXml.append("" + alarmType + "\r\n"); - } - if (!XmlUtil.isEmpty(startTime)) { - cmdXml.append("" + startTime + "\r\n"); - } - if (!XmlUtil.isEmpty(endTime)) { - cmdXml.append("" + endTime + "\r\n"); - } - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromAlarm" + tm, null); - 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 (XmlUtil.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()); - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null); - 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 (XmlUtil.isEmpty(channelId)) { - cmdXml.append("" + device.getDeviceId() + "\r\n"); - } else { - cmdXml.append("" + channelId + "\r\n"); - } - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null); - 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()); - Request request = headerProvider.createMessageRequest(device, mobilePostitionXml.toString(), "viaTagPos" + tm, "fromTagPos" + tm, null); - - 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()); - Request request = headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), "viaTagPos" + tm, "fromTagPos" + tm, null, expires, "presence" ); //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 (!XmlUtil.isEmpty(startPriority)) { - cmdXml.append("" + startPriority + "\r\n"); - } - if (!XmlUtil.isEmpty(endPriority)) { - cmdXml.append("" + endPriority + "\r\n"); - } - if (!XmlUtil.isEmpty(alarmMethod)) { - cmdXml.append("" + alarmMethod + "\r\n"); - } - if (!XmlUtil.isEmpty(alarmType)) { - cmdXml.append("" + alarmType + "\r\n"); - } - if (!XmlUtil.isEmpty(startTime)) { - cmdXml.append("" + startTime + "\r\n"); - } - if (!XmlUtil.isEmpty(endTime)) { - cmdXml.append("" + endTime + "\r\n"); - } - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), "viaTagPos" + tm, "fromTagPos" + tm, null, expires, "presence" ); - transmitRequest(device, request); - - 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(), errorEvent); - } - // 添加订阅 - if (okEvent != null) { - sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), okEvent); - } - - clientTransaction.sendRequest(); - return clientTransaction; - } - - - - - @Override - public void closeRTPServer(Device device, String channelId) { - if (rtpEnable) { - String streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId); - zlmrtpServerFactory.closeRTPServer(streamId); - } - } + 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); + //优化zoom变倍速率 + if ((combineCode2 > 0) && (combineCode2 <16)) + { + combineCode2 = 16; + } + 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.getSdpIp(), ssrcInfo.getPort()); + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId()); + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, HookParam hookParam) -> { + if (event != null) { + event.response(mediaServerItemInUse, hookParam); + 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"); + } + } + + if( device.isSwitchPrimarySubStream() ){ + if("TP-LINK".equals(device.getManufacturer())){ + if (device.isSwitchPrimarySubStream()){ + content.append("a=streamMode:sub\r\n"); + }else { + content.append("a=streamMode:main\r\n"); + } + }else { + if (device.isSwitchPrimarySubStream()){ + content.append("a=streamprofile:1\r\n"); + }else { + content.append("a=streamprofile:0\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 -> { + ResponseEvent responseEvent = (ResponseEvent) e.event; + SIPResponse response = (SIPResponse) responseEvent.getResponse(); + streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, + InviteSessionType.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, ZlmHttpHookSubscribe.Event hookEvent, + SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + + logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getSdpIp(), 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, HookParam hookParam) -> { + if (hookEvent != null) { + hookEvent.response(mediaServerItemInUse, hookParam); + } + subscribe.removeSubscribe(hookSubscribe); + }); + Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()), ssrcInfo.getSsrc()); + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> { + ResponseEvent responseEvent = (ResponseEvent) event.event; + SIPResponse response = (SIPResponse) responseEvent.getResponse(); + streamSession.put(device.getDeviceId(), channelId,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAYBACK); + okEvent.response(event); + }); + } + + /** + * 请求历史媒体下载 + * + * @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, + ZlmHttpHookSubscribe.Event hookEvent, + SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException { + + logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getSdpIp(), 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 + logger.debug("此时请求下载信令的ssrc===>{}",ssrcInfo.getSsrc()); + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); + // 添加订阅 + CallIdHeader newCallIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport()); + String callId= newCallIdHeader.getCallId(); + subscribe.addSubscribe(hookSubscribe, (mediaServerItemInUse, hookParam) -> { + logger.debug("sipc 添加订阅===callId {}",callId); + hookEvent.response(mediaServerItemInUse, hookParam); + subscribe.removeSubscribe(hookSubscribe); + hookSubscribe.getContent().put("regist", false); + hookSubscribe.getContent().put("schema", "rtsp"); + // 添加流注销的订阅,注销了后向设备发送bye + subscribe.addSubscribe(hookSubscribe, + (mediaServerItemForEnd, hookParam1) -> { + logger.info("[录像]下载结束, 发送BYE"); + try { + streamByeCmd(device, channelId, ssrcInfo.getStream(), callId); + } catch (InvalidArgumentException | ParseException | SipException | + SsrcTransactionNotFoundException e) { + logger.error("[录像]下载结束, 发送BYE失败 {}", e.getMessage()); + } + }); + }); + + Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,newCallIdHeader, ssrcInfo.getSsrc()); + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> { + ResponseEvent responseEvent = (ResponseEvent) event.event; + SIPResponse response = (SIPResponse) responseEvent.getResponse(); + String contentString =new String(response.getRawContent()); + String ssrc = SipUtils.getSsrcFromSdp(contentString); + streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrc, mediaServerItem.getId(), response, InviteSessionType.DOWNLOAD); + okEvent.response(event); + }); + } + + /** + * 视频流停止, 不使用回调 + */ + @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, SipSubscribe.Event okEvent) 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,okEvent); + } + + /** + * 远程启动控制命令 + * + * @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, SipSubscribe.Event okEvent) 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,okEvent); + } + + /** + * 报警复位命令 + * + * @param device 视频设备 + */ + @Override + public void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) 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,okEvent); + } + + /** + * 强制关键帧命令,设备收到此命令应立刻发送一个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 channelId 通道id,非通道则是设备本身 + * @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,SipSubscribe.Event okEvent) 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,okEvent); + } + + /** + * 设备配置命令 + * + * @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 = SipFactory.getInstance().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 = SipFactory.getInstance().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("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(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 new file mode 100644 index 000000000..1d4b23ef3 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java @@ -0,0 +1,831 @@ +package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl; + +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.ZLMServerFactory; +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 com.genersoft.iot.vmp.utils.GitUtil; +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.context.annotation.DependsOn; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import javax.sip.SipFactory; +import javax.sip.header.CallIdHeader; +import javax.sip.header.WWWAuthenticateHeader; +import javax.sip.message.Request; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; + +@Component +@DependsOn("sipLayer") +public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { + + private final Logger logger = LoggerFactory.getLogger(SIPCommanderFroPlatform.class); + + @Autowired + private SIPRequestHeaderPlarformProvider headerProviderPlatformProvider; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private SipSubscribe sipSubscribe; + + @Autowired + private ZLMServerFactory zlmServerFactory; + + @Autowired + private SipLayer sipLayer; + + @Autowired + private SIPSender sipSender; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private GitUtil gitUtil; + + @Override + public void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException { + register(parentPlatform, null, null, errorEvent, okEvent, true); + } + + @Override + public void register(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException { + + register(parentPlatform, sipTransactionInfo, null, errorEvent, okEvent, true); + } + + @Override + public void unregister(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException { + register(parentPlatform, sipTransactionInfo, null, errorEvent, okEvent, false); + } + + @Override + public void register(ParentPlatform parentPlatform, @Nullable SipTransactionInfo sipTransactionInfo, @Nullable WWWAuthenticateHeader www, + SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean isRegister) throws SipException, InvalidArgumentException, ParseException { + Request request; + + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); + String fromTag = SipUtils.getNewFromTag(); + String toTag = null; + if (sipTransactionInfo != null ) { + if (sipTransactionInfo.getCallId() != null) { + callIdHeader.setCallId(sipTransactionInfo.getCallId()); + } + if (sipTransactionInfo.getFromTag() != null) { + fromTag = sipTransactionInfo.getFromTag(); + } + if (sipTransactionInfo.getToTag() != null) { + toTag = sipTransactionInfo.getToTag(); + } + } + + if (www == null ) { + request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform, + redisCatchStorage.getCSEQ(), fromTag, + toTag, callIdHeader, isRegister? parentPlatform.getExpires() : 0); + // 将 callid 写入缓存, 等注册成功可以更新状态 + String callIdFromHeader = callIdHeader.getCallId(); + redisCatchStorage.updatePlatformRegisterInfo(callIdFromHeader, PlatformRegisterInfo.getInstance(parentPlatform.getServerGBId(), isRegister)); + + sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (event)->{ + if (event != null) { + logger.info("向上级平台 [ {} ] 注册发生错误: {} ", + parentPlatform.getServerGBId(), + event.msg); + } + redisCatchStorage.delPlatformRegisterInfo(callIdFromHeader); + if (errorEvent != null ) { + errorEvent.response(event); + } + }); + + }else { + request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform, fromTag, toTag, www, callIdHeader, isRegister? parentPlatform.getExpires() : 0); + } + + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, null, okEvent); + } + + @Override + 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") + .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 = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); + + Request request = headerProviderPlatformProvider.createMessageRequest( + parentPlatform, + keepaliveXml.toString(), + SipUtils.getNewFromTag(), + SipUtils.getNewViaTag(), + callIdHeader); + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, errorEvent, okEvent); + return callIdHeader.getCallId(); + } + + /** + * 向上级回复通道信息 + * @param channel 通道信息 + * @param parentPlatform 平台信息 + */ + @Override + public void catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size) throws SipException, InvalidArgumentException, ParseException { + + if ( parentPlatform ==null) { + return ; + } + 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"); + if (channel.getChannelId().length() <= 8) { + catalogXml.append("\r\n"); + continue; + }else { + if (channel.getChannelId().length() != 20) { + catalogXml.append("\r\n"); + logger.warn("[编号长度异常] {} 长度错误,请使用20位长度的国标编号,当前长度:{}", channel.getChannelId(), channel.getChannelId().length()); + catalogXml.append("\r\n"); + continue; + } + switch (Integer.parseInt(channel.getChannelId().substring(10, 13))){ + case 200: +// catalogXml.append("三永华通\r\n"); +// GitUtil gitUtil = SpringBeanFactory.getBean("gitUtil"); +// String model = (gitUtil == null || gitUtil.getBuildVersion() == null)?"1.0": gitUtil.getBuildVersion(); +// catalogXml.append("" + model + "\r\n"); +// catalogXml.append("三永华通\r\n"); + if (channel.getCivilCode() != null) { + catalogXml.append(""+channel.getCivilCode()+"\r\n"); + }else { + catalogXml.append("\r\n"); + } + + catalogXml.append("1\r\n"); + catalogXml.append("0\r\n"); + break; + case 215: + if (!ObjectUtils.isEmpty(channel.getParentId())) { + catalogXml.append("" + channel.getParentId() + "\r\n"); + } + + break; + case 216: + if (!ObjectUtils.isEmpty(channel.getParentId())) { + catalogXml.append("" + channel.getParentId() + "\r\n"); + }else { + catalogXml.append("\r\n"); + } + if (!ObjectUtils.isEmpty(channel.getBusinessGroupId())) { + catalogXml.append("" + channel.getBusinessGroupId() + "\r\n"); + }else { + catalogXml.append("\r\n"); + } + break; + default: + // 通道项 + if (channel.getManufacture() != null) { + catalogXml.append("" + channel.getManufacture() + "\r\n"); + }else { + catalogXml.append("\r\n"); + } + if (channel.getSecrecy() != null) { + catalogXml.append("" + channel.getSecrecy() + "\r\n"); + }else { + catalogXml.append("\r\n"); + } + catalogXml.append("" + channel.getRegisterWay() + "\r\n"); + if (channel.getModel() != null) { + catalogXml.append("" + channel.getModel() + "\r\n"); + }else { + catalogXml.append("\r\n"); + } + if (channel.getOwner() != null) { + catalogXml.append("" + channel.getOwner()+ "\r\n"); + }else { + catalogXml.append("\r\n"); + } + if (channel.getCivilCode() != null) { + catalogXml.append("" + channel.getCivilCode() + "\r\n"); + }else { + catalogXml.append("\r\n"); + } + if (channel.getAddress() == null) { + catalogXml.append("
    \r\n"); + }else { + catalogXml.append("
    " + channel.getAddress() + "
    \r\n"); + } + if (!ObjectUtils.isEmpty(channel.getParentId())) { + catalogXml.append("" + channel.getParentId() + "\r\n"); + }else { + catalogXml.append("\r\n"); + } + if (!ObjectUtils.isEmpty(channel.getBlock())) { + catalogXml.append("" + channel.getBlock() + "\r\n"); + }else { + catalogXml.append("\r\n"); + } + if (!ObjectUtils.isEmpty(channel.getSafetyWay())) { + catalogXml.append("" + channel.getSafetyWay() + "\r\n"); + }else { + catalogXml.append("\r\n"); + } + if (!ObjectUtils.isEmpty(channel.getCertNum())) { + catalogXml.append("" + channel.getCertNum() + "\r\n"); + }else { + catalogXml.append("\r\n"); + } + if (!ObjectUtils.isEmpty(channel.getCertifiable())) { + catalogXml.append("" + channel.getCertifiable() + "\r\n"); + }else { + catalogXml.append("\r\n"); + } + if (!ObjectUtils.isEmpty(channel.getErrCode())) { + catalogXml.append("" + channel.getErrCode() + "\r\n"); + }else { + catalogXml.append("\r\n"); + } + if (!ObjectUtils.isEmpty(channel.getEndTime())) { + catalogXml.append("" + channel.getEndTime() + "\r\n"); + }else { + catalogXml.append("\r\n"); + } + if (!ObjectUtils.isEmpty(channel.getSecrecy())) { + catalogXml.append("" + channel.getSecrecy() + "\r\n"); + }else { + catalogXml.append("\r\n"); + } + if (!ObjectUtils.isEmpty(channel.getIpAddress())) { + catalogXml.append("" + channel.getIpAddress() + "\r\n"); + }else { + catalogXml.append("\r\n"); + } + catalogXml.append("" + channel.getPort() + "\r\n"); + if (!ObjectUtils.isEmpty(channel.getPassword())) { + catalogXml.append("" + channel.getPassword() + "\r\n"); + }else { + catalogXml.append("\r\n"); + } + if (!ObjectUtils.isEmpty(channel.getPTZType())) { + catalogXml.append("" + channel.getPTZType() + "\r\n"); + }else { + catalogXml.append("\r\n"); + } + catalogXml.append("" + (channel.isStatus() ?"ON":"OFF") + "\r\n"); + + catalogXml.append("" + + (channel.getLongitudeWgs84() != 0? channel.getLongitudeWgs84():channel.getLongitude()) + + "\r\n"); + catalogXml.append("" + + (channel.getLatitudeWgs84() != 0? channel.getLatitudeWgs84():channel.getLatitude()) + + "\r\n"); + break; + + } + catalogXml.append("\r\n"); + } + } + } + + 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(); + + logger.info("[命令发送] 国标级联{} 目录查询回复: 共{}条,已发送{}条", parentPlatform.getServerGBId(), + channels.size(), Math.min(index + parentPlatform.getCatalogGroup(), channels.size())); + logger.debug(catalogXml); + if (sendAfterResponse) { + // 默认按照收到200回复后发送下一条, 如果超时收不到回复,就以30毫秒的间隔直接发送。 + dynamicTask.startDelay(timeoutTaskKey, ()->{ + sipSubscribe.removeOkSubscribe(callId); + int indexNext = index + parentPlatform.getCatalogGroup(); + try { + sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, false); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage()); + } + }, 3000); + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, eventResult -> { + logger.error("[目录推送失败] 国标级联 platform : {}, code: {}, msg: {}, 停止发送", parentPlatform.getServerGBId(), eventResult.statusCode, eventResult.msg); + 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); + } + } + + /** + * 向上级回复DeviceInfo查询信息 + * @param parentPlatform 平台信息 + * @param sn + * @param fromTag + * @return + */ + @Override + public void deviceInfoResponse(ParentPlatform parentPlatform,Device device, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException { + if (parentPlatform == null) { + return; + } + String deviceId = device == null ? parentPlatform.getDeviceGBId() : device.getDeviceId(); + String deviceName = device == null ? parentPlatform.getName() : device.getName(); + String manufacturer = device == null ? "WVP-28181-PRO" : device.getManufacturer(); + String model = device == null ? "platform" : device.getModel(); + String firmware = device == null ? gitUtil.getBuildVersion() : device.getFirmware(); + 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("" + deviceId + "\r\n"); + deviceInfoXml.append("" + deviceName + "\r\n"); + deviceInfoXml.append("" + manufacturer + "\r\n"); + deviceInfoXml.append("" + model + "\r\n"); + deviceInfoXml.append("" + firmware + "\r\n"); + deviceInfoXml.append("OK\r\n"); + deviceInfoXml.append("\r\n"); + + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); + + Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, deviceInfoXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader); + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request); + } + + /** + * 向上级回复DeviceStatus查询信息 + * @param parentPlatform 平台信息 + * @param sn + * @param fromTag + * @return + */ + @Override + public void deviceStatusResponse(ParentPlatform parentPlatform,String channelId, String sn, String fromTag,boolean status) throws SipException, InvalidArgumentException, ParseException { + if (parentPlatform == null) { + return ; + } + String statusStr = (status)?"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 = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); + + Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, deviceStatusXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader); + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request); + } + + @Override + public void sendNotifyMobilePosition(ParentPlatform parentPlatform, GPSMsgInfo gpsMsgInfo, SubscribeInfo subscribeInfo) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException { + if (parentPlatform == null) { + return; + } + 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("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(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) SipFactory.getInstance().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.isStatus() ? "ON" : "OFF") + "\r\n"); + + if (channel.getChannelType() != 2) { // 业务分组/虚拟组织/行政区划 不设置以下属性 + catalogXml.append("" + channel.getModel() + "\r\n") + .append(" " + channel.getOwner()+ "\r\n") + .append("" + channel.getCivilCode() + "\r\n") + .append("
    " + channel.getAddress() + "
    \r\n"); + } + 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()); + zlmServerFactory.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/ISIPRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/ISIPRequestProcessor.java new file mode 100644 index 000000000..8e79941f9 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/ISIPRequestProcessor.java @@ -0,0 +1,14 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request; + +import javax.sip.RequestEvent; + +/** + * @description: 对SIP事件进行处理,包括request, response, timeout, ioException, transactionTerminated,dialogTerminated + * @author: panlinlin + * @date: 2021年11月5日 15:47 + */ +public interface ISIPRequestProcessor { + + void process(RequestEvent event); + +} 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 new file mode 100644 index 000000000..d52f4f6a1 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java @@ -0,0 +1,223 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request; + +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.message.SIPResponse; +import org.apache.commons.lang3.ArrayUtils; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.sip.*; +import javax.sip.address.Address; +import javax.sip.address.SipURI; +import javax.sip.header.ContentTypeHeader; +import javax.sip.header.ExpiresHeader; +import javax.sip.header.HeaderFactory; +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协议请求消息 + * @author: songww + * @date: 2020年5月3日 下午4:42:22 + */ +public abstract class SIPRequestProcessorParent { + + private final static Logger logger = LoggerFactory.getLogger(SIPRequestProcessorParent.class); + + @Autowired + private SIPSender sipSender; + + public HeaderFactory getHeaderFactory() { + try { + return SipFactory.getInstance().createHeaderFactory(); + } catch (PeerUnavailableException e) { + logger.error("未处理的异常 ", e); + } + return null; + } + + public MessageFactory getMessageFactory() { + try { + return SipFactory.getInstance().createMessageFactory(); + } catch (PeerUnavailableException e) { + logger.error("未处理的异常 ", e); + } + return null; + } + + class ResponseAckExtraParam{ + String content; + ContentTypeHeader contentTypeHeader; + SipURI sipURI; + int expires = -1; + } + + /*** + * 回复状态码 + * 100 trying + * 200 OK + * 400 + * 404 + */ + public SIPResponse responseAck(SIPRequest sipRequest, int statusCode) throws SipException, InvalidArgumentException, ParseException { + return responseAck(sipRequest, statusCode, null); + } + + 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 + */ + public SIPResponse responseSdpAck(SIPRequest request, String sdp, ParentPlatform platform) throws SipException, InvalidArgumentException, ParseException { + + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); + + // 兼容国标中的使用编码@域名作为RequestURI的情况 + SipURI sipURI = (SipURI)request.getRequestURI(); + if (sipURI.getPort() == -1) { + sipURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerIP()+":"+platform.getServerPort()); + } + 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"; + } + Request request = evt.getRequest(); + SAXReader reader = new SAXReader(); + reader.setEncoding(charset); + // 对海康出现的未转义字符做处理。 + 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; + try { + xml = reader.read(new ByteArrayInputStream(bytesResult)); + }catch (DocumentException e) { + logger.warn("[xml解析异常]: 愿文如下: \r\n{}", new String(bytesResult)); + logger.warn("[xml解析异常]: 愿文如下: 尝试兼容性处理"); + String[] xmlLineArray = new String(bytesResult).split("\\r?\\n"); + + // 兼容海康的address字段带有<破换xml结构导致无法解析xml的问题 + StringBuilder stringBuilder = new StringBuilder(); + for (String s : xmlLineArray) { + if (s.startsWith("{}", 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 = zlmServerFactory.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")); + if (sendRtpItem.getPlayType() == InviteStreamType.PUSH) { + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, sendRtpItem.getApp(), sendRtpItem.getStreamId(), + sendRtpItem.getChannelId(), parentPlatform.getServerGBId(), parentPlatform.getName(), userSetting.getServerId(), + sendRtpItem.getMediaServerId()); + messageForPushChannel.setPlatFormIndex(parentPlatform.getId()); + redisCatchStorage.sendPlatformStartPlayMsg(messageForPushChannel); + } + } else { + logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"), JSON.toJSONString(param)); + if (sendRtpItem.isOnlyAudio()) { + // TODO 可能是语音对讲 + }else { + // 向上级平台 + try { + 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 new file mode 100644 index 000000000..374e5dcc0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java @@ -0,0 +1,184 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; + +import com.genersoft.iot.vmp.common.InviteInfo; +import com.genersoft.iot.vmp.common.InviteSessionType; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; +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.ZLMServerFactory; +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.service.*; +import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; +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.CallIdHeader; +import javax.sip.message.Response; +import java.text.ParseException; +import java.util.HashMap; +import java.util.Map; + +/** + * SIP命令类型: BYE请求 + */ +@Component +public class ByeRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { + + private final Logger logger = LoggerFactory.getLogger(ByeRequestProcessor.class); + private final String method = "BYE"; + + @Autowired + private ISIPCommander cmder; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IInviteStreamService inviteStreamService; + + @Autowired + private IPlatformService platformService; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private IDeviceChannelService channelService; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private ZLMServerFactory zlmServerFactory; + + @Autowired + private SSRCFactory ssrcFactory; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Autowired + private VideoStreamSessionManager streamSession; + + @Autowired + private UserSetting userSetting; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addRequestProcessor(method, this); + } + + /** + * 处理BYE请求 + * @param evt + */ + @Override + public void process(RequestEvent evt) { + SIPRequest request = (SIPRequest) evt.getRequest(); + try { + responseAck(request, Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[回复BYE信息失败],{}", e.getMessage()); + } + CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME); + + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, null, callIdHeader.getCallId()); + + if (sendRtpItem != null){ + logger.info("[收到bye] 来自平台{}, 停止通道:{}", sendRtpItem.getPlatformId(), sendRtpItem.getChannelId()); + 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(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(), + callIdHeader.getCallId(), null); + zlmServerFactory.stopSendRtpStream(mediaInfo, param); + if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) { + ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId()); + if (platform != null) { + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, + sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getChannelId(), + sendRtpItem.getPlatformId(), platform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId()); + messageForPushChannel.setPlatFormIndex(platform.getId()); + redisCatchStorage.sendPlatformStopPlayMsg(messageForPushChannel); + }else { + logger.info("[上级平台停止观看] 未找到平台{}的信息,发送redis消息失败", sendRtpItem.getPlatformId()); + } + } + + int totalReaderCount = zlmServerFactory.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(), sendRtpItem.getChannelId()); + cmder.streamByeCmd(device, sendRtpItem.getChannelId(), streamId, null); + } catch (InvalidArgumentException | ParseException | SipException | + SsrcTransactionNotFoundException e) { + logger.error("[收到bye] {} 无其它观看者,通知设备停止推流, 发送BYE失败 {}",streamId, e.getMessage()); + } + } + } + }else { + + // 可能是设备发送的停止 + SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(null, null, callIdHeader.getCallId(), null); + if (ssrcTransaction == null) { + return; + } + logger.info("[收到bye] 来自设备:{}, 通道已停止推流: {}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); + + Device device = deviceService.getDevice(ssrcTransaction.getDeviceId()); + if (device == null) { + logger.info("[收到bye] 未找到设备:{} ", ssrcTransaction.getDeviceId()); + return; + } + DeviceChannel channel = channelService.getOne(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); + if (channel == null) { + logger.info("[收到bye] 未找到通道,设备:{}, 通道:{}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); + return; + } + storager.stopPlay(device.getDeviceId(), channel.getChannelId()); + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId()); + if (inviteInfo != null) { + inviteStreamService.removeInviteInfo(inviteInfo); + if (inviteInfo.getStreamInfo() != null) { + mediaServerService.closeRTPServer(inviteInfo.getStreamInfo().getMediaServerId(), inviteInfo.getStreamInfo().getStream()); + } + } + // 释放ssrc + MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransaction.getMediaServerId()); + if (mediaServerItem != null) { + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransaction.getSsrc()); + } + streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcTransaction.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 new file mode 100644 index 000000000..b04352a62 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/CancelRequestProcessor.java @@ -0,0 +1,40 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; + +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 org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.RequestEvent; + +/** + * SIP命令类型: CANCEL请求 + */ +@Component +public class CancelRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { + + private final String method = "CANCEL"; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addRequestProcessor(method, this); + } + + /** + * 处理CANCEL请求 + * + * @param evt 事件 + */ + @Override + public void process(RequestEvent evt) { + // TODO 优先级99 Cancel Request消息实现,此消息一般为级联消息,上级给下级发送请求取消指令 + + } + +} 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 new file mode 100644 index 000000000..c0a1fec9c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java @@ -0,0 +1,944 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; + +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.DynamicTask; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; +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.ZLMServerFactory; +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; +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.IPlayService; +import com.genersoft.iot.vmp.service.IStreamProxyService; +import com.genersoft.iot.vmp.service.IStreamPushService; +import com.genersoft.iot.vmp.service.bean.ErrorCallback; +import com.genersoft.iot.vmp.service.bean.InviteErrorCode; +import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; +import com.genersoft.iot.vmp.service.bean.SSRCInfo; +import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener; +import com.genersoft.iot.vmp.service.redisMsg.RedisPushStreamResponseListener; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.utils.DateUtil; +import gov.nist.javax.sdp.TimeDescriptionImpl; +import gov.nist.javax.sdp.fields.TimeField; +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; +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.sdp.*; +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.time.Instant; +import java.util.Random; +import java.util.Vector; + +/** + * SIP命令类型: INVITE请求 + */ +@SuppressWarnings("rawtypes") +@Component +public class InviteRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { + + private final static Logger logger = LoggerFactory.getLogger(InviteRequestProcessor.class); + + private final String method = "INVITE"; + + @Autowired + private SIPCommanderFroPlatform cmderFroPlatform; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IStreamPushService streamPushService; + @Autowired + private IStreamProxyService streamProxyService; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private SSRCFactory ssrcFactory; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private RedisPushStreamResponseListener redisPushStreamResponseListener; + + @Autowired + private IPlayService playService; + + @Autowired + private SIPSender sipSender; + + @Autowired + private ZLMServerFactory zlmServerFactory; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private ZlmHttpHookSubscribe zlmHttpHookSubscribe; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Autowired + private UserSetting userSetting; + + @Autowired + private ZLMMediaListManager mediaListManager; + + + @Autowired + private RedisGbPlayMsgListener redisGbPlayMsgListener; + + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addRequestProcessor(method, this); + } + + /** + * 处理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; + } + + + // 查询请求是否来自上级平台\设备 + ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId); + if (platform == null) { + inviteFromDeviceHandle(request, requesterId); + + } else { + // 查询平台下是否有该通道 + DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId); + GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId); + PlatformCatalog catalog = storager.getCatalog(channelId); + + 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) { + + String mediaServerId = gbStream.getMediaServerId(); + mediaServerItem = mediaServerService.getOne(mediaServerId); + if (mediaServerItem == null) { + if ("proxy".equals(gbStream.getStreamType())) { + logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId); + try { + responseAck(request, Response.GONE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite GONE: {}", e.getMessage()); + } + return; + } else { + streamPushItem = streamPushService.getPush(gbStream.getApp(), gbStream.getStream()); + if (streamPushItem != null) { + mediaServerItem = mediaServerService.getOne(streamPushItem.getMediaServerId()); + } + if (mediaServerItem == null) { + mediaServerItem = mediaServerService.getDefaultMediaServer(); + } + } + } else { + if ("push".equals(gbStream.getStreamType())) { + streamPushItem = streamPushService.getPush(gbStream.getApp(), gbStream.getStream()); + if (streamPushItem == null) { + logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId); + try { + responseAck(request, Response.GONE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite GONE: {}", e.getMessage()); + } + return; + } + }else if("proxy".equals(gbStream.getStreamType())){ + proxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(gbStream.getApp(), gbStream.getStream()); + if (proxyByAppAndStream == null) { + logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId); + try { + responseAck(request, Response.GONE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite GONE: {}", e.getMessage()); + } + return; + } + } + } + try { + responseAck(request, Response.CALL_IS_BEING_FORWARDED); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite CALL_IS_BEING_FORWARDED: {}", e.getMessage()); + } + } else if (catalog != null) { + try { + // 目录不支持点播 + responseAck(request, Response.BAD_REQUEST, "catalog channel can not play"); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 目录不支持点播: {}", e.getMessage()); + } + return; + } else { + logger.info("通道不存在,返回404: {}", channelId); + 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()); + + Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); + SessionDescription sdp = gb28181Sdp.getBaseSdb(); + String sessionName = sdp.getSessionName().getValue(); + + Long startTime = null; + Long stopTime = null; + Instant start = null; + Instant end = null; + if (sdp.getTimeDescriptions(false) != null && sdp.getTimeDescriptions(false).size() > 0) { + TimeDescriptionImpl timeDescription = (TimeDescriptionImpl) (sdp.getTimeDescriptions(false).get(0)); + TimeField startTimeFiled = (TimeField) timeDescription.getTime(); + startTime = startTimeFiled.getStartTime(); + stopTime = startTimeFiled.getStopTime(); + + start = Instant.ofEpochSecond(startTime); + end = Instant.ofEpochSecond(stopTime); + } + // 获取支持的格式 + Vector mediaDescriptions = sdp.getMediaDescriptions(true); + // 查看是否支持PS 负载96 + //String ip = null; + int port = -1; + boolean mediaTransmissionTCP = false; + Boolean tcpActive = null; + for (Object description : mediaDescriptions) { + MediaDescription mediaDescription = (MediaDescription) description; + Media media = mediaDescription.getMedia(); + + Vector mediaFormats = media.getMediaFormats(false); + if (mediaFormats.contains("96")) { + port = media.getMediaPort(); + //String mediaType = media.getMediaType(); + String protocol = media.getProtocol(); + + // 区分TCP发流还是udp, 当前默认udp + if ("TCP/RTP/AVP".equalsIgnoreCase(protocol)) { + String setup = mediaDescription.getAttribute("setup"); + if (setup != null) { + mediaTransmissionTCP = true; + if ("active".equalsIgnoreCase(setup)) { + tcpActive = true; + } else if ("passive".equalsIgnoreCase(setup)) { + tcpActive = false; + } + } + } + break; + } + } + if (port == -1) { + logger.info("不支持的媒体格式,返回415"); + // 回复不支持的格式 + try { + // 不支持的格式,发415 + responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 不支持的格式: {}", e.getMessage()); + } + return; + } + String username = sdp.getOrigin().getUsername(); + String addressStr = sdp.getConnection().getAddress(); + + + 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; + } + + String ssrc; + if (userSetting.getUseCustomSsrcForParentInvite() || gb28181Sdp.getSsrc() == null) { + // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式 + ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId()); + }else { + ssrc = gb28181Sdp.getSsrc(); + } + String streamTypeStr = null; + if (mediaTransmissionTCP) { + if (tcpActive) { + streamTypeStr = "TCP-ACTIVE"; + }else { + streamTypeStr = "TCP-PASSIVE"; + } + }else { + streamTypeStr = "UDP"; + } + logger.info("[上级Invite] {}, 平台:{}, 通道:{}, 收流地址:{}:{},收流方式:{}, ssrc:{}", sessionName, username, channelId, addressStr, port, streamTypeStr, ssrc); + SendRtpItem sendRtpItem = zlmServerFactory.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; + ErrorCallback hookEvent = (code, msg, data) -> { + StreamInfo streamInfo = (StreamInfo)data; + MediaServerItem mediaServerItemInUSe = mediaServerService.getOne(streamInfo.getMediaServerId()); + logger.info("[上级Invite]下级已经开始推流。 回复200OK(SDP), {}/{}", streamInfo.getApp(), streamInfo.getStream()); + // * 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 | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 回复SdpAck", e); + } + }; + ErrorCallback errorEvent = ((statusCode, msg, data) -> { + // 未知错误。直接转发设备点播的错误 + try { + if (statusCode > 0) { + Response response = getMessageFactory().createResponse(statusCode, evt.getRequest()); + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + } + } catch (ParseException | SipException e) { + logger.error("未处理的异常 ", e); + } + }); + sendRtpItem.setApp("rtp"); + if ("Playback".equalsIgnoreCase(sessionName)) { + sendRtpItem.setPlayType(InviteStreamType.PLAYBACK); + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, null, device.isSsrcCheck(), true, 0, false, device.getStreamModeForParam()); + sendRtpItem.setStreamId(ssrcInfo.getStream()); + // 写入redis, 超时时回复 + redisCatchStorage.updateSendRTPSever(sendRtpItem); + playService.playBack(mediaServerItem, ssrcInfo, device.getDeviceId(), channelId, DateUtil.formatter.format(start), + DateUtil.formatter.format(end), + (code, msg, data) -> { + if (code == InviteErrorCode.SUCCESS.getCode()){ + hookEvent.run(code, msg, data); + }else if (code == InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode() || code == InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode()){ + logger.info("[录像回放]超时, 用户:{}, 通道:{}", username, channelId); + redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null); + errorEvent.run(code, msg, data); + }else { + errorEvent.run(code, msg, data); + } + }); + }else if ("Download".equalsIgnoreCase(sessionName)) { + // 获取指定的下载速度 + Vector sdpMediaDescriptions = sdp.getMediaDescriptions(true); + MediaDescription mediaDescription = null; + String downloadSpeed = "1"; + if (sdpMediaDescriptions.size() > 0) { + mediaDescription = (MediaDescription)sdpMediaDescriptions.get(0); + } + if (mediaDescription != null) { + downloadSpeed = mediaDescription.getAttribute("downloadspeed"); + } + + sendRtpItem.setPlayType(InviteStreamType.DOWNLOAD); + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, null, device.isSsrcCheck(), true, 0, false, device.getStreamModeForParam()); + sendRtpItem.setStreamId(ssrcInfo.getStream()); + // 写入redis, 超时时回复 + redisCatchStorage.updateSendRTPSever(sendRtpItem); + playService.download(mediaServerItem, ssrcInfo, device.getDeviceId(), channelId, DateUtil.formatter.format(start), + DateUtil.formatter.format(end), Integer.parseInt(downloadSpeed), + (code, msg, data) -> { + if (code == InviteErrorCode.SUCCESS.getCode()){ + hookEvent.run(code, msg, data); + }else if (code == InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode() || code == InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode()){ + logger.info("[录像下载]超时, 用户:{}, 通道:{}", username, channelId); + redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null); + errorEvent.run(code, msg, data); + }else { + errorEvent.run(code, msg, data); + } + }); + }else { + sendRtpItem.setPlayType(InviteStreamType.PLAY); + String streamId = null; + if (mediaServerItem.isRtpEnable()) { + streamId = String.format("%s_%s", device.getDeviceId(), channelId); + }else { + streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); + } + sendRtpItem.setStreamId(streamId); + redisCatchStorage.updateSendRTPSever(sendRtpItem); + playService.play(mediaServerItem, device.getDeviceId(), channelId, ((code, msg, data) -> { + if (code == InviteErrorCode.SUCCESS.getCode()){ + hookEvent.run(code, msg, data); + }else if (code == InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode() || code == InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode()){ + logger.info("[上级点播]超时, 用户:{}, 通道:{}", username, channelId); + redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null); + errorEvent.run(code, msg, data); + }else { + errorEvent.run(code, msg, data); + } + })); + + } + } else if (gbStream != null) { + + String ssrc; + if (userSetting.getUseCustomSsrcForParentInvite() || gb28181Sdp.getSsrc() == null) { + // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式 + ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId()); + }else { + ssrc = gb28181Sdp.getSsrc(); + } + + 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) { + logger.error("未处理的异常 ", e); + } + } + + /** + * 安排推流 + */ + 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 = zlmServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream()); + if (streamReady != null && streamReady) { + // 自平台内容 + SendRtpItem sendRtpItem = zlmServerFactory.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 = zlmServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream()); + if (streamReady != null && streamReady) { + // 自平台内容 + SendRtpItem sendRtpItem = zlmServerFactory.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, hookParam) -> { + OnStreamChangedHookParam streamChangedHookParam = (OnStreamChangedHookParam)hookParam; + logger.info("[上级点播]拉流代理已经就绪, {}/{}", streamChangedHookParam.getApp(), streamChangedHookParam.getStream()); + 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) { + logger.error("未处理的异常 ", e); + } catch (InvalidArgumentException e) { + logger.error("未处理的异常 ", e); + } catch (ParseException e) { + logger.error("未处理的异常 ", e); + } + }, 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 = zlmServerFactory.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) { + logger.error("未处理的异常 ", e); + } catch (InvalidArgumentException e) { + logger.error("未处理的异常 ", e); + } catch (ParseException e) { + logger.error("未处理的异常 ", e); + } + 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) { + logger.error("未处理的异常 ", e); + } catch (InvalidArgumentException e) { + logger.error("未处理的异常 ", e); + } catch (ParseException e) { + logger.error("未处理的异常 ", e); + } + 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"); + // 非严格模式端口不统一, 增加兼容性,修改为一个不为0的端口 + int localPort = sendRtpItem.getLocalPort(); + if(localPort == 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"); + 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) { + logger.error("未处理的异常 ", e); + } catch (InvalidArgumentException e) { + logger.error("未处理的异常 ", e); + } catch (ParseException e) { + logger.error("未处理的异常 ", e); + } + 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 ssrc = "0000000404"; + + try { + Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); + SessionDescription sdp = gb28181Sdp.getBaseSdb(); + // 获取支持的格式 + 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.getConnection().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/NotifyRequestForCatalogProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java new file mode 100644 index 000000000..281d780e7 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java @@ -0,0 +1,310 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; + +import com.genersoft.iot.vmp.conf.CivilCodeFileConf; +import com.genersoft.iot.vmp.conf.DynamicTask; +import com.genersoft.iot.vmp.conf.SipConfig; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; +import com.genersoft.iot.vmp.gb28181.utils.SipUtils; +import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; +import com.genersoft.iot.vmp.service.IDeviceChannelService; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.RequestEvent; +import javax.sip.header.FromHeader; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * SIP命令类型: NOTIFY请求中的目录请求处理 + */ +@Component +public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent { + + + private final static Logger logger = LoggerFactory.getLogger(NotifyRequestForCatalogProcessor.class); + + private final List updateChannelOnlineList = new CopyOnWriteArrayList<>(); + private final List updateChannelOfflineList = new CopyOnWriteArrayList<>(); + private final Map updateChannelMap = new ConcurrentHashMap<>(); + + private final Map addChannelMap = new ConcurrentHashMap<>(); + private final List deleteChannelList = new CopyOnWriteArrayList<>(); + + + @Autowired + private UserSetting userSetting; + + @Autowired + private EventPublisher eventPublisher; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IDeviceChannelService deviceChannelService; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private CivilCodeFileConf civilCodeFileConf; + + @Autowired + private SipConfig sipConfig; + + private final static String talkKey = "notify-request-for-catalog-task"; + + public void process(RequestEvent evt) { + try { + long start = System.currentTimeMillis(); + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); + String deviceId = SipUtils.getUserIdFromFromHeader(fromHeader); + + Device device = redisCatchStorage.getDevice(deviceId); + if (device == null || !device.isOnLine()) { + logger.warn("[收到目录订阅]:{}, 但是设备已经离线", (device != null ? device.getDeviceId():"" )); + return; + } + 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) { + return; + } + Iterator deviceListIterator = deviceListElement.elementIterator(); + if (deviceListIterator != null) { + + // 遍历DeviceList + while (deviceListIterator.hasNext()) { + Element itemDevice = deviceListIterator.next(); + Element channelDeviceElement = itemDevice.element("DeviceID"); + if (channelDeviceElement == null) { + continue; + } + Element eventElement = itemDevice.element("Event"); + String event; + if (eventElement == null) { + logger.warn("[收到目录订阅]:{}, 但是Event为空, 设为默认值 ADD", (device != null ? device.getDeviceId():"" )); + event = CatalogEvent.ADD; + }else { + event = eventElement.getText().toUpperCase(); + } + DeviceChannel channel = XmlUtil.channelContentHandler(itemDevice, device, event, civilCodeFileConf); + if (channel == null) { + logger.info("[收到目录订阅]:但是解析失败 {}", new String(evt.getRequest().getRawContent())); + continue; + } + if (channel.getParentId() != null && channel.getParentId().equals(sipConfig.getId())) { + channel.setParentId(null); + } + channel.setDeviceId(device.getDeviceId()); + logger.info("[收到目录订阅]:{}/{}", device.getDeviceId(), channel.getChannelId()); + switch (event) { + case CatalogEvent.ON: + // 上线 + logger.info("[收到通道上线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + updateChannelOnlineList.add(channel); + if (updateChannelOnlineList.size() > 300) { + executeSaveForOnline(); + } + if (userSetting.getDeviceStatusNotify()) { + // 发送redis消息 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), channel.getChannelId(), true); + } + + break; + case CatalogEvent.OFF : + // 离线 + logger.info("[收到通道离线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + if (userSetting.getRefuseChannelStatusChannelFormNotify()) { + logger.info("[收到通道离线通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + }else { + updateChannelOfflineList.add(channel); + if (updateChannelOfflineList.size() > 300) { + executeSaveForOffline(); + } + if (userSetting.getDeviceStatusNotify()) { + // 发送redis消息 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), channel.getChannelId(), false); + } + } + break; + case CatalogEvent.VLOST: + // 视频丢失 + logger.info("[收到通道视频丢失通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + if (userSetting.getRefuseChannelStatusChannelFormNotify()) { + logger.info("[收到通道视频丢失通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + }else { + updateChannelOfflineList.add(channel); + if (updateChannelOfflineList.size() > 300) { + executeSaveForOffline(); + } + if (userSetting.getDeviceStatusNotify()) { + // 发送redis消息 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), channel.getChannelId(), false); + } + } + break; + case CatalogEvent.DEFECT: + // 故障 + logger.info("[收到通道视频故障通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + if (userSetting.getRefuseChannelStatusChannelFormNotify()) { + logger.info("[收到通道视频故障通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + }else { + updateChannelOfflineList.add(channel); + if (updateChannelOfflineList.size() > 300) { + executeSaveForOffline(); + } + if (userSetting.getDeviceStatusNotify()) { + // 发送redis消息 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), channel.getChannelId(), false); + } + } + break; + case CatalogEvent.ADD: + // 增加 + logger.info("[收到增加通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + // 判断此通道是否存在 + DeviceChannel deviceChannel = deviceChannelService.getOne(deviceId, channel.getChannelId()); + if (deviceChannel != null) { + channel.setId(deviceChannel.getId()); + updateChannelMap.put(channel.getChannelId(), channel); + if (updateChannelMap.keySet().size() > 300) { + executeSaveForUpdate(); + } + }else { + addChannelMap.put(channel.getChannelId(), channel); + if (userSetting.getDeviceStatusNotify()) { + // 发送redis消息 + redisCatchStorage.sendChannelAddOrDelete(device.getDeviceId(), channel.getChannelId(), true); + } + + if (addChannelMap.keySet().size() > 300) { + executeSaveForAdd(); + } + } + + break; + case CatalogEvent.DEL: + // 删除 + logger.info("[收到删除通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + deleteChannelList.add(channel); + if (userSetting.getDeviceStatusNotify()) { + // 发送redis消息 + redisCatchStorage.sendChannelAddOrDelete(device.getDeviceId(), channel.getChannelId(), false); + } + if (deleteChannelList.size() > 300) { + executeSaveForDelete(); + } + break; + case CatalogEvent.UPDATE: + // 更新 + logger.info("[收到更新通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + // 判断此通道是否存在 + DeviceChannel deviceChannelForUpdate = deviceChannelService.getOne(deviceId, channel.getChannelId()); + if (deviceChannelForUpdate != null) { + channel.setId(deviceChannelForUpdate.getId()); + updateChannelMap.put(channel.getChannelId(), channel); + if (updateChannelMap.keySet().size() > 300) { + executeSaveForUpdate(); + } + }else { + addChannelMap.put(channel.getChannelId(), channel); + if (addChannelMap.keySet().size() > 300) { + executeSaveForAdd(); + } + if (userSetting.getDeviceStatusNotify()) { + // 发送redis消息 + redisCatchStorage.sendChannelAddOrDelete(device.getDeviceId(), channel.getChannelId(), true); + } + } + break; + default: + logger.warn("[ NotifyCatalog ] event not found : {}", event ); + + } + // 转发变化信息 + eventPublisher.catalogEventPublish(null, channel, event); + + if (updateChannelMap.keySet().size() > 0 + || addChannelMap.keySet().size() > 0 + || updateChannelOnlineList.size() > 0 + || updateChannelOfflineList.size() > 0 + || deleteChannelList.size() > 0) { + + if (!dynamicTask.contains(talkKey)) { + dynamicTask.startDelay(talkKey, this::executeSave, 1000); + } + } + } + } + } catch (DocumentException e) { + logger.error("未处理的异常 ", e); + } + } + + private void executeSave(){ + executeSaveForUpdate(); + executeSaveForDelete(); + executeSaveForOnline(); + executeSaveForOffline(); + dynamicTask.stop(talkKey); + } + + private void executeSaveForUpdate(){ + if (updateChannelMap.values().size() > 0) { + ArrayList deviceChannels = new ArrayList<>(updateChannelMap.values()); + updateChannelMap.clear(); + deviceChannelService.batchUpdateChannel(deviceChannels); + } + + } + + private void executeSaveForAdd(){ + if (addChannelMap.values().size() > 0) { + ArrayList deviceChannels = new ArrayList<>(addChannelMap.values()); + addChannelMap.clear(); + deviceChannelService.batchAddChannel(deviceChannels); + } + } + + private void executeSaveForDelete(){ + if (deleteChannelList.size() > 0) { + deviceChannelService.deleteChannels(deleteChannelList); + deleteChannelList.clear(); + } + } + + private void executeSaveForOnline(){ + if (updateChannelOnlineList.size() > 0) { + deviceChannelService.channelsOnline(updateChannelOnlineList); + updateChannelOnlineList.clear(); + } + } + + private void executeSaveForOffline(){ + if (updateChannelOfflineList.size() > 0) { + deviceChannelService.channelsOffline(updateChannelOfflineList); + updateChannelOfflineList.clear(); + } + } + +} 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 new file mode 100644 index 000000000..d44ff5a85 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java @@ -0,0 +1,505 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; + +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.conf.CivilCodeFileConf; +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.gb28181.transmit.SIPProcessorObserver; +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; +import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; +import com.genersoft.iot.vmp.gb28181.utils.SipUtils; +import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; +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.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.ObjectUtils; + +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.Iterator; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * SIP命令类型: NOTIFY请求,这是作为上级发送订阅请求后,设备才会响应的 + */ +@Component +public class NotifyRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { + + + private final static Logger logger = LoggerFactory.getLogger(NotifyRequestProcessor.class); + + @Autowired + private UserSetting userSetting; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private EventPublisher eventPublisher; + + @Autowired + private SipConfig sipConfig; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private EventPublisher publisher; + + private final String method = "NOTIFY"; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Autowired + private IDeviceChannelService deviceChannelService; + + @Autowired + private NotifyRequestForCatalogProcessor notifyRequestForCatalogProcessor; + + @Autowired + private CivilCodeFileConf civilCodeFileConf; + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + private int maxQueueCount = 30000; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addRequestProcessor(method, this); + } + + @Override + public void process(RequestEvent evt) { + try { + + if (taskQueue.size() >= userSetting.getMaxNotifyCountQueue()) { + responseAck((SIPRequest) evt.getRequest(), Response.BUSY_HERE, null, null); + logger.error("[notify] 待处理消息队列已满 {},返回486 BUSY_HERE,消息不做处理", userSetting.getMaxNotifyCountQueue()); + return; + }else { + responseAck((SIPRequest) evt.getRequest(), Response.OK, null, null); + } + + }catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("未处理的异常 ", e); + } + boolean runed = !taskQueue.isEmpty(); + logger.info("[notify] 待处理消息数量: {}", taskQueue.size()); + taskQueue.offer(new HandlerCatchData(evt, null, null)); + if (!runed) { + taskExecutor.execute(()-> { + while (!taskQueue.isEmpty()) { + try { + HandlerCatchData take = taskQueue.poll(); + if (take == null) { + continue; + } + 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()); + notifyRequestForCatalogProcessor.process(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); + } + } + }); + } + } + + /** + * 处理MobilePosition移动位置Notify + * + * @param evt + */ + 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 channelId = deviceIdElement.getTextTrim().toString(); + Device device = redisCatchStorage.getDevice(deviceId); + + if (device == null) { + device = redisCatchStorage.getDevice(channelId); + if (device == null) { + // 根据通道id查询设备Id + List deviceList = deviceChannelService.getDeviceByChannelId(channelId); + if (deviceList.size() > 0) { + device = deviceList.get(0); + } + } + } + if (device == null) { + logger.warn("[mobilePosition移动位置Notify] 未找到通道{}所属的设备", channelId); + return; + } + if (!ObjectUtils.isEmpty(device.getName())) { + mobilePosition.setDeviceName(device.getName()); + } + + mobilePosition.setDeviceId(device.getDeviceId()); + mobilePosition.setChannelId(channelId); + String time = XmlUtil.getText(rootElement, "Time"); + if (ObjectUtils.isEmpty(time)){ + mobilePosition.setTime(DateUtil.getNow()); + }else { + mobilePosition.setTime(SipUtils.parseTime(time)); + } + + mobilePosition.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude"))); + mobilePosition.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude"))); + if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Speed"))) { + mobilePosition.setSpeed(Double.parseDouble(XmlUtil.getText(rootElement, "Speed"))); + } else { + mobilePosition.setSpeed(0.0); + } + if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Direction"))) { + mobilePosition.setDirection(Double.parseDouble(XmlUtil.getText(rootElement, "Direction"))); + } else { + mobilePosition.setDirection(0.0); + } + if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Altitude"))) { + mobilePosition.setAltitude(Double.parseDouble(XmlUtil.getText(rootElement, "Altitude"))); + } else { + mobilePosition.setAltitude(0.0); + } + logger.info("[收到移动位置订阅通知]:{}/{}->{}.{}", mobilePosition.getDeviceId(), mobilePosition.getChannelId(), + mobilePosition.getLongitude(), mobilePosition.getLatitude()); + mobilePosition.setReportSource("Mobile Position"); + + + // 更新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.updateChannelPosition(deviceChannel); + + // 发送redis消息。 通知位置信息的变化 + JSONObject jsonObject = new JSONObject(); + jsonObject.put("time", DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(mobilePosition.getTime())); + 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) { + logger.error("未处理的异常 ", e); + } + } + + /*** + * 处理alarm设备报警Notify + * + * @param evt + */ + private void processNotifyAlarm(RequestEvent evt) { + if (!sipConfig.isAlarm()) { + 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 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")); + 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 { + deviceAlarm.setAlarmDescription(XmlUtil.getText(rootElement, "AlarmDescription")); + } + if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Longitude"))) { + deviceAlarm.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude"))); + } else { + deviceAlarm.setLongitude(0.00); + } + if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Latitude"))) { + deviceAlarm.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude"))); + } else { + deviceAlarm.setLatitude(0.00); + } + 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"); + + // 更新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.updateChannelPosition(deviceChannel); + // 发送redis消息。 通知位置信息的变化 + JSONObject jsonObject = new JSONObject(); + jsonObject.put("time", DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(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 + if (redisCatchStorage.deviceIsOnline(deviceId)) { + publisher.deviceAlarmEventPublish(deviceAlarm); + } + } catch (DocumentException e) { + logger.error("未处理的异常 ", e); + } + } + + /*** + * 处理catalog设备目录列表Notify + * + * @param evt + */ + private void processNotifyCatalogList(RequestEvent evt) { + try { + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); + String deviceId = SipUtils.getUserIdFromFromHeader(fromHeader); + + Device device = redisCatchStorage.getDevice(deviceId); + if (device == null || !device.isOnLine()) { + logger.warn("[收到目录订阅]:{}, 但是设备已经离线", (device != null ? device.getDeviceId():"" )); + return; + } + 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) { + return; + } + Iterator deviceListIterator = deviceListElement.elementIterator(); + if (deviceListIterator != null) { + + // 遍历DeviceList + while (deviceListIterator.hasNext()) { + Element itemDevice = deviceListIterator.next(); + Element channelDeviceElement = itemDevice.element("DeviceID"); + if (channelDeviceElement == null) { + continue; + } + Element eventElement = itemDevice.element("Event"); + String event; + if (eventElement == null) { + logger.warn("[收到目录订阅]:{}, 但是Event为空, 设为默认值 ADD", (device != null ? device.getDeviceId():"" )); + event = CatalogEvent.ADD; + }else { + event = eventElement.getText().toUpperCase(); + } + DeviceChannel channel = XmlUtil.channelContentHandler(itemDevice, device, event, civilCodeFileConf); + if (channel == null) { + logger.info("[收到目录订阅]:但是解析失败 {}", new String(evt.getRequest().getRawContent())); + continue; + } + if (channel.getParentId() != null && channel.getParentId().equals(sipConfig.getId())) { + channel.setParentId(null); + } + 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()); + break; + case CatalogEvent.OFF : + // 离线 + logger.info("[收到通道离线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + if (userSetting.getRefuseChannelStatusChannelFormNotify()) { + storager.deviceChannelOffline(deviceId, channel.getChannelId()); + }else { + logger.info("[收到通道离线通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + } + break; + case CatalogEvent.VLOST: + // 视频丢失 + logger.info("[收到通道视频丢失通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + if (userSetting.getRefuseChannelStatusChannelFormNotify()) { + storager.deviceChannelOffline(deviceId, channel.getChannelId()); + }else { + logger.info("[收到通道视频丢失通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + } + break; + case CatalogEvent.DEFECT: + // 故障 + break; + case CatalogEvent.ADD: + // 增加 + logger.info("[收到增加通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + deviceChannelService.updateChannel(deviceId, channel); + break; + case CatalogEvent.DEL: + // 删除 + logger.info("[收到删除通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + storager.delChannel(deviceId, channel.getChannelId()); + break; + case CatalogEvent.UPDATE: + // 更新 + logger.info("[收到更新通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + deviceChannelService.updateChannel(deviceId, channel); + break; + default: + logger.warn("[ NotifyCatalog ] event not found : {}", event ); + + } + // 转发变化信息 + eventPublisher.catalogEventPublish(null, channel, event); + + } + } + } catch (DocumentException e) { + logger.error("未处理的异常 ", e); + } + } + + public void setCmder(SIPCommander cmder) { + } + + public void setStorager(IVideoManagerStorage storager) { + this.storager = storager; + } + + public void setPublisher(EventPublisher publisher) { + this.publisher = publisher; + } + + public void setRedis(RedisUtil redis) { + } + + public void setDeferredResultHolder(DeferredResultHolder deferredResultHolder) { + } + + public IRedisCatchStorage getRedisCatchStorage() { + return redisCatchStorage; + } + + public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) { + this.redisCatchStorage = 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 new file mode 100644 index 000000000..ceeb3b17e --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java @@ -0,0 +1,233 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; + +import com.genersoft.iot.vmp.conf.SipConfig; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo; +import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo; +import com.genersoft.iot.vmp.gb28181.bean.GbSipDate; +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; +import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; +import com.genersoft.iot.vmp.gb28181.utils.SipUtils; +import com.genersoft.iot.vmp.service.IDeviceService; +import com.genersoft.iot.vmp.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.SIPDateHeader; +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; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.header.AuthorizationHeader; +import javax.sip.header.ContactHeader; +import javax.sip.header.FromHeader; +import javax.sip.header.ViaHeader; +import javax.sip.message.Request; +import javax.sip.message.Response; +import java.security.NoSuchAlgorithmException; +import java.text.ParseException; +import java.util.Calendar; +import java.util.Locale; + +/** + * SIP命令类型: REGISTER请求 + */ +@Component +public class RegisterRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { + + private final Logger logger = LoggerFactory.getLogger(RegisterRequestProcessor.class); + + public final String method = "REGISTER"; + + @Autowired + private SipConfig sipConfig; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private SIPSender sipSender; + + @Autowired + private UserSetting userSetting; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addRequestProcessor(method, this); + } + + /** + * 收到注册请求 处理 + * + * @param evt + */ + @Override + public void process(RequestEvent evt) { + try { + RequestEventExt evtExt = (RequestEventExt) evt; + + 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); + + RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, + userSetting.getSipUseSourceIpAsRemoteAddress()); + String requestAddress = remoteAddressInfo.getIp() + ":" + remoteAddressInfo.getPort(); + logger.info("[注册请求] 设备:{}, 开始处理: {}", deviceId, requestAddress); + if (device != null && + device.getSipTransactionInfo() != null && + request.getCallIdHeader().getCallId().equals(device.getSipTransactionInfo().getCallId())) { + logger.info("[注册请求] 设备:{}, 注册续订: {}",device.getDeviceId(), device.getDeviceId()); + device.setExpires(request.getExpires().getExpires()); + device.setIp(remoteAddressInfo.getIp()); + device.setPort(remoteAddressInfo.getPort()); + device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort()))); + device.setLocalIp(request.getLocalAddress().getHostAddress()); + Response registerOkResponse = getRegisterOkResponse(request); + // 判断TCP还是UDP + ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME); + String transport = reqViaHeader.getTransport(); + device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP"); + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), registerOkResponse); + device.setRegisterTime(DateUtil.getNow()); + SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)registerOkResponse); + deviceService.online(device, sipTransactionInfo); + return; + } + 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: {}",deviceId, requestAddress); + response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request); + new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain()); + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + return; + } + + // 校验密码是否正确 + passwordCorrect = ObjectUtils.isEmpty(password) || + new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request, password); + + if (!passwordCorrect) { + // 注册失败 + response = getMessageFactory().createResponse(Response.FORBIDDEN, request); + response.setReasonPhrase("wrong password"); + logger.info("[注册请求] 设备:{}, 密码/SIP服务器ID错误, 回复403: {}", deviceId, requestAddress); + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + return; + } + + // 携带授权头并且密码正确 + response = getMessageFactory().createResponse(Response.OK, request); + // 添加date头 + SIPDateHeader dateHeader = new SIPDateHeader(); + // 使用自己修改的 + GbSipDate gbSipDate = new GbSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis()); + dateHeader.setDate(gbSipDate); + 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()); + + if (device == null) { + device = new Device(); + device.setStreamMode("UDP"); + device.setCharset("GB2312"); + device.setGeoCoordSys("WGS84"); + device.setDeviceId(deviceId); + device.setOnLine(false); + }else { + if (ObjectUtils.isEmpty(device.getStreamMode())) { + device.setStreamMode("UDP"); + } + if (ObjectUtils.isEmpty(device.getCharset())) { + device.setCharset("GB2312"); + } + if (ObjectUtils.isEmpty(device.getGeoCoordSys())) { + device.setGeoCoordSys("WGS84"); + } + } + + 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()); + SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)response); + deviceService.online(device, sipTransactionInfo); + } else { + logger.info("[注销成功] deviceId: {}->{}" ,deviceId, requestAddress); + deviceService.offline(deviceId, "主动注销"); + } + } catch (SipException | NoSuchAlgorithmException | ParseException e) { + logger.error("未处理的异常 ", e); + } + } + + private Response getRegisterOkResponse(Request request) throws ParseException { + // 携带授权头并且密码正确 + Response response = getMessageFactory().createResponse(Response.OK, request); + // 添加date头 + SIPDateHeader dateHeader = new SIPDateHeader(); + // 使用自己修改的 + GbSipDate gbSipDate = new GbSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis()); + dateHeader.setDate(gbSipDate); + response.addHeader(dateHeader); + + // 添加Contact头 + response.addHeader(request.getHeader(ContactHeader.NAME)); + // 添加Expires头 + response.addHeader(request.getExpires()); + + return response; + + } +} 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 new file mode 100644 index 000000000..4ff4e980f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java @@ -0,0 +1,195 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; + +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.transmit.SIPProcessorObserver; +import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; +import com.genersoft.iot.vmp.gb28181.utils.SipUtils; +import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +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.SipException; +import javax.sip.header.ExpiresHeader; +import javax.sip.message.Response; +import java.text.ParseException; + +/** + * SIP命令类型: SUBSCRIBE请求 + * @author lin + */ +@Component +public class SubscribeRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { + + private final Logger logger = LoggerFactory.getLogger(SubscribeRequestProcessor.class); + private final String method = "SUBSCRIBE"; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SubscribeHolder subscribeHolder; + + @Autowired + private SIPSender sipSender; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addRequestProcessor(method, this); + } + + /** + * 处理SUBSCRIBE请求 + * + * @param evt 事件 + */ + @Override + public void process(RequestEvent evt) { + SIPRequest request = (SIPRequest) evt.getRequest(); + try { + Element rootElement = getRootElement(evt); + if (rootElement == null) { + logger.error("处理SUBSCRIBE请求 未获取到消息体{}", evt.getRequest()); + return; + } + 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("接收到消息:" + cmd); + + 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) { + logger.error("未处理的异常 ", e); + } + + } + + /** + * 处理移动位置订阅消息 + */ + 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) { + logger.error("未处理的异常 ", e); + } + } + + 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) { + logger.error("未处理的异常 ", e); + } + } +} 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 000000000..fedf0ecad --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/info/InfoRequestProcessor.java @@ -0,0 +1,149 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.info; + +import com.genersoft.iot.vmp.common.InviteInfo; +import com.genersoft.iot.vmp.common.InviteSessionType; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; +import com.genersoft.iot.vmp.gb28181.utils.SipUtils; +import com.genersoft.iot.vmp.service.IInviteStreamService; +import com.genersoft.iot.vmp.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.CallIdHeader; +import javax.sip.header.ContentTypeHeader; +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 IInviteStreamService inviteStreamService; + + @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(); + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId); + if (null == inviteInfo) { + responseAck(request, Response.NOT_FOUND, "stream " + streamId + " not found"); + return; + } + Device device1 = storager.queryVideoDevice(inviteInfo.getDeviceId()); + if (inviteInfo.getStreamInfo() != null) { + cmder.playbackControlCmd(device1,inviteInfo.getStreamInfo(),new String(evt.getRequest().getRawContent()),eventResult -> { + // 失败的回复 + try { + responseAck(request, eventResult.statusCode, eventResult.msg); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 录像控制: {}", e.getMessage()); + } + }, eventResult -> { + // 成功的回复 + try { + responseAck(request, eventResult.statusCode); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 录像控制: {}", e.getMessage()); + } + }); + } + + } + } + } 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/IMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/IMessageHandler.java new file mode 100644 index 000000000..7eba0d52a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/IMessageHandler.java @@ -0,0 +1,23 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message; + +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import org.dom4j.Element; + +import javax.sip.RequestEvent; + +public interface IMessageHandler { + /** + * 处理来自设备的信息 + * @param evt + * @param device + */ + void handForDevice(RequestEvent evt, Device device, Element element); + + /** + * 处理来自平台的信息 + * @param evt + * @param parentPlatform + */ + void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element); +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageHandlerAbstract.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageHandlerAbstract.java new file mode 100644 index 000000000..afaa7cb39 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageHandlerAbstract.java @@ -0,0 +1,40 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message; + +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; +import org.dom4j.Element; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.sip.RequestEvent; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; + +public abstract class MessageHandlerAbstract extends SIPRequestProcessorParent implements IMessageHandler{ + + public Map messageHandlerMap = new ConcurrentHashMap<>(); + + public void addHandler(String cmdType, IMessageHandler messageHandler) { + messageHandlerMap.put(cmdType, messageHandler); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + String cmd = getText(element, "CmdType"); + IMessageHandler messageHandler = messageHandlerMap.get(cmd); + if (messageHandler != null) { + messageHandler.handForDevice(evt, device, element); + } + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + String cmd = getText(element, "CmdType"); + IMessageHandler messageHandler = messageHandlerMap.get(cmd); + if (messageHandler != null) { + messageHandler.handForPlatform(evt, parentPlatform, element); + } + } +} 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 new file mode 100644 index 000000000..0c23acea1 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java @@ -0,0 +1,142 @@ +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.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.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; +import java.util.concurrent.ConcurrentHashMap; + +@Component +public class MessageRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { + + private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class); + + private final String method = "MESSAGE"; + + private static Map messageHandlerMap = new ConcurrentHashMap<>(); + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Autowired + private IVideoManagerStorage storage; + + @Autowired + private SipSubscribe sipSubscribe; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private VideoStreamSessionManager sessionManager; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addRequestProcessor(method, this); + } + + public void addHandler(String name, IMessageHandler handler) { + messageHandlerMap.put(name, handler); + } + + @Override + public void process(RequestEvent evt) { + SIPRequest sipRequest = (SIPRequest)evt.getRequest(); +// logger.info("接收到消息:" + 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(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 = 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) { + if (device != null) { + messageHandler.handForDevice(evt, device, rootElement); + }else { // 由于上面已经判断都为null则直接返回,所以这里device和parentPlatform必有一个不为null + messageHandler.handForPlatform(evt, parentPlatform, rootElement); + } + }else { + // 不支持的message + // 不存在则回复415 + responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE, "Unsupported message type, must Control/Notify/Query/Response"); + } + } + } 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/control/ControlMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/ControlMessageHandler.java new file mode 100644 index 000000000..235a47705 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/ControlMessageHandler.java @@ -0,0 +1,27 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.control; + +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.MessageHandlerAbstract; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.MessageRequestProcessor; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 命令类型: 控制命令 + * 命令类型: 设备控制: 远程启动, 录像控制(TODO), 报警布防/撤防命令(TODO), 报警复位命令(TODO), + * 强制关键帧命令(TODO), 拉框放大/缩小控制命令(TODO), 看守位控制(TODO), 报警复位(TODO) + * 命令类型: 设备配置: SVAC编码配置(TODO), 音频参数(TODO), SVAC解码配置(TODO) + */ +@Component +public class ControlMessageHandler extends MessageHandlerAbstract implements InitializingBean { + + private final String messageType = "Control"; + + @Autowired + private MessageRequestProcessor messageRequestProcessor; + + @Override + public void afterPropertiesSet() throws Exception { + messageRequestProcessor.addHandler(messageType, this); + } +} 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 new file mode 100644 index 000000000..b2dd76b58 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java @@ -0,0 +1,358 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.control.cmd; + +import com.genersoft.iot.vmp.common.enums.DeviceControlType; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DragZoomRequest; +import com.genersoft.iot.vmp.gb28181.bean.HomePositionRequest; +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.control.ControlMessageHandler; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import javax.sip.*; +import javax.sip.address.SipURI; +import javax.sip.message.Response; +import java.text.ParseException; +import java.util.List; + +import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.*; + +@Component +public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(DeviceControlQueryMessageHandler.class); + private final String cmdType = "DeviceControl"; + + @Autowired + private ControlMessageHandler controlMessageHandler; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommander cmder; + + @Autowired + private SIPCommanderFroPlatform cmderFroPlatform; + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + @Override + public void afterPropertiesSet() throws Exception { + controlMessageHandler.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(); + + // 此处是上级发出的DeviceControl指令 + String targetGBId = ((SipURI) request.getToHeader().getAddress().getURI()).getUser(); + String channelId = getText(rootElement, "DeviceID"); + // 远程启动功能 + if (!ObjectUtils.isEmpty(getText(rootElement, "TeleBoot"))) { + // TODO 拒绝远程启动命令 + logger.warn("[国标级联]收到平台的远程启动命令, 不处理"); + +// if (parentPlatform.getServerGBId().equals(targetGBId)) { +// // 远程启动本平台:需要在重新启动程序后先对SipStack解绑 +// logger.info("执行远程启动本平台命令"); +// try { +// cmderFroPlatform.unregister(parentPlatform, null, null); +// } catch (InvalidArgumentException | ParseException | SipException e) { +// logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage()); +// } +// taskExecutor.execute(() -> { +// // 远程启动 +//// try { +//// Thread.sleep(3000); +//// SipProvider up = (SipProvider) SpringBeanFactory.getBean("udpSipProvider"); +//// SipStackImpl stack = (SipStackImpl)up.getSipStack(); +//// stack.stop(); +//// Iterator listener = stack.getListeningPoints(); +//// while (listener.hasNext()) { +//// stack.deleteListeningPoint((ListeningPoint) listener.next()); +//// } +//// Iterator providers = stack.getSipProviders(); +//// while (providers.hasNext()) { +//// stack.deleteSipProvider((SipProvider) providers.next()); +//// } +//// VManageBootstrap.restart(); +//// } catch (InterruptedException | ObjectInUseException e) { +//// logger.error("[任务执行失败] 服务重启: {}", e.getMessage()); +//// } +// }); +// } + } + DeviceControlType deviceControlType = DeviceControlType.typeOf(rootElement); + logger.info("[接受deviceControl命令] 命令: {}", deviceControlType); + if (!ObjectUtils.isEmpty(deviceControlType) && !parentPlatform.getServerGBId().equals(targetGBId)) { + //判断是否存在该通道 + Device deviceForPlatform = storager.queryVideoDeviceByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId); + if (deviceForPlatform == null) { + try { + responseAck(request, Response.NOT_FOUND); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + switch (deviceControlType) { + case PTZ: + handlePtzCmd(deviceForPlatform, channelId, rootElement, request, DeviceControlType.PTZ); + break; + case ALARM: + handleAlarmCmd(deviceForPlatform, rootElement, request); + break; + case GUARD: + handleGuardCmd(deviceForPlatform, rootElement, request, DeviceControlType.GUARD); + break; + case RECORD: + handleRecordCmd(deviceForPlatform, channelId, rootElement, request, DeviceControlType.RECORD); + break; + case I_FRAME: + handleIFameCmd(deviceForPlatform, request, channelId); + break; + case TELE_BOOT: + handleTeleBootCmd(deviceForPlatform, request); + break; + case DRAG_ZOOM_IN: + handleDragZoom(deviceForPlatform, channelId, rootElement, request, DeviceControlType.DRAG_ZOOM_IN); + break; + case DRAG_ZOOM_OUT: + handleDragZoom(deviceForPlatform, channelId, rootElement, request, DeviceControlType.DRAG_ZOOM_OUT); + break; + case HOME_POSITION: + handleHomePositionCmd(deviceForPlatform, channelId, rootElement, request, DeviceControlType.HOME_POSITION); + break; + default: + break; + } + } + } + + /** + * 处理云台指令 + * + * @param device 设备 + * @param channelId 通道id + * @param rootElement + * @param request + */ + private void handlePtzCmd(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) { + String cmdString = getText(rootElement, type.getVal()); + try { + cmder.fronEndCmd(device, channelId, cmdString, + errorResult -> onError(request, errorResult), + okResult -> onOk(request, okResult)); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 云台/前端: {}", e.getMessage()); + } + } + + /** + * 处理强制关键帧 + * + * @param device 设备 + * @param channelId 通道id + */ + private void handleIFameCmd(Device device, SIPRequest request, String channelId) { + try { + cmder.iFrameCmd(device, channelId); + responseAck(request, Response.OK); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 强制关键帧: {}", e.getMessage()); + } + } + + /** + * 处理重启命令 + * + * @param device 设备信息 + */ + private void handleTeleBootCmd(Device device, SIPRequest request) { + try { + cmder.teleBootCmd(device); + responseAck(request, Response.OK); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 重启: {}", e.getMessage()); + } + + } + + /** + * 处理拉框控制*** + * + * @param device 设备信息 + * @param channelId 通道id + * @param rootElement 根节点 + * @param type 消息类型 + */ + private void handleDragZoom(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) { + try { + DragZoomRequest dragZoomRequest = loadElement(rootElement, DragZoomRequest.class); + DragZoomRequest.DragZoom dragZoom = dragZoomRequest.getDragZoomIn(); + if (dragZoom == null) { + dragZoom = dragZoomRequest.getDragZoomOut(); + } + StringBuffer cmdXml = new StringBuffer(200); + cmdXml.append("<" + type.getVal() + ">\r\n"); + cmdXml.append("" + dragZoom.getLength() + "\r\n"); + cmdXml.append("" + dragZoom.getWidth() + "\r\n"); + cmdXml.append("" + dragZoom.getMidPointX() + "\r\n"); + cmdXml.append("" + dragZoom.getMidPointY() + "\r\n"); + cmdXml.append("" + dragZoom.getLengthX() + "\r\n"); + cmdXml.append("" + dragZoom.getLengthY() + "\r\n"); + cmdXml.append("\r\n"); + cmder.dragZoomCmd(device, channelId, cmdXml.toString()); + responseAck(request, Response.OK); + } catch (Exception e) { + logger.error("[命令发送失败] 拉框控制: {}", e.getMessage()); + } + + } + + /** + * 处理看守位命令*** + * + * @param device 设备信息 + * @param channelId 通道id + * @param rootElement 根节点 + * @param request 请求信息 + * @param type 消息类型 + */ + private void handleHomePositionCmd(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) { + try { + HomePositionRequest homePosition = loadElement(rootElement, HomePositionRequest.class); + //获取整个消息主体,我们只需要修改请求头即可 + HomePositionRequest.HomePosition info = homePosition.getHomePosition(); + cmder.homePositionCmd(device, channelId, info.getEnabled(), info.getResetTime(), info.getPresetIndex(), + errorResult -> onError(request, errorResult), + okResult -> onOk(request, okResult)); + } catch (Exception e) { + logger.error("[命令发送失败] 看守位设置: {}", e.getMessage()); + } + } + + /** + * 处理告警消息*** + * + * @param device 设备信息 + * @param rootElement 根节点 + * @param request 请求信息 + */ + private void handleAlarmCmd(Device device, Element rootElement, SIPRequest request) { + //告警方法 + String alarmMethod = ""; + //告警类型 + String alarmType = ""; + List info = rootElement.elements("Info"); + if (info != null) { + for (Element element : info) { + alarmMethod = getText(element, "AlarmMethod"); + alarmType = getText(element, "AlarmType"); + } + } + try { + cmder.alarmCmd(device, alarmMethod, alarmType, + errorResult -> onError(request, errorResult), + okResult -> onOk(request, okResult)); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 告警消息: {}", e.getMessage()); + } + } + + /** + * 处理录像控制 + * + * @param device 设备信息 + * @param channelId 通道id + * @param rootElement 根节点 + * @param request 请求信息 + * @param type 消息类型 + */ + private void handleRecordCmd(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) { + //获取整个消息主体,我们只需要修改请求头即可 + String cmdString = getText(rootElement, type.getVal()); + try { + cmder.recordCmd(device, channelId, cmdString, + errorResult -> onError(request, errorResult), + okResult -> onOk(request, okResult)); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 录像控制: {}", e.getMessage()); + } + } + + /** + * 处理报警布防/撤防命令 + * + * @param device 设备信息 + * @param rootElement 根节点 + * @param request 请求信息 + * @param type 消息类型 + */ + private void handleGuardCmd(Device device, Element rootElement, SIPRequest request, DeviceControlType type) { + //获取整个消息主体,我们只需要修改请求头即可 + String cmdString = getText(rootElement, type.getVal()); + try { + cmder.guardCmd(device, cmdString, + errorResult -> onError(request, errorResult), + okResult -> onOk(request, okResult)); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 布防/撤防命令: {}", e.getMessage()); + } + } + + + /** + * 错误响应处理 + * + * @param request 请求 + * @param eventResult 响应结构 + */ + private void onError(SIPRequest request, SipSubscribe.EventResult eventResult) { + // 失败的回复 + try { + responseAck(request, eventResult.statusCode, eventResult.msg); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 回复: {}", e.getMessage()); + } + } + + /** + * 成功响应处理 + * + * @param request 请求 + * @param eventResult 响应结构 + */ + private void onOk(SIPRequest request, SipSubscribe.EventResult eventResult) { + // 成功的回复 + try { + responseAck(request, eventResult.statusCode); + } 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/NotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/NotifyMessageHandler.java new file mode 100644 index 000000000..bb34189e4 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/NotifyMessageHandler.java @@ -0,0 +1,26 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify; + +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.MessageHandlerAbstract; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.MessageRequestProcessor; +import org.springframework.beans.factory.InitializingBean; +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 { + + private final String messageType = "Notify"; + + @Autowired + private MessageRequestProcessor messageRequestProcessor; + + @Override + public void afterPropertiesSet() throws Exception { + messageRequestProcessor.addHandler(messageType, this); + } +} 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 new file mode 100644 index 000000000..6928def26 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java @@ -0,0 +1,283 @@ +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.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.*; +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.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.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 final Logger logger = LoggerFactory.getLogger(AlarmNotifyMessageHandler.class); + private final String cmdType = "Alarm"; + + @Autowired + private NotifyMessageHandler notifyMessageHandler; + + @Autowired + private EventPublisher publisher; + + @Autowired + private UserSetting userSetting; + + @Autowired + private SipConfig sipConfig; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IDeviceAlarmService deviceAlarmService; + + @Autowired + private IDeviceChannelService deviceChannelService; + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + + @Override + public void afterPropertiesSet() throws Exception { + notifyMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element rootElement) { + logger.info("[收到报警通知]设备:{}", device.getDeviceId()); + boolean isEmpty = taskQueue.isEmpty(); + taskQueue.offer(new SipMsgInfo(evt, device, rootElement)); + // 回复200 OK + try { + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 报警通知回复: {}", e.getMessage()); + } + if (isEmpty) { + taskExecutor.execute(() -> { + logger.info("[处理报警通知]待处理数量:{}", taskQueue.size() ); + while (!taskQueue.isEmpty()) { + try { + SipMsgInfo sipMsgInfo = taskQueue.poll(); + + 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", DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(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)); + // 作者自用判断,其他小伙伴需要此消息可以自行修改,但是不要提在pr里 + if (DeviceAlarmMethod.Other.getVal() == Integer.parseInt(deviceAlarm.getAlarmMethod())) { + // 发送给平台的报警信息。 发送redis通知 + logger.info("[发送给平台的报警信息]内容:{}", JSONObject.toJSONString(deviceAlarm)); + AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage(); + if (deviceAlarm.getAlarmMethod() != null) { + alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod())); + } + alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription()); + if (deviceAlarm.getAlarmType() != null) { + alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType())); + } + 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.error("未处理的异常 ", e); + logger.warn("[收到报警通知] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest()); + } + } + }); + } + } + + @Override + 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(); + if (deviceAlarm.getAlarmMethod() != null) { + alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod())); + } + alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription()); + alarmChannelMessage.setGbId(channelId); + if (deviceAlarm.getAlarmType() != null) { + alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType())); + } + redisCatchStorage.sendAlarmMsg(alarmChannelMessage); + } + } +} 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 new file mode 100644 index 000000000..5ec76dd80 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java @@ -0,0 +1,107 @@ +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.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; +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; + +/** + * 状态信息(心跳)报送 + */ +@Component +public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + + private Logger logger = LoggerFactory.getLogger(KeepaliveNotifyMessageHandler.class); + private final static String cmdType = "Keepalive"; + + @Autowired + private NotifyMessageHandler notifyMessageHandler; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private UserSetting userSetting; + + @Autowired + private DynamicTask dynamicTask; + + @Override + public void afterPropertiesSet() throws Exception { + notifyMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + 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()) { + logger.info("[心跳] 设备{}地址变化, 远程地址为: {}:{}", device.getDeviceId(), remoteAddressInfo.getIp(), remoteAddressInfo.getPort()); + device.setPort(remoteAddressInfo.getPort()); + device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort()))); + device.setIp(remoteAddressInfo.getIp()); + } + 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.isOnLine()) { + deviceService.updateDevice(device); + }else { + // 对于已经离线的设备判断他的注册是否已经过期 + if (!deviceService.expire(device)){ + device.setOnLine(false); + deviceService.online(device, null); + } + } + // 刷新过期任务 + String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId(); + // 如果三次心跳失败,则设置设备离线 + dynamicTask.startDelay(registerExpireTaskKey, ()-> deviceService.offline(device.getDeviceId(), "三次心跳失败"), device.getKeepaliveIntervalTime()*1000*3); + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + // 不会收到上级平台的心跳信息 + + } +} 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 new file mode 100644 index 000000000..fdcd5e4b3 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java @@ -0,0 +1,136 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd; + +import com.genersoft.iot.vmp.common.InviteInfo; +import com.genersoft.iot.vmp.common.InviteSessionType; +import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.bean.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.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.service.IInviteStreamService; +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; +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.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 { + + private Logger logger = LoggerFactory.getLogger(MediaStatusNotifyMessageHandler.class); + private final String cmdType = "MediaStatus"; + + @Autowired + private NotifyMessageHandler notifyMessageHandler; + + @Autowired + private SIPCommander cmder; + + @Autowired + private SIPCommanderFroPlatform sipCommanderFroPlatform; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IVideoManagerStorage storage; + + @Autowired + private VideoStreamSessionManager sessionManager; + + @Autowired + private ZlmHttpHookSubscribe subscribe; + + @Autowired + private IInviteStreamService inviteStreamService; + + @Autowired + private VideoStreamSessionManager streamSession; + + @Override + public void afterPropertiesSet() throws Exception { + notifyMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element rootElement) { + + // 回复200 OK + try { + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 录像流推送完毕,回复200OK: {}", e.getMessage()); + } + CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME); + String NotifyType =getText(rootElement, "NotifyType"); + if ("121".equals(NotifyType)){ + logger.info("[录像流]推送完毕,收到关流通知"); + + SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(null, null, callIdHeader.getCallId(), null); + if (ssrcTransaction != null) { + logger.info("[录像流]推送完毕,关流通知, device: {}, channelId: {}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); + InviteInfo inviteInfo = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream()); + if (inviteInfo.getStreamInfo() != null) { + inviteInfo.getStreamInfo().setProgress(1); + inviteStreamService.updateInviteInfo(inviteInfo); + } + + try { + cmder.streamByeCmd(device, ssrcTransaction.getChannelId(), null, callIdHeader.getCallId()); + } catch (InvalidArgumentException | ParseException | SsrcTransactionNotFoundException | SipException e) { + logger.error("[录像流]推送完毕,收到关流通知, 发送BYE失败 {}", e.getMessage()); + } + // 去除监听流注销自动停止下载的监听 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcTransaction.getStream(), false, "rtsp", ssrcTransaction.getMediaServerId()); + subscribe.removeSubscribe(hookSubscribe); + + // 如果级联播放,需要给上级发送此通知 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()); + } + } + }else { + logger.info("[录像流]推送完毕,关流通知, 但是未找到对应的下载信息"); + } + } + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java new file mode 100644 index 000000000..9a82b8ab4 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java @@ -0,0 +1,172 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd; + +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.gb28181.utils.SipUtils; +import com.genersoft.iot.vmp.service.IDeviceChannelService; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.utils.DateUtil; +import gov.nist.javax.sip.message.SIPRequest; +import 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; +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 { + + private Logger logger = LoggerFactory.getLogger(MobilePositionNotifyMessageHandler.class); + private final String cmdType = "MobilePosition"; + + @Autowired + private NotifyMessageHandler notifyMessageHandler; + + @Autowired + private UserSetting userSetting; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IDeviceChannelService deviceChannelService; + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + @Override + public void afterPropertiesSet() throws Exception { + notifyMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element rootElement) { + + 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(() -> { + 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")); + String time = getText(rootElementAfterCharset, "Time"); + if (ObjectUtils.isEmpty(time)){ + mobilePosition.setTime(DateUtil.getNow()); + }else { + mobilePosition.setTime(SipUtils.parseTime(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"); + + + // 更新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", DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(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) { + logger.error("未处理的异常 ", e); + } catch (Exception e) { + logger.warn("[移动位置通知] 发现未处理的异常, \r\n{}", evt.getRequest()); + logger.error("[移动位置通知] 异常内容: ", e); + } + } + }); + } + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/QueryMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/QueryMessageHandler.java new file mode 100644 index 000000000..9a29955c9 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/QueryMessageHandler.java @@ -0,0 +1,25 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query; + +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.MessageHandlerAbstract; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.MessageRequestProcessor; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 命令类型: 查询指令 + * 命令类型: 设备状态, 设备目录信息, 设备信息, 文件目录检索(TODO), 报警(TODO), 设备配置(TODO), 设备预置位(TODO), 移动设备位置数据(TODO) + */ +@Component +public class QueryMessageHandler extends MessageHandlerAbstract implements InitializingBean { + + private final String messageType = "Query"; + + @Autowired + private MessageRequestProcessor messageRequestProcessor; + + @Override + public void afterPropertiesSet() throws Exception { + messageRequestProcessor.addHandler(messageType, this); + } +} 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 new file mode 100644 index 000000000..ce8c9397a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/AlarmQueryMessageHandler.java @@ -0,0 +1,68 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd; + +import com.genersoft.iot.vmp.conf.SipConfig; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.genersoft.iot.vmp.gb28181.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; +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; + +@Component +public class AlarmQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(AlarmQueryMessageHandler.class); + private final String cmdType = "Alarm"; + + @Autowired + private QueryMessageHandler queryMessageHandler; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommanderFroPlatform cmderFroPlatform; + + @Autowired + private SipConfig config; + + @Autowired + private EventPublisher publisher; + + @Override + public void afterPropertiesSet() throws Exception { + queryMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + + logger.info("不支持alarm查询"); + try { + responseAck((SIPRequest) evt.getRequest(), Response.NOT_FOUND, "not support alarm query"); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 alarm查询回复200OK: {}", e.getMessage()); + } + + } +} 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 new file mode 100644 index 000000000..25a3df375 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java @@ -0,0 +1,130 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd; + +import com.genersoft.iot.vmp.conf.SipConfig; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.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.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.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.ArrayList; +import java.util.List; + +@Component +public class CatalogQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(CatalogQueryMessageHandler.class); + private final String cmdType = "Catalog"; + + @Autowired + private QueryMessageHandler queryMessageHandler; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommanderFroPlatform cmderFroPlatform; + + @Autowired + private SipConfig config; + + @Autowired + private EventPublisher publisher; + + @Autowired + private IVideoManagerStorage storage; + + @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) { + + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); + try { + // 回复200 OK + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 目录查询回复200OK: {}", e.getMessage()); + } + Element snElement = rootElement.element("SN"); + String sn = snElement.getText(); + // 准备回复通道信息 + List deviceChannelInPlatforms = storager.queryChannelWithCatalog(parentPlatform.getServerGBId()); + // 查询关联的直播通道 + List gbStreams = storager.queryGbStreamListInPlatform(parentPlatform.getServerGBId()); + // 回复目录信息 + List catalogs = storager.queryCatalogInPlatform(parentPlatform.getServerGBId()); + + List 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(), 0); + } + } 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 new file mode 100644 index 000000000..8b774e100 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java @@ -0,0 +1,85 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd; + +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.genersoft.iot.vmp.gb28181.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; +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 static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; + +@Component +public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(DeviceInfoQueryMessageHandler.class); + private final String cmdType = "DeviceInfo"; + + @Autowired + private QueryMessageHandler queryMessageHandler; + + @Autowired + private SIPCommanderFroPlatform cmderFroPlatform; + @Autowired + private IVideoManagerStorage storager; + + @Override + public void afterPropertiesSet() throws Exception { + queryMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element rootElement) { + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + logger.info("[DeviceInfo查询]消息"); + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); + try { + // 回复200 OK + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] DeviceInfo查询回复: {}", e.getMessage()); + return; + } + String sn = rootElement.element("SN").getText(); + + /*根据WVP原有的数据结构,设备和通道是分开放置,设备信息都是存放在设备表里,通道表里的设备信息不可作为真实信息处理 + 大部分NVR/IPC设备对他的通道信息实现都是返回默认的值没有什么参考价值。NVR/IPC通道我们统一使用设备表的设备信息来作为返回。 + 我们这里使用查询数据库的方式来实现这个设备信息查询的功能,在其他地方对设备信息更新达到正确的目的。*/ + + String channelId = getText(rootElement, "DeviceID"); + // 查询这是通道id还是设备id + Device device = null; + // 如果id指向平台的国标编号,那么就是查询平台的信息 + if (!parentPlatform.getDeviceGBId().equals(channelId)) { + device = storager.queryDeviceInfoByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId); + if (device ==null){ + logger.error("[平台没有该通道的使用权限]:platformId"+parentPlatform.getServerGBId()+" deviceID:"+channelId); + 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 new file mode 100644 index 000000000..f9edfb396 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceStatusQueryMessageHandler.java @@ -0,0 +1,85 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd; + +import com.genersoft.iot.vmp.conf.SipConfig; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.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.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.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 static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; + +@Component +public class DeviceStatusQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(DeviceStatusQueryMessageHandler.class); + private final String cmdType = "DeviceStatus"; + + @Autowired + private QueryMessageHandler queryMessageHandler; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommanderFroPlatform cmderFroPlatform; + + @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) { + + logger.info("接收到DeviceStatus查询消息"); + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); + // 回复200 OK + try { + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 DeviceStatus查询回复200OK: {}", e.getMessage()); + } + String sn = rootElement.element("SN").getText(); + String channelId = getText(rootElement, "DeviceID"); + DeviceChannel deviceChannel = storager.queryChannelInParentPlatform(parentPlatform.getServerGBId(), channelId); + if (deviceChannel ==null){ + logger.error("[平台没有该通道的使用权限]:platformId"+parentPlatform.getServerGBId()+" deviceID:"+channelId); + return; + } + try { + cmderFroPlatform.deviceStatusResponse(parentPlatform,channelId, sn, fromHeader.getTag(),deviceChannel.isStatus()); + } 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 000000000..f3170a5dd --- /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/ResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/ResponseMessageHandler.java new file mode 100644 index 000000000..18da9cde9 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/ResponseMessageHandler.java @@ -0,0 +1,25 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response; + +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.MessageHandlerAbstract; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.MessageRequestProcessor; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 命令类型: 请求动作的应答 + * 命令类型: 设备控制, 报警通知, 设备目录信息查询, 目录信息查询, 目录收到, 设备信息查询, 设备状态信息查询 ...... + */ +@Component +public class ResponseMessageHandler extends MessageHandlerAbstract implements InitializingBean { + + private final String messageType = "Response"; + + @Autowired + private MessageRequestProcessor messageRequestProcessor; + + @Override + public void afterPropertiesSet() throws Exception { + messageRequestProcessor.addHandler(messageType, this); + } +} 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 new file mode 100644 index 000000000..3edf02fea --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/AlarmResponseMessageHandler.java @@ -0,0 +1,58 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; + +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; +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; +import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.RequestEvent; + +@Component +public class AlarmResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(AlarmResponseMessageHandler.class); + private final String cmdType = "Alarm"; + + @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 rootElement) { + Element deviceIdElement = rootElement.element("DeviceID"); + String channelId = deviceIdElement.getText().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_ALARM + device.getDeviceId() + channelId; + JSONObject json = new JSONObject(); + XmlUtil.node2Json(rootElement, json); + if (logger.isDebugEnabled()) { + logger.debug(json.toJSONString()); + } + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setData(json); + deferredResultHolder.invokeAllResult(msg); + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java new file mode 100644 index 000000000..2e0d07166 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java @@ -0,0 +1,73 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; + +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; +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; +import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; +import 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 static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; + +@Component +public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(BroadcastResponseMessageHandler.class); + private final String cmdType = "Broadcast"; + + @Autowired + private ResponseMessageHandler responseMessageHandler; + + @Autowired + private DeferredResultHolder deferredResultHolder; + + @Override + public void afterPropertiesSet() throws Exception { + responseMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element rootElement) { + try { + String channelId = getText(rootElement, "DeviceID"); + String key = DeferredResultHolder.CALLBACK_CMD_BROADCAST + device.getDeviceId() + channelId; + // 回复200 OK + responseAck((SIPRequest) evt.getRequest(), Response.OK); + // 此处是对本平台发出Broadcast指令的应答 + JSONObject json = new JSONObject(); + XmlUtil.node2Json(rootElement, json); + if (logger.isDebugEnabled()) { + logger.debug(json.toJSONString()); + } + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setData(json); + deferredResultHolder.invokeAllResult(msg); + + + } catch (ParseException | SipException | InvalidArgumentException e) { + logger.error("[命令发送失败] 国标级联 语音喊话: {}", e.getMessage()); + } + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java new file mode 100644 index 000000000..8648dc455 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java @@ -0,0 +1,187 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; + +import com.genersoft.iot.vmp.conf.CivilCodeFileConf; +import com.genersoft.iot.vmp.conf.SipConfig; +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.SipUtils; +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; +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 java.util.concurrent.ConcurrentLinkedQueue; + +/** + * 目录查询的回复 + */ +@Component +public class CatalogResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(CatalogResponseMessageHandler.class); + private final String cmdType = "Catalog"; + + @Autowired + private ResponseMessageHandler responseMessageHandler; + + private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private CatalogDataCatch catalogDataCatch; + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + @Autowired + private CivilCodeFileConf civilCodeFileConf; + + @Autowired + private SipConfig sipConfig; + + @Override + public void afterPropertiesSet() throws Exception { + responseMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + boolean isEmpty = taskQueue.isEmpty(); + taskQueue.offer(new HandlerCatchData(evt, device, element)); + // 回复200 OK + try { + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 目录查询回复: {}", e.getMessage()); + } + // 如果不为空则说明已经开启消息处理 + 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<>(); + List parentChannelIds = new ArrayList<>(); + // 遍历DeviceList + while (deviceListIterator.hasNext()) { + Element itemDevice = deviceListIterator.next(); + Element channelDeviceElement = itemDevice.element("DeviceID"); + if (channelDeviceElement == null) { + continue; + } + DeviceChannel channel = XmlUtil.channelContentHandler(itemDevice, device, null, civilCodeFileConf); + if (channel == null) { + logger.info("[收到目录订阅]:但是解析失败 {}", new String(evt.getRequest().getRawContent())); + continue; + } + if (channel.getParentId() != null && channel.getParentId().equals(sipConfig.getId())) { + channel.setParentId(null); + } + SipUtils.updateGps(channel, device.getGeoCoordSys()); + channel.setDeviceId(take.getDevice().getDeviceId()); + + channelList.add(channel); + } + int sn = Integer.parseInt(snElement.getText()); + catalogDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, take.getDevice(), channelList); + logger.info("[收到通道]设备: {} -> {}个,{}/{}", take.getDevice().getDeviceId(), channelList.size(), catalogDataCatch.get(take.getDevice().getDeviceId()) == null ? 0 : catalogDataCatch.get(take.getDevice().getDeviceId()).size(), sumNum); + if (catalogDataCatch.get(take.getDevice().getDeviceId()).size() == sumNum) { + // 数据已经完整接收, 此时可能存在某个设备离线变上线的情况,但是考虑到性能,此处不做处理, + // 目前支持设备通道上线通知时和设备上线时向上级通知 + 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{}", evt.getRequest()); + logger.error("[收到通道] 异常内容: ", e); + } + } + }); + } + + } + + @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 new file mode 100644 index 000000000..47ae1a9c1 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/ConfigDownloadResponseMessageHandler.java @@ -0,0 +1,79 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; + +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; +import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; +import 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 static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; + +@Component +public class ConfigDownloadResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(ConfigDownloadResponseMessageHandler.class); + private final String cmdType = "ConfigDownload"; + + @Autowired + private ResponseMessageHandler responseMessageHandler; + + @Autowired + private EventPublisher publisher; + + @Autowired + private DeferredResultHolder deferredResultHolder; + + @Override + public void afterPropertiesSet() throws Exception { + responseMessageHandler.addHandler(cmdType, this); + } + + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + String channelId = getText(element, "DeviceID"); + String key = DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + device.getDeviceId() + channelId; + try { + // 回复200 OK + 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); + + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + // 不会收到上级平台的心跳信息 + + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceConfigResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceConfigResponseMessageHandler.java new file mode 100644 index 000000000..b18a87b29 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceConfigResponseMessageHandler.java @@ -0,0 +1,58 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; + +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; +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; +import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.RequestEvent; + +import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; + +@Component +public class DeviceConfigResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(DeviceConfigResponseMessageHandler.class); + private final String cmdType = "DeviceConfig"; + + @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) { + JSONObject json = new JSONObject(); + XmlUtil.node2Json(element, json); + String channelId = getText(element, "DeviceID"); + if (logger.isDebugEnabled()) { + logger.debug(json.toJSONString()); + } + String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + device.getDeviceId() + channelId; + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setData(json); + deferredResultHolder.invokeAllResult(msg); + } + + @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/DeviceControlResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceControlResponseMessageHandler.java new file mode 100644 index 000000000..af08e13ba --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceControlResponseMessageHandler.java @@ -0,0 +1,71 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; + +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; +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; +import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; +import 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 static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; + +@Component +public class DeviceControlResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(DeviceControlResponseMessageHandler.class); + private final String cmdType = "DeviceControl"; + + @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) { + // 此处是对本平台发出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); + if (logger.isDebugEnabled()) { + logger.debug(json.toJSONString()); + } + RequestMessage msg = new RequestMessage(); + String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + device.getDeviceId() + channelId; + msg.setKey(key); + msg.setData(json); + deferredResultHolder.invokeAllResult(msg); + + } + + @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/DeviceInfoResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java new file mode 100644 index 000000000..849be81f0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java @@ -0,0 +1,107 @@ +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.transmit.callback.DeferredResultHolder; +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; +import com.genersoft.iot.vmp.service.IDeviceService; +import 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 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 static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; + +/** + * @author lin + */ +@Component +public class DeviceInfoResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(DeviceInfoResponseMessageHandler.class); + private final String cmdType = "DeviceInfo"; + + @Autowired + private ResponseMessageHandler responseMessageHandler; + + @Autowired + private DeferredResultHolder deferredResultHolder; + + + @Autowired + private IDeviceService deviceService; + + @Override + public void afterPropertiesSet() throws Exception { + responseMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element rootElement) { + logger.debug("接收到DeviceInfo应答消息"); + // 检查设备是否存在, 不存在则不回复 + if (device == null || !device.isOnLine()) { + 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; + device.setName(getText(rootElement, "DeviceName")); + + device.setManufacturer(getText(rootElement, "Manufacturer")); + device.setModel(getText(rootElement, "Model")); + device.setFirmware(getText(rootElement, "Firmware")); + if (ObjectUtils.isEmpty(device.getStreamMode())) { + device.setStreamMode("UDP"); + } + deviceService.updateDevice(device); + + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setData(device); + deferredResultHolder.invokeAllResult(msg); + } catch (DocumentException e) { + throw new RuntimeException(e); + } + try { + // 回复200 OK + responseAck(request, Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] DeviceInfo应答消息 200: {}", e.getMessage()); + } + + } + + @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/DeviceStatusResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java new file mode 100644 index 000000000..da4743f68 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java @@ -0,0 +1,89 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; + +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; +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; +import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; +import com.genersoft.iot.vmp.service.IDeviceService; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +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; + +@Component +public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(DeviceStatusResponseMessageHandler.class); + private final String cmdType = "DeviceStatus"; + + @Autowired + private ResponseMessageHandler responseMessageHandler; + + @Autowired + private DeferredResultHolder deferredResultHolder; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Override + public void afterPropertiesSet() throws Exception { + responseMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + logger.info("接收到DeviceStatus应答消息"); + // 检查设备是否存在, 不存在则不回复 + if (device == null) { + return; + } + // 回复200 OK + try { + 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, null); + }else { + deviceService.offline(device.getDeviceId(), "设备状态查询结果:" + text.trim()); + } + RequestMessage msg = new RequestMessage(); + msg.setKey(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + device.getDeviceId()); + msg.setData(json); + deferredResultHolder.invokeAllResult(msg); + } + + @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/MobilePositionResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java new file mode 100644 index 000000000..36a72bcd5 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java @@ -0,0 +1,173 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; + +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.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +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.NumericUtil; +import com.genersoft.iot.vmp.gb28181.utils.SipUtils; +import com.genersoft.iot.vmp.service.IDeviceChannelService; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.utils.DateUtil; +import gov.nist.javax.sip.message.SIPRequest; +import 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 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 static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; + +/** + * 移动设备位置数据查询回复 + * @author lin + */ +@Component +public class MobilePositionResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(MobilePositionResponseMessageHandler.class); + private final String cmdType = "MobilePosition"; + + @Autowired + private ResponseMessageHandler responseMessageHandler; + + @Autowired + private UserSetting userSetting; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IDeviceChannelService deviceChannelService; + + @Autowired + private DeferredResultHolder resultHolder; + + @Override + public void afterPropertiesSet() throws Exception { + responseMessageHandler.addHandler(cmdType, this); + } + + @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(); + mobilePosition.setCreateTime(DateUtil.getNow()); + if (!ObjectUtils.isEmpty(device.getName())) { + mobilePosition.setDeviceName(device.getName()); + } + mobilePosition.setDeviceId(device.getDeviceId()); + mobilePosition.setChannelId(getText(rootElement, "DeviceID")); + //兼容ISO 8601格式时间 + String time = getText(rootElement, "Time"); + if (ObjectUtils.isEmpty(time)){ + mobilePosition.setTime(DateUtil.getNow()); + }else { + mobilePosition.setTime(SipUtils.parseTime(time)); + } + mobilePosition.setLongitude(Double.parseDouble(getText(rootElement, "Longitude"))); + mobilePosition.setLatitude(Double.parseDouble(getText(rootElement, "Latitude"))); + if (NumericUtil.isDouble(getText(rootElement, "Speed"))) { + mobilePosition.setSpeed(Double.parseDouble(getText(rootElement, "Speed"))); + } else { + mobilePosition.setSpeed(0.0); + } + if (NumericUtil.isDouble(getText(rootElement, "Direction"))) { + mobilePosition.setDirection(Double.parseDouble(getText(rootElement, "Direction"))); + } else { + mobilePosition.setDirection(0.0); + } + if (NumericUtil.isDouble(getText(rootElement, "Altitude"))) { + mobilePosition.setAltitude(Double.parseDouble(getText(rootElement, "Altitude"))); + } else { + mobilePosition.setAltitude(0.0); + } + mobilePosition.setReportSource("Mobile Position"); + + // 更新device channel 的经纬度 + DeviceChannel 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.updateChannelPosition(deviceChannel); + + String key = DeferredResultHolder.CALLBACK_CMD_MOBILE_POSITION + device.getDeviceId(); + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setData(mobilePosition); + resultHolder.invokeAllResult(msg); + + // 发送redis消息。 通知位置信息的变化 + JSONObject jsonObject = new JSONObject(); + jsonObject.put("time", DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(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 + try { + responseAck(request, Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 移动设备位置数据查询 200: {}", e.getMessage()); + } + + } catch (DocumentException e) { + logger.error("未处理的异常 ", e); + } + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/PresetQueryResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/PresetQueryResponseMessageHandler.java new file mode 100644 index 000000000..862a8eea2 --- /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 new file mode 100644 index 000000000..36e5df299 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java @@ -0,0 +1,183 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; + +import com.genersoft.iot.vmp.common.VideoManagerConstants; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.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.utils.DateUtil; +import com.genersoft.iot.vmp.utils.UJson; +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.data.redis.core.RedisTemplate; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; +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.*; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; + +/** + * @author lin + */ +@Component +public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private final Logger logger = LoggerFactory.getLogger(RecordInfoResponseMessageHandler.class); + private final String cmdType = "RecordInfo"; + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Autowired + private ResponseMessageHandler responseMessageHandler; + + @Autowired + private DeferredResultHolder deferredResultHolder; + + @Autowired + private EventPublisher eventPublisher; + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + @Autowired + private RedisTemplate redisTemplate; + + private Long recordInfoTtl = 1800L; + + @Override + public void afterPropertiesSet() throws Exception { + responseMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element rootElement) { + try { + // 回复200 OK + responseAck((SIPRequest) evt.getRequest(), Response.OK); + }catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 国标录像: {}", e.getMessage()); + } + taskExecutor.execute(()->{ + try { + + String sn = getText(rootElement, "SN"); + String channelId = getText(rootElement, "DeviceID"); + RecordInfo recordInfo = new RecordInfo(); + recordInfo.setChannelId(channelId); + recordInfo.setDeviceId(device.getDeviceId()); + recordInfo.setSn(sn); + recordInfo.setName(getText(rootElement, "Name")); + String sumNumStr = getText(rootElement, "SumNum"); + int sumNum = 0; + if (!ObjectUtils.isEmpty(sumNumStr)) { + sumNum = Integer.parseInt(sumNumStr); + } + recordInfo.setSumNum(sumNum); + Element recordListElement = rootElement.element("RecordList"); + if (recordListElement == null || sumNum == 0) { + logger.info("无录像数据"); + recordInfo.setCount(sumNum); + eventPublisher.recordEndEventPush(recordInfo); + releaseRequest(device.getDeviceId(), sn,recordInfo); + } 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 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); + } + Map map = recordList.stream() + .filter(record -> record.getDeviceId() != null) + .collect(Collectors.toMap(record -> record.getStartTime()+ record.getEndTime(), UJson::writeJson)); + // 获取任务结果数据 + String resKey = VideoManagerConstants.REDIS_RECORD_INFO_RES_PRE + channelId + sn; + redisTemplate.opsForHash().putAll(resKey, map); + redisTemplate.expire(resKey, recordInfoTtl, TimeUnit.SECONDS); + String resCountKey = VideoManagerConstants.REDIS_RECORD_INFO_RES_COUNT_PRE + channelId + sn; + long incr = redisTemplate.opsForValue().increment(resCountKey, map.size()); + redisTemplate.expire(resCountKey, recordInfoTtl, TimeUnit.SECONDS); + recordInfo.setRecordList(recordList); + recordInfo.setCount(Math.toIntExact(incr)); + eventPublisher.recordEndEventPush(recordInfo); + if (incr < sumNum) { + return; + } + // 已接收完成 + List resList = redisTemplate.opsForHash().entries(resKey).values().stream().map(e -> UJson.readJson(e.toString(), RecordItem.class)).collect(Collectors.toList()); + if (resList.size() < sumNum) { + return; + } + recordInfo.setRecordList(resList); + releaseRequest(device.getDeviceId(), sn,recordInfo); + } + } + } catch (Exception e) { + logger.error("[国标录像] 发现未处理的异常, \r\n{}", evt.getRequest()); + logger.error("[国标录像] 异常内容: ", e); + } + }); + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + + } + + public void releaseRequest(String deviceId, String sn,RecordInfo recordInfo){ + String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + sn; + // 对数据进行排序 + if(recordInfo!=null && recordInfo.getRecordList()!=null) { + Collections.sort(recordInfo.getRecordList()); + }else{ + recordInfo.setRecordList(new ArrayList<>()); + } + + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setData(recordInfo); + deferredResultHolder.invokeAllResult(msg); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/ISIPResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/ISIPResponseProcessor.java new file mode 100644 index 000000000..50fb20211 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/ISIPResponseProcessor.java @@ -0,0 +1,15 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.response; + +import javax.sip.ResponseEvent; + +/** + * @description:处理接收IPCamera发来的SIP协议响应消息 + * @author: swwheihei + * @date: 2020年5月3日 下午4:42:22 + */ +public interface ISIPResponseProcessor { + + void process(ResponseEvent evt); + + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/SIPResponseProcessorAbstract.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/SIPResponseProcessorAbstract.java new file mode 100644 index 000000000..9f2a10b07 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/SIPResponseProcessorAbstract.java @@ -0,0 +1,8 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.response; + +import org.springframework.beans.factory.InitializingBean; + +public abstract class SIPResponseProcessorAbstract implements InitializingBean, ISIPResponseProcessor { + + +} 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 new file mode 100644 index 000000000..ff63fad04 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/ByeResponseProcessor.java @@ -0,0 +1,48 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl; + +import com.genersoft.iot.vmp.conf.SipConfig; +import com.genersoft.iot.vmp.gb28181.SipLayer; +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; +import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.ResponseEvent; + +/** + * @description: BYE请求响应器 + * @author: swwheihei + * @date: 2020年5月3日 下午5:32:05 + */ +@Component +public class ByeResponseProcessor extends SIPResponseProcessorAbstract { + + private final String method = "BYE"; + + @Autowired + private SipLayer sipLayer; + + @Autowired + private SipConfig config; + + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addResponseProcessor(method, this); + } + /** + * 处理BYE响应 + * + * @param evt + */ + @Override + public void process(ResponseEvent evt) { + // TODO Auto-generated method stub + } + + +} 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 new file mode 100644 index 000000000..775beeb6d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/CancelResponseProcessor.java @@ -0,0 +1,47 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl; + +import com.genersoft.iot.vmp.conf.SipConfig; +import com.genersoft.iot.vmp.gb28181.SipLayer; +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; +import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.ResponseEvent; + +/** + * @description: CANCEL响应处理器 + * @author: panlinlin + * @date: 2021年11月5日 16:35 + */ +@Component +public class CancelResponseProcessor extends SIPResponseProcessorAbstract { + + private final String method = "CANCEL"; + + @Autowired + private SipLayer sipLayer; + + @Autowired + private SipConfig config; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addResponseProcessor(method, this); + } + /** + * 处理CANCEL响应 + * + * @param evt + */ + @Override + public void process(ResponseEvent evt) { + // TODO Auto-generated method stub + + } + +} 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 new file mode 100644 index 000000000..436d2a438 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java @@ -0,0 +1,96 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl; + +import com.genersoft.iot.vmp.gb28181.SipLayer; +import com.genersoft.iot.vmp.gb28181.bean.Gb28181Sdp; +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; +import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; +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 gov.nist.javax.sip.ResponseEventExt; +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.stereotype.Component; + +import javax.sdp.SdpParseException; +import javax.sdp.SessionDescription; +import javax.sip.InvalidArgumentException; +import javax.sip.ResponseEvent; +import javax.sip.SipException; +import javax.sip.SipFactory; +import javax.sip.address.SipURI; +import javax.sip.message.Request; +import javax.sip.message.Response; +import java.text.ParseException; + + +/** + * @description: 处理INVITE响应 + * @author: panlinlin + * @date: 2021年11月5日 16:40 + */ +@Component +public class InviteResponseProcessor extends SIPResponseProcessorAbstract { + + private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class); + private final String method = "INVITE"; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + + @Autowired + private SipLayer sipLayer; + + @Autowired + private SIPSender sipSender; + + @Autowired + private SIPRequestHeaderProvider headerProvider; + + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addResponseProcessor(method, this); + } + + + + /** + * 处理invite响应 + * + * @param evt 响应消息 + * @throws ParseException + */ + @Override + public void process(ResponseEvent evt ){ + logger.debug("接收到消息:" + evt.getResponse()); + try { + SIPResponse response = (SIPResponse)evt.getResponse(); + int statusCode = response.getStatusCode(); + // trying不会回复 + if (statusCode == Response.TRYING) { + } + // 成功响应 + // 下发ack + if (statusCode == Response.OK) { + ResponseEventExt event = (ResponseEventExt)evt; + + String contentString = new String(response.getRawContent()); + Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); + SessionDescription sdp = gb28181Sdp.getBaseSdb(); + SipURI requestUri = SipFactory.getInstance().createAddressFactory().createSipURI(sdp.getOrigin().getUsername(), event.getRemoteIpAddress() + ":" + event.getRemotePort()); + Request reqAck = headerProvider.createAckRequest(response.getLocalAddress().getHostAddress(), requestUri, response); + + logger.info("[回复ack] {}-> {}:{} ", sdp.getOrigin().getUsername(), event.getRemoteIpAddress(), event.getRemotePort()); + sipSender.transmitRequest( response.getLocalAddress().getHostAddress(), reqAck); + } + } 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 new file mode 100644 index 000000000..edf2c6807 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java @@ -0,0 +1,113 @@ +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.SipTransactionInfo; +import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder; +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; +import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract; +import com.genersoft.iot.vmp.service.IPlatformService; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo; +import gov.nist.javax.sip.message.SIPResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import 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.WWWAuthenticateHeader; +import javax.sip.message.Response; +import java.text.ParseException; + +/** + * @description:Register响应处理器 + * @author: swwheihei + * @date: 2020年5月3日 下午5:32:23 + */ +@Component +public class RegisterResponseProcessor extends SIPResponseProcessorAbstract { + + private final Logger logger = LoggerFactory.getLogger(RegisterResponseProcessor.class); + private final String method = "REGISTER"; + + @Autowired + private ISIPCommanderForPlatform sipCommanderForPlatform; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Autowired + private SubscribeHolder subscribeHolder; + + @Autowired + private IPlatformService platformService; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addResponseProcessor(method, this); + } + + /** + * 处理Register响应 + * + * @param evt 事件 + */ + @Override + public void process(ResponseEvent evt) { + SIPResponse response = (SIPResponse)evt.getResponse(); + String callId = response.getCallIdHeader().getCallId(); + PlatformRegisterInfo platformRegisterInfo = redisCatchStorage.queryPlatformRegisterInfo(callId); + if (platformRegisterInfo == null) { + logger.info(String.format("[国标级联]未找到callId: %s 的注册/注销平台id", callId )); + return; + } + + ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(platformRegisterInfo.getPlatformId()); + if (parentPlatformCatch == null) { + logger.warn(String.format("[国标级联]收到注册/注销%S请求,平台:%s,但是平台缓存信息未查询到!!!", response.getStatusCode(),platformRegisterInfo.getPlatformId())); + return; + } + + String action = platformRegisterInfo.isRegister() ? "注册" : "注销"; + logger.info(String.format("[国标级联]%s %S响应,%s ", action, response.getStatusCode(), platformRegisterInfo.getPlatformId() )); + ParentPlatform parentPlatform = parentPlatformCatch.getParentPlatform(); + if (parentPlatform == null) { + logger.warn(String.format("[国标级联]收到 %s %s的%S请求, 但是平台信息未查询到!!!", platformRegisterInfo.getPlatformId(), action, response.getStatusCode())); + return; + } + + if (response.getStatusCode() == Response.UNAUTHORIZED) { + WWWAuthenticateHeader www = (WWWAuthenticateHeader)response.getHeader(WWWAuthenticateHeader.NAME); + SipTransactionInfo sipTransactionInfo = new SipTransactionInfo(response); + try { + sipCommanderForPlatform.register(parentPlatform, sipTransactionInfo, www, null, null, platformRegisterInfo.isRegister()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 再次注册: {}", e.getMessage()); + } + }else if (response.getStatusCode() == Response.OK){ + + if (platformRegisterInfo.isRegister()) { + SipTransactionInfo sipTransactionInfo = new SipTransactionInfo(response); + platformService.online(parentPlatform, sipTransactionInfo); + }else { + platformService.offline(parentPlatform, true); + } + + // 注册/注销成功移除缓存的信息 + redisCatchStorage.delPlatformRegisterInfo(callId); + } + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/timeout/ITimeoutProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/timeout/ITimeoutProcessor.java new file mode 100644 index 000000000..e0bb1f895 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/timeout/ITimeoutProcessor.java @@ -0,0 +1,7 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.timeout; + +import javax.sip.TimeoutEvent; + +public interface ITimeoutProcessor { + void process(TimeoutEvent event); +} 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 new file mode 100644 index 000000000..531505d28 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/timeout/impl/TimeoutProcessorImpl.java @@ -0,0 +1,47 @@ +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; + +import javax.sip.TimeoutEvent; +import javax.sip.header.CallIdHeader; + +@Component +public class TimeoutProcessorImpl implements InitializingBean, ITimeoutProcessor { + + private Logger logger = LoggerFactory.getLogger(TimeoutProcessorImpl.class); + + @Autowired + private SIPProcessorObserver processorObserver; + + @Autowired + private SipSubscribe sipSubscribe; + + @Override + public void afterPropertiesSet() throws Exception { + processorObserver.addTimeoutProcessor(this); + } + + @Override + public void process(TimeoutEvent event) { + 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/transmit/request/ISIPRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/ISIPRequestProcessor.java deleted file mode 100644 index 82e2f1d7e..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/ISIPRequestProcessor.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit.request; - -/** - * @Description:处理接收IPCamera发来的SIP协议请求消息 - * @author: swwheihei - * @date: 2020年5月3日 下午4:42:22 - */ -public interface ISIPRequestProcessor { - - public void process(); - -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/SIPRequestAbstractProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/SIPRequestAbstractProcessor.java deleted file mode 100644 index 54108208d..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/SIPRequestAbstractProcessor.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit.request; - -import javax.sip.PeerUnavailableException; -import javax.sip.RequestEvent; -import javax.sip.ServerTransaction; -import javax.sip.SipFactory; -import javax.sip.SipProvider; -import javax.sip.TransactionAlreadyExistsException; -import javax.sip.TransactionUnavailableException; -import javax.sip.address.AddressFactory; -import javax.sip.header.HeaderFactory; -import javax.sip.header.ViaHeader; -import javax.sip.message.MessageFactory; -import javax.sip.message.Request; - -import gov.nist.javax.sip.SipStackImpl; -import gov.nist.javax.sip.message.SIPRequest; -import gov.nist.javax.sip.stack.SIPServerTransaction; - -/** - * @Description:处理接收IPCamera发来的SIP协议请求消息 - * @author: songww - * @date: 2020年5月3日 下午4:42:22 - */ -public abstract class SIPRequestAbstractProcessor implements ISIPRequestProcessor { - - protected RequestEvent evt; - - private SipProvider tcpSipProvider; - - private SipProvider udpSipProvider; - - @Override - public void process() { - this.process(evt); - } - - public abstract void process(RequestEvent evt); - - 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) { - e.printStackTrace(); - } catch (TransactionUnavailableException e) { - e.printStackTrace(); - } - } - return serverTransaction; - } - - public AddressFactory getAddressFactory() { - try { - return SipFactory.getInstance().createAddressFactory(); - } catch (PeerUnavailableException e) { - e.printStackTrace(); - } - return null; - } - - public HeaderFactory getHeaderFactory() { - try { - return SipFactory.getInstance().createHeaderFactory(); - } catch (PeerUnavailableException e) { - e.printStackTrace(); - } - return null; - } - - public MessageFactory getMessageFactory() { - try { - return SipFactory.getInstance().createMessageFactory(); - } catch (PeerUnavailableException e) { - e.printStackTrace(); - } - return null; - } - - public RequestEvent getRequestEvent() { - return evt; - } - - public void setRequestEvent(RequestEvent evt) { - this.evt = evt; - } - - public SipProvider getTcpSipProvider() { - return tcpSipProvider; - } - - public void setTcpSipProvider(SipProvider tcpSipProvider) { - this.tcpSipProvider = tcpSipProvider; - } - - public SipProvider getUdpSipProvider() { - return udpSipProvider; - } - - public void setUdpSipProvider(SipProvider udpSipProvider) { - this.udpSipProvider = udpSipProvider; - } - - -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java deleted file mode 100644 index 13d630c49..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit.request.impl; - -import javax.sip.Dialog; -import javax.sip.InvalidArgumentException; -import javax.sip.RequestEvent; -import javax.sip.SipException; -import javax.sip.message.Request; - -import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor; - -import gov.nist.javax.sip.header.CSeq; - -/** - * @Description:ACK请求处理器 - * @author: swwheihei - * @date: 2020年5月3日 下午5:31:45 - */ -public class AckRequestProcessor extends SIPRequestAbstractProcessor { - - /** - * 处理 ACK请求 - * - * @param evt - */ - @Override - public void process(RequestEvent evt) { - Request request = evt.getRequest(); - Dialog dialog = evt.getDialog(); - if (dialog == null) return; - try { - Request ackRequest = null; - CSeq csReq = (CSeq) request.getHeader(CSeq.NAME); - ackRequest = dialog.createAck(csReq.getSeqNumber()); - dialog.sendAck(ackRequest); - System.out.println("send ack to callee:" + ackRequest.toString()); - } catch (SipException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - e.printStackTrace(); - } - - } - -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java deleted file mode 100644 index 0ba6bd83b..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit.request.impl; - -import javax.sip.InvalidArgumentException; -import javax.sip.RequestEvent; -import javax.sip.SipException; -import javax.sip.message.Response; - -import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor; - -import java.text.ParseException; - -/** - * @Description: BYE请求处理器 - * @author: swwheihei - * @date: 2020年5月3日 下午5:32:05 - */ -public class ByeRequestProcessor extends SIPRequestAbstractProcessor { - - /** - * 处理BYE请求 - * @param evt - */ - @Override - public void process(RequestEvent evt) { - try { - responseAck(evt); - } catch (SipException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - e.printStackTrace(); - } catch (ParseException e) { - e.printStackTrace(); - } - // TODO 优先级99 Bye Request消息实现,此消息一般为级联消息,上级给下级发送视频停止指令 - - } - - /*** - * 回复200 OK - * @param evt - * @throws SipException - * @throws InvalidArgumentException - * @throws ParseException - */ - private void responseAck(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException { - Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest()); - getServerTransaction(evt).sendResponse(response); - } - -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/CancelRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/CancelRequestProcessor.java deleted file mode 100644 index 1b5b886da..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/CancelRequestProcessor.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit.request.impl; - -import javax.sip.RequestEvent; - -import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor; - -/** - * @Description:CANCEL请求处理器 - * @author: swwheihei - * @date: 2020年5月3日 下午5:32:23 - */ -public class CancelRequestProcessor extends SIPRequestAbstractProcessor { - - /** - * 处理CANCEL请求 - * - * @param evt - * @param layer - * @param transaction - * @param config - */ - @Override - public void process(RequestEvent evt) { - // TODO 优先级99 Cancel Request消息实现,此消息一般为级联消息,上级给下级发送请求取消指令 - - } - -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java deleted file mode 100644 index ab70a5ede..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit.request.impl; - -import javax.sip.RequestEvent; - -import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor; - -/** - * @Description:处理INVITE请求 - * @author: swwheihei - * @date: 2020年5月3日 下午4:43:52 - */ -public class InviteRequestProcessor extends SIPRequestAbstractProcessor { - - /** - * 处理invite请求 - * - * @param request - * 请求消息 - */ - @Override - public void process(RequestEvent evt) { - // TODO 优先级99 Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令 -// Request request = requestEvent.getRequest(); -// -// try { -// // 发送100 Trying -// ServerTransaction serverTransaction = getServerTransaction(requestEvent); -// // 查询目标地址 -// URI reqUri = request.getRequestURI(); -// URI contactURI = currUser.get(reqUri); -// -// System.out.println("processInvite rqStr=" + reqUri + " contact=" + contactURI); -// -// // 根据Request uri来路由,后续的响应消息通过VIA来路由 -// Request cliReq = messageFactory.createRequest(request.toString()); -// cliReq.setRequestURI(contactURI); -// -// HeaderFactory headerFactory = SipFactory.getInstance().createHeaderFactory(); -// Via callerVia = (Via) request.getHeader(Via.NAME); -// Via via = (Via) headerFactory.createViaHeader(SIPMain.ip, SIPMain.port, "UDP", -// callerVia.getBranch() + "sipphone"); -// -// cliReq.removeHeader(Via.NAME); -// cliReq.addHeader(via); -// -// // 更新contact的地址 -// ContactHeader contactHeader = headerFactory.createContactHeader(); -// Address address = SipFactory.getInstance().createAddressFactory() -// .createAddress("sip:sipsoft@" + SIPMain.ip + ":" + SIPMain.port); -// contactHeader.setAddress(address); -// contactHeader.setExpires(3600); -// cliReq.setHeader(contactHeader); -// -// clientTransactionId = sipProvider.getNewClientTransaction(cliReq); -// clientTransactionId.sendRequest(); -// -// System.out.println("processInvite clientTransactionId=" + clientTransactionId.toString()); -// -// System.out.println("send invite to callee: " + cliReq); -// } catch (TransactionUnavailableException e1) { -// e1.printStackTrace(); -// } catch (SipException e) { -// e.printStackTrace(); -// } catch (ParseException e) { -// e.printStackTrace(); -// } catch (Exception e) { -// e.printStackTrace(); -// } - } - -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java deleted file mode 100644 index e7fbfe0db..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java +++ /dev/null @@ -1,825 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit.request.impl; - -import java.io.ByteArrayInputStream; -import java.text.ParseException; -import java.util.*; - -import javax.sip.InvalidArgumentException; -import javax.sip.RequestEvent; -import javax.sip.SipException; -import javax.sip.message.Request; -import javax.sip.message.Response; - -import com.alibaba.fastjson.JSONObject; -import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.common.VideoManagerConstants; -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.DeviceAlarm; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; -import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; -import com.genersoft.iot.vmp.gb28181.bean.RecordItem; -import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector; -import com.genersoft.iot.vmp.gb28181.event.EventPublisher; -import com.genersoft.iot.vmp.gb28181.transmit.callback.CheckForAllRecordsThread; -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.request.SIPRequestAbstractProcessor; -import com.genersoft.iot.vmp.gb28181.utils.DateUtil; -import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; -import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; -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.utils.SpringBeanFactory; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; - -import org.dom4j.Document; -import org.dom4j.DocumentException; -import org.dom4j.Element; -import org.dom4j.io.SAXReader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.StringUtils; - -/** - * @Description:MESSAGE请求处理器 - * @author: swwheihei - * @date: 2020年5月3日 下午5:32:41 - */ -@SuppressWarnings(value={"unchecked", "rawtypes"}) -public class MessageRequestProcessor extends SIPRequestAbstractProcessor { - - public static volatile List threadNameList = new ArrayList(); - - private UserSetup userSetup = (UserSetup) SpringBeanFactory.getBean("userSetup"); - - private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class); - - private SIPCommander cmder; - - private IVideoManagerStorager storager; - - private IRedisCatchStorage redisCatchStorage; - - private EventPublisher publisher; - - private RedisUtil redis; - - private DeferredResultHolder deferredResultHolder; - - private DeviceOffLineDetector offLineDetector; - - private final static String CACHE_RECORDINFO_KEY = "CACHE_RECORDINFO_"; - - private static final String MESSAGE_KEEP_ALIVE = "Keepalive"; - private static final String MESSAGE_CONFIG_DOWNLOAD = "ConfigDownload"; - private static final String MESSAGE_CATALOG = "Catalog"; - private static final String MESSAGE_DEVICE_INFO = "DeviceInfo"; - private static final String MESSAGE_ALARM = "Alarm"; - private static final String MESSAGE_RECORD_INFO = "RecordInfo"; - private static final String MESSAGE_MEDIA_STATUS = "MediaStatus"; - // private static final String MESSAGE_BROADCAST = "Broadcast"; - private static final String MESSAGE_DEVICE_STATUS = "DeviceStatus"; - private static final String MESSAGE_DEVICE_CONTROL = "DeviceControl"; - private static final String MESSAGE_DEVICE_CONFIG = "DeviceConfig"; - private static final String MESSAGE_MOBILE_POSITION = "MobilePosition"; - // private static final String MESSAGE_MOBILE_POSITION_INTERVAL = "Interval"; - private static final String MESSAGE_PRESET_QUERY = "PresetQuery"; - - /** - * 处理MESSAGE请求 - * - * @param evt - */ - @Override - public void process(RequestEvent evt) { - - try { - Element rootElement = getRootElement(evt); - String cmd = XmlUtil.getText(rootElement, "CmdType"); - - if (MESSAGE_KEEP_ALIVE.equals(cmd)) { - logger.info("接收到KeepAlive消息"); - processMessageKeepAlive(evt); - } else if (MESSAGE_CONFIG_DOWNLOAD.equals(cmd)) { - logger.info("接收到ConfigDownload消息"); - processMessageConfigDownload(evt); - } else if (MESSAGE_CATALOG.equals(cmd)) { - logger.info("接收到Catalog消息"); - processMessageCatalogList(evt); - } else if (MESSAGE_DEVICE_INFO.equals(cmd)) { - logger.info("接收到DeviceInfo消息"); - processMessageDeviceInfo(evt); - } else if (MESSAGE_DEVICE_STATUS.equals(cmd)) { - logger.info("接收到DeviceStatus消息"); - processMessageDeviceStatus(evt); - } else if (MESSAGE_DEVICE_CONTROL.equals(cmd)) { - logger.info("接收到DeviceControl消息"); - processMessageDeviceControl(evt); - } else if (MESSAGE_DEVICE_CONFIG.equals(cmd)) { - logger.info("接收到DeviceConfig消息"); - processMessageDeviceConfig(evt); - } else if (MESSAGE_ALARM.equals(cmd)) { - logger.info("接收到Alarm消息"); - processMessageAlarm(evt); - } else if (MESSAGE_RECORD_INFO.equals(cmd)) { - logger.info("接收到RecordInfo消息"); - processMessageRecordInfo(evt); - }else if (MESSAGE_MEDIA_STATUS.equals(cmd)) { - logger.info("接收到MediaStatus消息"); - processMessageMediaStatus(evt); - } else if (MESSAGE_MOBILE_POSITION.equals(cmd)) { - logger.info("接收到MobilePosition消息"); - processMessageMobilePosition(evt); - } else if (MESSAGE_PRESET_QUERY.equals(cmd)) { - logger.info("接收到PresetQuery消息"); - processMessagePresetQuery(evt); - } else { - logger.info("接收到消息:" + cmd); - responseAck(evt); - } - } catch (DocumentException | SipException |InvalidArgumentException | ParseException e) { - e.printStackTrace(); - } - } - - /** - * 处理MobilePosition移动位置消息 - * - * @param evt - */ - private void processMessageMobilePosition(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - MobilePosition mobilePosition = new MobilePosition(); - Element deviceIdElement = rootElement.element("DeviceID"); - String deviceId = deviceIdElement.getTextTrim().toString(); - Device device = storager.queryVideoDevice(deviceId); - if (device != null) { - if (!StringUtils.isEmpty(device.getName())) { - mobilePosition.setDeviceName(device.getName()); - } - } - mobilePosition.setDeviceId(XmlUtil.getText(rootElement, "DeviceID")); - mobilePosition.setTime(XmlUtil.getText(rootElement, "Time")); - mobilePosition.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude"))); - mobilePosition.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude"))); - if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Speed"))) { - mobilePosition.setSpeed(Double.parseDouble(XmlUtil.getText(rootElement, "Speed"))); - } else { - mobilePosition.setSpeed(0.0); - } - if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Direction"))) { - mobilePosition.setDirection(Double.parseDouble(XmlUtil.getText(rootElement, "Direction"))); - } else { - mobilePosition.setDirection(0.0); - } - if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Altitude"))) { - mobilePosition.setAltitude(Double.parseDouble(XmlUtil.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(deviceId); - } - storager.insertMobilePosition(mobilePosition); - //回复 200 OK - responseAck(evt); - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { - e.printStackTrace(); - } - } - - /** - * 处理DeviceStatus设备状态Message - * - * @param evt - */ - private void processMessageDeviceStatus(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - String deviceId = XmlUtil.getText(rootElement, "DeviceID"); - // 检查设备是否存在, 不存在则不回复 - if (storager.exists(deviceId)) { - // 回复200 OK - responseAck(evt); - JSONObject json = new JSONObject(); - XmlUtil.node2Json(rootElement, json); - if (logger.isDebugEnabled()) { - logger.debug(json.toJSONString()); - } - RequestMessage msg = new RequestMessage(); - msg.setDeviceId(deviceId); - msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS); - msg.setData(json); - deferredResultHolder.invokeResult(msg); - - if (offLineDetector.isOnline(deviceId)) { - publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); - } else { - } - } - } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { - e.printStackTrace(); - } - } - - /** - * 处理DeviceControl设备状态Message - * - * @param evt - */ - private void processMessageDeviceControl(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - String deviceId = XmlUtil.getText(rootElement, "DeviceID"); - //String result = XmlUtil.getText(rootElement, "Result"); - // 回复200 OK - responseAck(evt); - if (rootElement.getName().equals("Response")) {//} !XmlUtil.isEmpty(result)) { - // 此处是对本平台发出DeviceControl指令的应答 - JSONObject json = new JSONObject(); - XmlUtil.node2Json(rootElement, json); - if (logger.isDebugEnabled()) { - logger.debug(json.toJSONString()); - } - RequestMessage msg = new RequestMessage(); - msg.setDeviceId(deviceId); - msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL); - msg.setData(json); - deferredResultHolder.invokeResult(msg); - } else { - // 此处是上级发出的DeviceControl指令 - } - } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { - e.printStackTrace(); - } - } - - /** - * 处理DeviceConfig设备状态Message - * - * @param evt - */ - private void processMessageDeviceConfig(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - String deviceId = XmlUtil.getText(rootElement, "DeviceID"); - // 回复200 OK - responseAck(evt); - if (rootElement.getName().equals("Response")) { - // 此处是对本平台发出DeviceControl指令的应答 - JSONObject json = new JSONObject(); - XmlUtil.node2Json(rootElement, json); - if (logger.isDebugEnabled()) { - logger.debug(json.toJSONString()); - } - RequestMessage msg = new RequestMessage(); - msg.setDeviceId(deviceId); - msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG); - msg.setData(json); - deferredResultHolder.invokeResult(msg); - } else { - // 此处是上级发出的DeviceConfig指令 - } - } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { - e.printStackTrace(); - } - } - - /** - * 处理ConfigDownload设备状态Message - * - * @param evt - */ - private void processMessageConfigDownload(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - String deviceId = XmlUtil.getText(rootElement, "DeviceID"); - // 回复200 OK - responseAck(evt); - if (rootElement.getName().equals("Response")) { - // 此处是对本平台发出DeviceControl指令的应答 - JSONObject json = new JSONObject(); - XmlUtil.node2Json(rootElement, json); - if (logger.isDebugEnabled()) { - logger.debug(json.toJSONString()); - } - RequestMessage msg = new RequestMessage(); - msg.setDeviceId(deviceId); - msg.setType(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD); - msg.setData(json); - deferredResultHolder.invokeResult(msg); - } else { - // 此处是上级发出的DeviceConfig指令 - } - } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { - e.printStackTrace(); - } - } - - /** - * 处理PresetQuery预置位列表Message - * - * @param evt - */ - private void processMessagePresetQuery(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - String deviceId = XmlUtil.getText(rootElement, "DeviceID"); - // 回复200 OK - responseAck(evt); - if (rootElement.getName().equals("Response")) {// !XmlUtil.isEmpty(result)) { - // 此处是对本平台发出DeviceControl指令的应答 - JSONObject json = new JSONObject(); - XmlUtil.node2Json(rootElement, json); - if (logger.isDebugEnabled()) { - logger.debug(json.toJSONString()); - } - RequestMessage msg = new RequestMessage(); - msg.setDeviceId(deviceId); - msg.setType(DeferredResultHolder.CALLBACK_CMD_PRESETQUERY); - msg.setData(json); - deferredResultHolder.invokeResult(msg); - } else { - // 此处是上级发出的DeviceControl指令 - } - } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { - e.printStackTrace(); - } - } - - /** - * 处理DeviceInfo设备信息Message - * - * @param evt - */ - private void processMessageDeviceInfo(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - Element deviceIdElement = rootElement.element("DeviceID"); - String deviceId = deviceIdElement.getTextTrim().toString(); - - Device device = storager.queryVideoDevice(deviceId); - if (device == null) { - return; - } - device.setName(XmlUtil.getText(rootElement, "DeviceName")); - device.setManufacturer(XmlUtil.getText(rootElement, "Manufacturer")); - device.setModel(XmlUtil.getText(rootElement, "Model")); - device.setFirmware(XmlUtil.getText(rootElement, "Firmware")); - if (StringUtils.isEmpty(device.getStreamMode())) { - device.setStreamMode("UDP"); - } - storager.updateDevice(device); - - RequestMessage msg = new RequestMessage(); - msg.setDeviceId(deviceId); - msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICEINFO); - msg.setData(device); - deferredResultHolder.invokeResult(msg); - // 回复200 OK - responseAck(evt); - if (offLineDetector.isOnline(deviceId)) { - publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); - } - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { - e.printStackTrace(); - } - } - - /*** - * 收到catalog设备目录列表请求 处理 - * - * @param evt - */ - private void processMessageCatalogList(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - Element deviceIdElement = rootElement.element("DeviceID"); - String deviceId = deviceIdElement.getText(); - Element deviceListElement = rootElement.element("DeviceList"); - if (deviceListElement == null) { - return; - } - Iterator deviceListIterator = deviceListElement.elementIterator(); - if (deviceListIterator != null) { - Device device = storager.queryVideoDevice(deviceId); - if (device == null) { - return; - } - // 遍历DeviceList - while (deviceListIterator.hasNext()) { - Element itemDevice = deviceListIterator.next(); - Element channelDeviceElement = itemDevice.element("DeviceID"); - if (channelDeviceElement == null) { - continue; - } - String channelDeviceId = channelDeviceElement.getTextTrim(); - 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); - 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(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 - storager.updateChannel(device.getDeviceId(), deviceChannel); - } - - RequestMessage msg = new RequestMessage(); - msg.setDeviceId(deviceId); - msg.setType(DeferredResultHolder.CALLBACK_CMD_CATALOG); - msg.setData(device); - deferredResultHolder.invokeResult(msg); - // 回复200 OK - responseAck(evt); - if (offLineDetector.isOnline(deviceId)) { - publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); - } - } - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { - e.printStackTrace(); - } - } - - /*** - * alarm设备报警信息处理 - * @param evt - */ - private void processMessageAlarm(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - Element deviceIdElement = rootElement.element("DeviceID"); - String deviceId = deviceIdElement.getText().toString(); - // 回复200 OK - responseAck(evt); - - Device device = storager.queryVideoDevice(deviceId); - if (device == null) { - return; - } - - if (rootElement.getName().equals("Notify")) { // 处理报警通知 - 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")); - if (XmlUtil.getText(rootElement, "AlarmDescription") == null) { - deviceAlarm.setAlarmDescription(""); - } else { - deviceAlarm.setAlarmDescription(XmlUtil.getText(rootElement, "AlarmDescription")); - } - if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Longitude"))) { - deviceAlarm.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude"))); - } else { - deviceAlarm.setLongitude(0.00); - } - if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Latitude"))) { - deviceAlarm.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude"))); - } else { - deviceAlarm.setLatitude(0.00); - } - - if (!XmlUtil.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(deviceId); - } - storager.insertMobilePosition(mobilePosition); - } - } - // TODO: 需要实现存储报警信息、报警分类 - - if (offLineDetector.isOnline(deviceId)) { - publisher.deviceAlarmEventPublish(deviceAlarm); - } - } else if (rootElement.getName().equals("Response")) { // 处理报警查询响应 - JSONObject json = new JSONObject(); - XmlUtil.node2Json(rootElement, json); - if (logger.isDebugEnabled()) { - logger.debug(json.toJSONString()); - } - RequestMessage msg = new RequestMessage(); - msg.setDeviceId(deviceId); - msg.setType(DeferredResultHolder.CALLBACK_CMD_ALARM); - msg.setData(json); - deferredResultHolder.invokeResult(msg); - } - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { - // } catch (DocumentException e) { - e.printStackTrace(); - } - } - - /*** - * 收到keepalive请求 处理 - * - * @param evt - */ - private void processMessageKeepAlive(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - String deviceId = XmlUtil.getText(rootElement, "DeviceID"); - // 检查设备是否存在, 不存在则不回复 - if (storager.exists(deviceId)) { - // 回复200 OK - responseAck(evt); - if (offLineDetector.isOnline(deviceId)) { - publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); - } else { - } - } - } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { - e.printStackTrace(); - } - } - - /*** - * 处理RecordInfo设备录像列表Message请求 TODO 过期时间暂时写死180秒,后续与DeferredResult超时时间保持一致 - * - * @param evt - */ - private void processMessageRecordInfo(RequestEvent evt) { - try { - // 回复200 OK - responseAck(evt); - String uuid = UUID.randomUUID().toString().replace("-", ""); - RecordInfo recordInfo = new RecordInfo(); - Element rootElement = getRootElement(evt); - Element deviceIdElement = rootElement.element("DeviceID"); - String deviceId = deviceIdElement.getText().toString(); - recordInfo.setDeviceId(deviceId); - recordInfo.setName(XmlUtil.getText(rootElement, "Name")); - if (XmlUtil.getText(rootElement, "SumNum")== null || XmlUtil.getText(rootElement, "SumNum") =="") { - recordInfo.setSumNum(0); - } else { - recordInfo.setSumNum(Integer.parseInt(XmlUtil.getText(rootElement, "SumNum"))); - } - String sn = XmlUtil.getText(rootElement, "SN"); - Element recordListElement = rootElement.element("RecordList"); - if (recordListElement == null || recordInfo.getSumNum() == 0) { - logger.info("无录像数据"); - RequestMessage msg = new RequestMessage(); - msg.setDeviceId(deviceId); - msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO); - msg.setData(recordInfo); - deferredResultHolder.invokeResult(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("记录为空,下一个..."); - continue; - } - record = new RecordItem(); - record.setDeviceId(XmlUtil.getText(itemRecord, "DeviceID")); - record.setName(XmlUtil.getText(itemRecord, "Name")); - record.setFilePath(XmlUtil.getText(itemRecord, "FilePath")); - record.setAddress(XmlUtil.getText(itemRecord, "Address")); - record.setStartTime( - DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(XmlUtil.getText(itemRecord, "StartTime"))); - record.setEndTime( - DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(XmlUtil.getText(itemRecord, "EndTime"))); - record.setSecrecy(itemRecord.element("Secrecy") == null ? 0 - : Integer.parseInt(XmlUtil.getText(itemRecord, "Secrecy"))); - record.setType(XmlUtil.getText(itemRecord, "Type")); - record.setRecorderId(XmlUtil.getText(itemRecord, "RecorderID")); - recordList.add(record); - } - recordInfo.setRecordList(recordList); - } - - // 改用单独线程统计已获取录像文件数量,避免多包并行分别统计不完整的问题 - String cacheKey = CACHE_RECORDINFO_KEY + deviceId + 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."); - } - } - - // 存在录像且如果当前录像明细个数小于总条数,说明拆包返回,需要组装,暂不返回 - // if (recordInfo.getSumNum() > 0 && recordList.size() > 0 && recordList.size() < recordInfo.getSumNum()) { - // // 为防止连续请求该设备的录像数据,返回数据错乱,特增加sn进行区分 - // String cacheKey = CACHE_RECORDINFO_KEY + deviceId + sn; - - // redis.set(cacheKey + "_" + uuid, recordList, 90); - // 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() < recordInfo.getSumNum()) { - // logger.info("已获取" + totalRecordList.size() + "项录像数据,共" + recordInfo.getSumNum() + "项"); - // return; - // } - // logger.info("录像数据已全部获取,共" + recordInfo.getSumNum() + "项"); - // recordInfo.setRecordList(totalRecordList); - // for (int i = 0; i < cacheKeys.size(); i++) { - // redis.del(cacheKeys.get(i).toString()); - // } - // } - // // 自然顺序排序, 元素进行升序排列 - // recordInfo.getRecordList().sort(Comparator.naturalOrder()); - } - // 走到这里,有以下可能:1、没有录像信息,第一次收到recordinfo的消息即返回响应数据,无redis操作 - // 2、有录像数据,且第一次即收到完整数据,返回响应数据,无redis操作 - // 3、有录像数据,在超时时间内收到多次包组装后数量足够,返回数据 - - // RequestMessage msg = new RequestMessage(); - // msg.setDeviceId(deviceId); - // msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO); - // msg.setData(recordInfo); - // deferredResultHolder.invokeResult(msg); - // logger.info("处理完成,返回结果"); - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { - e.printStackTrace(); - } - } - - /** - * 收到MediaStatus消息处理 - * - * @param evt - */ - private void processMessageMediaStatus(RequestEvent evt){ - try { - // 回复200 OK - responseAck(evt); - Element rootElement = getRootElement(evt); - String deviceId = XmlUtil.getText(rootElement, "DeviceID"); - String NotifyType =XmlUtil.getText(rootElement, "NotifyType"); - if (NotifyType.equals("121")){ - logger.info("媒体播放完毕,通知关流"); - StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(deviceId, "*"); - if (streamInfo != null) { - redisCatchStorage.stopPlayback(streamInfo); - cmder.streamByeCmd(streamInfo.getStreamId()); - } - } - } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { - e.printStackTrace(); - } - } - - - /*** - * 回复200 OK - * @param evt - * @throws SipException - * @throws InvalidArgumentException - * @throws ParseException - */ - private void responseAck(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException { - Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest()); - getServerTransaction(evt).sendResponse(response); - } - - private Element getRootElement(RequestEvent evt) throws DocumentException { - Request request = evt.getRequest(); - SAXReader reader = new SAXReader(); - reader.setEncoding("gbk"); - Document xml = reader.read(new ByteArrayInputStream(request.getRawContent())); - return xml.getRootElement(); - } - - public void setCmder(SIPCommander cmder) { - this.cmder = cmder; - } - - public void setStorager(IVideoManagerStorager storager) { - this.storager = storager; - } - - public void setPublisher(EventPublisher publisher) { - this.publisher = publisher; - } - - public void setRedis(RedisUtil redis) { - this.redis = redis; - } - - public void setDeferredResultHolder(DeferredResultHolder deferredResultHolder) { - this.deferredResultHolder = deferredResultHolder; - } - - public void setOffLineDetector(DeviceOffLineDetector offLineDetector) { - this.offLineDetector = offLineDetector; - } - - public IRedisCatchStorage getRedisCatchStorage() { - return redisCatchStorage; - } - - public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) { - this.redisCatchStorage = redisCatchStorage; - } -} \ No newline at end of file diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/NotifyRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/NotifyRequestProcessor.java deleted file mode 100644 index 897b6ab87..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/NotifyRequestProcessor.java +++ /dev/null @@ -1,384 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit.request.impl; - -import java.io.ByteArrayInputStream; -import java.text.ParseException; -import java.util.Iterator; - -import javax.sip.InvalidArgumentException; -import javax.sip.RequestEvent; -import javax.sip.SipException; -import javax.sip.message.Request; -import javax.sip.message.Response; - -import com.genersoft.iot.vmp.common.VideoManagerConstants; -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.DeviceAlarm; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; -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.cmd.impl.SIPCommander; -import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor; -import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; -import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; -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.utils.SpringBeanFactory; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; - -import org.dom4j.Document; -import org.dom4j.DocumentException; -import org.dom4j.Element; -import org.dom4j.io.SAXReader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.StringUtils; - -/** - * @Description: Notify请求处理器 - * @author: lawrencehj - * @date: 2021年1月27日 - */ - -public class NotifyRequestProcessor extends SIPRequestAbstractProcessor { - - private UserSetup userSetup = (UserSetup) SpringBeanFactory.getBean("userSetup"); - - private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class); - - private IVideoManagerStorager storager; - - private IRedisCatchStorage redisCatchStorage; - - private EventPublisher publisher; - - 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"; - - @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); - response200Ok(evt); - } - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { - e.printStackTrace(); - } - } - - /** - * 处理MobilePosition移动位置Notify - * - * @param evt - */ - private void processNotifyMobilePosition(RequestEvent evt) { - try { - // 回复 200 OK - Element rootElement = getRootElement(evt); - MobilePosition mobilePosition = new MobilePosition(); - Element deviceIdElement = rootElement.element("DeviceID"); - String deviceId = deviceIdElement.getTextTrim().toString(); - Device device = storager.queryVideoDevice(deviceId); - if (device != null) { - if (!StringUtils.isEmpty(device.getName())) { - mobilePosition.setDeviceName(device.getName()); - } - } - mobilePosition.setDeviceId(XmlUtil.getText(rootElement, "DeviceID")); - mobilePosition.setTime(XmlUtil.getText(rootElement, "Time")); - mobilePosition.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude"))); - mobilePosition.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude"))); - if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Speed"))) { - mobilePosition.setSpeed(Double.parseDouble(XmlUtil.getText(rootElement, "Speed"))); - } else { - mobilePosition.setSpeed(0.0); - } - if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Direction"))) { - mobilePosition.setDirection(Double.parseDouble(XmlUtil.getText(rootElement, "Direction"))); - } else { - mobilePosition.setDirection(0.0); - } - if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Altitude"))) { - mobilePosition.setAltitude(Double.parseDouble(XmlUtil.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(deviceId); - } - storager.insertMobilePosition(mobilePosition); - response200Ok(evt); - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { - e.printStackTrace(); - } - } - - /*** - * 处理alarm设备报警Notify - * - * @param evt - */ - private void processNotifyAlarm(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - Element deviceIdElement = rootElement.element("DeviceID"); - String deviceId = deviceIdElement.getText().toString(); - - Device device = storager.queryVideoDevice(deviceId); - if (device == null) { - 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")); - if (XmlUtil.getText(rootElement, "AlarmDescription") == null) { - deviceAlarm.setAlarmDescription(""); - } else { - deviceAlarm.setAlarmDescription(XmlUtil.getText(rootElement, "AlarmDescription")); - } - if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Longitude"))) { - deviceAlarm.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude"))); - } else { - deviceAlarm.setLongitude(0.00); - } - if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Latitude"))) { - deviceAlarm.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude"))); - } else { - deviceAlarm.setLatitude(0.00); - } - - 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(deviceId); - } - storager.insertMobilePosition(mobilePosition); - } - // TODO: 需要实现存储报警信息、报警分类 - - // 回复200 OK - response200Ok(evt); - if (offLineDetector.isOnline(deviceId)) { - publisher.deviceAlarmEventPublish(deviceAlarm); - } - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { - e.printStackTrace(); - } - } - - /*** - * 处理catalog设备目录列表Notify - * - * @param evt - */ - private void processNotifyCatalogList(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - Element deviceIdElement = rootElement.element("DeviceID"); - String deviceId = deviceIdElement.getText(); - Element deviceListElement = rootElement.element("DeviceList"); - if (deviceListElement == null) { - return; - } - Iterator deviceListIterator = deviceListElement.elementIterator(); - if (deviceListIterator != null) { - Device device = storager.queryVideoDevice(deviceId); - if (device == null) { - return; - } - // 遍历DeviceList - while (deviceListIterator.hasNext()) { - Element itemDevice = deviceListIterator.next(); - Element channelDeviceElement = itemDevice.element("DeviceID"); - if (channelDeviceElement == null) { - continue; - } - String channelDeviceId = channelDeviceElement.getTextTrim(); - 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); - 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(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 - storager.updateChannel(device.getDeviceId(), deviceChannel); - } - - // RequestMessage msg = new RequestMessage(); - // msg.setDeviceId(deviceId); - // msg.setType(DeferredResultHolder.CALLBACK_CMD_CATALOG); - // msg.setData(device); - // deferredResultHolder.invokeResult(msg); - // 回复200 OK - response200Ok(evt); - if (offLineDetector.isOnline(deviceId)) { - publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); - } - } - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { - e.printStackTrace(); - } - } - - /*** - * 回复200 OK - * - * @param evt - * @throws SipException - * @throws InvalidArgumentException - * @throws ParseException - */ - private void response200Ok(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException { - Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest()); - getServerTransaction(evt).sendResponse(response); - } - - private Element getRootElement(RequestEvent evt) throws DocumentException { - Request request = evt.getRequest(); - SAXReader reader = new SAXReader(); - reader.setEncoding("gbk"); - Document xml = reader.read(new ByteArrayInputStream(request.getRawContent())); - return xml.getRootElement(); - } - - public void setCmder(SIPCommander cmder) { - } - - public void setStorager(IVideoManagerStorager storager) { - this.storager = storager; - } - - public void setPublisher(EventPublisher publisher) { - this.publisher = publisher; - } - - public void setRedis(RedisUtil redis) { - } - - public void setDeferredResultHolder(DeferredResultHolder deferredResultHolder) { - } - - public void setOffLineDetector(DeviceOffLineDetector offLineDetector) { - this.offLineDetector = offLineDetector; - } - - public IRedisCatchStorage getRedisCatchStorage() { - return redisCatchStorage; - } - - public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) { - this.redisCatchStorage = redisCatchStorage; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/OtherRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/OtherRequestProcessor.java deleted file mode 100644 index 028835b3c..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/OtherRequestProcessor.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit.request.impl; - -import javax.sip.RequestEvent; - -import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor; - -/** - * @Description:暂不支持的消息请求处理器 - * @author: swwheihei - * @date: 2020年5月3日 下午5:32:59 - */ -public class OtherRequestProcessor extends SIPRequestAbstractProcessor { - - /** - *

    Title: process

    - *

    Description:

    - * @param evt - * @param layer - * @param transaction - * @param config - */ - @Override - public void process(RequestEvent evt) { - System.out.println("Unsupported the method: " + evt.getRequest().getMethod()); - } - -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java deleted file mode 100644 index 22d54a716..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java +++ /dev/null @@ -1,183 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit.request.impl; - -import java.security.NoSuchAlgorithmException; -import java.text.ParseException; -import java.util.Calendar; -import java.util.Locale; - -import javax.sip.InvalidArgumentException; -import javax.sip.RequestEvent; -import javax.sip.SipException; -import javax.sip.header.AuthorizationHeader; -import javax.sip.header.ContactHeader; -import javax.sip.header.ExpiresHeader; -import javax.sip.header.FromHeader; -import javax.sip.header.ViaHeader; -import javax.sip.message.Request; -import javax.sip.message.Response; - -import com.genersoft.iot.vmp.gb28181.bean.WvpSipDate; -import gov.nist.javax.sip.header.SIPDateHeader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.StringUtils; - -import com.genersoft.iot.vmp.common.VideoManagerConstants; -import com.genersoft.iot.vmp.conf.SipConfig; -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.event.EventPublisher; -import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; - -import gov.nist.javax.sip.address.AddressImpl; -import gov.nist.javax.sip.address.SipUri; -import gov.nist.javax.sip.header.Expires; - -/** - * @Description:收到注册请求 处理 - * @author: swwheihei - * @date: 2020年5月3日 下午4:47:25 - */ -public class RegisterRequestProcessor extends SIPRequestAbstractProcessor { - - private Logger logger = LoggerFactory.getLogger(RegisterRequestProcessor.class); - - private SipConfig sipConfig; - - private RegisterLogicHandler handler; - - private IVideoManagerStorager storager; - - private EventPublisher publisher; - - /** - * 收到注册请求 处理 - * @param evt - */ - @Override - public void process(RequestEvent evt) { - try { - logger.info("收到注册请求,开始处理"); - Request request = evt.getRequest(); - - Response response = null; - boolean passwordCorrect = false; - // 注册标志 0:未携带授权头或者密码错误 1:注册成功 2:注销成功 - int registerFlag = 0; - Device device = null; - AuthorizationHeader authorhead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME); - // 校验密码是否正确 - if (authorhead != null) { - passwordCorrect = new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request, - sipConfig.getSipPassword()); - } - - // 未携带授权头或者密码错误 回复401 - if (authorhead == null || !passwordCorrect) { - - if (authorhead == null) { - logger.info("未携带授权头 回复401"); - } else if (!passwordCorrect) { - logger.info("密码错误 回复401"); - } - response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request); - new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getSipDomain()); - } - // 携带授权头并且密码正确 - else if (passwordCorrect) { - 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); - - ExpiresHeader expiresHeader = (ExpiresHeader) request.getHeader(Expires.NAME); - // 添加Contact头 - response.addHeader(request.getHeader(ContactHeader.NAME)); - // 添加Expires头 - response.addHeader(request.getExpires()); - - // 1.获取到通信地址等信息,保存到Redis - FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME); - ViaHeader viaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME); - String received = viaHeader.getReceived(); - int rPort = viaHeader.getRPort(); - // 本地模拟设备 received 为空 rPort 为 -1 - // 解析本地地址替代 - if (StringUtils.isEmpty(received) || rPort == -1) { - received = viaHeader.getHost(); - rPort = viaHeader.getPort(); - } - // - AddressImpl address = (AddressImpl) fromHeader.getAddress(); - SipUri uri = (SipUri) address.getURI(); - String deviceId = uri.getUser(); - device = new Device(); - device.setStreamMode("UDP"); - device.setDeviceId(deviceId); - device.setIp(received); - device.setPort(rPort); - device.setHostAddress(received.concat(":").concat(String.valueOf(rPort))); - // 注销成功 - if (expiresHeader != null && expiresHeader.getExpires() == 0) { - registerFlag = 2; - } - // 注册成功 - else { - 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"); - } - } - getServerTransaction(evt).sendResponse(response); - // 注册成功 - // 保存到redis - // 下发catelog查询目录 - if (registerFlag == 1 && device != null) { - logger.info("注册成功! deviceId:" + device.getDeviceId()); - // boolean exists = storager.exists(device.getDeviceId()); - device.setRegisterTimeMillis(System.currentTimeMillis()); - storager.updateDevice(device); - publisher.onlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_ONLINE_REGISTER); - - // 重新注册更新设备和通道,以免设备替换或更新后信息无法更新 - //if (!exists) { - handler.onRegister(device); - //} - } else if (registerFlag == 2) { - logger.info("注销成功! deviceId:" + device.getDeviceId()); - publisher.outlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_OUTLINE_UNREGISTER); - } - } catch (SipException | InvalidArgumentException | NoSuchAlgorithmException | ParseException e) { - e.printStackTrace(); - } - - } - - public void setSipConfig(SipConfig sipConfig) { - this.sipConfig = sipConfig; - } - - public void setHandler(RegisterLogicHandler handler) { - this.handler = handler; - } - - public void setVideoManagerStorager(IVideoManagerStorager storager) { - this.storager = storager; - } - - public void setPublisher(EventPublisher publisher) { - this.publisher = publisher; - } - -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/SubscribeRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/SubscribeRequestProcessor.java deleted file mode 100644 index 010659eee..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/SubscribeRequestProcessor.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit.request.impl; - -import java.text.ParseException; - -import javax.sip.InvalidArgumentException; -import javax.sip.RequestEvent; -import javax.sip.ServerTransaction; -import javax.sip.SipException; -import javax.sip.header.ExpiresHeader; -import javax.sip.message.Request; -import javax.sip.message.Response; - -import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor; - -/** - * @Description:SUBSCRIBE请求处理器 - * @author: swwheihei - * @date: 2020年5月3日 下午5:31:20 - */ -public class SubscribeRequestProcessor extends SIPRequestAbstractProcessor { - - /** - * 处理SUBSCRIBE请求 - * - * @param evt - * @param layer - * @param transaction - * @param config - */ - @Override - public void process(RequestEvent evt) { - Request request = evt.getRequest(); - - try { - Response response = null; - response = getMessageFactory().createResponse(200, request); - if (response != null) { - ExpiresHeader expireHeader = getHeaderFactory().createExpiresHeader(30); - response.setExpires(expireHeader); - } - System.out.println("response : " + response.toString()); - ServerTransaction transaction = getServerTransaction(evt); - if (transaction != null) { - transaction.sendResponse(response); - transaction.terminate(); - } else { - System.out.println("processRequest serverTransactionId is null."); - } - - } catch (ParseException e) { - e.printStackTrace(); - } catch (SipException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - e.printStackTrace(); - } - - } - -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/ISIPResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/ISIPResponseProcessor.java deleted file mode 100644 index fee80ebad..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/ISIPResponseProcessor.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit.response; - -import java.text.ParseException; - -import javax.sip.ResponseEvent; - -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.gb28181.SipLayer; - -/** - * @Description:处理接收IPCamera发来的SIP协议响应消息 - * @author: swwheihei - * @date: 2020年5月3日 下午4:42:22 - */ -public interface ISIPResponseProcessor { - - public void process(ResponseEvent evt, SipLayer layer, SipConfig config) throws ParseException; - -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/ByeResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/ByeResponseProcessor.java deleted file mode 100644 index 53bc8f794..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/ByeResponseProcessor.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit.response.impl; - -import javax.sip.ResponseEvent; - -import org.springframework.stereotype.Component; - -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.gb28181.SipLayer; -import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; - -/** - * @Description: BYE请求响应器 - * @author: swwheihei - * @date: 2020年5月3日 下午5:32:05 - */ -@Component -public class ByeResponseProcessor implements ISIPResponseProcessor { - - /** - * 处理BYE响应 - * - * @param evt - * @param layer - * @param transaction - * @param config - */ - @Override - public void process(ResponseEvent evt, SipLayer layer, SipConfig config) { - // TODO Auto-generated method stub - - } - -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/CancelResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/CancelResponseProcessor.java deleted file mode 100644 index c6fd9b394..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/CancelResponseProcessor.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit.response.impl; - -import javax.sip.ResponseEvent; - -import org.springframework.stereotype.Component; - -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.gb28181.SipLayer; -import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; - -/** - * @Description:CANCEL响应处理器 - * @author: swwheihei - * @date: 2020年5月3日 下午5:32:23 - */ -@Component -public class CancelResponseProcessor implements ISIPResponseProcessor { - - /** - * 处理CANCEL响应 - * - * @param evt - * @param layer - * @param transaction - * @param config - */ - @Override - public void process(ResponseEvent evt, SipLayer layer, SipConfig config) { - // TODO Auto-generated method stub - - } - -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java deleted file mode 100644 index d67267af9..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit.response.impl; - -import java.text.ParseException; - -import javax.sip.Dialog; -import javax.sip.InvalidArgumentException; -import javax.sip.ResponseEvent; -import javax.sip.SipException; -import javax.sip.address.SipURI; -import javax.sip.header.CSeqHeader; -import javax.sip.header.ViaHeader; -import javax.sip.message.Request; -import javax.sip.message.Response; - -// import org.slf4j.Logger; -// import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.gb28181.SipLayer; -import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; - - -/** - * @Description:处理INVITE响应 - * @author: swwheihei - * @date: 2020年5月3日 下午4:43:52 - */ -@Component -public class InviteResponseProcessor implements ISIPResponseProcessor { - - // private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class); - - /** - * 处理invite响应 - * - * @param evt 响应消息 - * @throws ParseException - */ - @Override - public void process(ResponseEvent evt, SipLayer layer, SipConfig config) throws ParseException { - try { - Response response = evt.getResponse(); - int statusCode = response.getStatusCode(); - // trying不会回复 - if (statusCode == Response.TRYING) { - } - // 成功响应 - // 下发ack - if (statusCode == Response.OK) { - Dialog dialog = evt.getDialog(); - CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME); - Request reqAck = dialog.createAck(cseq.getSeqNumber()); - - SipURI requestURI = (SipURI) reqAck.getRequestURI(); - ViaHeader viaHeader = (ViaHeader) response.getHeader(ViaHeader.NAME); - requestURI.setHost(viaHeader.getHost()); - requestURI.setPort(viaHeader.getPort()); - reqAck.setRequestURI(requestURI); - - dialog.sendAck(reqAck); - } - } catch (InvalidArgumentException | SipException e) { - e.printStackTrace(); - } - } - -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/OtherResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/OtherResponseProcessor.java deleted file mode 100644 index 16314409d..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/OtherResponseProcessor.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit.response.impl; - -import javax.sip.ResponseEvent; - -import org.springframework.stereotype.Component; - -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.gb28181.SipLayer; -import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; - -/** - * @Description:暂不支持的消息响应处理器 - * @author: swwheihei - * @date: 2020年5月3日 下午5:32:59 - */ -@Component -public class OtherResponseProcessor implements ISIPResponseProcessor { - - /** - *

    Title: process

    - *

    Description:

    - * @param evt - * @param layer - * @param config - */ - @Override - public void process(ResponseEvent evt, SipLayer layer, SipConfig config) { - // TODO Auto-generated method stub - - } - -} 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 000000000..5c12ff6c3 --- /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 195b7e368..000000000 --- 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/MessageElement.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/MessageElement.java new file mode 100644 index 000000000..f94237cd8 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/MessageElement.java @@ -0,0 +1,17 @@ +package com.genersoft.iot.vmp.gb28181.utils; + +import java.lang.annotation.*; + +/** + * @author gaofuwang + * @version 1.0 + * @date 2022/6/28 14:58 + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface MessageElement { + String value(); + + String subVal() default ""; +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/NumericUtil.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/NumericUtil.java index 4a399b898..d990db93e 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/NumericUtil.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/NumericUtil.java @@ -1,5 +1,8 @@ package com.genersoft.iot.vmp.gb28181.utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * 数值格式判断和处理 * @author lawrencehj @@ -7,6 +10,8 @@ package com.genersoft.iot.vmp.gb28181.utils; */ public class NumericUtil { + private static Logger logger = LoggerFactory.getLogger(NumericUtil.class); + /** * 判断是否Double格式 * @param str @@ -14,11 +19,11 @@ public class NumericUtil { */ public static boolean isDouble(String str) { try { - Double num2 = Double.valueOf(str); - System.out.println(num2 + " is a valid numeric string!"); + Double num2 = Double.valueOf(str); +// logger.debug(num2 + " is a valid numeric string!"); return true; } catch (Exception e) { - System.out.println(str + " is an invalid numeric string!"); +// logger.debug(str + " is an invalid numeric string!"); return false; } } @@ -31,10 +36,10 @@ public class NumericUtil { public static boolean isInteger(String str) { try { int num2 = Integer.valueOf(str); - System.out.println(num2 + " is an integer!"); +// logger.debug(num2 + " is an integer!"); return true; } catch (Exception e) { - System.out.println(str + " is not an integer!"); +// logger.debug(str + " is not an integer!"); return false; } } 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 new file mode 100644 index 000000000..58fc6012b --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java @@ -0,0 +1,266 @@ +package com.genersoft.iot.vmp.gb28181.utils; + +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.bean.Gb28181Sdp; +import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo; +import com.genersoft.iot.vmp.utils.DateUtil; +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.apache.commons.lang3.RandomStringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.ObjectUtils; + +import javax.sdp.SdpFactory; +import javax.sdp.SdpParseException; +import javax.sdp.SessionDescription; +import javax.sip.PeerUnavailableException; +import javax.sip.SipFactory; +import javax.sip.header.FromHeader; +import javax.sip.header.Header; +import javax.sip.header.UserAgentHeader; +import javax.sip.message.Request; +import java.text.ParseException; +import java.time.LocalDateTime; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * @author panlinlin + * @version 1.0.0 + * @description JAIN SIP的工具类 + * @createTime 2021年09月27日 15:12:00 + */ +public class SipUtils { + + private final static Logger logger = LoggerFactory.getLogger(SipUtils.class); + + public static String getUserIdFromFromHeader(Request request) { + FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME); + return getUserIdFromFromHeader(fromHeader); + } + /** + * 从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(); + SipUri uri = (SipUri) address.getURI(); + return uri.getUser(); + } + + public static String getNewViaTag() { + return "z9hG4bK" + RandomStringUtils.randomNumeric(10); + } + + public static UserAgentHeader createUserAgentHeader(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.getInstance().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); + + //优化zoom低倍速下的变倍速率 + if ((zoomSpeed > 0) && (zoomSpeed <16)) + { + zoomSpeed = 16; + } + 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.getPeerPacketSourceAddress().getHostAddress(); + remotePort = request.getPeerPacketSourcePort(); + + }else { + // 判断RPort是否改变,改变则说明路由nat信息变化,修改设备信息 + // 获取到通信地址等信息 + remoteAddress = request.getTopmostViaHeader().getReceived(); + remotePort = request.getTopmostViaHeader().getRPort(); + // 解析本地地址替代 + if (ObjectUtils.isEmpty(remoteAddress) || remotePort == -1) { + remoteAddress = request.getPeerPacketSourceAddress().getHostAddress(); + remotePort = request.getPeerPacketSourcePort(); + } + } + + return new RemoteAddressInfo(remoteAddress, remotePort); + } + + public static DeviceChannel updateGps(DeviceChannel deviceChannel, String geoCoordSys) { + if (deviceChannel.getLongitude()*deviceChannel.getLatitude() > 0) { + + if (geoCoordSys == null) { + geoCoordSys = "WGS84"; + } + if ("WGS84".equals(geoCoordSys)) { + deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude()); + deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude()); + Double[] position = Coordtransform.WGS84ToGCJ02(deviceChannel.getLongitude(), deviceChannel.getLatitude()); + deviceChannel.setLongitudeGcj02(position[0]); + deviceChannel.setLatitudeGcj02(position[1]); + }else if ("GCJ02".equals(geoCoordSys)) { + deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude()); + deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude()); + Double[] position = Coordtransform.GCJ02ToWGS84(deviceChannel.getLongitude(), deviceChannel.getLatitude()); + deviceChannel.setLongitudeWgs84(position[0]); + deviceChannel.setLatitudeWgs84(position[1]); + }else { + deviceChannel.setLongitudeGcj02(0.00); + deviceChannel.setLatitudeGcj02(0.00); + deviceChannel.setLongitudeWgs84(0.00); + deviceChannel.setLatitudeWgs84(0.00); + } + }else { + deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude()); + deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude()); + deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude()); + deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude()); + } + return deviceChannel; + } + + public static Gb28181Sdp parseSDP(String sdpStr) throws SdpParseException { + + // jainSip不支持y= f=字段, 移除以解析。 + int ssrcIndex = sdpStr.indexOf("y="); + int mediaDescriptionIndex = sdpStr.indexOf("f="); + // 检查是否有y字段 + SessionDescription sdp; + String ssrc = null; + String mediaDescription = null; + if (mediaDescriptionIndex == 0 && ssrcIndex == 0) { + sdp = SdpFactory.getInstance().createSessionDescription(sdpStr); + }else { + String lines[] = sdpStr.split("\\r?\\n"); + StringBuilder sdpBuffer = new StringBuilder(); + for (String line : lines) { + if (line.trim().startsWith("y=")) { + ssrc = line.substring(2); + }else if (line.trim().startsWith("f=")) { + mediaDescription = line.substring(2); + }else { + sdpBuffer.append(line.trim()).append("\r\n"); + } + } + sdp = SdpFactory.getInstance().createSessionDescription(sdpBuffer.toString()); + } + return Gb28181Sdp.getInstance(sdp, ssrc, mediaDescription); + } + + public static String getSsrcFromSdp(String sdpStr) { + + // jainSip不支持y= f=字段, 移除以解析。 + int ssrcIndex = sdpStr.indexOf("y="); + if (ssrcIndex == 0) { + return null; + } + String lines[] = sdpStr.split("\\r?\\n"); + for (String line : lines) { + if (line.trim().startsWith("y=")) { + return line.substring(2); + } + } + return null; + } + + public static String parseTime(String timeStr) { + if (ObjectUtils.isEmpty(timeStr)){ + return null; + } + LocalDateTime localDateTime; + try { + localDateTime = LocalDateTime.parse(timeStr); + }catch (DateTimeParseException e) { + try { + localDateTime = LocalDateTime.parse(timeStr, DateUtil.formatterISO8601); + }catch (DateTimeParseException e2) { + logger.error("[格式化时间] 无法格式化时间: {}", timeStr); + return null; + } + } + return localDateTime.format(DateUtil.formatterISO8601); + } +} \ No newline at end of file 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 c82ba600d..a08231c87 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,15 +1,14 @@ package com.genersoft.iot.vmp.gb28181.utils; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -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.common.CivilCodePo; +import com.genersoft.iot.vmp.conf.CivilCodeFileConf; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; +import com.genersoft.iot.vmp.utils.DateUtil; +import org.apache.commons.lang3.math.NumberUtils; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.DocumentException; @@ -17,23 +16,35 @@ import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.ObjectUtils; +import org.springframework.util.ReflectionUtils; + +import javax.sip.RequestEvent; +import javax.sip.message.Request; +import java.io.ByteArrayInputStream; +import java.io.StringReader; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +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) { @@ -44,14 +55,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 节点 @@ -62,12 +73,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> @@ -106,7 +117,7 @@ public class XmlUtil { /** * xml转json - * + * * @param element * @param json */ @@ -114,12 +125,12 @@ public class XmlUtil { // 如果是属性 for (Object o : element.attributes()) { Attribute attr = (Attribute) o; - if (!isEmpty(attr.getValue())) { + if (!ObjectUtils.isEmpty(attr.getValue())) { json.put("@" + attr.getName(), attr.getValue()); } } List chdEl = element.elements(); - if (chdEl.isEmpty() && !isEmpty(element.getText())) {// 如果没有子元素,只有一个值 + if (chdEl.isEmpty() && !ObjectUtils.isEmpty(element.getText())) {// 如果没有子元素,只有一个值 json.put(element.getName(), element.getText()); } @@ -150,7 +161,7 @@ public class XmlUtil { } else { // 子元素没有子元素 for (Object o : element.attributes()) { Attribute attr = (Attribute) o; - if (!isEmpty(attr.getValue())) { + if (!ObjectUtils.isEmpty(attr.getValue())) { json.put("@" + attr.getName(), attr.getValue()); } } @@ -160,11 +171,477 @@ public class XmlUtil { } } } + public static Element getRootElement(RequestEvent evt) throws DocumentException { - public static boolean isEmpty(String str) { - if (str == null || str.trim().isEmpty() || "null".equals(str)) { - return true; - } - return false; + return getRootElement(evt, "gb2312"); } -} + + public static Element getRootElement(RequestEvent evt, String charset) throws DocumentException { + Request request = evt.getRequest(); + return getRootElement(request.getRawContent(), charset); + } + + public static Element getRootElement(byte[] content, String charset) throws DocumentException { + if (charset == null) { + charset = "gb2312"; + } + SAXReader reader = new SAXReader(); + reader.setEncoding(charset); + Document xml = reader.read(new ByteArrayInputStream(content)); + return xml.getRootElement(); + } + + private enum ChannelType{ + CivilCode, BusinessGroup,VirtualOrganization,Other + } + + public static DeviceChannel channelContentHandler(Element itemDevice, Device device, String event, CivilCodeFileConf civilCodeFileConf){ + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setDeviceId(device.getDeviceId()); + Element channdelIdElement = itemDevice.element("DeviceID"); + if (channdelIdElement == null) { + logger.warn("解析Catalog消息时发现缺少 DeviceID"); + return null; + } + String channelId = channdelIdElement.getTextTrim(); + if (ObjectUtils.isEmpty(channelId)) { + logger.warn("解析Catalog消息时发现缺少 DeviceID"); + return null; + } + deviceChannel.setChannelId(channelId); + if (event != null && !event.equals(CatalogEvent.ADD) && !event.equals(CatalogEvent.UPDATE)) { + // 除了ADD和update情况下需要识别全部内容, + return deviceChannel; + } + Element nameElement = itemDevice.element("Name"); + if (nameElement != null) { + deviceChannel.setName(nameElement.getText()); + } + if(channelId.length() <= 8) { + deviceChannel.setHasAudio(false); + CivilCodePo parentCode = civilCodeFileConf.getParentCode(channelId); + if (parentCode != null) { + deviceChannel.setParentId(parentCode.getCode()); + deviceChannel.setCivilCode(parentCode.getCode()); + }else { + logger.warn("[xml解析] 无法确定行政区划{}的上级行政区划", channelId); + } + deviceChannel.setStatus(true); + return deviceChannel; + }else { + if(channelId.length() != 20) { + logger.warn("[xml解析] 失败,编号不符合国标28181定义: {}", channelId); + return null; + } + + int code = Integer.parseInt(channelId.substring(10, 13)); + if (code == 136 || code == 137 || code == 138) { + deviceChannel.setHasAudio(true); + }else { + deviceChannel.setHasAudio(false); + } + // 设备厂商 + String manufacturer = getText(itemDevice, "Manufacturer"); + // 设备型号 + String model = getText(itemDevice, "Model"); + // 设备归属 + String owner = getText(itemDevice, "Owner"); + // 行政区域 + String civilCode = getText(itemDevice, "CivilCode"); + // 虚拟组织所属的业务分组ID,业务分组根据特定的业务需求制定,一个业务分组包含一组特定的虚拟组织 + String businessGroupID = getText(itemDevice, "BusinessGroupID"); + // 父设备/区域/系统ID + String parentID = getText(itemDevice, "ParentID"); + if (parentID != null && parentID.equalsIgnoreCase("null")) { + parentID = null; + } + // 注册方式(必选)缺省为1;1:符合IETFRFC3261标准的认证注册模式;2:基于口令的双向认证注册模式;3:基于数字证书的双向认证注册模式 + String registerWay = getText(itemDevice, "RegisterWay"); + // 保密属性(必选)缺省为0;0:不涉密,1:涉密 + String secrecy = getText(itemDevice, "Secrecy"); + // 安装地址 + String address = getText(itemDevice, "Address"); + + switch (code){ + case 200: + // 系统目录 + if (!ObjectUtils.isEmpty(manufacturer)) { + deviceChannel.setManufacture(manufacturer); + } + if (!ObjectUtils.isEmpty(model)) { + deviceChannel.setModel(model); + } + if (!ObjectUtils.isEmpty(owner)) { + deviceChannel.setOwner(owner); + } + if (!ObjectUtils.isEmpty(civilCode)) { + deviceChannel.setCivilCode(civilCode); + deviceChannel.setParentId(civilCode); + }else { + if (!ObjectUtils.isEmpty(parentID)) { + deviceChannel.setParentId(parentID); + } + } + if (!ObjectUtils.isEmpty(address)) { + deviceChannel.setAddress(address); + } + deviceChannel.setStatus(true); + if (!ObjectUtils.isEmpty(registerWay)) { + try { + deviceChannel.setRegisterWay(Integer.parseInt(registerWay)); + }catch (NumberFormatException exception) { + logger.warn("[xml解析] 从通道数据获取registerWay失败: {}", registerWay); + } + } + if (!ObjectUtils.isEmpty(secrecy)) { + deviceChannel.setSecrecy(secrecy); + } + return deviceChannel; + case 215: + // 业务分组 + deviceChannel.setStatus(true); + if (!ObjectUtils.isEmpty(parentID)) { + if (!parentID.trim().equalsIgnoreCase(device.getDeviceId())) { + deviceChannel.setParentId(parentID); + } + }else { + logger.warn("[xml解析] 业务分组数据中缺少关键信息->ParentId"); + if (!ObjectUtils.isEmpty(civilCode)) { + deviceChannel.setCivilCode(civilCode); + } + } + break; + case 216: + // 虚拟组织 + deviceChannel.setStatus(true); + if (!ObjectUtils.isEmpty(businessGroupID)) { + deviceChannel.setBusinessGroupId(businessGroupID); + } + + if (!ObjectUtils.isEmpty(parentID)) { + if (parentID.contains("/")) { + String[] parentIdArray = parentID.split("/"); + parentID = parentIdArray[parentIdArray.length - 1]; + } + deviceChannel.setParentId(parentID); + }else { + if (!ObjectUtils.isEmpty(businessGroupID)) { + deviceChannel.setParentId(businessGroupID); + } + } + break; + default: + // 设备目录 + if (!ObjectUtils.isEmpty(manufacturer)) { + deviceChannel.setManufacture(manufacturer); + } + if (!ObjectUtils.isEmpty(model)) { + deviceChannel.setModel(model); + } + if (!ObjectUtils.isEmpty(owner)) { + deviceChannel.setOwner(owner); + } + if (!ObjectUtils.isEmpty(civilCode) + && civilCode.length() <= 8 + && NumberUtils.isParsable(civilCode) + && Integer.parseInt(civilCode)%2 == 0 + ) { + deviceChannel.setCivilCode(civilCode); + } + if (!ObjectUtils.isEmpty(businessGroupID)) { + deviceChannel.setBusinessGroupId(businessGroupID); + } + + // 警区 + String block = getText(itemDevice, "Block"); + if (!ObjectUtils.isEmpty(block)) { + deviceChannel.setBlock(block); + } + if (!ObjectUtils.isEmpty(address)) { + deviceChannel.setAddress(address); + } + + if (!ObjectUtils.isEmpty(secrecy)) { + deviceChannel.setSecrecy(secrecy); + } + + // 当为设备时,是否有子设备(必选)1有,0没有 + String parental = getText(itemDevice, "Parental"); + if (!ObjectUtils.isEmpty(parental)) { + try { + // 由于海康会错误的发送65535作为这里的取值,所以这里除非是0否则认为是1 + if (!ObjectUtils.isEmpty(parental) && parental.length() == 1 && Integer.parseInt(parental) == 0) { + deviceChannel.setParental(0); + }else { + deviceChannel.setParental(1); + } + }catch (NumberFormatException e) { + logger.warn("[xml解析] 从通道数据获取 parental失败: {}", parental); + } + } + // 父设备/区域/系统ID + + if (!ObjectUtils.isEmpty(parentID) ) { + if (parentID.contains("/")) { + String[] parentIdArray = parentID.split("/"); + deviceChannel.setParentId(parentIdArray[parentIdArray.length - 1]); + }else { + if (parentID.length()%2 == 0) { + deviceChannel.setParentId(parentID); + }else { + logger.warn("[xml解析] 不规范的parentID:{}, 已舍弃", parentID); + } + } + }else { + if (!ObjectUtils.isEmpty(businessGroupID)) { + deviceChannel.setParentId(businessGroupID); + }else { + if (!ObjectUtils.isEmpty(deviceChannel.getCivilCode())) { + deviceChannel.setParentId(deviceChannel.getCivilCode()); + } + } + } + // 注册方式 + if (!ObjectUtils.isEmpty(registerWay)) { + try { + int registerWayInt = Integer.parseInt(registerWay); + deviceChannel.setRegisterWay(registerWayInt); + }catch (NumberFormatException exception) { + logger.warn("[xml解析] 从通道数据获取registerWay失败: {}", registerWay); + deviceChannel.setRegisterWay(1); + } + }else { + deviceChannel.setRegisterWay(1); + } + + // 信令安全模式(可选)缺省为0; 0:不采用;2:S/MIME 签名方式;3:S/MIME加密签名同时采用方式;4:数字摘要方式 + String safetyWay = getText(itemDevice, "SafetyWay"); + if (!ObjectUtils.isEmpty(safetyWay)) { + try { + deviceChannel.setSafetyWay(Integer.parseInt(safetyWay)); + }catch (NumberFormatException e) { + logger.warn("[xml解析] 从通道数据获取 safetyWay失败: {}", safetyWay); + } + } + + // 证书序列号(有证书的设备必选) + String certNum = getText(itemDevice, "CertNum"); + if (!ObjectUtils.isEmpty(certNum)) { + deviceChannel.setCertNum(certNum); + } + + // 证书有效标识(有证书的设备必选)缺省为0;证书有效标识:0:无效 1:有效 + String certifiable = getText(itemDevice, "Certifiable"); + if (!ObjectUtils.isEmpty(certifiable)) { + try { + deviceChannel.setCertifiable(Integer.parseInt(certifiable)); + }catch (NumberFormatException e) { + logger.warn("[xml解析] 从通道数据获取 Certifiable失败: {}", certifiable); + } + } + + // 无效原因码(有证书且证书无效的设备必选) + String errCode = getText(itemDevice, "ErrCode"); + if (!ObjectUtils.isEmpty(errCode)) { + try { + deviceChannel.setErrCode(Integer.parseInt(errCode)); + }catch (NumberFormatException e) { + logger.warn("[xml解析] 从通道数据获取 ErrCode失败: {}", errCode); + } + } + + // 证书终止有效期(有证书的设备必选) + String endTime = getText(itemDevice, "EndTime"); + if (!ObjectUtils.isEmpty(endTime)) { + deviceChannel.setEndTime(endTime); + } + + + // 设备/区域/系统IP地址 + String ipAddress = getText(itemDevice, "IPAddress"); + if (!ObjectUtils.isEmpty(ipAddress)) { + deviceChannel.setIpAddress(ipAddress); + } + + // 设备/区域/系统端口 + String port = getText(itemDevice, "Port"); + if (!ObjectUtils.isEmpty(port)) { + try { + deviceChannel.setPort(Integer.parseInt(port)); + }catch (NumberFormatException e) { + logger.warn("[xml解析] 从通道数据获取 Port失败: {}", port); + } + } + + // 设备口令 + String password = getText(itemDevice, "Password"); + if (!ObjectUtils.isEmpty(password)) { + deviceChannel.setPassword(password); + } + + + // 设备状态 + String status = getText(itemDevice, "Status"); + if (status != null) { + // ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理 + if (status.equals("ON") || status.equals("On") || status.equals("ONLINE") || status.equals("OK")) { + deviceChannel.setStatus(true); + } + if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) { + deviceChannel.setStatus(false); + } + }else { + deviceChannel.setStatus(true); + } + + // 经度 + String longitude = getText(itemDevice, "Longitude"); + if (NumericUtil.isDouble(longitude)) { + deviceChannel.setLongitude(Double.parseDouble(longitude)); + } else { + deviceChannel.setLongitude(0.00); + } + + // 纬度 + String latitude = getText(itemDevice, "Latitude"); + if (NumericUtil.isDouble(latitude)) { + deviceChannel.setLatitude(Double.parseDouble(latitude)); + } else { + deviceChannel.setLatitude(0.00); + } + + deviceChannel.setGpsTime(DateUtil.getNow()); + + // -摄像机类型扩展,标识摄像机类型:1-球机;2-半球;3-固定枪机;4-遥控枪机。当目录项为摄像机时可选 + String ptzType = getText(itemDevice, "PTZType"); + if (ObjectUtils.isEmpty(ptzType)) { + //兼容INFO中的信息 + Element info = itemDevice.element("Info"); + String ptzTypeFromInfo = XmlUtil.getText(info, "PTZType"); + if(!ObjectUtils.isEmpty(ptzTypeFromInfo)){ + try { + deviceChannel.setPTZType(Integer.parseInt(ptzTypeFromInfo)); + }catch (NumberFormatException e){ + logger.warn("[xml解析] 从通道数据info中获取PTZType失败: {}", ptzTypeFromInfo); + } + } + } else { + try { + deviceChannel.setPTZType(Integer.parseInt(ptzType)); + }catch (NumberFormatException e){ + logger.warn("[xml解析] 从通道数据中获取PTZType失败: {}", ptzType); + } + } + + // TODO 摄像机位置类型扩展。 + // 1-省际检查站、 + // 2-党政机关、 + // 3-车站码头、 + // 4-中心广场、 + // 5-体育场馆、 + // 6-商业中心、 + // 7-宗教场所、 + // 8-校园周边、 + // 9-治安复杂区域、 + // 10-交通干线。 + // String positionType = getText(itemDevice, "PositionType"); + + // TODO 摄像机安装位置室外、室内属性。1-室外、2-室内。 + // String roomType = getText(itemDevice, "RoomType"); + // TODO 摄像机用途属性 + // String useType = getText(itemDevice, "UseType"); + // TODO 摄像机补光属性。1-无补光、2-红外补光、3-白光补光 + // String supplyLightType = getText(itemDevice, "SupplyLightType"); + // TODO 摄像机监视方位属性。1-东、2-西、3-南、4-北、5-东南、6-东北、7-西南、8-西北。 + // String directionType = getText(itemDevice, "DirectionType"); + // TODO 摄像机支持的分辨率,可有多个分辨率值,各个取值间以“/”分隔。分辨率取值参见附录 F中SDPf字段规定 + // String resolution = getText(itemDevice, "Resolution"); + + // TODO 下载倍速范围(可选),各可选参数以“/”分隔,如设备支持1,2,4倍速下载则应写为“1/2/4 + // String downloadSpeed = getText(itemDevice, "DownloadSpeed"); + // TODO 空域编码能力,取值0:不支持;1:1级增强(1个增强层);2:2级增强(2个增强层);3:3级增强(3个增强层) + // String svcSpaceSupportMode = getText(itemDevice, "SVCSpaceSupportMode"); + // TODO 时域编码能力,取值0:不支持;1:1级增强;2:2级增强;3:3级增强 + // String svcTimeSupportMode = getText(itemDevice, "SVCTimeSupportMode"); + + + deviceChannel.setSecrecy(secrecy); + break; + } + } + + return deviceChannel; + } + + /** + * 新增方法支持内部嵌套 + * + * @param element xmlElement + * @param clazz 结果类 + * @param 泛型 + * @return 结果对象 + * @throws NoSuchMethodException + * @throws InvocationTargetException + * @throws InstantiationException + * @throws IllegalAccessException + */ + public static T loadElement(Element element, Class clazz) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + Field[] fields = clazz.getDeclaredFields(); + T t = clazz.getDeclaredConstructor().newInstance(); + for (Field field : fields) { + ReflectionUtils.makeAccessible(field); + MessageElement annotation = field.getAnnotation(MessageElement.class); + if (annotation == null) { + continue; + } + String value = annotation.value(); + String subVal = annotation.subVal(); + Element element1 = element.element(value); + if (element1 == null) { + continue; + } + if ("".equals(subVal)) { + // 无下级数据 + Object fieldVal = element1.isTextOnly() ? element1.getText() : loadElement(element1, field.getType()); + Object o = simpleTypeDeal(field.getType(), fieldVal); + ReflectionUtils.setField(field, t, o); + } else { + // 存在下级数据 + ArrayList list = new ArrayList<>(); + Type genericType = field.getGenericType(); + if (!(genericType instanceof ParameterizedType)) { + continue; + } + Class aClass = (Class) ((ParameterizedType) genericType).getActualTypeArguments()[0]; + for (Element element2 : element1.elements(subVal)) { + list.add(loadElement(element2, aClass)); + } + ReflectionUtils.setField(field, t, list); + } + } + return t; + } + + /** + * 简单类型处理 + * + * @param tClass + * @param val + * @return + */ + private static Object simpleTypeDeal(Class tClass, Object val) { + if (tClass.equals(String.class)) { + return val.toString(); + } + if (tClass.equals(Integer.class)) { + return Integer.valueOf(val.toString()); + } + if (tClass.equals(Double.class)) { + return Double.valueOf(val.toString()); + } + if (tClass.equals(Long.class)) { + return Long.valueOf(val.toString()); + } + return val; + } +} \ No newline at end of file diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/annotation/MsgId.java b/src/main/java/com/genersoft/iot/vmp/jt1078/annotation/MsgId.java new file mode 100644 index 000000000..d5c2de46b --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/annotation/MsgId.java @@ -0,0 +1,15 @@ +package com.genersoft.iot.vmp.jt1078.annotation; + +import java.lang.annotation.*; + +/** + * @author QingtaiJiang + * @date 2023/4/27 18:31 + * @email qingtaij@163.com + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface MsgId { + String id(); +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/cmd/JT1078Template.java b/src/main/java/com/genersoft/iot/vmp/jt1078/cmd/JT1078Template.java new file mode 100644 index 000000000..c55c62760 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/cmd/JT1078Template.java @@ -0,0 +1,115 @@ +package com.genersoft.iot.vmp.jt1078.cmd; + +import com.genersoft.iot.vmp.jt1078.proc.entity.Cmd; +import com.genersoft.iot.vmp.jt1078.proc.response.*; +import com.genersoft.iot.vmp.jt1078.session.SessionManager; + +import java.util.Random; + +/** + * @author QingtaiJiang + * @date 2023/4/27 18:58 + * @email qingtaij@163.com + */ +public class JT1078Template { + + private final Random random = new Random(); + + private static final String H9101 = "9101"; + private static final String H9102 = "9102"; + private static final String H9201 = "9201"; + private static final String H9202 = "9202"; + private static final String H9205 = "9205"; + + private static final String H0001 = "0001"; + private static final String H1205 = "1205"; + + /** + * 开启直播视频 + * + * @param devId 设备号 + * @param j9101 开启视频参数 + */ + public String startLive(String devId, J9101 j9101, Integer timeOut) { + Cmd cmd = new Cmd.Builder() + .setDevId(devId) + .setPackageNo(randomInt()) + .setMsgId(H9101) + .setRespId(H0001) + .setRs(j9101) + .build(); + return SessionManager.INSTANCE.request(cmd, timeOut); + } + + /** + * 关闭直播视频 + * + * @param devId 设备号 + * @param j9102 关闭视频参数 + */ + public String stopLive(String devId, J9102 j9102, Integer timeOut) { + Cmd cmd = new Cmd.Builder() + .setDevId(devId) + .setPackageNo(randomInt()) + .setMsgId(H9102) + .setRespId(H0001) + .setRs(j9102) + .build(); + return SessionManager.INSTANCE.request(cmd, timeOut); + } + + /** + * 查询音视频列表 + * + * @param devId 设备号 + * @param j9205 查询音视频列表 + */ + public String queryBackTime(String devId, J9205 j9205, Integer timeOut) { + Cmd cmd = new Cmd.Builder() + .setDevId(devId) + .setPackageNo(randomInt()) + .setMsgId(H9205) + .setRespId(H1205) + .setRs(j9205) + .build(); + return SessionManager.INSTANCE.request(cmd, timeOut); + } + + /** + * 开启视频回放 + * + * @param devId 设备号 + * @param j9201 视频回放参数 + */ + public String startBackLive(String devId, J9201 j9201, Integer timeOut) { + Cmd cmd = new Cmd.Builder() + .setDevId(devId) + .setPackageNo(randomInt()) + .setMsgId(H9201) + .setRespId(H1205) + .setRs(j9201) + .build(); + return SessionManager.INSTANCE.request(cmd, timeOut); + } + + /** + * 视频回放控制 + * + * @param devId 设备号 + * @param j9202 控制视频回放参数 + */ + public String controlBackLive(String devId, J9202 j9202, Integer timeOut) { + Cmd cmd = new Cmd.Builder() + .setDevId(devId) + .setPackageNo(randomInt()) + .setMsgId(H9202) + .setRespId(H0001) + .setRs(j9202) + .build(); + return SessionManager.INSTANCE.request(cmd, timeOut); + } + + private Long randomInt() { + return (long) random.nextInt(1000) + 1; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/codec/decode/Jt808Decoder.java b/src/main/java/com/genersoft/iot/vmp/jt1078/codec/decode/Jt808Decoder.java new file mode 100644 index 000000000..4817c665f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/codec/decode/Jt808Decoder.java @@ -0,0 +1,146 @@ +package com.genersoft.iot.vmp.jt1078.codec.decode; + +import com.genersoft.iot.vmp.jt1078.proc.Header; +import com.genersoft.iot.vmp.jt1078.proc.factory.CodecFactory; +import com.genersoft.iot.vmp.jt1078.proc.request.Re; +import com.genersoft.iot.vmp.jt1078.proc.response.Rs; +import com.genersoft.iot.vmp.jt1078.session.Session; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.CompositeByteBuf; +import io.netty.buffer.UnpooledByteBufAllocator; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author QingtaiJiang + * @date 2023/4/27 18:10 + * @email qingtaij@163.com + */ +public class Jt808Decoder extends ByteToMessageDecoder { + private final static Logger log = LoggerFactory.getLogger(Jt808Decoder.class); + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + Session session = ctx.channel().attr(Session.KEY).get(); + log.info("> {} hex:{}", session, ByteBufUtil.hexDump(in)); + + try { + ByteBuf buf = unEscapeAndCheck(in); + + Header header = new Header(); + header.setMsgId(ByteBufUtil.hexDump(buf.readSlice(2))); + header.setMsgPro(buf.readUnsignedShort()); + if (header.is2019Version()) { + header.setVersion(buf.readUnsignedByte()); + String devId = ByteBufUtil.hexDump(buf.readSlice(10)); + header.setDevId(devId.replaceFirst("^0*", "")); + } else { + header.setDevId(ByteBufUtil.hexDump(buf.readSlice(6)).replaceFirst("^0*", "")); + } + header.setSn(buf.readUnsignedShort()); + + Re handler = CodecFactory.getHandler(header.getMsgId()); + if (handler == null) { + log.error("get msgId is null {}", header.getMsgId()); + return; + } + Rs decode = handler.decode(buf, header, session); + if (decode != null) { + out.add(decode); + } + } finally { + in.skipBytes(in.readableBytes()); + } + + + } + + + /** + * 转义与验证校验码 + * + * @param byteBuf 转义Buf + * @return 转义好的数据 + */ + public ByteBuf unEscapeAndCheck(ByteBuf byteBuf) throws Exception { + int low = byteBuf.readerIndex(); + int high = byteBuf.writerIndex(); + byte checkSum = 0; + int calculationCheckSum = 0; + + byte aByte = byteBuf.getByte(high - 2); + byte protocolEscapeFlag7d = 0x7d; + //0x7d转义 + byte protocolEscapeFlag01 = 0x01; + //0x7e转义 + byte protocolEscapeFlag02 = 0x02; + if (aByte == protocolEscapeFlag7d) { + byte b2 = byteBuf.getByte(high - 1); + if (b2 == protocolEscapeFlag01) { + checkSum = protocolEscapeFlag7d; + } else if (b2 == protocolEscapeFlag02) { + checkSum = 0x7e; + } else { + log.error("转义1异常:{}", ByteBufUtil.hexDump(byteBuf)); + throw new Exception("转义错误"); + } + high = high - 2; + } else { + high = high - 1; + checkSum = byteBuf.getByte(high); + } + List bufList = new ArrayList<>(); + int index = low; + while (index < high) { + byte b = byteBuf.getByte(index); + if (b == protocolEscapeFlag7d) { + byte c = byteBuf.getByte(index + 1); + if (c == protocolEscapeFlag01) { + ByteBuf slice = slice0x01(byteBuf, low, index); + bufList.add(slice); + b = protocolEscapeFlag7d; + } else if (c == protocolEscapeFlag02) { + ByteBuf slice = slice0x02(byteBuf, low, index); + bufList.add(slice); + b = 0x7e; + } else { + log.error("转义2异常:{}", ByteBufUtil.hexDump(byteBuf)); + throw new Exception("转义错误"); + } + index += 2; + low = index; + } else { + index += 1; + } + calculationCheckSum = calculationCheckSum ^ b; + } + + if (calculationCheckSum == checkSum) { + if (bufList.size() == 0) { + return byteBuf.slice(low, high); + } else { + bufList.add(byteBuf.slice(low, high - low)); + return new CompositeByteBuf(UnpooledByteBufAllocator.DEFAULT, false, bufList.size(), bufList); + } + } else { + log.info("{} 解析校验码:{}--计算校验码:{}", ByteBufUtil.hexDump(byteBuf), checkSum, calculationCheckSum); + throw new Exception("校验码错误!"); + } + } + + + private ByteBuf slice0x01(ByteBuf buf, int low, int sign) { + return buf.slice(low, sign - low + 1); + } + + private ByteBuf slice0x02(ByteBuf buf, int low, int sign) { + buf.setByte(sign, 0x7e); + return buf.slice(low, sign - low + 1); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/codec/encode/Jt808Encoder.java b/src/main/java/com/genersoft/iot/vmp/jt1078/codec/encode/Jt808Encoder.java new file mode 100644 index 000000000..afb1a7973 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/codec/encode/Jt808Encoder.java @@ -0,0 +1,33 @@ +package com.genersoft.iot.vmp.jt1078.codec.encode; + + +import com.genersoft.iot.vmp.jt1078.proc.response.Rs; +import com.genersoft.iot.vmp.jt1078.session.Session; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author QingtaiJiang + * @date 2023/4/27 18:10 + * @email qingtaij@163.com + */ +public class Jt808Encoder extends MessageToByteEncoder { + private final static Logger log = LoggerFactory.getLogger(Jt808Encoder.class); + + @Override + protected void encode(ChannelHandlerContext ctx, Rs msg, ByteBuf out) throws Exception { + Session session = ctx.channel().attr(Session.KEY).get(); + + ByteBuf encode = Jt808EncoderCmd.encode(msg, session, session.nextSerialNo()); + if(encode!=null){ + log.info("< {} hex:{}", session, ByteBufUtil.hexDump(encode)); + out.writeBytes(encode); + } + } + + +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/codec/encode/Jt808EncoderCmd.java b/src/main/java/com/genersoft/iot/vmp/jt1078/codec/encode/Jt808EncoderCmd.java new file mode 100644 index 000000000..0e9e11f6f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/codec/encode/Jt808EncoderCmd.java @@ -0,0 +1,151 @@ +package com.genersoft.iot.vmp.jt1078.codec.encode; + +import com.genersoft.iot.vmp.jt1078.annotation.MsgId; +import com.genersoft.iot.vmp.jt1078.proc.Header; +import com.genersoft.iot.vmp.jt1078.proc.entity.Cmd; +import com.genersoft.iot.vmp.jt1078.proc.response.Rs; +import com.genersoft.iot.vmp.jt1078.session.Session; +import com.genersoft.iot.vmp.jt1078.util.Bin; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.CompositeByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import io.netty.util.ByteProcessor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; + +import java.util.LinkedList; + +/** + * @author QingtaiJiang + * @date 2023/4/27 18:25 + * @email qingtaij@163.com + */ +public class Jt808EncoderCmd extends MessageToByteEncoder { + private final static Logger log = LoggerFactory.getLogger(Jt808EncoderCmd.class); + + @Override + protected void encode(ChannelHandlerContext ctx, Cmd cmd, ByteBuf out) throws Exception { + Session session = ctx.channel().attr(Session.KEY).get(); + Rs msg = cmd.getRs(); + ByteBuf encode = encode(msg, session, cmd.getPackageNo().intValue()); + if (encode != null) { + log.info("< {} hex:{}", session, ByteBufUtil.hexDump(encode)); + out.writeBytes(encode); + } + } + + + public static ByteBuf encode(Rs msg, Session session, Integer packageNo) { + String id = msg.getClass().getAnnotation(MsgId.class).id(); + if (!StringUtils.hasLength(id)) { + log.error("Not find msgId"); + return null; + } + + ByteBuf byteBuf = Unpooled.buffer(); + + byteBuf.writeBytes(ByteBufUtil.decodeHexDump(id)); + + ByteBuf encode = msg.encode(); + + Header header = msg.getHeader(); + if (header == null) { + header = session.getHeader(); + } + + if (header.is2019Version()) { + // 消息体属性 + byteBuf.writeShort(encode.readableBytes() | 1 << 14); + + // 版本号 + byteBuf.writeByte(header.getVersion()); + + // 终端手机号 + byteBuf.writeBytes(ByteBufUtil.decodeHexDump(Bin.strHexPaddingLeft(header.getDevId(), 20))); + } else { + // 消息体属性 + byteBuf.writeShort(encode.readableBytes()); + + byteBuf.writeBytes(ByteBufUtil.decodeHexDump(Bin.strHexPaddingLeft(header.getDevId(), 12))); + } + + // 消息体流水号 + byteBuf.writeShort(packageNo); + + // 写入消息体 + byteBuf.writeBytes(encode); + + // 计算校验码,并反转义 + byteBuf = escapeAndCheck0(byteBuf); + return byteBuf; + } + + + private static final ByteProcessor searcher = value -> !(value == 0x7d || value == 0x7e); + + //转义与校验 + public static ByteBuf escapeAndCheck0(ByteBuf source) { + + sign(source); + + int low = source.readerIndex(); + int high = source.writerIndex(); + + LinkedList bufList = new LinkedList<>(); + int mark, len; + while ((mark = source.forEachByte(low, high - low, searcher)) > 0) { + + len = mark + 1 - low; + ByteBuf[] slice = slice(source, low, len); + bufList.add(slice[0]); + bufList.add(slice[1]); + low += len; + } + + if (bufList.size() > 0) { + bufList.add(source.slice(low, high - low)); + } else { + bufList.add(source); + } + + ByteBuf delimiter = Unpooled.buffer(1, 1).writeByte(0x7e).retain(); + bufList.addFirst(delimiter); + bufList.addLast(delimiter); + + CompositeByteBuf byteBufLs = Unpooled.compositeBuffer(bufList.size()); + byteBufLs.addComponents(true, bufList); + return byteBufLs; + } + + public static void sign(ByteBuf buf) { + byte checkCode = bcc(buf); + buf.writeByte(checkCode); + } + + public static byte bcc(ByteBuf byteBuf) { + byte cs = 0; + while (byteBuf.isReadable()) + cs ^= byteBuf.readByte(); + byteBuf.resetReaderIndex(); + return cs; + } + + protected static ByteBuf[] slice(ByteBuf byteBuf, int index, int length) { + byte first = byteBuf.getByte(index + length - 1); + + ByteBuf[] byteBufList = new ByteBuf[2]; + byteBufList[0] = byteBuf.retainedSlice(index, length); + + if (first == 0x7d) { + byteBufList[1] = Unpooled.buffer(1, 1).writeByte(0x01); + } else { + byteBuf.setByte(index + length - 1, 0x7d); + byteBufList[1] = Unpooled.buffer(1, 1).writeByte(0x02); + } + return byteBufList; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/codec/netty/Jt808Handler.java b/src/main/java/com/genersoft/iot/vmp/jt1078/codec/netty/Jt808Handler.java new file mode 100644 index 000000000..fd5030272 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/codec/netty/Jt808Handler.java @@ -0,0 +1,72 @@ +package com.genersoft.iot.vmp.jt1078.codec.netty; + +import com.genersoft.iot.vmp.jt1078.proc.response.Rs; +import com.genersoft.iot.vmp.jt1078.session.Session; +import com.genersoft.iot.vmp.jt1078.session.SessionManager; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.timeout.IdleState; +import io.netty.handler.timeout.IdleStateEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author QingtaiJiang + * @date 2023/4/27 18:14 + * @email qingtaij@163.com + */ +public class Jt808Handler extends ChannelInboundHandlerAdapter { + + private final static Logger log = LoggerFactory.getLogger(Jt808Handler.class); + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + if (msg instanceof Rs) { + ctx.writeAndFlush(msg); + } else { + ctx.fireChannelRead(msg); + } + } + + @Override + public void channelActive(ChannelHandlerContext ctx) { + Channel channel = ctx.channel(); + Session session = SessionManager.INSTANCE.newSession(channel); + channel.attr(Session.KEY).set(session); + log.info("> Tcp connect {}", session); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) { + Session session = ctx.channel().attr(Session.KEY).get(); + log.info("< Tcp disconnect {}", session); + ctx.close(); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) { + Session session = ctx.channel().attr(Session.KEY).get(); + String message = e.getMessage(); + if (message.toLowerCase().contains("Connection reset by peer".toLowerCase())) { + log.info("< exception{} {}", session, e.getMessage()); + } else { + log.info("< exception{} {}", session, e.getMessage(), e); + } + + } + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { + if (evt instanceof IdleStateEvent) { + IdleStateEvent event = (IdleStateEvent) evt; + IdleState state = event.state(); + if (state == IdleState.READER_IDLE || state == IdleState.WRITER_IDLE) { + Session session = ctx.channel().attr(Session.KEY).get(); + log.warn("< Proactively disconnect{}", session); + ctx.close(); + } + } + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/codec/netty/TcpServer.java b/src/main/java/com/genersoft/iot/vmp/jt1078/codec/netty/TcpServer.java new file mode 100644 index 000000000..a7e4df826 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/codec/netty/TcpServer.java @@ -0,0 +1,112 @@ +package com.genersoft.iot.vmp.jt1078.codec.netty; + +import com.genersoft.iot.vmp.jt1078.codec.decode.Jt808Decoder; +import com.genersoft.iot.vmp.jt1078.codec.encode.Jt808Encoder; +import com.genersoft.iot.vmp.jt1078.codec.encode.Jt808EncoderCmd; +import com.genersoft.iot.vmp.jt1078.proc.factory.CodecFactory; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioChannelOption; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.DelimiterBasedFrameDecoder; +import io.netty.handler.timeout.IdleStateHandler; +import io.netty.util.concurrent.Future; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.TimeUnit; + +/** + * @author QingtaiJiang + * @date 2023/4/27 18:01 + * @email qingtaij@163.com + */ + +public class TcpServer { + private final static Logger log = LoggerFactory.getLogger(TcpServer.class); + + private final Integer port; + private boolean isRunning = false; + private EventLoopGroup bossGroup = null; + private EventLoopGroup workerGroup = null; + + private final ByteBuf DECODER_JT808 = Unpooled.wrappedBuffer(new byte[]{0x7e}); + + public TcpServer(Integer port) { + this.port = port; + } + + private void startTcpServer() { + try { + CodecFactory.init(); + this.bossGroup = new NioEventLoopGroup(); + this.workerGroup = new NioEventLoopGroup(); + ServerBootstrap bootstrap = new ServerBootstrap(); + bootstrap.channel(NioServerSocketChannel.class); + bootstrap.group(bossGroup, workerGroup); + + bootstrap.option(NioChannelOption.SO_BACKLOG, 1024) + .option(NioChannelOption.SO_REUSEADDR, true) + .childOption(NioChannelOption.TCP_NODELAY, true) + .childHandler(new ChannelInitializer() { + @Override + public void initChannel(NioSocketChannel channel) { + channel.pipeline() + .addLast(new IdleStateHandler(10, 0, 0, TimeUnit.MINUTES)) + .addLast(new DelimiterBasedFrameDecoder(1024 * 2, DECODER_JT808)) + .addLast(new Jt808Decoder()) + .addLast(new Jt808Encoder()) + .addLast(new Jt808EncoderCmd()) + .addLast(new Jt808Handler()); + } + }); + ChannelFuture channelFuture = bootstrap.bind(port).sync(); + // 监听设备TCP端口是否启动成功 + channelFuture.addListener(future -> { + if (!future.isSuccess()) { + log.error("Binding port:{} fail! cause: {}", port, future.cause().getCause(), future.cause()); + } + }); + log.info("服务:JT808 Server 启动成功, port:{}", port); + channelFuture.channel().closeFuture().sync(); + } catch (Exception e) { + log.warn("服务:JT808 Server 启动异常, port:{},{}", port, e.getMessage(), e); + } finally { + stop(); + } + } + + /** + * 开启一个新的线程,拉起来Netty + */ + public synchronized void start() { + if (this.isRunning) { + log.warn("服务:JT808 Server 已经启动, port:{}", port); + return; + } + this.isRunning = true; + new Thread(this::startTcpServer).start(); + } + + public synchronized void stop() { + if (!this.isRunning) { + log.warn("服务:JT808 Server 已经停止, port:{}", port); + } + this.isRunning = false; + Future future = this.bossGroup.shutdownGracefully(); + if (!future.isSuccess()) { + log.warn("bossGroup 无法正常停止", future.cause()); + } + future = this.workerGroup.shutdownGracefully(); + if (!future.isSuccess()) { + log.warn("workerGroup 无法正常停止", future.cause()); + } + log.warn("服务:JT808 Server 已经停止, port:{}", port); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/config/JT1078AutoConfiguration.java b/src/main/java/com/genersoft/iot/vmp/jt1078/config/JT1078AutoConfiguration.java new file mode 100644 index 000000000..6cac30cec --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/config/JT1078AutoConfiguration.java @@ -0,0 +1,30 @@ +package com.genersoft.iot.vmp.jt1078.config; + +import com.genersoft.iot.vmp.jt1078.cmd.JT1078Template; +import com.genersoft.iot.vmp.jt1078.codec.netty.TcpServer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; + +/** + * @author QingtaiJiang + * @date 2023/4/27 19:35 + * @email qingtaij@163.com + */ +@Order(Integer.MIN_VALUE) +@Configuration +@ConditionalOnProperty(value = "jt1078.enable", havingValue = "true") +public class JT1078AutoConfiguration { + + @Bean(initMethod = "start", destroyMethod = "stop") + public TcpServer jt1078Server(@Value("${jt1078.port}") Integer port) { + return new TcpServer(port); + } + + @Bean + public JT1078Template jt1078Template() { + return new JT1078Template(); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/config/JT1078Controller.java b/src/main/java/com/genersoft/iot/vmp/jt1078/config/JT1078Controller.java new file mode 100644 index 000000000..0c71d26a9 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/config/JT1078Controller.java @@ -0,0 +1,51 @@ +package com.genersoft.iot.vmp.jt1078.config; + +import com.genersoft.iot.vmp.jt1078.cmd.JT1078Template; +import com.genersoft.iot.vmp.jt1078.proc.response.*; +import com.genersoft.iot.vmp.vmanager.bean.WVPResult; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +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 javax.annotation.Resource; + +/** + * curl http://localhost:18080/api/jt1078/start/live/18864197066/1 + * + * @author QingtaiJiang + * @date 2023/4/27 18:12 + * @email qingtaij@163.com + */ +@ConditionalOnProperty(value = "jt1078.enable", havingValue = "true") +@RestController +@RequestMapping("/api/jt1078") +public class JT1078Controller { + + @Resource + JT1078Template jt1078Template; + + /** + * jt1078Template 调用示例 + */ + @GetMapping("/start/live/{deviceId}/{channelId}") + public WVPResult startLive(@PathVariable String deviceId, @PathVariable String channelId) { + J9101 j9101 = new J9101(); + j9101.setChannel(Integer.valueOf(channelId)); + j9101.setIp("192.168.1.1"); + j9101.setRate(1); + j9101.setTcpPort(7618); + j9101.setUdpPort(7618); + j9101.setType(0); + // TODO 分配ZLM,获取IP、端口 + String s = jt1078Template.startLive(deviceId, j9101, 6); + // TODO 设备响应成功后,封装拉流结果集 + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(200); + wvpResult.setData(String.format("http://192.168.1.1/rtp/%s_%s.live.mp4", deviceId, channelId)); + return wvpResult; + } + +} + diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/Header.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/Header.java new file mode 100644 index 000000000..86c5fff26 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/Header.java @@ -0,0 +1,76 @@ +package com.genersoft.iot.vmp.jt1078.proc; + +import com.genersoft.iot.vmp.jt1078.util.Bin; + +/** + * @author QingtaiJiang + * @date 2023/4/27 18:22 + * @email qingtaij@163.com + */ +public class Header { + // 消息ID + String msgId; + + // 消息体属性 + Integer msgPro; + + // 标识 + String devId; + + // 消息体流水号 + Integer sn; + + // 协议版本号 + Short version = -1; + + + public String getMsgId() { + return msgId; + } + + public void setMsgId(String msgId) { + this.msgId = msgId; + } + + public Integer getMsgPro() { + return msgPro; + } + + public void setMsgPro(Integer msgPro) { + this.msgPro = msgPro; + } + + public String getDevId() { + return devId; + } + + public void setDevId(String devId) { + this.devId = devId; + } + + public Integer getSn() { + return sn; + } + + public void setSn(Integer sn) { + this.sn = sn; + } + + public Short getVersion() { + return version; + } + + public void setVersion(Short version) { + this.version = version; + } + + /** + * 判断是否是2019的版本 + * + * @return true 2019后的版本。false 2013 + */ + public boolean is2019Version() { + return Bin.get(msgPro, 14); + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/entity/Cmd.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/entity/Cmd.java new file mode 100644 index 000000000..28726e83b --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/entity/Cmd.java @@ -0,0 +1,116 @@ +package com.genersoft.iot.vmp.jt1078.proc.entity; + +import com.genersoft.iot.vmp.jt1078.proc.response.Rs; + +/** + * @author QingtaiJiang + * @date 2023/4/27 18:23 + * @email qingtaij@163.com + */ +public class Cmd { + String devId; + Long packageNo; + String msgId; + String respId; + Rs rs; + + public Cmd() { + } + + public Cmd(Builder builder) { + this.devId = builder.devId; + this.packageNo = builder.packageNo; + this.msgId = builder.msgId; + this.respId = builder.respId; + this.rs = builder.rs; + } + + public String getDevId() { + return devId; + } + + public void setDevId(String devId) { + this.devId = devId; + } + + public Long getPackageNo() { + return packageNo; + } + + public void setPackageNo(Long packageNo) { + this.packageNo = packageNo; + } + + public String getMsgId() { + return msgId; + } + + public void setMsgId(String msgId) { + this.msgId = msgId; + } + + public String getRespId() { + return respId; + } + + public void setRespId(String respId) { + this.respId = respId; + } + + public Rs getRs() { + return rs; + } + + public void setRs(Rs rs) { + this.rs = rs; + } + + public static class Builder { + String devId; + Long packageNo; + String msgId; + String respId; + Rs rs; + + public Builder setDevId(String devId) { + this.devId = devId.replaceFirst("^0*", ""); + return this; + } + + public Builder setPackageNo(Long packageNo) { + this.packageNo = packageNo; + return this; + } + + public Builder setMsgId(String msgId) { + this.msgId = msgId; + return this; + } + + public Builder setRespId(String respId) { + this.respId = respId; + return this; + } + + public Builder setRs(Rs re) { + this.rs = re; + return this; + } + + public Cmd build() { + return new Cmd(this); + } + } + + + @Override + public String toString() { + return "Cmd{" + + "devId='" + devId + '\'' + + ", packageNo=" + packageNo + + ", msgId='" + msgId + '\'' + + ", respId='" + respId + '\'' + + ", rs=" + rs + + '}'; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/factory/CodecFactory.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/factory/CodecFactory.java new file mode 100644 index 000000000..45d5fc71f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/factory/CodecFactory.java @@ -0,0 +1,44 @@ +package com.genersoft.iot.vmp.jt1078.proc.factory; + +import com.genersoft.iot.vmp.jt1078.annotation.MsgId; +import com.genersoft.iot.vmp.jt1078.proc.request.Re; +import com.genersoft.iot.vmp.jt1078.util.ClassUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author QingtaiJiang + * @date 2023/4/27 18:29 + * @email qingtaij@163.com + */ + +public class CodecFactory { + private final static Logger log = LoggerFactory.getLogger(CodecFactory.class); + + private static Map> protocolHash; + + public static void init() { + protocolHash = new HashMap<>(); + List> classList = ClassUtil.getClassList("com.genersoft.iot.vmp.jt1078.proc", MsgId.class); + for (Class handlerClass : classList) { + String id = handlerClass.getAnnotation(MsgId.class).id(); + protocolHash.put(id, handlerClass); + } + if (log.isDebugEnabled()) { + log.debug("消息ID缓存表 protocolHash:{}", protocolHash); + } + } + + public static Re getHandler(String msgId) { + Class aClass = protocolHash.get(msgId); + Object bean = ClassUtil.getBean(aClass); + if (bean instanceof Re) { + return (Re) bean; + } + return null; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0001.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0001.java new file mode 100644 index 000000000..1d7f85db0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0001.java @@ -0,0 +1,50 @@ +package com.genersoft.iot.vmp.jt1078.proc.request; + +import com.alibaba.fastjson2.JSON; +import com.genersoft.iot.vmp.jt1078.annotation.MsgId; +import com.genersoft.iot.vmp.jt1078.proc.Header; +import com.genersoft.iot.vmp.jt1078.proc.response.Rs; +import com.genersoft.iot.vmp.jt1078.session.Session; +import com.genersoft.iot.vmp.jt1078.session.SessionManager; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; + +/** + * 终端通用应答 + * + * @author QingtaiJiang + * @date 2023/4/27 18:04 + * @email qingtaij@163.com + */ +@MsgId(id = "0001") +public class J0001 extends Re { + int respNo; + String respId; + int result; + + @Override + protected Rs decode0(ByteBuf buf, Header header, Session session) { + respNo = buf.readUnsignedShort(); + respId = ByteBufUtil.hexDump(buf.readSlice(2)); + result = buf.readUnsignedByte(); + return null; + } + + @Override + protected Rs handler(Header header, Session session) { + SessionManager.INSTANCE.response(header.getDevId(), "0001", (long) respNo, JSON.toJSONString(this)); + return null; + } + + public int getRespNo() { + return respNo; + } + + public String getRespId() { + return respId; + } + + public int getResult() { + return result; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0002.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0002.java new file mode 100644 index 000000000..f52303a6f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0002.java @@ -0,0 +1,32 @@ +package com.genersoft.iot.vmp.jt1078.proc.request; + +import com.genersoft.iot.vmp.jt1078.annotation.MsgId; +import com.genersoft.iot.vmp.jt1078.proc.Header; +import com.genersoft.iot.vmp.jt1078.proc.response.J8001; +import com.genersoft.iot.vmp.jt1078.proc.response.Rs; +import com.genersoft.iot.vmp.jt1078.session.Session; +import io.netty.buffer.ByteBuf; + +/** + * 终端心跳 + * + * @author QingtaiJiang + * @date 2023/4/27 18:04 + * @email qingtaij@163.com + */ +@MsgId(id = "0002") +public class J0002 extends Re { + @Override + protected Rs decode0(ByteBuf buf, Header header, Session session) { + return null; + } + + @Override + protected Rs handler(Header header, Session session) { + J8001 j8001 = new J8001(); + j8001.setRespNo(header.getSn()); + j8001.setRespId(header.getMsgId()); + j8001.setResult(J8001.SUCCESS); + return j8001; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0004.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0004.java new file mode 100644 index 000000000..0f00a8013 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0004.java @@ -0,0 +1,27 @@ +package com.genersoft.iot.vmp.jt1078.proc.request; + +import com.genersoft.iot.vmp.jt1078.annotation.MsgId; +import com.genersoft.iot.vmp.jt1078.proc.Header; +import com.genersoft.iot.vmp.jt1078.proc.response.Rs; +import com.genersoft.iot.vmp.jt1078.session.Session; +import io.netty.buffer.ByteBuf; + +/** + * 查询服务器时间 + * + * @author QingtaiJiang + * @date 2023/4/27 18:06 + * @email qingtaij@163.com + */ +@MsgId(id = "0004") +public class J0004 extends Re { + @Override + protected Rs decode0(ByteBuf buf, Header header, Session session) { + return null; + } + + @Override + protected Rs handler(Header header, Session session) { + return null; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0100.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0100.java new file mode 100644 index 000000000..a731dda63 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0100.java @@ -0,0 +1,56 @@ +package com.genersoft.iot.vmp.jt1078.proc.request; + +import com.genersoft.iot.vmp.jt1078.annotation.MsgId; +import com.genersoft.iot.vmp.jt1078.proc.Header; +import com.genersoft.iot.vmp.jt1078.proc.response.J8100; +import com.genersoft.iot.vmp.jt1078.proc.response.Rs; +import com.genersoft.iot.vmp.jt1078.session.Session; +import io.netty.buffer.ByteBuf; + +/** + * 终端注册 + * + * @author QingtaiJiang + * @date 2023/4/27 18:06 + * @email qingtaij@163.com + */ +@MsgId(id = "0100") +public class J0100 extends Re { + + private int provinceId; + + private int cityId; + + private String makerId; + + private String deviceModel; + + private String deviceId; + + private int plateColor; + + private String plateNo; + + @Override + protected Rs decode0(ByteBuf buf, Header header, Session session) { + Short version = header.getVersion(); + provinceId = buf.readUnsignedShort(); + if (version > 1) { + cityId = buf.readUnsignedShort(); + // decode as 2019 + } else { + int i = buf.readUnsignedShort(); + // decode as 2013 + } + return null; + } + + @Override + protected Rs handler(Header header, Session session) { + J8100 j8100 = new J8100(); + j8100.setRespNo(header.getSn()); + j8100.setResult(J8100.SUCCESS); + j8100.setCode("WVP_YYDS"); + return j8100; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0102.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0102.java new file mode 100644 index 000000000..8e531ae41 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0102.java @@ -0,0 +1,36 @@ +package com.genersoft.iot.vmp.jt1078.proc.request; + +import com.genersoft.iot.vmp.jt1078.annotation.MsgId; +import com.genersoft.iot.vmp.jt1078.proc.Header; +import com.genersoft.iot.vmp.jt1078.proc.response.J8001; +import com.genersoft.iot.vmp.jt1078.proc.response.Rs; +import com.genersoft.iot.vmp.jt1078.session.Session; +import io.netty.buffer.ByteBuf; + +/** + * 终端鉴权 + * + * @author QingtaiJiang + * @date 2023/4/27 18:06 + * @email qingtaij@163.com + */ +@MsgId(id = "0102") +public class J0102 extends Re { + @Override + protected Rs decode0(ByteBuf buf, Header header, Session session) { + int lenCode = buf.readUnsignedByte(); +// String code = buf.readCharSequence(lenCode, CharsetUtil.UTF_8).toString(); + // if 2019 to decode next + return null; + } + + @Override + protected Rs handler(Header header, Session session) { + J8001 j8001 = new J8001(); + j8001.setRespNo(header.getSn()); + j8001.setRespId(header.getMsgId()); + j8001.setResult(J8001.SUCCESS); + return j8001; + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0200.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0200.java new file mode 100644 index 000000000..d027dd2e0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0200.java @@ -0,0 +1,32 @@ +package com.genersoft.iot.vmp.jt1078.proc.request; + +import com.genersoft.iot.vmp.jt1078.annotation.MsgId; +import com.genersoft.iot.vmp.jt1078.proc.Header; +import com.genersoft.iot.vmp.jt1078.proc.response.J8001; +import com.genersoft.iot.vmp.jt1078.proc.response.Rs; +import com.genersoft.iot.vmp.jt1078.session.Session; +import io.netty.buffer.ByteBuf; + +/** + * 实时消息上报 + * + * @author QingtaiJiang + * @date 2023/4/27 18:06 + * @email qingtaij@163.com + */ +@MsgId(id = "0200") +public class J0200 extends Re { + @Override + protected Rs decode0(ByteBuf buf, Header header, Session session) { + return null; + } + + @Override + protected Rs handler(Header header, Session session) { + J8001 j8001 = new J8001(); + j8001.setRespNo(header.getSn()); + j8001.setRespId(header.getMsgId()); + j8001.setResult(J8001.SUCCESS); + return j8001; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J1205.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J1205.java new file mode 100644 index 000000000..da0b89ec3 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J1205.java @@ -0,0 +1,190 @@ +package com.genersoft.iot.vmp.jt1078.proc.request; + +import com.alibaba.fastjson2.JSON; +import com.genersoft.iot.vmp.jt1078.annotation.MsgId; +import com.genersoft.iot.vmp.jt1078.proc.Header; +import com.genersoft.iot.vmp.jt1078.proc.response.J8001; +import com.genersoft.iot.vmp.jt1078.proc.response.Rs; +import com.genersoft.iot.vmp.jt1078.session.Session; +import com.genersoft.iot.vmp.jt1078.session.SessionManager; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; + +import java.util.ArrayList; +import java.util.List; + +/** + * 终端上传音视频资源列表 + * + * @author QingtaiJiang + * @date 2023/4/28 10:36 + * @email qingtaij@163.com + */ +@MsgId(id = "1205") +public class J1205 extends Re { + Integer respNo; + + private List recordList = new ArrayList(); + + @Override + protected Rs decode0(ByteBuf buf, Header header, Session session) { + respNo = buf.readUnsignedShort(); + long size = buf.readUnsignedInt(); + + for (int i = 0; i < size; i++) { + JRecordItem item = new JRecordItem(); + item.setChannelId(buf.readUnsignedByte()); + item.setStartTime(ByteBufUtil.hexDump(buf.readSlice(6))); + item.setEndTime(ByteBufUtil.hexDump(buf.readSlice(6))); + item.setWarn(buf.readLong()); + item.setMediaType(buf.readUnsignedByte()); + item.setStreamType(buf.readUnsignedByte()); + item.setStorageType(buf.readUnsignedByte()); + item.setSize(buf.readUnsignedInt()); + recordList.add(item); + } + + return null; + } + + @Override + protected Rs handler(Header header, Session session) { + SessionManager.INSTANCE.response(header.getDevId(), "1205", (long) respNo, JSON.toJSONString(this)); + + J8001 j8001 = new J8001(); + j8001.setRespNo(header.getSn()); + j8001.setRespId(header.getMsgId()); + j8001.setResult(J8001.SUCCESS); + return j8001; + } + + + public Integer getRespNo() { + return respNo; + } + + public void setRespNo(Integer respNo) { + this.respNo = respNo; + } + + public List getRecordList() { + return recordList; + } + + public void setRecordList(List recordList) { + this.recordList = recordList; + } + + public static class JRecordItem { + + // 逻辑通道号 + private int channelId; + + // 开始时间 + private String startTime; + + // 结束时间 + private String endTime; + + // 报警标志 + private long warn; + + // 音视频资源类型 + private int mediaType; + + // 码流类型 + private int streamType = 1; + + // 存储器类型 + private int storageType; + + // 文件大小 + private long size; + + public int getChannelId() { + return channelId; + } + + public void setChannelId(int channelId) { + this.channelId = channelId; + } + + 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 long getWarn() { + return warn; + } + + public void setWarn(long warn) { + this.warn = warn; + } + + public int getMediaType() { + return mediaType; + } + + public void setMediaType(int mediaType) { + this.mediaType = mediaType; + } + + public int getStreamType() { + return streamType; + } + + public void setStreamType(int streamType) { + this.streamType = streamType; + } + + public int getStorageType() { + return storageType; + } + + public void setStorageType(int storageType) { + this.storageType = storageType; + } + + public long getSize() { + return size; + } + + public void setSize(long size) { + this.size = size; + } + + @Override + public String toString() { + return "JRecordItem{" + + "channelId=" + channelId + + ", startTime='" + startTime + '\'' + + ", endTime='" + endTime + '\'' + + ", warn=" + warn + + ", mediaType=" + mediaType + + ", streamType=" + streamType + + ", storageType=" + storageType + + ", size=" + size + + '}'; + } + } + + @Override + public String toString() { + return "J1205{" + + "respNo=" + respNo + + ", recordList=" + recordList + + '}'; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/Re.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/Re.java new file mode 100644 index 000000000..0a24ad274 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/Re.java @@ -0,0 +1,40 @@ +package com.genersoft.iot.vmp.jt1078.proc.request; + +import com.genersoft.iot.vmp.jt1078.proc.Header; +import com.genersoft.iot.vmp.jt1078.proc.response.Rs; +import com.genersoft.iot.vmp.jt1078.session.Session; +import io.netty.buffer.ByteBuf; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; + +/** + * @author QingtaiJiang + * @date 2023/4/27 18:50 + * @email qingtaij@163.com + */ +public abstract class Re { + private final static Logger log = LoggerFactory.getLogger(Re.class); + + protected abstract Rs decode0(ByteBuf buf, Header header, Session session); + + protected abstract Rs handler(Header header, Session session); + + public Rs decode(ByteBuf buf, Header header, Session session) { + if (session != null && !StringUtils.hasLength(session.getDevId())) { + session.register(header.getDevId(), (int) header.getVersion(), header); + } + Rs rs = decode0(buf, header, session); + Rs rsHand = handler(header, session); + if (rs == null && rsHand != null) { + rs = rsHand; + } else if (rs != null && rsHand != null) { + log.warn("decode0:{} 与 handler:{} 返回值冲突,采用decode0返回值", rs, rsHand); + } + if (rs != null) { + rs.setHeader(header); + } + + return rs; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J8001.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J8001.java new file mode 100644 index 000000000..ec9e31f19 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J8001.java @@ -0,0 +1,43 @@ +package com.genersoft.iot.vmp.jt1078.proc.response; + +import com.genersoft.iot.vmp.jt1078.annotation.MsgId; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; + +/** + * @author QingtaiJiang + * @date 2023/4/27 18:48 + * @email qingtaij@163.com + */ +@MsgId(id = "8001") +public class J8001 extends Rs { + public static final Integer SUCCESS = 0; + + Integer respNo; + String respId; + Integer result; + + @Override + public ByteBuf encode() { + ByteBuf buffer = Unpooled.buffer(); + buffer.writeShort(respNo); + buffer.writeBytes(ByteBufUtil.decodeHexDump(respId)); + buffer.writeByte(result); + + return buffer; + } + + + public void setRespNo(Integer respNo) { + this.respNo = respNo; + } + + public void setRespId(String respId) { + this.respId = respId; + } + + public void setResult(Integer result) { + this.result = result; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J8100.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J8100.java new file mode 100644 index 000000000..48a9c95e6 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J8100.java @@ -0,0 +1,41 @@ +package com.genersoft.iot.vmp.jt1078.proc.response; + +import com.genersoft.iot.vmp.jt1078.annotation.MsgId; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.util.CharsetUtil; + +/** + * @author QingtaiJiang + * @date 2023/4/27 18:40 + * @email qingtaij@163.com + */ +@MsgId(id = "8100") +public class J8100 extends Rs { + public static final Integer SUCCESS = 0; + + Integer respNo; + Integer result; + String code; + + @Override + public ByteBuf encode() { + ByteBuf buffer = Unpooled.buffer(); + buffer.writeShort(respNo); + buffer.writeByte(result); + buffer.writeCharSequence(code, CharsetUtil.UTF_8); + return buffer; + } + + public void setRespNo(Integer respNo) { + this.respNo = respNo; + } + + public void setResult(Integer result) { + this.result = result; + } + + public void setCode(String code) { + this.code = code; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J9101.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J9101.java new file mode 100644 index 000000000..77e90b726 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J9101.java @@ -0,0 +1,112 @@ +package com.genersoft.iot.vmp.jt1078.proc.response; + +import com.genersoft.iot.vmp.jt1078.annotation.MsgId; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.util.CharsetUtil; + +/** + * 实时音视频传输请求 + * + * @author QingtaiJiang + * @date 2023/4/27 18:25 + * @email qingtaij@163.com + */ +@MsgId(id = "9101") +public class J9101 extends Rs { + String ip; + + // TCP端口 + Integer tcpPort; + + // UDP端口 + Integer udpPort; + + // 逻辑通道号 + Integer channel; + + // 数据类型 + /** + * 0:音视频,1:视频,2:双向对讲,3:监听,4:中心广播,5:透传 + */ + Integer type; + + // 码流类型 + /** + * 0:主码流,1:子码流 + */ + Integer rate; + + @Override + public ByteBuf encode() { + ByteBuf buffer = Unpooled.buffer(); + buffer.writeByte(ip.getBytes().length); + buffer.writeCharSequence(ip, CharsetUtil.UTF_8); + buffer.writeShort(tcpPort); + buffer.writeShort(udpPort); + buffer.writeByte(channel); + buffer.writeByte(type); + buffer.writeByte(rate); + return buffer; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public Integer getTcpPort() { + return tcpPort; + } + + public void setTcpPort(Integer tcpPort) { + this.tcpPort = tcpPort; + } + + public Integer getUdpPort() { + return udpPort; + } + + public void setUdpPort(Integer udpPort) { + this.udpPort = udpPort; + } + + public Integer getChannel() { + return channel; + } + + public void setChannel(Integer channel) { + this.channel = channel; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } + + public Integer getRate() { + return rate; + } + + public void setRate(Integer rate) { + this.rate = rate; + } + + @Override + public String toString() { + return "J9101{" + + "ip='" + ip + '\'' + + ", tcpPort=" + tcpPort + + ", udpPort=" + udpPort + + ", channel=" + channel + + ", type=" + type + + ", rate=" + rate + + '}'; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J9102.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J9102.java new file mode 100644 index 000000000..8d560b20c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J9102.java @@ -0,0 +1,99 @@ +package com.genersoft.iot.vmp.jt1078.proc.response; + +import com.genersoft.iot.vmp.jt1078.annotation.MsgId; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; + +/** + * 音视频实时传输控制 + * + * @author QingtaiJiang + * @date 2023/4/27 18:49 + * @email qingtaij@163.com + */ +@MsgId(id = "9102") +public class J9102 extends Rs { + + // 通道号 + Integer channel; + + // 控制指令 + /** + * 0:关闭音视频传输指令; + * 1:切换码流(增加暂停和继续); + * 2:暂停该通道所有流的发送; + * 3:恢复暂停前流的发送,与暂停前的流类型一致; + * 4:关闭双向对讲 + */ + Integer command; + + // 数据类型 + /** + * 0:关闭该通道有关的音视频数据; + * 1:只关闭该通道有关的音频,保留该通道 + * 有关的视频; + * 2:只关闭该通道有关的视频,保留该通道 + * 有关的音频 + */ + Integer closeType; + + // 数据类型 + /** + * 0:主码流; + * 1:子码流 + */ + Integer streamType; + + @Override + public ByteBuf encode() { + ByteBuf buffer = Unpooled.buffer(); + buffer.writeByte(channel); + buffer.writeByte(command); + buffer.writeByte(closeType); + buffer.writeByte(streamType); + return buffer; + } + + + public Integer getChannel() { + return channel; + } + + public void setChannel(Integer channel) { + this.channel = channel; + } + + public Integer getCommand() { + return command; + } + + public void setCommand(Integer command) { + this.command = command; + } + + public Integer getCloseType() { + return closeType; + } + + public void setCloseType(Integer closeType) { + this.closeType = closeType; + } + + public Integer getStreamType() { + return streamType; + } + + public void setStreamType(Integer streamType) { + this.streamType = streamType; + } + + @Override + public String toString() { + return "J9102{" + + "channel=" + channel + + ", command=" + command + + ", closeType=" + closeType + + ", streamType=" + streamType + + '}'; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J9201.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J9201.java new file mode 100644 index 000000000..8a66f3544 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J9201.java @@ -0,0 +1,173 @@ +package com.genersoft.iot.vmp.jt1078.proc.response; + +import com.genersoft.iot.vmp.jt1078.annotation.MsgId; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; +import io.netty.util.CharsetUtil; + +/** + * 回放请求 + * + * @author QingtaiJiang + * @date 2023/4/28 10:37 + * @email qingtaij@163.com + */ +@MsgId(id = "9201") +public class J9201 extends Rs { + // 服务器IP地址 + private String ip; + + // 实时视频服务器TCP端口号 + private int tcpPort; + + // 实时视频服务器UDP端口号 + private int udpPort; + + // 逻辑通道号 + private int channel; + + // 音视频资源类型:0.音视频 1.音频 2.视频 3.视频或音视频 + private int type; + + // 码流类型:0.所有码流 1.主码流 2.子码流(如果此通道只传输音频,此字段置0) + private int rate; + + // 存储器类型:0.所有存储器 1.主存储器 2.灾备存储器" + private int storageType; + + // 回放方式:0.正常回放 1.快进回放 2.关键帧快退回放 3.关键帧播放 4.单帧上传 + private int playbackType; + + // 快进或快退倍数:0.无效 1.1倍 2.2倍 3.4倍 4.8倍 5.16倍 (回放控制为1和2时,此字段内容有效,否则置0) + private int playbackSpeed; + + // 开始时间YYMMDDHHMMSS,回放方式为4时,该字段表示单帧上传时间 + private String startTime; + + // 结束时间YYMMDDHHMMSS,回放方式为4时,该字段无效,为0表示一直回放 + private String endTime; + + @Override + public ByteBuf encode() { + ByteBuf buffer = Unpooled.buffer(); + buffer.writeByte(ip.getBytes().length); + buffer.writeCharSequence(ip, CharsetUtil.UTF_8); + buffer.writeShort(tcpPort); + buffer.writeShort(udpPort); + buffer.writeByte(channel); + buffer.writeByte(type); + buffer.writeByte(rate); + buffer.writeByte(storageType); + buffer.writeByte(playbackType); + buffer.writeByte(playbackSpeed); + buffer.writeBytes(ByteBufUtil.decodeHexDump(startTime)); + buffer.writeBytes(ByteBufUtil.decodeHexDump(endTime)); + return buffer; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public int getTcpPort() { + return tcpPort; + } + + public void setTcpPort(int tcpPort) { + this.tcpPort = tcpPort; + } + + public int getUdpPort() { + return udpPort; + } + + public void setUdpPort(int udpPort) { + this.udpPort = udpPort; + } + + public int getChannel() { + return channel; + } + + public void setChannel(int channel) { + this.channel = channel; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public int getRate() { + return rate; + } + + public void setRate(int rate) { + this.rate = rate; + } + + public int getStorageType() { + return storageType; + } + + public void setStorageType(int storageType) { + this.storageType = storageType; + } + + public int getPlaybackType() { + return playbackType; + } + + public void setPlaybackType(int playbackType) { + this.playbackType = playbackType; + } + + public int getPlaybackSpeed() { + return playbackSpeed; + } + + public void setPlaybackSpeed(int playbackSpeed) { + this.playbackSpeed = playbackSpeed; + } + + 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; + } + + @Override + public String toString() { + return "J9201{" + + "ip='" + ip + '\'' + + ", tcpPort=" + tcpPort + + ", udpPort=" + udpPort + + ", channel=" + channel + + ", type=" + type + + ", rate=" + rate + + ", storageType=" + storageType + + ", playbackType=" + playbackType + + ", playbackSpeed=" + playbackSpeed + + ", startTime='" + startTime + '\'' + + ", endTime='" + endTime + '\'' + + '}'; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J9202.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J9202.java new file mode 100644 index 000000000..7cb4e53ef --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J9202.java @@ -0,0 +1,80 @@ +package com.genersoft.iot.vmp.jt1078.proc.response; + +import com.genersoft.iot.vmp.jt1078.annotation.MsgId; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; + +/** + * 平台下发远程录像回放控制 + * + * @author QingtaiJiang + * @date 2023/4/28 10:37 + * @email qingtaij@163.com + */ +@MsgId(id = "9202") +public class J9202 extends Rs { + // 逻辑通道号 + private int channel; + + // 回放控制:0.开始回放 1.暂停回放 2.结束回放 3.快进回放 4.关键帧快退回放 5.拖动回放 6.关键帧播放 + private int playbackType; + + // 快进或快退倍数:0.无效 1.1倍 2.2倍 3.4倍 4.8倍 5.16倍 (回放控制为3和4时,此字段内容有效,否则置0) + private int playbackSpeed; + + // 拖动回放位置(YYMMDDHHMMSS,回放控制为5时,此字段有效) + private String playbackTime; + + @Override + public ByteBuf encode() { + ByteBuf buffer = Unpooled.buffer(); + buffer.writeByte(channel); + buffer.writeByte(playbackType); + buffer.writeByte(playbackSpeed); + buffer.writeBytes(ByteBufUtil.decodeHexDump(playbackTime)); + return buffer; + } + + public int getChannel() { + return channel; + } + + public void setChannel(int channel) { + this.channel = channel; + } + + public int getPlaybackType() { + return playbackType; + } + + public void setPlaybackType(int playbackType) { + this.playbackType = playbackType; + } + + public int getPlaybackSpeed() { + return playbackSpeed; + } + + public void setPlaybackSpeed(int playbackSpeed) { + this.playbackSpeed = playbackSpeed; + } + + public String getPlaybackTime() { + return playbackTime; + } + + public void setPlaybackTime(String playbackTime) { + this.playbackTime = playbackTime; + } + + @Override + public String toString() { + return "J9202{" + + "channel=" + channel + + ", playbackType=" + playbackType + + ", playbackSpeed=" + playbackSpeed + + ", playbackTime='" + playbackTime + '\'' + + '}'; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J9205.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J9205.java new file mode 100644 index 000000000..36b858ebc --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J9205.java @@ -0,0 +1,94 @@ +package com.genersoft.iot.vmp.jt1078.proc.response; + +import com.genersoft.iot.vmp.jt1078.annotation.MsgId; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; + +/** + * 查询资源列表 + * + * @author QingtaiJiang + * @date 2023/4/28 10:36 + * @email qingtaij@163.com + */ +@MsgId(id = "9205") +public class J9205 extends Rs { + // 逻辑通道号 + private int channelId; + + // 开始时间YYMMDDHHMMSS,全0表示无起始时间 + private String startTime; + + // 结束时间YYMMDDHHMMSS,全0表示无终止时间 + private String endTime; + + // 报警标志 + private final int warnType = 0; + + // 音视频资源类型:0.音视频 1.音频 2.视频 3.视频或音视频 + private int mediaType; + + // 码流类型:0.所有码流 1.主码流 2.子码流 + private int streamType = 0; + + // 存储器类型:0.所有存储器 1.主存储器 2.灾备存储器 + private int storageType = 0; + + @Override + public ByteBuf encode() { + ByteBuf buffer = Unpooled.buffer(); + + buffer.writeByte(channelId); + buffer.writeBytes(ByteBufUtil.decodeHexDump(startTime)); + buffer.writeBytes(ByteBufUtil.decodeHexDump(endTime)); + buffer.writeLong(warnType); + buffer.writeByte(mediaType); + buffer.writeByte(streamType); + buffer.writeByte(storageType); + + return buffer; + } + + + public void setChannelId(int channelId) { + this.channelId = channelId; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public void setMediaType(int mediaType) { + this.mediaType = mediaType; + } + + public void setStreamType(int streamType) { + this.streamType = streamType; + } + + public void setStorageType(int storageType) { + this.storageType = storageType; + } + + public int getWarnType() { + return warnType; + } + + @Override + public String toString() { + return "J9205{" + + "channelId=" + channelId + + ", startTime='" + startTime + '\'' + + ", endTime='" + endTime + '\'' + + ", warnType=" + warnType + + ", mediaType=" + mediaType + + ", streamType=" + streamType + + ", storageType=" + storageType + + '}'; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/Rs.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/Rs.java new file mode 100644 index 000000000..243cd9424 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/Rs.java @@ -0,0 +1,27 @@ +package com.genersoft.iot.vmp.jt1078.proc.response; + + +import com.genersoft.iot.vmp.jt1078.proc.Header; +import io.netty.buffer.ByteBuf; + + +/** + * @author QingtaiJiang + * @date 2021/8/30 18:54 + * @email qingtaij@163.com + */ + +public abstract class Rs { + private Header header; + + public abstract ByteBuf encode(); + + + public Header getHeader() { + return header; + } + + public void setHeader(Header header) { + this.header = header; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/session/Session.java b/src/main/java/com/genersoft/iot/vmp/jt1078/session/Session.java new file mode 100644 index 000000000..f7df8de0d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/session/Session.java @@ -0,0 +1,114 @@ +package com.genersoft.iot.vmp.jt1078.session; + +import com.genersoft.iot.vmp.jt1078.proc.Header; +import io.netty.channel.Channel; +import io.netty.util.AttributeKey; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author QingtaiJiang + * @date 2023/4/27 18:54 + * @email qingtaij@163.com + */ +public class Session { + private final static Logger log = LoggerFactory.getLogger(Session.class); + + public static final AttributeKey KEY = AttributeKey.newInstance(Session.class.getName()); + + // Netty的channel + protected final Channel channel; + + // 原子类的自增ID + private final AtomicInteger serialNo = new AtomicInteger(0); + + // 是否注册成功 + private boolean registered = false; + + // 设备ID + private String devId; + + // 创建时间 + private final long creationTime; + + // 协议版本号 + private Integer protocolVersion; + + private Header header; + + protected Session(Channel channel) { + this.channel = channel; + this.creationTime = System.currentTimeMillis(); + } + + public void writeObject(Object message) { + log.info("<<<<<<<<<< cmd{},{}", this, message); + channel.writeAndFlush(message); + } + + /** + * 获得下一个流水号 + * + * @return 流水号 + */ + public int nextSerialNo() { + int current; + int next; + do { + current = serialNo.get(); + next = current > 0xffff ? 0 : current; + } while (!serialNo.compareAndSet(current, next + 1)); + return next; + } + + /** + * 注册session + * + * @param devId 设备ID + */ + public void register(String devId, Integer version, Header header) { + this.devId = devId; + this.registered = true; + this.protocolVersion = version; + this.header = header; + SessionManager.INSTANCE.put(devId, this); + } + + /** + * 获取设备号 + * + * @return 设备号 + */ + public String getDevId() { + return devId; + } + + + public boolean isRegistered() { + return registered; + } + + public long getCreationTime() { + return creationTime; + } + + public Integer getProtocolVersion() { + return protocolVersion; + } + + public Header getHeader() { + return header; + } + + @Override + public String toString() { + return "[" + + "devId=" + devId + + ", reg=" + registered + + ", version=" + protocolVersion + + ",ip=" + channel.remoteAddress() + + ']'; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/session/SessionManager.java b/src/main/java/com/genersoft/iot/vmp/jt1078/session/SessionManager.java new file mode 100644 index 000000000..c2876e561 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/session/SessionManager.java @@ -0,0 +1,127 @@ +package com.genersoft.iot.vmp.jt1078.session; + +import com.genersoft.iot.vmp.jt1078.proc.entity.Cmd; +import io.netty.channel.Channel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.TimeUnit; + + +/** + * @author QingtaiJiang + * @date 2023/4/27 19:54 + * @email qingtaij@163.com + */ +public enum SessionManager { + INSTANCE; + private final static Logger log = LoggerFactory.getLogger(SessionManager.class); + + // 用与消息的缓存 + private final Map> topicSubscribers = new ConcurrentHashMap<>(); + + // session的缓存 + private final Map sessionMap; + + SessionManager() { + this.sessionMap = new ConcurrentHashMap<>(); + } + + /** + * 创建新的Session + * + * @param channel netty通道 + * @return 创建的session对象 + */ + public Session newSession(Channel channel) { + return new Session(channel); + } + + + /** + * 获取指定设备的Session + * + * @param clientId 设备Id + * @return Session + */ + public Session get(Object clientId) { + return sessionMap.get(clientId); + } + + /** + * 放入新设备连接的session + * + * @param clientId 设备ID + * @param newSession session + */ + protected void put(Object clientId, Session newSession) { + sessionMap.put(clientId, newSession); + } + + + /** + * 发送同步消息,接收响应 + * 默认超时时间6秒 + */ + public String request(Cmd cmd) { + // 默认6秒 + int timeOut = 6000; + return request(cmd, timeOut); + } + + public String request(Cmd cmd, Integer timeOut) { + Session session = this.get(cmd.getDevId()); + if (session == null) { + log.error("DevId: {} not online!", cmd.getDevId()); + return null; + } + String requestKey = requestKey(cmd.getDevId(), cmd.getRespId(), cmd.getPackageNo()); + SynchronousQueue subscribe = subscribe(requestKey); + if (subscribe == null) { + log.error("DevId: {} key:{} send repaid", cmd.getDevId(), requestKey); + return null; + } + session.writeObject(cmd); + try { + return subscribe.poll(timeOut, TimeUnit.SECONDS); + } catch (InterruptedException e) { + log.warn("<<<<<<<<<< timeout" + session, e); + } finally { + this.unsubscribe(requestKey); + } + return null; + } + + public Boolean response(String devId, String respId, Long responseNo, String data) { + String requestKey = requestKey(devId, respId, responseNo); + SynchronousQueue queue = topicSubscribers.get(requestKey); + if (queue != null) { + try { + return queue.offer(data, 2, TimeUnit.SECONDS); + } catch (InterruptedException e) { + log.error("{}", e.getMessage(), e); + } + } + log.warn("Not find response,key:{} data:{} ", requestKey, data); + return false; + } + + private void unsubscribe(String key) { + topicSubscribers.remove(key); + } + + private SynchronousQueue subscribe(String key) { + SynchronousQueue queue = null; + if (!topicSubscribers.containsKey(key)) + topicSubscribers.put(key, queue = new SynchronousQueue()); + return queue; + } + + private String requestKey(String devId, String respId, Long requestNo) { + return String.join("_", devId.replaceFirst("^0*", ""), respId, requestNo.toString()); + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/util/Bin.java b/src/main/java/com/genersoft/iot/vmp/jt1078/util/Bin.java new file mode 100644 index 000000000..31f8b9301 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/util/Bin.java @@ -0,0 +1,41 @@ +package com.genersoft.iot.vmp.jt1078.util; + +/** + * 32位整型的二进制读写 + */ +public class Bin { + + private static final int[] bits = new int[32]; + + static { + bits[0] = 1; + for (int i = 1; i < bits.length; i++) { + bits[i] = bits[i - 1] << 1; + } + } + + /** + * 读取n的第i位 + * + * @param n int32 + * @param i 取值范围0-31 + */ + public static boolean get(int n, int i) { + return (n & bits[i]) == bits[i]; + } + + /** + * 不足位数从左边加0 + */ + public static String strHexPaddingLeft(String data, int length) { + int dataLength = data.length(); + if (dataLength < length) { + StringBuilder dataBuilder = new StringBuilder(data); + for (int i = dataLength; i < length; i++) { + dataBuilder.insert(0, "0"); + } + data = dataBuilder.toString(); + } + return data; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/util/ClassUtil.java b/src/main/java/com/genersoft/iot/vmp/jt1078/util/ClassUtil.java new file mode 100644 index 000000000..3dcb1b860 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/util/ClassUtil.java @@ -0,0 +1,112 @@ +package com.genersoft.iot.vmp.jt1078.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; + +import java.lang.annotation.Annotation; +import java.util.LinkedList; +import java.util.List; + +public class ClassUtil { + + private static final Logger logger = LoggerFactory.getLogger(ClassUtil.class); + + + public static Object getBean(Class clazz) { + if (clazz != null) { + try { + return clazz.getDeclaredConstructor().newInstance(); + } catch (Exception ex) { + logger.error("ClassUtil:找不到指定的类", ex); + } + } + return null; + } + + + public static Object getBean(String className) { + Class clazz = null; + try { + clazz = Class.forName(className); + } catch (Exception ex) { + logger.error("ClassUtil:找不到指定的类"); + } + if (clazz != null) { + try { + //获取声明的构造器--》创建实例 + return clazz.getDeclaredConstructor().newInstance(); + } catch (Exception ex) { + logger.error("ClassUtil:找不到指定的类", ex); + } + } + return null; + } + + + /** + * 获取包下所有带注解的class + * + * @param packageName 包名 + * @param annotationClass 注解类型 + * @return list + */ + public static List> getClassList(String packageName, Class annotationClass) { + List> classList = getClassList(packageName); + classList.removeIf(next -> !next.isAnnotationPresent(annotationClass)); + return classList; + } + + public static List> getClassList(String... packageName) { + List> classList = new LinkedList<>(); + for (String s : packageName) { + List> c = getClassList(s); + classList.addAll(c); + } + return classList; + } + + public static List> getClassList(String packageName) { + List> classList = new LinkedList<>(); + try { + ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); + Resource[] resources = resourcePatternResolver.getResources(packageName.replace(".", "/") + "/**/*.class"); + for (Resource resource : resources) { + String url = resource.getURL().toString(); + + String[] split = url.split(packageName.replace(".", "/")); + String s = split[split.length - 1]; + String className = s.replace("/", "."); + className = className.substring(0, className.lastIndexOf(".")); + doAddClass(classList, packageName + className); + } + + } catch (Exception e) { + throw new RuntimeException(e); + } + return classList; + } + + private static void doAddClass(List> classList, String className) { + Class cls = loadClass(className, false); + classList.add(cls); + } + + public static Class loadClass(String className, boolean isInitialized) { + Class cls; + try { + cls = Class.forName(className, isInitialized, getClassLoader()); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + return cls; + } + + + public static ClassLoader getClassLoader() { + return Thread.currentThread().getContextClassLoader(); + } + +} 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 000000000..863ff32cb --- /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 java.io.IOException; +import java.net.ConnectException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +@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 getInfo(MediaServerItem mediaServerItem, RequestCallback callback){ + Map param = new HashMap<>(); + return sendGet(mediaServerItem, "api/record/info",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/SendRtpPortManager.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/SendRtpPortManager.java new file mode 100644 index 000000000..8366a4a2d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/SendRtpPortManager.java @@ -0,0 +1,55 @@ +package com.genersoft.iot.vmp.media.zlm; + +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.media.zlm.dto.MediaSendRtpPortInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +@Component +public class SendRtpPortManager { + + private final static Logger logger = LoggerFactory.getLogger(SendRtpPortManager.class); + + @Autowired + private UserSetting userSetting; + + @Autowired + private RedisTemplate redisTemplate; + + private final String KEY = "VM_MEDIA_SEND_RTP_PORT_RANGE_"; + + + public void initServerPort(String mediaServerId, int startPort, int endPort){ + String key = KEY + userSetting.getServerId() + "_" + mediaServerId; + MediaSendRtpPortInfo mediaSendRtpPortInfo = new MediaSendRtpPortInfo(startPort, endPort, mediaServerId); + redisTemplate.opsForValue().set(key, mediaSendRtpPortInfo); + } + + public int getNextPort(String mediaServerId) { + String key = KEY + userSetting.getServerId() + "_" + mediaServerId; + MediaSendRtpPortInfo mediaSendRtpPortInfo = (MediaSendRtpPortInfo)redisTemplate.opsForValue().get(key); + if (mediaSendRtpPortInfo == null) { + logger.warn("[发送端口管理] 获取{}的发送端口时未找到端口信息", mediaSendRtpPortInfo); + return 0; + } + int port; + if (mediaSendRtpPortInfo.getCurrent() %2 != 0) { + port = mediaSendRtpPortInfo.getCurrent() + 1; + }else { + port = mediaSendRtpPortInfo.getCurrent() + 2; + } + if (port > mediaSendRtpPortInfo.getEnd()) { + if (mediaSendRtpPortInfo.getStart() %2 != 0) { + port = mediaSendRtpPortInfo.getStart() + 1; + }else { + port = mediaSendRtpPortInfo.getStart(); + } + } + mediaSendRtpPortInfo.setCurrent(port); + redisTemplate.opsForValue().set(key, mediaSendRtpPortInfo); + return port; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java deleted file mode 100644 index d5c218cdc..000000000 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.genersoft.iot.vmp.media.zlm; - -import com.genersoft.iot.vmp.conf.MediaServerConfig; -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.beans.factory.annotation.Value; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.client.HttpClientErrorException; -import org.springframework.web.client.RestTemplate; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -@RestController -@RequestMapping("/zlm") -public class ZLMHTTPProxyController { - - - // private final static Logger logger = LoggerFactory.getLogger(ZLMHTTPProxyController.class); - - // @Autowired - // private IVideoManagerStorager storager; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - @Value("${media.port}") - private int mediaHttpPort; - - @ResponseBody - @RequestMapping(value = "/**/**/**", produces = "application/json;charset=UTF-8") - public Object proxy(HttpServletRequest request, HttpServletResponse response){ - - if (redisCatchStorage.getMediaInfo() == null) { - return "未接入流媒体"; - } - MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); - String requestURI = String.format("http://%s:%s%s?%s&%s", - mediaInfo.getLocalIP(), - mediaHttpPort, - request.getRequestURI().replace("/zlm",""), - mediaInfo.getHookAdminParams(), - request.getQueryString() - ); - // 发送请求 - RestTemplate restTemplate = new RestTemplate(); - //将指定的url返回的参数自动封装到自定义好的对应类对象中 - Object result = null; - try { - result = restTemplate.getForObject(requestURI,Object.class); - - }catch (HttpClientErrorException httpClientErrorException) { - response.setStatus(httpClientErrorException.getStatusCode().value()); - } - return result; - } -} 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 ba1a6f155..eac7401df 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,347 +1,702 @@ package com.genersoft.iot.vmp.media.zlm; -import java.util.UUID; - -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.common.InviteInfo; +import com.genersoft.iot.vmp.common.InviteSessionType; import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.conf.MediaServerConfig; -import com.genersoft.iot.vmp.gb28181.bean.Device; +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.event.subscribe.catalog.CatalogEvent; +import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; +import com.genersoft.iot.vmp.gb28181.transmit.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.MessageForPushChannel; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.vmanager.service.IPlayService; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.bean.StreamContent; 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.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.util.StringUtils; -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事件监听 +/** + * @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 SIPCommanderFroPlatform commanderFroPlatform; + + @Autowired + private IPlayService playService; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IInviteStreamService inviteStreamService; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private IStreamProxyService streamProxyService; + + @Autowired + private DeferredResultHolder resultHolder; + + @Autowired + private IMediaService mediaService; + + @Autowired + private EventPublisher eventPublisher; + + @Autowired + private ZLMMediaListManager zlmMediaListManager; + + @Autowired + private ZlmHttpHookSubscribe subscribe; + + @Autowired + private UserSetting userSetting; + + @Autowired + private IUserService userService; + + @Autowired + private VideoStreamSessionManager sessionManager; + + @Autowired + private AssistRESTfulUtils assistRESTfulUtils; + + @Autowired + private SSRCFactory ssrcFactory; + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + /** + * 服务器定时上报时间,上报间隔可配置,默认10s上报一次 + */ + @ResponseBody + + @PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8") + public HookResult onServerKeepalive(@RequestBody OnServerKeepaliveHookParam param) { - @Autowired - private SIPCommander cmder; + taskExecutor.execute(() -> { + List subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive); + if (subscribes != null && subscribes.size() > 0) { + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) { + subscribe.response(null, param); + } + } + }); + mediaServerService.updateMediaServerKeepalive(param.getMediaServerId(), param.getData()); - @Autowired - private IPlayService playService; + return HookResult.SUCCESS(); + } - @Autowired - private IVideoManagerStorager storager; + /** + * 播放器鉴权事件,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(); - @Autowired - private IRedisCatchStorage redisCatchStorage; + 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, param); + } + } + }); + 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"); + } + } - // @Autowired - // private ZLMRESTfulUtils zlmresTfulUtils; + return HookResult.SUCCESS(); + } - @Autowired - private ZLMHttpHookSubscribe subscribe; + /** + * rtsp/rtmp/rtp推流鉴权事件。 + */ + @ResponseBody + @PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8") + public HookResultForOnPublish onPublish(@RequestBody OnPublishHookParam param) { - @Value("${media.autoApplyPlay}") - private boolean autoApplyPlay; + JSONObject json = (JSONObject) JSON.toJSON(param); - @Value("${media.ip}") - private String mediaIp; + logger.info("[ZLM HOOK]推流鉴权:{}->{}", param.getMediaServerId(), param); - @Value("${media.wanIp}") - private String mediaWanIp; + String mediaServerId = json.getString("mediaServerId"); + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); - @Value("${media.port}") - private int mediaPort; + 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()); + } - /** - * 流量统计事件,播放器或推流器断开时并且耗用流量超过特定阈值时会触发此事件,阈值通过配置文件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()); - } - 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()); - } - 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()); - } - 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){ - - if (logger.isDebugEnabled()) { - logger.debug("ZLM HOOK on_publish API调用,参数:" + json.toString()); - } - // String app = json.getString("app"); - // String streamId = json.getString("id"); - ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json); - if (subscribe != null) subscribe.response(json); + HookResultForOnPublish result = HookResultForOnPublish.SUCCESS(); + if (!"rtp".equals(param.getApp())) { + result.setEnable_audio(true); + } - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("msg", "success"); - ret.put("enableHls", true); - ret.put("enableMP4", false); - ret.put("enableRtxp", true); - 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()); - } - 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()); - } - 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()); - } - 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()); - } - 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 JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("ZLM HOOK on_stream_changed API调用,参数:" + json.toString()); - } - // 流消失移除redis play - String app = json.getString("app"); - String streamId = json.getString("stream"); - boolean regist = json.getBoolean("regist"); - StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); - if ("rtp".equals(app) && !regist ) { - if (streamInfo!=null){ - redisCatchStorage.stopPlay(streamInfo); - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); - }else{ - streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId); - redisCatchStorage.stopPlayback(streamInfo); - } - } - 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 streamId = json.getString("stream"); + taskExecutor.execute(() -> { + ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json); + if (subscribe != null) { + if (mediaInfo != null) { + subscribe.response(mediaInfo, param); + } else { + new HookResultForOnPublish(1, "zlm not register"); + } + } + }); - cmder.streamByeCmd(streamId); - 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); - } - - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("close", true); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - /** - * 流未找到事件,用户可以在此事件触发时,立即去拉流,这样可以实现按需拉流;此事件对回复不敏感。 - * - */ - @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()); - } - if (autoApplyPlay) { - String app = json.getString("app"); - String streamId = json.getString("stream"); - StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); - if ("rtp".equals(app) && streamId.indexOf("gb_play") > -1 && streamInfo == null) { - String[] s = streamId.split("_"); - if (s.length == 4) { - String deviceId = s[2]; - String channelId = s[3]; - Device device = storager.queryVideoDevice(deviceId); - if (device != null) { - UUID uuid = UUID.randomUUID(); - cmder.playStreamCmd(device, channelId, (JSONObject response) -> { - logger.info("收到订阅消息: " + response.toJSONString()); - playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); - }, null); - } + 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() == InviteSessionType.DOWNLOAD) { + result.setMp4_max_second(10); + result.setEnable_audio(true); + result.setEnable_mp4(true); + } + } + if (mediaInfo.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) { + logger.info("推流时发现尚未设置录像路径,从assist服务中读取"); + JSONObject info = assistRESTfulUtils.getInfo(mediaInfo, null); + if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0 ) { + JSONObject dataJson = info.getJSONObject("data"); + if (dataJson != null) { + String recordPath = dataJson.getString("record"); + userSetting.setRecordPath(recordPath); + result.setMp4_save_path(recordPath); + // 修改zlm中的录像路径 + if (mediaInfo.isAutoConfig()) { + taskExecutor.execute(() -> { + mediaServerService.setZLMConfig(mediaInfo, false); + }); + } + } + } + } + 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 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 json){ - - if (logger.isDebugEnabled()) { - logger.debug("ZLM HOOK on_server_started API调用,参数:" + json.toString()); - } -// String data = json.getString("data"); -// List mediaServerConfigs = JSON.parseArray(JSON.toJSONString(json), MediaServerConfig.class); -// MediaServerConfig mediaServerConfig = mediaServerConfigs.get(0); - MediaServerConfig mediaServerConfig = JSON.toJavaObject(json, MediaServerConfig.class); - mediaServerConfig.setWanIp(StringUtils.isEmpty(mediaWanIp)? mediaIp: mediaWanIp); - mediaServerConfig.setLocalIP(mediaIp); - redisCatchStorage.updateMediaInfo(mediaServerConfig); - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("msg", "success"); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } + JSONObject json = (JSONObject) JSON.toJSON(param); + taskExecutor.execute(() -> { + ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json); + MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId()); + if (mediaInfo == null) { + logger.info("[ZLM HOOK] 流变化未找到ZLM, {}", param.getMediaServerId()); + return; + } + if (subscribe != null) { + subscribe.response(mediaInfo, param); + } + + List tracks = param.getTracks(); + // TODO 重构此处逻辑 + boolean isPush = false; + if (param.isRegist()) { + // 处理流注册的鉴权信息 + if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal() + || param.getOriginType() == OriginType.RTSP_PUSH.ordinal() + || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) { + isPush = true; + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream()); + if (streamAuthorityInfo == null) { + streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param); + } 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()); + } + // 设置拉流代理上线/离线 + int updateStatusResult = streamProxyService.updateStatus(param.isRegist(), param.getApp(), param.getStream()); + if (updateStatusResult > 0) { + + } + + if ("rtp".equals(param.getApp()) && !param.isRegist()) { + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream()); + if (inviteInfo != null && (inviteInfo.getType() == InviteSessionType.PLAY || inviteInfo.getType() == InviteSessionType.PLAYBACK)) { + inviteStreamService.removeInviteInfo(inviteInfo); + storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId()); + } + } else { + if (!"rtp".equals(param.getApp())) { + String type = OriginType.values()[param.getOriginType()].getType(); + if (param.isRegist()) { + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo( + param.getApp(), param.getStream()); + String callId = null; + if (streamAuthorityInfo != null) { + callId = streamAuthorityInfo.getCallId(); + } + StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaInfo, + param.getApp(), param.getStream(), tracks, callId); + param.setStreamInfo(new StreamContent(streamInfoByAppAndStream)); + redisCatchStorage.addStream(mediaInfo, 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(mediaInfo.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()); + } + GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream()); + if (gbStream != null) { + if (userSetting.isUsePushingAsStatus()) { + eventPublisher.catalogEventPublishForStream(null, gbStream, param.isRegist()?CatalogEvent.ON:CatalogEvent.OFF); + } + } + 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 != null && 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); + redisCatchStorage.deleteSendRTPServer(platformId, sendRtpItem.getChannelId(), + sendRtpItem.getCallId(), sendRtpItem.getStreamId()); + } 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()); + // 国标流, 点播/录像回放/录像下载 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream()); + // 点播 + if (inviteInfo != null) { + // 录像下载 + if (inviteInfo.getType() == InviteSessionType.DOWNLOAD) { + ret.put("close", false); + return ret; + } + // 收到无人观看说明流也没有在往上级推送 + if (redisCatchStorage.isChannelSendingRTP(inviteInfo.getChannelId())) { + List sendRtpItems = redisCatchStorage.querySendRTPServerByChnnelId( + inviteInfo.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()); + if (InviteStreamType.PUSH == sendRtpItem.getPlayType()) { + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, + sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getChannelId(), + sendRtpItem.getPlatformId(), parentPlatform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId()); + messageForPushChannel.setPlatFormIndex(parentPlatform.getId()); + redisCatchStorage.sendPlatformStopPlayMsg(messageForPushChannel); + } + } + } + } + Device device = deviceService.getDevice(inviteInfo.getDeviceId()); + if (device != null) { + try { + InviteInfo info = inviteStreamService.getInviteInfo(inviteInfo.getType(), + inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()); + if (info != null) { + cmder.streamByeCmd(device, inviteInfo.getChannelId(), + inviteInfo.getStream(), null); + } + } catch (InvalidArgumentException | ParseException | SipException | + SsrcTransactionNotFoundException e) { + logger.error("[无人观看]点播, 发送BYE失败 {}", e.getMessage()); + } + } + + inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), + inviteInfo.getChannelId(), inviteInfo.getStream()); + storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId()); + return ret; + } + } else { + // 非国标流 推流/拉流代理 + // 拉流代理 + StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream()); + if (streamProxyItem != null) { + if (streamProxyItem.isEnableRemoveNoneReader()) { + // 无人观看自动移除 + ret.put("close", true); + streamProxyService.del(param.getApp(), param.getStream()); + String url = streamProxyItem.getUrl() != null ? streamProxyItem.getUrl() : streamProxyItem.getSrcUrl(); + logger.info("[{}/{}]<-[{}] 拉流代理无人观看已经移除", param.getApp(), param.getStream(), url); + } else if (streamProxyItem.isEnableDisableNoneReader()) { + // 无人观看停用 + ret.put("close", true); + // 修改数据 + streamProxyService.stop(param.getApp(), param.getStream()); + } else { + // 无人观看不做处理 + ret.put("close", false); + } + return ret; + } + // TODO 推流具有主动性,暂时不做处理 +// 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()); + + result.onTimeout(() -> { + logger.info("[ZLM HOOK] 自动点播, 等待超时"); + // 释放rtpserver + msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "点播超时")); + resultHolder.invokeResult(msg); + }); + + // 录像查询以channelId作为deviceId查询 + resultHolder.put(key, uuid, result); + + if (!exist) { + playService.play(mediaInfo, deviceId, channelId, (code, message, data) -> { + msg.setData(new HookResult(code, message)); + resultHolder.invokeResult(msg); + }); + } + return result; + } else { + // 拉流代理 + StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream()); + if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnableDisableNoneReader()) { + 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, zlmServerConfig); + } + } + 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()); + ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc()); + 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, param); + } + } + }); + + 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 995f9166e..000000000 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.genersoft.iot.vmp.media.zlm; - -import com.alibaba.fastjson.JSONObject; -import org.springframework.stereotype.Component; - -import java.util.HashMap; -import java.util.Map; -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 - } - - public interface Event{ - void response(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 { - result = result && key.getString(s).equals(hookResponse.getString(s)); - } - - } - if (result) { - event = eventMap.get(key); - } - } - return event; - } -} 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 new file mode 100644 index 000000000..cbc5fde6e --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java @@ -0,0 +1,138 @@ +package com.genersoft.iot.vmp.media.zlm; + +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.GbStream; +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.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.storager.dao.GbStreamMapper; +import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper; +import com.genersoft.iot.vmp.storager.dao.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 java.text.ParseException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author lin + */ +@Component +public class ZLMMediaListManager { + + private Logger logger = LoggerFactory.getLogger("ZLMMediaListManager"); + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private GbStreamMapper gbStreamMapper; + + @Autowired + private PlatformGbStreamMapper platformGbStreamMapper; + + @Autowired + private IStreamPushService streamPushService; + + @Autowired + private IStreamProxyService streamProxyService; + + @Autowired + private StreamPushMapper streamPushMapper; + + @Autowired + private ZlmHttpHookSubscribe subscribe; + + @Autowired + private UserSetting userSetting; + + @Autowired + private ZLMServerFactory zlmServerFactory; + + @Autowired + private IMediaServerService mediaServerService; + + private Map channelOnPublishEvents = new ConcurrentHashMap<>(); + + 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); + } + removedChannelOnlineEventLister(transform.getApp(), transform.getStream()); + } + return transform; + } + + public void sendStreamEvent(String app, String stream, String mediaServerId) { + MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); + // 查看推流状态 + Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, app, stream); + if (streamReady != null && streamReady) { + ChannelOnlineEvent channelOnlineEventLister = getChannelOnlineEventLister(app, stream); + if (channelOnlineEventLister != null) { + try { + channelOnlineEventLister.run(app, stream, mediaServerId); + } catch (ParseException e) { + logger.error("sendStreamEvent: ", e); + } + removedChannelOnlineEventLister(app, stream); + } + } + } + + public int removeMedia(String app, String streamId) { + // 查找是否关联了国标, 关联了不删除, 置为离线 + GbStream gbStream = gbStreamMapper.selectOne(app, streamId); + int result; + if (gbStream == null) { + result = storager.removeMedia(app, streamId); + }else { + 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); + } + +} 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 58de847ea..054e5fccd 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,43 +1,80 @@ 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; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import java.io.IOException; +import java.io.*; import java.net.ConnectException; +import java.net.SocketTimeoutException; import java.util.HashMap; import java.util.Map; +import java.util.Objects; +import java.util.concurrent.TimeUnit; @Component public class ZLMRESTfulUtils { private final static Logger logger = LoggerFactory.getLogger(ZLMRESTfulUtils.class); - @Value("${media.ip}") - private String mediaIp; + private OkHttpClient client; - @Value("${media.port}") - private int mediaPort; - @Value("${media.secret}") - private String mediaSecret; - public JSONObject sendPost(String api, Map param) { - OkHttpClient client = new OkHttpClient(); - String url = String.format("http://%s:%s/index/api/%s", mediaIp, mediaPort, api); - JSONObject responseJSON = null; - logger.debug(url); + 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(10,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 = getClient(); + + if (mediaServerItem == null) { + return null; + } + String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api); + JSONObject responseJSON = new JSONObject(); + //-2自定义流媒体 调用错误码 + responseJSON.put("code",-2); + responseJSON.put("msg","流媒体调用失败"); FormBody.Builder builder = new FormBody.Builder(); - builder.add("secret",mediaSecret); - if (param != null) { + builder.add("secret",mediaServerItem.getSecret()); + if (param != null && param.keySet().size() > 0) { for (String key : param.keySet()){ - builder.add(key, param.get(key).toString()); + if (param.get(key) != null) { + builder.add(key, param.get(key).toString()); + } } } @@ -47,76 +84,282 @@ public class ZLMRESTfulUtils { .post(body) .url(url) .build(); - try { - Response response = client.newCall(request).execute(); - if (response.isSuccessful()) { - String responseStr = response.body().string(); - if (responseStr != null) { - responseJSON = JSON.parseObject(responseStr); + if (callback == null) { + try { + Response response = client.newCall(request).execute(); + if (response.isSuccessful()) { + ResponseBody responseBody = response.body(); + if (responseBody != null) { + String responseStr = responseBody.string(); + responseJSON = JSON.parseObject(responseStr); + } + }else { + response.close(); + Objects.requireNonNull(response.body()).close(); + } + }catch (IOException e) { + logger.error(String.format("[ %s ]请求失败: %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(){ + + @Override + public void onResponse(@NotNull Call call, @NotNull Response response){ + if (response.isSuccessful()) { + try { + String responseStr = Objects.requireNonNull(response.body()).string(); + callback.run(JSON.parseObject(responseStr)); + } catch (IOException e) { + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + } + + }else { + response.close(); + Objects.requireNonNull(response.body()).close(); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage())); + + if(e instanceof SocketTimeoutException){ + //读取超时超时异常 + logger.error(String.format("读取ZLM数据失败: %s, %s", call.request().toString(), e.getMessage())); + } + if(e instanceof ConnectException){ + //判断连接异常,我这里是报Failed to connect to 10.7.5.144 + logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage())); + } + } + }); } - } catch (ConnectException e) { - logger.error(String.format("连接ZLM失败: %s, %s", e.getCause().getMessage(), e.getMessage())); - logger.info("请检查media配置并确认ZLM已启动..."); - }catch (IOException e) { - e.printStackTrace(); - } + + return responseJSON; } - public JSONObject getMediaList(String app, String schema){ - Map param = new HashMap<>(); - param.put("app",app); - param.put("schema",schema); - param.put("vhost","__defaultVhost__"); - return sendPost("getMediaList",param); + public void sendGetForImg(MediaServerItem mediaServerItem, String api, Map params, String targetPath, String fileName) { + String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api); + HttpUrl parseUrl = HttpUrl.parse(url); + if (parseUrl == null) { + return; + } + HttpUrl.Builder httpBuilder = parseUrl.newBuilder(); + + httpBuilder.addQueryParameter("secret", mediaServerItem.getSecret()); + if (params != null) { + for (Map.Entry param : params.entrySet()) { + httpBuilder.addQueryParameter(param.getKey(), param.getValue().toString()); + } + } + + Request request = new Request.Builder() + .url(httpBuilder.build()) + .build(); + logger.info(request.toString()); + try { + OkHttpClient client = getClient(); + Response response = client.newCall(request).execute(); + if (response.isSuccessful()) { + if (targetPath != null) { + File snapFolder = new File(targetPath); + if (!snapFolder.exists()) { + if (!snapFolder.mkdirs()) { + logger.warn("{}路径创建失败", snapFolder.getAbsolutePath()); + } + + } + File snapFile = new File(targetPath + File.separator + fileName); + FileOutputStream outStream = new FileOutputStream(snapFile); + + outStream.write(Objects.requireNonNull(response.body()).bytes()); + outStream.flush(); + outStream.close(); + } else { + logger.error(String.format("[ %s ]请求失败: %s %s", url, response.code(), response.message())); + } + } else { + logger.error(String.format("[ %s ]请求失败: %s %s", url, response.code(), response.message())); + } + Objects.requireNonNull(response.body()).close(); + } catch (ConnectException e) { + logger.error(String.format("连接ZLM失败: %s, %s", e.getCause().getMessage(), e.getMessage())); + logger.info("请检查media配置并确认ZLM已启动..."); + } catch (IOException e) { + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + } } - public JSONObject getMediaInfo(String app, String schema, String stream){ + public JSONObject getMediaList(MediaServerItem mediaServerItem, String app, String stream, String schema, RequestCallback callback){ + Map param = new HashMap<>(); + if (app != null) { + param.put("app",app); + } + if (stream != null) { + param.put("stream",stream); + } + if (schema != null) { + param.put("schema",schema); + } + param.put("vhost","__defaultVhost__"); + return sendPost(mediaServerItem, "getMediaList",param, callback); + } + + public JSONObject getMediaList(MediaServerItem mediaServerItem, String app, String stream){ + return getMediaList(mediaServerItem, app, stream,null, null); + } + + public JSONObject getMediaList(MediaServerItem mediaServerItem, RequestCallback callback){ + return sendPost(mediaServerItem, "getMediaList",null, callback); + } + + public JSONObject getMediaInfo(MediaServerItem mediaServerItem, String app, String schema, String stream){ Map param = new HashMap<>(); param.put("app",app); param.put("schema",schema); param.put("stream",stream); param.put("vhost","__defaultVhost__"); - return sendPost("getMediaInfo",param); + return sendPost(mediaServerItem, "getMediaInfo",param, null); } - public JSONObject getRtpInfo(String stream_id){ + public JSONObject getRtpInfo(MediaServerItem mediaServerItem, String stream_id){ Map param = new HashMap<>(); param.put("stream_id",stream_id); - return sendPost("getRtpInfo",param); + return sendPost(mediaServerItem, "getRtpInfo",param, null); } - public JSONObject addFFmpegSource(String src_url, String dst_url, String timeout_ms){ - System.out.println(src_url); - System.out.println(dst_url); + public JSONObject addFFmpegSource(MediaServerItem mediaServerItem, String src_url, String dst_url, String timeout_ms, + 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); - return sendPost("addFFmpegSource",param); + param.put("enable_mp4", enable_mp4); + param.put("ffmpeg_cmd_key", ffmpeg_cmd_key); + return sendPost(mediaServerItem, "addFFmpegSource",param, null); } - public JSONObject delFFmpegSource(String key){ + public JSONObject delFFmpegSource(MediaServerItem mediaServerItem, String key){ Map param = new HashMap<>(); param.put("key", key); - return sendPost("delFFmpegSource",param); + return sendPost(mediaServerItem, "delFFmpegSource",param, null); } - public JSONObject getMediaServerConfig(){ - return sendPost("getServerConfig",null); + public JSONObject getMediaServerConfig(MediaServerItem mediaServerItem){ + return sendPost(mediaServerItem, "getServerConfig",null, null); } - public JSONObject setServerConfig(Map param){ - return sendPost("setServerConfig",param); + public JSONObject setServerConfig(MediaServerItem mediaServerItem, Map param){ + return sendPost(mediaServerItem,"setServerConfig",param, null); } - public JSONObject openRtpServer(Map param){ - return sendPost("openRtpServer",param); + public JSONObject openRtpServer(MediaServerItem mediaServerItem, Map param){ + return sendPost(mediaServerItem, "openRtpServer",param, null); } - public JSONObject closeRtpServer(Map param) { - return sendPost("closeRtpServer",param); + public JSONObject closeRtpServer(MediaServerItem mediaServerItem, Map param) { + return sendPost(mediaServerItem, "closeRtpServer",param, null); + } + + public void closeRtpServer(MediaServerItem mediaServerItem, Map param, RequestCallback callback) { + sendPost(mediaServerItem, "closeRtpServer",param, callback); + } + + public JSONObject listRtpServer(MediaServerItem mediaServerItem) { + return sendPost(mediaServerItem, "listRtpServer",null, null); + } + + public JSONObject startSendRtp(MediaServerItem mediaServerItem, Map param) { + return sendPost(mediaServerItem, "startSendRtp",param, null); + } + + public JSONObject stopSendRtp(MediaServerItem mediaServerItem, Map param) { + return sendPost(mediaServerItem, "stopSendRtp",param, null); + } + + 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_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); + } + + public JSONObject closeStreams(MediaServerItem mediaServerItem, String app, String stream) { + Map param = new HashMap<>(); + param.put("vhost", "__defaultVhost__"); + param.put("app", app); + param.put("stream", stream); + param.put("force", 1); + return sendPost(mediaServerItem, "close_streams",param, null); + } + + public JSONObject getAllSession(MediaServerItem mediaServerItem) { + return sendPost(mediaServerItem, "getAllSession",null, null); + } + + public void kickSessions(MediaServerItem mediaServerItem, String localPortSStr) { + Map param = new HashMap<>(); + param.put("local_port", localPortSStr); + sendPost(mediaServerItem, "kick_sessions",param, null); + } + + public void getSnap(MediaServerItem mediaServerItem, String streamUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) { + Map param = new HashMap<>(3); + param.put("url", streamUrl); + 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); + } + + public JSONObject connectRtpServer(MediaServerItem mediaServerItem, String dst_url, int dst_port, String stream_id) { + Map param = new HashMap<>(1); + param.put("dst_url", dst_url); + param.put("dst_port", dst_port); + param.put("stream_id", stream_id); + return sendPost(mediaServerItem, "connectRtpServer",param, null); + } + + public JSONObject updateRtpServerSSRC(MediaServerItem mediaServerItem, String streamId, String ssrc) { + Map param = new HashMap<>(1); + param.put("ssrc", ssrc); + param.put("stream_id", streamId); + return sendPost(mediaServerItem, "updateRtpServerSSRC",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 deleted file mode 100644 index f69ff0f07..000000000 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.genersoft.iot.vmp.media.zlm; - -import com.alibaba.fastjson.JSONObject; -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.stereotype.Component; - -import java.util.HashMap; -import java.util.Map; - -@Component -public class ZLMRTPServerFactory { - - private Logger logger = LoggerFactory.getLogger("ZLMRTPServerFactory"); - - @Value("${media.rtp.udpPortRange}") - private String udpPortRange; - - @Autowired - private ZLMRESTfulUtils zlmresTfulUtils; - - private int[] udpPortRangeArray = new int[2]; - - private int currentPort = 0; - - public int createRTPServer(String streamId) { - Map param = new HashMap<>(); - int result = -1; - int newPort = getPortFromUdpPortRange(); - param.put("port", newPort); - param.put("enable_tcp", 1); - param.put("stream_id", streamId); - JSONObject jsonObject = zlmresTfulUtils.openRtpServer(param); - System.out.println(jsonObject); - - if (jsonObject != null) { - switch (jsonObject.getInteger("code")){ - case 0: - result= newPort; - break; - case -300: // id已经存在 - result = newPort; - break; - case -400: // 端口占用 - result= createRTPServer(streamId); - break; - default: - logger.error("创建RTP Server 失败: " + jsonObject.getString("msg")); - break; - } - }else { - // 检查ZLM状态 - logger.error("创建RTP Server 失败: 请检查ZLM服务"); - } - return result; - } - - public boolean closeRTPServer(String streamId) { - boolean result = false; - Map param = new HashMap<>(); - param.put("stream_id", streamId); - JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(param); - if (jsonObject != null ) { - if (jsonObject.getInteger("code") == 0) { - result = jsonObject.getInteger("hit") == 1; - }else { - logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg")); - } - }else { - // 检查ZLM状态 - logger.error("关闭RTP Server 失败: 请检查ZLM服务"); - } - return result; - } - - private int getPortFromUdpPortRange() { - if (currentPort == 0) { - String[] udpPortRangeStrArray = udpPortRange.split(","); - udpPortRangeArray[0] = Integer.parseInt(udpPortRangeStrArray[0]); - udpPortRangeArray[1] = Integer.parseInt(udpPortRangeStrArray[1]); - } - - if (currentPort == 0 || currentPort++ > udpPortRangeArray[1]) { - currentPort = udpPortRangeArray[0]; - return udpPortRangeArray[0]; - } else { - if (currentPort % 2 == 1) { - currentPort++; - } - return currentPort++; - } - } -} 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 490bfd8e4..6e5940243 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,133 +1,172 @@ package com.genersoft.iot.vmp.media.zlm; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; -import com.genersoft.iot.vmp.conf.MediaServerConfig; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -//import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +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 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.scheduling.annotation.Async; import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; 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=12) public class ZLMRunner implements CommandLineRunner { private final static Logger logger = LoggerFactory.getLogger(ZLMRunner.class); - // @Autowired - // private IVideoManagerStorager storager; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - @Value("${media.ip}") - private String mediaIp; - - @Value("${media.wanIp}") - private String mediaWanIp; - - @Value("${media.hookIp}") - private String mediaHookIp; - - @Value("${media.port}") - private int mediaPort; - - @Value("${media.secret}") - private String mediaSecret; - - @Value("${media.streamNoneReaderDelayMS}") - private String streamNoneReaderDelayMS; - - @Value("${sip.ip}") - private String sipIP; - - @Value("${server.port}") - private String serverPort; - - @Value("${media.autoConfig}") - private boolean autoConfig; + private Map startGetMedia; @Autowired private ZLMRESTfulUtils zlmresTfulUtils; + @Autowired + private ZlmHttpHookSubscribe hookSubscribe; + + @Autowired + private EventPublisher publisher; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private MediaConfig mediaConfig; + + @Autowired + private DynamicTask dynamicTask; + + @Override public void run(String... strings) throws Exception { - // 获取zlm信息 - logger.info("等待zlm接入..."); - MediaServerConfig mediaServerConfig = getMediaServerConfig(); - if (mediaServerConfig != null) { - logger.info("zlm接入成功..."); - if (autoConfig) saveZLMConfig(); - mediaServerConfig = getMediaServerConfig(); - redisCatchStorage.updateMediaInfo(mediaServerConfig); + mediaServerService.clearMediaServerForOnline(); + MediaServerItem defaultMediaServer = mediaServerService.getDefaultMediaServer(); + if (defaultMediaServer == null) { + mediaServerService.addToDatabase(mediaConfig.getMediaSerItem()); + }else { + MediaServerItem mediaSerItem = mediaConfig.getMediaSerItem(); + mediaServerService.updateToDatabase(mediaSerItem); } + mediaServerService.syncCatchFromDatabase(); + HookSubscribeForServerStarted hookSubscribeForServerStarted = HookSubscribeFactory.on_server_started(); + // 订阅 zlm启动事件, 新的zlm也会从这里进入系统 + hookSubscribe.addSubscribe(hookSubscribeForServerStarted, + (mediaServerItem, hookParam)->{ + ZLMServerConfig zlmServerConfig = (ZLMServerConfig)hookParam; + if (zlmServerConfig !=null ) { + if (startGetMedia != null) { + startGetMedia.remove(zlmServerConfig.getGeneralMediaServerId()); + if (startGetMedia.size() == 0) { + hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started()); + } + } + } + }); + + // 获取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 ConcurrentHashMap<>(); + } + startGetMedia.put(mediaServerItem.getId(), true); + connectZlmServer(mediaServerItem); + allMap.put(mediaServerItem.getId(), mediaServerItem); + } + 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); + } + startGetMedia = null; + } + // 获取redis中所有的zlm + List allInRedis = mediaServerService.getAll(); + for (MediaServerItem mediaServerItem : allInRedis) { + if (!allMap.containsKey(mediaServerItem.getId())) { + mediaServerService.delete(mediaServerItem.getId()); + } + } + }, 60 * 1000 ); } - public MediaServerConfig getMediaServerConfig() { - JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(); - MediaServerConfig mediaServerConfig = null; - if (responseJSON != null) { - JSONArray data = responseJSON.getJSONArray("data"); + @Async("taskExecutor") + public void connectZlmServer(MediaServerItem mediaServerItem){ + 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()); + 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) { + if (startGetMedia == null) { return null;} + if (!mediaServerItem.isDefaultServer() && mediaServerService.getOne(mediaServerItem.getId()) == null) { + return null; + } + 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"); if (data != null && data.size() > 0) { - mediaServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), MediaServerConfig.class); - mediaServerConfig.setLocalIP(mediaIp); - mediaServerConfig.setWanIp(StringUtils.isEmpty(mediaWanIp)? mediaIp: mediaWanIp); + zlmServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class); } } else { - logger.error("getMediaServerConfig失败, 1s后重试"); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - mediaServerConfig = getMediaServerConfig(); + logger.error("[ {} ]-[ {}:{} ]主动连接失败, 2s后重试", + mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); } - return mediaServerConfig; - } + return zlmServerConfig; - private void saveZLMConfig() { - logger.info("设置zlm..."); - if (StringUtils.isEmpty(mediaHookIp)) { - mediaHookIp = sipIP; - } - - String hookPrex = String.format("http://%s:%s/index/hook", mediaHookIp, serverPort); - Map param = new HashMap<>(); - param.put("api.secret",mediaSecret); // -profile:v Baseline - param.put("ffmpeg.cmd","%s -fflags nobuffer -rtsp_transport tcp -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",""); - param.put("hook.on_http_access",""); - param.put("hook.on_publish",String.format("%s/on_publish", hookPrex)); - param.put("hook.on_record_mp4",""); - 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_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.timeoutSec","20"); - param.put("general.streamNoneReaderDelayMS",streamNoneReaderDelayMS); - - JSONObject responseJSON = zlmresTfulUtils.setServerConfig(param); - - if (responseJSON != null && responseJSON.getInteger("code") == 0) { - logger.info("设置zlm成功"); - }else { - logger.info("设置zlm失败: " + responseJSON.getString("msg")); - } } } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/MediaServerConfig.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java similarity index 54% rename from src/main/java/com/genersoft/iot/vmp/conf/MediaServerConfig.java rename to src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java index 76390e43f..80910c026 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/MediaServerConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java @@ -1,8 +1,9 @@ -package com.genersoft.iot.vmp.conf; +package com.genersoft.iot.vmp.media.zlm; -import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson2.annotation.JSONField; +import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam; -public class MediaServerConfig { +public class ZLMServerConfig extends HookParam { @JSONField(name = "api.apiDebug") private String apiDebug; @@ -10,15 +11,84 @@ public class MediaServerConfig { @JSONField(name = "api.secret") private String apiSecret; + @JSONField(name = "api.snapRoot") + private String apiSnapRoot; + + @JSONField(name = "api.defaultSnap") + private String apiDefaultSnap; + @JSONField(name = "ffmpeg.bin") private String ffmpegBin; @JSONField(name = "ffmpeg.cmd") private String ffmpegCmd; + @JSONField(name = "ffmpeg.snap") + private String ffmpegSnap; + @JSONField(name = "ffmpeg.log") private String ffmpegLog; + @JSONField(name = "ffmpeg.restart_sec") + private String ffmpegRestartSec; + + @JSONField(name = "protocol.modify_stamp") + private String protocolModifyStamp; + + @JSONField(name = "protocol.enable_audio") + private String protocolEnableAudio; + + @JSONField(name = "protocol.add_mute_audio") + private String protocolAddMuteAudio; + + @JSONField(name = "protocol.continue_push_ms") + private String protocolContinuePushMs; + + @JSONField(name = "protocol.enable_hls") + private String protocolEnableHls; + + @JSONField(name = "protocol.enable_mp4") + private String protocolEnableMp4; + + @JSONField(name = "protocol.enable_rtsp") + private String protocolEnableRtsp; + + @JSONField(name = "protocol.enable_rtmp") + private String protocolEnableRtmp; + + @JSONField(name = "protocol.enable_ts") + private String protocolEnableTs; + + @JSONField(name = "protocol.enable_fmp4") + private String protocolEnableFmp4; + + @JSONField(name = "protocol.mp4_as_player") + private String protocolMp4AsPlayer; + + @JSONField(name = "protocol.mp4_max_second") + private String protocolMp4MaxSecond; + + @JSONField(name = "protocol.mp4_save_path") + private String protocolMp4SavePath; + + @JSONField(name = "protocol.hls_save_path") + private String protocolHlsSavePath; + + @JSONField(name = "protocol.hls_demand") + private String protocolHlsDemand; + + @JSONField(name = "protocol.rtsp_demand") + private String protocolRtspDemand; + + @JSONField(name = "protocol.rtmp_demand") + private String protocolRtmpDemand; + + @JSONField(name = "protocol.ts_demand") + private String protocolTsDemand; + + @JSONField(name = "protocol.fmp4_demand") + private String protocolFmp4Demand; + @JSONField(name = "general.enableVhost") private String generalEnableVhost; @@ -29,11 +99,39 @@ public class MediaServerConfig { private String generalMaxStreamWaitMS; @JSONField(name = "general.streamNoneReaderDelayMS") - private String generalStreamNoneReaderDelayMS; + private int generalStreamNoneReaderDelayMS; - private String localIP; + @JSONField(name = "general.resetWhenRePlay") + private String generalResetWhenRePlay; - private String wanIp; + @JSONField(name = "general.mergeWriteMS") + private String generalMergeWriteMS; + + @JSONField(name = "general.mediaServerId") + private String generalMediaServerId; + + @JSONField(name = "general.wait_track_ready_ms") + private String generalWaitTrackReadyMs; + + @JSONField(name = "general.wait_add_track_ms") + private String generalWaitAddTrackMs; + + @JSONField(name = "general.unready_frame_cache") + private String generalUnreadyFrameCache; + + + @JSONField(name = "ip") + private String ip; + + private String sdpIp; + + private String streamIp; + + private String hookIp; + + private String updateTime; + + private String createTime; @JSONField(name = "hls.fileBufSize") private String hlsFileBufSize; @@ -47,12 +145,27 @@ public class MediaServerConfig { @JSONField(name = "hls.segNum") private String hlsSegNum; + @JSONField(name = "hls.segRetain") + private String hlsSegRetain; + + @JSONField(name = "hls.broadcastRecordTs") + private String hlsBroadcastRecordTs; + + @JSONField(name = "hls.deleteDelaySec") + private String hlsDeleteDelaySec; + + @JSONField(name = "hls.segKeep") + private String hlsSegKeep; + @JSONField(name = "hook.access_file_except_hls") private String hookAccessFileExceptHLS; @JSONField(name = "hook.admin_params") private String hookAdminParams; + @JSONField(name = "hook.alive_interval") + private Float hookAliveInterval; + @JSONField(name = "hook.enable") private String hookEnable; @@ -89,6 +202,18 @@ public class MediaServerConfig { @JSONField(name = "hook.on_stream_not_found") private String hookOnStreamNotFound; + @JSONField(name = "hook.on_server_started") + private String hookOnServerStarted; + + @JSONField(name = "hook.on_server_keepalive") + private String hookOnServerKeepalive; + + @JSONField(name = "hook.on_send_rtp_stopped") + private String hookOnSendRtpStopped; + + @JSONField(name = "hook.on_rtp_server_timeout") + private String hookOnRtpServerTimeout; + @JSONField(name = "hook.timeoutSec") private String hookTimeoutSec; @@ -108,7 +233,7 @@ public class MediaServerConfig { private String httpNotFound; @JSONField(name = "http.port") - private String httpPort; + private int httpPort; @JSONField(name = "http.rootPath") private String httpRootPath; @@ -117,7 +242,7 @@ public class MediaServerConfig { private String httpSendBufSize; @JSONField(name = "http.sslport") - private String httpSSLport; + private int httpSSLport; @JSONField(name = "multicast.addrMax") private String multicastAddrMax; @@ -150,7 +275,10 @@ public class MediaServerConfig { private String rtmpModifyStamp; @JSONField(name = "rtmp.port") - private String rtmpPort; + private int rtmpPort; + + @JSONField(name = "rtmp.sslport") + private int rtmpSslPort; @JSONField(name = "rtp.audioMtuSize") private String rtpAudioMtuSize; @@ -174,7 +302,10 @@ public class MediaServerConfig { private String rtpProxyDumpDir; @JSONField(name = "rtp_proxy.port") - private String rtpProxyPort; + private int rtpProxyPort; + + @JSONField(name = "rtp_proxy.port_range") + private String portRange; @JSONField(name = "rtp_proxy.timeoutSec") private String rtpProxyTimeoutSec; @@ -189,10 +320,10 @@ public class MediaServerConfig { private String rtspKeepAliveSecond; @JSONField(name = "rtsp.port") - private String rtspPort; + private int rtspPort; @JSONField(name = "rtsp.sslport") - private String rtspSSlport; + private int rtspSSlport; @JSONField(name = "shell.maxReqSize") private String shellMaxReqSize; @@ -201,6 +332,14 @@ public class MediaServerConfig { private String shellPhell; + public String getHookIp() { + return hookIp; + } + + public void setHookIp(String hookIp) { + this.hookIp = hookIp; + } + public String getApiDebug() { return apiDebug; } @@ -249,6 +388,14 @@ public class MediaServerConfig { this.generalEnableVhost = generalEnableVhost; } + public String getGeneralMediaServerId() { + return generalMediaServerId; + } + + public void setGeneralMediaServerId(String generalMediaServerId) { + this.generalMediaServerId = generalMediaServerId; + } + public String getGeneralFlowThreshold() { return generalFlowThreshold; } @@ -265,20 +412,52 @@ public class MediaServerConfig { this.generalMaxStreamWaitMS = generalMaxStreamWaitMS; } - public String getGeneralStreamNoneReaderDelayMS() { + public int getGeneralStreamNoneReaderDelayMS() { return generalStreamNoneReaderDelayMS; } - public void setGeneralStreamNoneReaderDelayMS(String generalStreamNoneReaderDelayMS) { + public void setGeneralStreamNoneReaderDelayMS(int generalStreamNoneReaderDelayMS) { this.generalStreamNoneReaderDelayMS = generalStreamNoneReaderDelayMS; } - public String getLocalIP() { - return localIP; + public String getIp() { + return ip; } - public void setLocalIP(String localIP) { - this.localIP = localIP; + public void setIp(String ip) { + this.ip = ip; + } + + public String getSdpIp() { + return sdpIp; + } + + public void setSdpIp(String sdpIp) { + this.sdpIp = sdpIp; + } + + public String getStreamIp() { + return streamIp; + } + + public void setStreamIp(String streamIp) { + this.streamIp = streamIp; + } + + 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 getHlsFileBufSize() { @@ -473,11 +652,11 @@ public class MediaServerConfig { this.httpNotFound = httpNotFound; } - public String getHttpPort() { + public int getHttpPort() { return httpPort; } - public void setHttpPort(String httpPort) { + public void setHttpPort(int httpPort) { this.httpPort = httpPort; } @@ -497,11 +676,11 @@ public class MediaServerConfig { this.httpSendBufSize = httpSendBufSize; } - public String getHttpSSLport() { + public int getHttpSSLport() { return httpSSLport; } - public void setHttpSSLport(String httpSSLport) { + public void setHttpSSLport(int httpSSLport) { this.httpSSLport = httpSSLport; } @@ -585,14 +764,22 @@ public class MediaServerConfig { this.rtmpModifyStamp = rtmpModifyStamp; } - public String getRtmpPort() { + public int getRtmpPort() { return rtmpPort; } - public void setRtmpPort(String rtmpPort) { + public void setRtmpPort(int rtmpPort) { this.rtmpPort = rtmpPort; } + public int getRtmpSslPort() { + return rtmpSslPort; + } + + public void setRtmpSslPort(int rtmpSslPort) { + this.rtmpSslPort = rtmpSslPort; + } + public String getRtpAudioMtuSize() { return rtpAudioMtuSize; } @@ -649,11 +836,11 @@ public class MediaServerConfig { this.rtpProxyDumpDir = rtpProxyDumpDir; } - public String getRtpProxyPort() { + public int getRtpProxyPort() { return rtpProxyPort; } - public void setRtpProxyPort(String rtpProxyPort) { + public void setRtpProxyPort(int rtpProxyPort) { this.rtpProxyPort = rtpProxyPort; } @@ -689,19 +876,19 @@ public class MediaServerConfig { this.rtspKeepAliveSecond = rtspKeepAliveSecond; } - public String getRtspPort() { + public int getRtspPort() { return rtspPort; } - public void setRtspPort(String rtspPort) { + public void setRtspPort(int rtspPort) { this.rtspPort = rtspPort; } - public String getRtspSSlport() { + public int getRtspSSlport() { return rtspSSlport; } - public void setRtspSSlport(String rtspSSlport) { + public void setRtspSSlport(int rtspSSlport) { this.rtspSSlport = rtspSSlport; } @@ -721,11 +908,307 @@ public class MediaServerConfig { this.shellPhell = shellPhell; } - public String getWanIp() { - return wanIp; + public Float getHookAliveInterval() { + return hookAliveInterval; } - public void setWanIp(String wanIp) { - this.wanIp = wanIp; + public void setHookAliveInterval(Float hookAliveInterval) { + this.hookAliveInterval = hookAliveInterval; + } + + public String getPortRange() { + return portRange; + } + + public void setPortRange(String portRange) { + this.portRange = portRange; + } + + public String getApiSnapRoot() { + return apiSnapRoot; + } + + public void setApiSnapRoot(String apiSnapRoot) { + this.apiSnapRoot = apiSnapRoot; + } + + public String getApiDefaultSnap() { + return apiDefaultSnap; + } + + public void setApiDefaultSnap(String apiDefaultSnap) { + this.apiDefaultSnap = apiDefaultSnap; + } + + public String getFfmpegSnap() { + return ffmpegSnap; + } + + public void setFfmpegSnap(String ffmpegSnap) { + this.ffmpegSnap = ffmpegSnap; + } + + public String getFfmpegRestartSec() { + return ffmpegRestartSec; + } + + public void setFfmpegRestartSec(String ffmpegRestartSec) { + this.ffmpegRestartSec = ffmpegRestartSec; + } + + public String getProtocolModifyStamp() { + return protocolModifyStamp; + } + + public void setProtocolModifyStamp(String protocolModifyStamp) { + this.protocolModifyStamp = protocolModifyStamp; + } + + public String getProtocolEnableAudio() { + return protocolEnableAudio; + } + + public void setProtocolEnableAudio(String protocolEnableAudio) { + this.protocolEnableAudio = protocolEnableAudio; + } + + public String getProtocolAddMuteAudio() { + return protocolAddMuteAudio; + } + + public void setProtocolAddMuteAudio(String protocolAddMuteAudio) { + this.protocolAddMuteAudio = protocolAddMuteAudio; + } + + public String getProtocolContinuePushMs() { + return protocolContinuePushMs; + } + + public void setProtocolContinuePushMs(String protocolContinuePushMs) { + this.protocolContinuePushMs = protocolContinuePushMs; + } + + public String getProtocolEnableHls() { + return protocolEnableHls; + } + + public void setProtocolEnableHls(String protocolEnableHls) { + this.protocolEnableHls = protocolEnableHls; + } + + public String getProtocolEnableMp4() { + return protocolEnableMp4; + } + + public void setProtocolEnableMp4(String protocolEnableMp4) { + this.protocolEnableMp4 = protocolEnableMp4; + } + + public String getProtocolEnableRtsp() { + return protocolEnableRtsp; + } + + public void setProtocolEnableRtsp(String protocolEnableRtsp) { + this.protocolEnableRtsp = protocolEnableRtsp; + } + + public String getProtocolEnableRtmp() { + return protocolEnableRtmp; + } + + public void setProtocolEnableRtmp(String protocolEnableRtmp) { + this.protocolEnableRtmp = protocolEnableRtmp; + } + + public String getProtocolEnableTs() { + return protocolEnableTs; + } + + public void setProtocolEnableTs(String protocolEnableTs) { + this.protocolEnableTs = protocolEnableTs; + } + + public String getProtocolEnableFmp4() { + return protocolEnableFmp4; + } + + public void setProtocolEnableFmp4(String protocolEnableFmp4) { + this.protocolEnableFmp4 = protocolEnableFmp4; + } + + public String getProtocolMp4AsPlayer() { + return protocolMp4AsPlayer; + } + + public void setProtocolMp4AsPlayer(String protocolMp4AsPlayer) { + this.protocolMp4AsPlayer = protocolMp4AsPlayer; + } + + public String getProtocolMp4MaxSecond() { + return protocolMp4MaxSecond; + } + + public void setProtocolMp4MaxSecond(String protocolMp4MaxSecond) { + this.protocolMp4MaxSecond = protocolMp4MaxSecond; + } + + public String getProtocolMp4SavePath() { + return protocolMp4SavePath; + } + + public void setProtocolMp4SavePath(String protocolMp4SavePath) { + this.protocolMp4SavePath = protocolMp4SavePath; + } + + public String getProtocolHlsSavePath() { + return protocolHlsSavePath; + } + + public void setProtocolHlsSavePath(String protocolHlsSavePath) { + this.protocolHlsSavePath = protocolHlsSavePath; + } + + public String getProtocolHlsDemand() { + return protocolHlsDemand; + } + + public void setProtocolHlsDemand(String protocolHlsDemand) { + this.protocolHlsDemand = protocolHlsDemand; + } + + public String getProtocolRtspDemand() { + return protocolRtspDemand; + } + + public void setProtocolRtspDemand(String protocolRtspDemand) { + this.protocolRtspDemand = protocolRtspDemand; + } + + public String getProtocolRtmpDemand() { + return protocolRtmpDemand; + } + + public void setProtocolRtmpDemand(String protocolRtmpDemand) { + this.protocolRtmpDemand = protocolRtmpDemand; + } + + public String getProtocolTsDemand() { + return protocolTsDemand; + } + + public void setProtocolTsDemand(String protocolTsDemand) { + this.protocolTsDemand = protocolTsDemand; + } + + public String getProtocolFmp4Demand() { + return protocolFmp4Demand; + } + + public void setProtocolFmp4Demand(String protocolFmp4Demand) { + this.protocolFmp4Demand = protocolFmp4Demand; + } + + public String getGeneralResetWhenRePlay() { + return generalResetWhenRePlay; + } + + public void setGeneralResetWhenRePlay(String generalResetWhenRePlay) { + this.generalResetWhenRePlay = generalResetWhenRePlay; + } + + public String getGeneralMergeWriteMS() { + return generalMergeWriteMS; + } + + public void setGeneralMergeWriteMS(String generalMergeWriteMS) { + this.generalMergeWriteMS = generalMergeWriteMS; + } + + public String getGeneralWaitTrackReadyMs() { + return generalWaitTrackReadyMs; + } + + public void setGeneralWaitTrackReadyMs(String generalWaitTrackReadyMs) { + this.generalWaitTrackReadyMs = generalWaitTrackReadyMs; + } + + public String getGeneralWaitAddTrackMs() { + return generalWaitAddTrackMs; + } + + public void setGeneralWaitAddTrackMs(String generalWaitAddTrackMs) { + this.generalWaitAddTrackMs = generalWaitAddTrackMs; + } + + public String getGeneralUnreadyFrameCache() { + return generalUnreadyFrameCache; + } + + public void setGeneralUnreadyFrameCache(String generalUnreadyFrameCache) { + this.generalUnreadyFrameCache = generalUnreadyFrameCache; + } + + public String getHlsSegRetain() { + return hlsSegRetain; + } + + public void setHlsSegRetain(String hlsSegRetain) { + this.hlsSegRetain = hlsSegRetain; + } + + public String getHlsBroadcastRecordTs() { + return hlsBroadcastRecordTs; + } + + public void setHlsBroadcastRecordTs(String hlsBroadcastRecordTs) { + this.hlsBroadcastRecordTs = hlsBroadcastRecordTs; + } + + public String getHlsDeleteDelaySec() { + return hlsDeleteDelaySec; + } + + public void setHlsDeleteDelaySec(String hlsDeleteDelaySec) { + this.hlsDeleteDelaySec = hlsDeleteDelaySec; + } + + public String getHlsSegKeep() { + return hlsSegKeep; + } + + public void setHlsSegKeep(String hlsSegKeep) { + this.hlsSegKeep = hlsSegKeep; + } + + public String getHookOnServerStarted() { + return hookOnServerStarted; + } + + public void setHookOnServerStarted(String hookOnServerStarted) { + this.hookOnServerStarted = hookOnServerStarted; + } + + public String getHookOnServerKeepalive() { + return hookOnServerKeepalive; + } + + public void setHookOnServerKeepalive(String hookOnServerKeepalive) { + this.hookOnServerKeepalive = hookOnServerKeepalive; + } + + public String getHookOnSendRtpStopped() { + return hookOnSendRtpStopped; + } + + public void setHookOnSendRtpStopped(String hookOnSendRtpStopped) { + this.hookOnSendRtpStopped = hookOnSendRtpStopped; + } + + public String getHookOnRtpServerTimeout() { + return hookOnRtpServerTimeout; + } + + public void setHookOnRtpServerTimeout(String hookOnRtpServerTimeout) { + this.hookOnRtpServerTimeout = hookOnRtpServerTimeout; } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerFactory.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerFactory.java new file mode 100644 index 000000000..d9c1fc861 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerFactory.java @@ -0,0 +1,312 @@ +package com.genersoft.iot.vmp.media.zlm; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.common.CommonCallback; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +@Component +public class ZLMServerFactory { + + private Logger logger = LoggerFactory.getLogger("ZLMRTPServerFactory"); + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private UserSetting userSetting; + + @Autowired + private ZlmHttpHookSubscribe hookSubscribe; + + @Autowired + private SendRtpPortManager sendRtpPortManager; + + + /** + * 开启rtpServer + * @param mediaServerItem zlm服务实例 + * @param streamId 流Id + * @param ssrc ssrc + * @param port 端口, 0/null为使用随机 + * @param reUsePort 是否重用端口 + * @param tcpMode 0/null udp 模式,1 tcp 被动模式, 2 tcp 主动模式。 + * @return + */ + public int createRTPServer(MediaServerItem mediaServerItem, String streamId, int ssrc, Integer port, Boolean reUsePort, Integer tcpMode) { + 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, reUsePort, tcpMode); + }else { + logger.warn("[开启rtpServer], 重启RtpServer错误"); + } + } + } + return result; + } + }else if(rtpInfo.getInteger("code") == -2){ + return result; + } + + Map param = new HashMap<>(); + + if (tcpMode == null) { + tcpMode = 0; + } + param.put("tcp_mode", tcpMode); + param.put("stream_id", streamId); + if (reUsePort != null) { + param.put("re_use_port", reUsePort?"1":"0"); + } + // 推流端口设置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<>(); + param.put("stream_id", streamId); + JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(serverItem, param); + if (jsonObject != null ) { + if (jsonObject.getInteger("code") == 0) { + result = jsonObject.getInteger("hit") == 1; + }else { + logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg")); + } + }else { + // 检查ZLM状态 + logger.error("关闭RTP Server 失败: 请检查ZLM服务"); + } + } + return result; + } + + public void closeRtpServer(MediaServerItem serverItem, String streamId, CommonCallback callback) { + if (serverItem == null) { + callback.run(false); + return; + } + Map param = new HashMap<>(); + param.put("stream_id", streamId); + zlmresTfulUtils.closeRtpServer(serverItem, param, jsonObject -> { + if (jsonObject != null ) { + if (jsonObject.getInteger("code") == 0) { + callback.run(jsonObject.getInteger("hit") == 1); + return; + }else { + logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg")); + } + }else { + // 检查ZLM状态 + logger.error("关闭RTP Server 失败: 请检查ZLM服务"); + } + callback.run(false); + }); + + + } + + + /** + * 创建一个国标推流 + * @param ip 推流ip + * @param port 推流端口 + * @param ssrc 推流唯一标识 + * @param platformId 平台id + * @param channelId 通道id + * @param tcp 是否为tcp + * @return SendRtpItem + */ + public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, + String deviceId, String channelId, boolean tcp, boolean rtcp){ + + // 默认为随机端口 + int localPort = 0; + if (userSetting.getGbSendStreamStrict()) { + localPort = sendRtpPortManager.getNextPort(serverItem.getId()); + if (localPort == 0) { + return null; + } + } + SendRtpItem sendRtpItem = new SendRtpItem(); + sendRtpItem.setIp(ip); + sendRtpItem.setPort(port); + sendRtpItem.setSsrc(ssrc); + sendRtpItem.setPlatformId(platformId); + 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; + } + + /** + * 创建一个直播推流 + * @param ip 推流ip + * @param port 推流端口 + * @param ssrc 推流唯一标识 + * @param platformId 平台id + * @param channelId 通道id + * @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, boolean rtcp){ + // 默认为随机端口 + int localPort = 0; + if (userSetting.getGbSendStreamStrict()) { + localPort = sendRtpPortManager.getNextPort(serverItem.getId()); + if (localPort == 0) { + return null; + } + } + SendRtpItem sendRtpItem = new SendRtpItem(); + sendRtpItem.setIp(ip); + sendRtpItem.setPort(port); + sendRtpItem.setSsrc(ssrc); + sendRtpItem.setApp(app); + sendRtpItem.setStreamId(stream); + sendRtpItem.setPlatformId(platformId); + sendRtpItem.setChannelId(channelId); + sendRtpItem.setTcp(tcp); + sendRtpItem.setLocalPort(localPort); + sendRtpItem.setServerId(userSetting.getServerId()); + sendRtpItem.setMediaServerId(serverItem.getId()); + sendRtpItem.setRtcp(rtcp); + return sendRtpItem; + } + + /** + * 调用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", "rtsp", streamId); + if (mediaInfo.getInteger("code") == -2) { + return null; + } + return (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")); + } + + /** + * 查询待转推的流是否就绪 + */ + public Boolean isStreamReady(MediaServerItem mediaServerItem, String app, String streamId) { + JSONObject mediaInfo = zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId); + if (mediaInfo == null || (mediaInfo.getInteger("code") == -2)) { + return null; + } + return (mediaInfo.getInteger("code") == 0 + && mediaInfo.getJSONArray("data") != null + && mediaInfo.getJSONArray("data").size() > 0); + } + + /** + * 查询转推的流是否有其它观看者 + * @param streamId + * @return + */ + public int totalReaderCount(MediaServerItem mediaServerItem, String app, String 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"); + } + + /** + * 调用zlm RESTful API —— stopSendRtp + */ + public Boolean stopSendRtpStream(MediaServerItem mediaServerItem, Mapparam) { + Boolean result = false; + JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaServerItem, param); + if (jsonObject == null) { + logger.error("[停止RTP推流] 失败: 请检查ZLM服务"); + } else if (jsonObject.getInteger("code") == 0) { + result= true; + logger.info("[停止RTP推流] 成功"); + } else { + logger.error("[停止RTP推流] 失败: {}, 参数:{}->\r\n{}",jsonObject.getString("msg"), JSON.toJSON(param), jsonObject); + } + return result; + } + + public void closeAllSendRtpStream() { + + } + + public Boolean updateRtpServerSSRC(MediaServerItem mediaServerItem, String streamId, String ssrc) { + boolean result = false; + JSONObject jsonObject = zlmresTfulUtils.updateRtpServerSSRC(mediaServerItem, streamId, ssrc); + if (jsonObject == null) { + logger.error("[更新RTPServer] 失败: 请检查ZLM服务"); + } else if (jsonObject.getInteger("code") == 0) { + result= true; + logger.info("[更新RTPServer] 成功"); + } else { + logger.error("[更新RTPServer] 失败: {}, streamId:{},ssrc:{}->\r\n{}",jsonObject.getString("msg"), + streamId, ssrc, jsonObject); + } + return result; + } +} 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 000000000..cad119ad8 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZlmHttpHookSubscribe.java @@ -0,0 +1,159 @@ +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 com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam; +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, HookParam hookParam); + } + + 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) { + eventMap.remove(entry.getKey()); + } + if (eventMap.size() == 0) { + allSubscribes.remove(hookSubscribe.getHookType()); + } + } + + } + } + + /** + * 获取某个类型的所有的订阅 + * @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分钟执行一次 + @Scheduled(fixedRate = 2 * 1000) + 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 000000000..714838edb --- /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 000000000..f3f389a7c --- /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 000000000..d633560d7 --- /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 000000000..8bcde0a05 --- /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 000000000..b73d74c1b --- /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 000000000..a4557e9a3 --- /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 000000000..7b76a9599 --- /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/MediaSendRtpPortInfo.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaSendRtpPortInfo.java new file mode 100644 index 000000000..2e9f63176 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaSendRtpPortInfo.java @@ -0,0 +1,50 @@ +package com.genersoft.iot.vmp.media.zlm.dto; + +public class MediaSendRtpPortInfo { + + private int start; + private int end; + private String mediaServerId; + + private int current; + + + public MediaSendRtpPortInfo(int start, int end, String mediaServerId) { + this.start = start; + this.current = start; + this.end = end; + this.mediaServerId = mediaServerId; + } + + public int getStart() { + return start; + } + + public void setStart(int start) { + this.start = start; + } + + public int getEnd() { + return end; + } + + public void setEnd(int end) { + this.end = end; + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + public int getCurrent() { + return current; + } + + public void setCurrent(int current) { + this.current = current; + } +} 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 new file mode 100644 index 000000000..066a67762 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java @@ -0,0 +1,311 @@ +package com.genersoft.iot.vmp.media.zlm.dto; + + +import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; +import io.swagger.v3.oas.annotations.media.Schema; +import org.springframework.util.ObjectUtils; + +@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; + + @Schema(description = "keepalive hook触发间隔,单位秒") + private Float hookAliveInterval; + + @Schema(description = "是否使用多端口模式") + private boolean rtpEnable; + + @Schema(description = "状态") + private boolean status; + + @Schema(description = "多端口RTP收流端口范围") + private String rtpPortRange; + + @Schema(description = "RTP发流端口范围") + 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 = "当前使用到的端口") + private int currentPort; + + + public MediaServerItem() { + } + + public MediaServerItem(ZLMServerConfig zlmServerConfig, String sipIp) { + id = zlmServerConfig.getGeneralMediaServerId(); + ip = zlmServerConfig.getIp(); + 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(); + rtmpSSlPort = zlmServerConfig.getRtmpSslPort(); + rtpProxyPort = zlmServerConfig.getRtpProxyPort(); + rtspPort = zlmServerConfig.getRtspPort(); + rtspSSLPort = zlmServerConfig.getRtspSSlport(); + autoConfig = true; // 默认值true; + secret = zlmServerConfig.getApiSecret(); + hookAliveInterval = zlmServerConfig.getHookAliveInterval(); + rtpEnable = false; // 默认使用单端口;直到用户自己设置开启多端口 + rtpPortRange = zlmServerConfig.getPortRange().replace("_",","); // 默认使用30000,30500作为级联时发送流的端口号 + recordAssistPort = 0; // 默认关闭 + + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getHookIp() { + return hookIp; + } + + public void setHookIp(String hookIp) { + this.hookIp = hookIp; + } + + public String getSdpIp() { + return sdpIp; + } + + public void setSdpIp(String sdpIp) { + this.sdpIp = sdpIp; + } + + public String getStreamIp() { + return streamIp; + } + + public void setStreamIp(String streamIp) { + this.streamIp = streamIp; + } + + public int getHttpPort() { + return httpPort; + } + + public void setHttpPort(int httpPort) { + this.httpPort = httpPort; + } + + public int getHttpSSlPort() { + return httpSSlPort; + } + + public void setHttpSSlPort(int httpSSlPort) { + this.httpSSlPort = httpSSlPort; + } + + public int getRtmpPort() { + return rtmpPort; + } + + public void setRtmpPort(int rtmpPort) { + this.rtmpPort = rtmpPort; + } + + public int getRtmpSSlPort() { + return rtmpSSlPort; + } + + public void setRtmpSSlPort(int rtmpSSlPort) { + this.rtmpSSlPort = rtmpSSlPort; + } + + public int getRtpProxyPort() { + return rtpProxyPort; + } + + public void setRtpProxyPort(int rtpProxyPort) { + this.rtpProxyPort = rtpProxyPort; + } + + public int getRtspPort() { + return rtspPort; + } + + public void setRtspPort(int rtspPort) { + this.rtspPort = rtspPort; + } + + public int getRtspSSLPort() { + return rtspSSLPort; + } + + public void setRtspSSLPort(int rtspSSLPort) { + this.rtspSSLPort = rtspSSLPort; + } + + public boolean isAutoConfig() { + return autoConfig; + } + + public void setAutoConfig(boolean autoConfig) { + this.autoConfig = autoConfig; + } + + public String getSecret() { + return secret; + } + + public void setSecret(String secret) { + this.secret = secret; + } + + public boolean isRtpEnable() { + return rtpEnable; + } + + public void setRtpEnable(boolean rtpEnable) { + this.rtpEnable = rtpEnable; + } + + public String getRtpPortRange() { + return rtpPortRange; + } + + public void setRtpPortRange(String rtpPortRange) { + this.rtpPortRange = rtpPortRange; + } + + public int getRecordAssistPort() { + return recordAssistPort; + } + + public void setRecordAssistPort(int recordAssistPort) { + this.recordAssistPort = recordAssistPort; + } + + public boolean isDefaultServer() { + return defaultServer; + } + + public void setDefaultServer(boolean defaultServer) { + this.defaultServer = defaultServer; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public int getCurrentPort() { + return currentPort; + } + + public void setCurrentPort(int currentPort) { + this.currentPort = currentPort; + } + + public boolean isStatus() { + return status; + } + + public void setStatus(boolean status) { + this.status = status; + } + + public String getLastKeepaliveTime() { + return lastKeepaliveTime; + } + + public void setLastKeepaliveTime(String lastKeepaliveTime) { + this.lastKeepaliveTime = lastKeepaliveTime; + } + + public Float getHookAliveInterval() { + return hookAliveInterval; + } + + public void setHookAliveInterval(Float hookAliveInterval) { + this.hookAliveInterval = hookAliveInterval; + } + + public String getSendRtpPortRange() { + return sendRtpPortRange; + } + + public void setSendRtpPortRange(String sendRtpPortRange) { + this.sendRtpPortRange = sendRtpPortRange; + } +} 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 000000000..0cc81f270 --- /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 000000000..ef77225d6 --- /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 new file mode 100644 index 000000000..dd517e3d3 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamProxyItem.java @@ -0,0 +1,171 @@ +package com.genersoft.iot.vmp.media.zlm.dto; + +import com.genersoft.iot.vmp.gb28181.bean.GbStream; +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * @author lin + */ +@Schema(description = "拉流代理的信息") +public class StreamProxyItem extends GbStream { + + @Schema(description = "类型") + private String type; + @Schema(description = "应用名") + private String app; + @Schema(description = "流ID") + private String stream; + @Schema(description = "流媒体服务ID") + private String mediaServerId; + @Schema(description = "拉流地址") + private String url; + @Schema(description = "拉流地址") + private String srcUrl; + @Schema(description = "目标地址") + private String dstUrl; + @Schema(description = "超时时间") + private int timeoutMs; + @Schema(description = "ffmpeg模板KEY") + private String ffmpegCmdKey; + @Schema(description = "rtsp拉流时,拉流方式,0:tcp,1:udp,2:组播") + private String rtpType; + @Schema(description = "是否启用") + private boolean enable; + @Schema(description = "是否启用音频") + private boolean enableAudio; + @Schema(description = "是否启用MP4") + private boolean enableMp4; + @Schema(description = "是否 无人观看时删除") + private boolean enableRemoveNoneReader; + + @Schema(description = "是否 无人观看时自动停用") + private boolean enableDisableNoneReader; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + public String getApp() { + return app; + } + + @Override + public void setApp(String app) { + this.app = app; + } + + @Override + public String getStream() { + return stream; + } + + @Override + public void setStream(String stream) { + this.stream = stream; + } + + @Override + public String getMediaServerId() { + return mediaServerId; + } + + @Override + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getSrcUrl() { + return srcUrl; + } + + public void setSrcUrl(String src_url) { + this.srcUrl = src_url; + } + + public String getDstUrl() { + return dstUrl; + } + + public void setDstUrl(String dst_url) { + this.dstUrl = dst_url; + } + + public int getTimeoutMs() { + return timeoutMs; + } + + public void setTimeoutMs(int timeout_ms) { + this.timeoutMs = timeout_ms; + } + + public String getFfmpegCmdKey() { + return ffmpegCmdKey; + } + + public void setFfmpegCmdKey(String ffmpeg_cmd_key) { + this.ffmpegCmdKey = ffmpeg_cmd_key; + } + + public String getRtpType() { + return rtpType; + } + + public void setRtpType(String rtp_type) { + this.rtpType = rtp_type; + } + + public boolean isEnable() { + return enable; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } + + public boolean isEnableMp4() { + return enableMp4; + } + + public void setEnableMp4(boolean enable_mp4) { + this.enableMp4 = enable_mp4; + } + + public boolean isEnableRemoveNoneReader() { + return enableRemoveNoneReader; + } + + public void setEnableRemoveNoneReader(boolean enable_remove_none_reader) { + this.enableRemoveNoneReader = enable_remove_none_reader; + } + + public boolean isEnableDisableNoneReader() { + return enableDisableNoneReader; + } + + public void setEnableDisableNoneReader(boolean enable_disable_none_reader) { + this.enableDisableNoneReader = enable_disable_none_reader; + } + + public boolean isEnableAudio() { + return enableAudio; + } + + public void setEnableAudio(boolean enable_audio) { + this.enableAudio = enable_audio; + } + + +} 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 new file mode 100644 index 000000000..6befe46fa --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java @@ -0,0 +1,389 @@ +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; + + /** + * 产生源类型, + * unknown = 0, + * rtmp_push=1, + * rtsp_push=2, + * rtp_push=3, + * pull=4, + * ffmpeg_pull=5, + * mp4_vod=6, + * device_chn=7 + */ + @Schema(description = "产生源类型") + private int originType; + + /** + * 客户端和服务器网络信息,可能为null类型 + */ + @Schema(description = "客户端和服务器网络信息,可能为null类型") + private OnStreamChangedHookParam.OriginSock originSock; + + /** + * 产生源类型的字符串描述 + */ + @Schema(description = "产生源类型的字符串描述") + private String originTypeStr; + + /** + * 产生源的url + */ + @Schema(description = "产生源的url") + private String originUrl; + + /** + * 存活时间,单位秒 + */ + @Schema(description = "存活时间,单位秒") + private Long aliveSecond; + + /** + * 音视频轨道 + */ + @Schema(description = "音视频轨道") + private List tracks; + + /** + * 音视频轨道 + */ + @Schema(description = "音视频轨道") + private String vhost; + + /** + * 使用的流媒体ID + */ + @Schema(description = "使用的流媒体ID") + private String mediaServerId; + + /** + * 使用的服务ID + */ + @Schema(description = "使用的服务ID") + private String serverId; + + /** + * 推流时间 + */ + @Schema(description = "推流时间") + private String pushTime; + + /** + * 更新时间 + */ + @Schema(description = "更新时间") + private String updateTime; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + private String createTime; + + /** + * 是否正在推流 + */ + @Schema(description = "是否正在推流") + private boolean pushIng; + + /** + * 是否自己平台的推流 + */ + @Schema(description = "是否自己平台的推流") + private boolean self; + + + + public String getVhost() { + return vhost; + } + + public void setVhost(String vhost) { + this.vhost = vhost; + } + + + @Override + public int compareTo(@NotNull StreamPushItem streamPushItem) { + return Long.valueOf(DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(super.createTime) + - DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(streamPushItem.getCreateTime())).intValue(); + } + + public static class MediaSchema { + private String schema; + private Long bytesSpeed; + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public Long getBytesSpeed() { + return bytesSpeed; + } + + public void setBytesSpeed(Long bytesSpeed) { + this.bytesSpeed = bytesSpeed; + } + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + @Override + public String getApp() { + return app; + } + + @Override + public void setApp(String app) { + this.app = app; + } + + @Override + public String getStream() { + return stream; + } + + @Override + public void setStream(String stream) { + this.stream = stream; + } + + public String getTotalReaderCount() { + return totalReaderCount; + } + + public void setTotalReaderCount(String totalReaderCount) { + this.totalReaderCount = totalReaderCount; + } + + public List getSchemas() { + return schemas; + } + + public void setSchemas(List schemas) { + this.schemas = schemas; + } + + public int getOriginType() { + return originType; + } + + public void setOriginType(int originType) { + this.originType = originType; + } + + public OnStreamChangedHookParam.OriginSock getOriginSock() { + return originSock; + } + + public void setOriginSock(OnStreamChangedHookParam.OriginSock originSock) { + this.originSock = originSock; + } + + + public String getOriginTypeStr() { + return originTypeStr; + } + + public void setOriginTypeStr(String originTypeStr) { + this.originTypeStr = originTypeStr; + } + + public String getOriginUrl() { + return originUrl; + } + + public void setOriginUrl(String originUrl) { + this.originUrl = originUrl; + } + + public Long getAliveSecond() { + return aliveSecond; + } + + public void setAliveSecond(Long aliveSecond) { + this.aliveSecond = aliveSecond; + } + + public List getTracks() { + return tracks; + } + + public void setTracks(List tracks) { + this.tracks = tracks; + } + + + @Override + public String getMediaServerId() { + return mediaServerId; + } + + @Override + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + public String getServerId() { + return serverId; + } + + public void setServerId(String serverId) { + this.serverId = serverId; + } + + + public String getPushTime() { + return pushTime; + } + + public void setPushTime(String pushTime) { + this.pushTime = pushTime; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + @Override + public String getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public boolean isPushIng() { + return pushIng; + } + + public void setPushIng(boolean pushIng) { + this.pushIng = pushIng; + } + + public boolean isSelf() { + return self; + } + + public void setSelf(boolean self) { + this.self = self; + } + +// @Override +// public Integer getGbStreamId() { +// return super.getGbStreamId(); +// } +// +// @Override +// public void setGbStreamId(Integer gbStreamId) { +// super.setGbStreamId(gbStreamId); +// } +// +// +// public String getGbId() { +// return super.getGbId(); +// } +// +// public void setGbId(String gbId) { +// super.setGbId(gbId); +// } +// +// public String getName() { +// return super.getName(); +// } +// +// public void setName(String name) { +// super.setName(name); +// } +// +// public double getLongitude() { +// return super.getLongitude(); +// } +// +// public void setLongitude(double longitude) { +// super.setLongitude(longitude); +// } +// +// public double getLatitude() { +// return super.getLatitude(); +// } +// +// public void setLatitude(double latitude) { +// super.setLatitude(latitude); +// } +// +// public String getStreamType() { +// return super.getStreamType(); +// } +// +// public void setStreamType(String streamType) { +// super.setStreamType(streamType); +// } +// +// public boolean isStatus() { +// return super.isStatus(); +// } +// +// public void setStatus(boolean status) { +// super.setStatus(status); +// } + +} + diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/ZLMRunInfo.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/ZLMRunInfo.java new file mode 100644 index 000000000..624e4e136 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/ZLMRunInfo.java @@ -0,0 +1,33 @@ +package com.genersoft.iot.vmp.media.zlm.dto; + +/** + * 记录zlm运行中一些参数 + */ +public class ZLMRunInfo { + + /** + * zlm当前流数量 + */ + private int mediaCount; + + /** + * 在线状态 + */ + private boolean online; + + public int getMediaCount() { + return mediaCount; + } + + public void setMediaCount(int mediaCount) { + this.mediaCount = mediaCount; + } + + public boolean isOnline() { + return online; + } + + public void setOnline(boolean online) { + this.online = online; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookParam.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookParam.java new file mode 100644 index 000000000..46ccf2271 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookParam.java @@ -0,0 +1,17 @@ +package com.genersoft.iot.vmp.media.zlm.dto.hook; + +/** + * zlm hook事件的参数 + * @author lin + */ +public class HookParam { + private String mediaServerId; + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResult.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResult.java new file mode 100644 index 000000000..b327f13a4 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResult.java @@ -0,0 +1,40 @@ +package com.genersoft.iot.vmp.media.zlm.dto.hook; + +public class HookResult { + + private int code; + private String msg; + + + public HookResult() { + } + + public HookResult(int code, String msg) { + this.code = code; + this.msg = msg; + } + + public static HookResult SUCCESS(){ + return new HookResult(0, "success"); + } + + public static HookResult Fail(){ + return new HookResult(-1, "fail"); + } + + 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/media/zlm/dto/hook/HookResultForOnPublish.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java new file mode 100644 index 000000000..3a58d1561 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java @@ -0,0 +1,55 @@ +package com.genersoft.iot.vmp.media.zlm.dto.hook; + +public class HookResultForOnPublish extends HookResult{ + + private boolean enable_audio; + private boolean enable_mp4; + private int mp4_max_second; + private String mp4_save_path; + + public HookResultForOnPublish() { + } + + public static HookResultForOnPublish SUCCESS(){ + return new HookResultForOnPublish(0, "success"); + } + + public HookResultForOnPublish(int code, String msg) { + setCode(code); + setMsg(msg); + } + + public boolean isEnable_audio() { + return enable_audio; + } + + public void setEnable_audio(boolean enable_audio) { + this.enable_audio = enable_audio; + } + + public boolean isEnable_mp4() { + return enable_mp4; + } + + public void setEnable_mp4(boolean enable_mp4) { + this.enable_mp4 = enable_mp4; + } + + public int getMp4_max_second() { + return mp4_max_second; + } + + public void setMp4_max_second(int mp4_max_second) { + this.mp4_max_second = mp4_max_second; + } + + public String getMp4_save_path() { + return mp4_save_path; + } + + public void setMp4_save_path(String mp4_save_path) { + this.mp4_save_path = mp4_save_path; + } + + +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnPlayHookParam.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnPlayHookParam.java new file mode 100644 index 000000000..6e41cceab --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnPlayHookParam.java @@ -0,0 +1,95 @@ +package com.genersoft.iot.vmp.media.zlm.dto.hook; + +/** + * zlm hook事件中的on_play事件的参数 + * @author lin + */ +public class OnPlayHookParam extends HookParam{ + private String id; + private String app; + private String stream; + private String ip; + private String params; + private int port; + private String schema; + private String vhost; + + + 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 String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getParams() { + return params; + } + + public void setParams(String params) { + this.params = params; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public String getVhost() { + return vhost; + } + + public void setVhost(String vhost) { + this.vhost = vhost; + } + + @Override + public String toString() { + return "OnPlayHookParam{" + + "id='" + id + '\'' + + ", app='" + app + '\'' + + ", stream='" + stream + '\'' + + ", ip='" + ip + '\'' + + ", params='" + params + '\'' + + ", port=" + port + + ", schema='" + schema + '\'' + + ", vhost='" + vhost + '\'' + + '}'; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnPublishHookParam.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnPublishHookParam.java new file mode 100644 index 000000000..8a3e084da --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnPublishHookParam.java @@ -0,0 +1,95 @@ +package com.genersoft.iot.vmp.media.zlm.dto.hook; + +/** + * zlm hook事件中的on_publish事件的参数 + * @author lin + */ +public class OnPublishHookParam extends HookParam{ + private String id; + private String app; + private String stream; + private String ip; + private String params; + private int port; + private String schema; + private String vhost; + + + 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 String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getParams() { + return params; + } + + public void setParams(String params) { + this.params = params; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public String getVhost() { + return vhost; + } + + public void setVhost(String vhost) { + this.vhost = vhost; + } + + @Override + public String toString() { + return "OnPublishHookParam{" + + "id='" + id + '\'' + + ", app='" + app + '\'' + + ", stream='" + stream + '\'' + + ", ip='" + ip + '\'' + + ", params='" + params + '\'' + + ", port=" + port + + ", schema='" + schema + '\'' + + ", vhost='" + vhost + '\'' + + '}'; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnRtpServerTimeoutHookParam.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnRtpServerTimeoutHookParam.java new file mode 100644 index 000000000..6179ce46f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnRtpServerTimeoutHookParam.java @@ -0,0 +1,64 @@ +package com.genersoft.iot.vmp.media.zlm.dto.hook; + +/** + * zlm hook事件中的on_rtp_server_timeout事件的参数 + * @author lin + */ +public class OnRtpServerTimeoutHookParam extends HookParam{ + private int local_port; + private String stream_id; + private int tcpMode; + private boolean re_use_port; + private String ssrc; + + public int getLocal_port() { + return local_port; + } + + public void setLocal_port(int local_port) { + this.local_port = local_port; + } + + public String getStream_id() { + return stream_id; + } + + public void setStream_id(String stream_id) { + this.stream_id = stream_id; + } + + public int getTcpMode() { + return tcpMode; + } + + public void setTcpMode(int tcpMode) { + this.tcpMode = tcpMode; + } + + public boolean isRe_use_port() { + return re_use_port; + } + + public void setRe_use_port(boolean re_use_port) { + this.re_use_port = re_use_port; + } + + public String getSsrc() { + return ssrc; + } + + public void setSsrc(String ssrc) { + this.ssrc = ssrc; + } + + @Override + public String toString() { + return "OnRtpServerTimeoutHookParam{" + + "local_port=" + local_port + + ", stream_id='" + stream_id + '\'' + + ", tcpMode=" + tcpMode + + ", re_use_port=" + re_use_port + + ", ssrc='" + ssrc + '\'' + + '}'; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnSendRtpStoppedHookParam.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnSendRtpStoppedHookParam.java new file mode 100644 index 000000000..4989b4abd --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnSendRtpStoppedHookParam.java @@ -0,0 +1,35 @@ +package com.genersoft.iot.vmp.media.zlm.dto.hook; + +/** + * zlm hook事件中的on_send_rtp_stopped事件的参数 + * @author lin + */ +public class OnSendRtpStoppedHookParam extends HookParam{ + private String app; + private String stream; + + + 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; + } + + @Override + public String toString() { + return "OnSendRtpStoppedHookParam{" + + "app='" + app + '\'' + + ", stream='" + stream + '\'' + + '}'; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnServerKeepaliveHookParam.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnServerKeepaliveHookParam.java new file mode 100644 index 000000000..5439f20f2 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnServerKeepaliveHookParam.java @@ -0,0 +1,27 @@ +package com.genersoft.iot.vmp.media.zlm.dto.hook; + +import com.genersoft.iot.vmp.media.zlm.dto.ServerKeepaliveData; + +/** + * zlm hook事件中的on_play事件的参数 + * @author lin + */ +public class OnServerKeepaliveHookParam extends HookParam{ + + private ServerKeepaliveData data; + + public ServerKeepaliveData getData() { + return data; + } + + public void setData(ServerKeepaliveData data) { + this.data = data; + } + + @Override + public String toString() { + return "OnServerKeepaliveHookParam{" + + "data=" + data + + '}'; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnStreamChangedHookParam.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnStreamChangedHookParam.java new file mode 100644 index 000000000..4587fb0f8 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnStreamChangedHookParam.java @@ -0,0 +1,443 @@ +package com.genersoft.iot.vmp.media.zlm.dto.hook; + +import com.genersoft.iot.vmp.vmanager.bean.StreamContent; + +import java.util.List; + +/** + * @author lin + */ +public class OnStreamChangedHookParam extends HookParam{ + + /** + * 注册/注销 + */ + private boolean regist; + + /** + * 应用名 + */ + private String app; + + /** + * 流id + */ + private String stream; + + /** + * 推流鉴权Id + */ + private String callId; + + /** + * 观看总人数,包括hls/rtsp/rtmp/http-flv/ws-flv + */ + private String totalReaderCount; + + /** + * 协议 包括hls/rtsp/rtmp/http-flv/ws-flv + */ + private String schema; + + + /** + * 产生源类型, + * 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; + + /** + * 客户端和服务器网络信息,可能为null类型 + */ + private OriginSock originSock; + + /** + * 产生源类型的字符串描述 + */ + private String originTypeStr; + + /** + * 产生源的url + */ + private String originUrl; + + /** + * 服务器id + */ + private String severId; + + /** + * GMT unix系统时间戳,单位秒 + */ + private Long createStamp; + + /** + * 存活时间,单位秒 + */ + private Long aliveSecond; + + /** + * 数据产生速度,单位byte/s + */ + private Long bytesSpeed; + + /** + * 音视频轨道 + */ + private List tracks; + + /** + * 音视频轨道 + */ + private String vhost; + + public boolean isRegist() { + return regist; + } + + public void setRegist(boolean regist) { + this.regist = regist; + } + + /** + * 是否是docker部署, docker部署不会自动更新zlm使用的端口,需要自己手动修改 + */ + private boolean docker; + + public static class MediaTrack { + /** + * 音频通道数 + */ + private int channels; + + /** + * H264 = 0, H265 = 1, AAC = 2, G711A = 3, G711U = 4 + */ + private int codecId; + + /** + * 编码类型名称 CodecAAC CodecH264 + */ + private String codecIdName; + + /** + * Video = 0, Audio = 1 + */ + private int codecType; + + /** + * 轨道是否准备就绪 + */ + private boolean ready; + + /** + * 音频采样位数 + */ + private int sampleBit; + + /** + * 音频采样率 + */ + private int sampleRate; + + /** + * 视频fps + */ + private int fps; + + /** + * 视频高 + */ + private int height; + + /** + * 视频宽 + */ + private int width; + + public int getChannels() { + return channels; + } + + public void setChannels(int channels) { + this.channels = channels; + } + + public int getCodecId() { + return codecId; + } + + public void setCodecId(int codecId) { + this.codecId = codecId; + } + + public String getCodecIdName() { + return codecIdName; + } + + public void setCodecIdName(String codecIdName) { + this.codecIdName = codecIdName; + } + + public int getCodecType() { + return codecType; + } + + public void setCodecType(int codecType) { + this.codecType = codecType; + } + + public boolean isReady() { + return ready; + } + + public void setReady(boolean ready) { + this.ready = ready; + } + + public int getSampleBit() { + return sampleBit; + } + + public void setSampleBit(int sampleBit) { + this.sampleBit = sampleBit; + } + + public int getSampleRate() { + return sampleRate; + } + + public void setSampleRate(int sampleRate) { + this.sampleRate = sampleRate; + } + + public int getFps() { + return fps; + } + + public void setFps(int fps) { + this.fps = fps; + } + + public int getHeight() { + return height; + } + + public void setHeight(int height) { + this.height = height; + } + + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } + } + + public static class OriginSock{ + private String identifier; + private String local_ip; + private int local_port; + private String peer_ip; + private int peer_port; + + public String getIdentifier() { + return identifier; + } + + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + public String getLocal_ip() { + return local_ip; + } + + public void setLocal_ip(String local_ip) { + this.local_ip = local_ip; + } + + public int getLocal_port() { + return local_port; + } + + public void setLocal_port(int local_port) { + this.local_port = local_port; + } + + public String getPeer_ip() { + return peer_ip; + } + + public void setPeer_ip(String peer_ip) { + this.peer_ip = peer_ip; + } + + public int getPeer_port() { + return peer_port; + } + + public void setPeer_port(int peer_port) { + this.peer_port = peer_port; + } + } + + private StreamContent streamInfo; + + 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 getTotalReaderCount() { + return totalReaderCount; + } + + public void setTotalReaderCount(String totalReaderCount) { + this.totalReaderCount = totalReaderCount; + } + + + 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 getOriginUrl() { + return originUrl; + } + + public void setOriginUrl(String originUrl) { + this.originUrl = originUrl; + } + + public Long getCreateStamp() { + return createStamp; + } + + public void setCreateStamp(Long createStamp) { + this.createStamp = createStamp; + } + + public Long getAliveSecond() { + return aliveSecond; + } + + public void setAliveSecond(Long aliveSecond) { + this.aliveSecond = aliveSecond; + } + + public List getTracks() { + return tracks; + } + + public void setTracks(List tracks) { + this.tracks = tracks; + } + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public void setOriginSock(OriginSock originSock) { + this.originSock = originSock; + } + + public Long getBytesSpeed() { + return bytesSpeed; + } + + public void setBytesSpeed(Long bytesSpeed) { + this.bytesSpeed = bytesSpeed; + } + + public String getVhost() { + return vhost; + } + + public void setVhost(String vhost) { + this.vhost = vhost; + } + + public OriginSock getOriginSock() { + return originSock; + } + + public boolean isDocker() { + return docker; + } + + public void setDocker(boolean docker) { + this.docker = docker; + } + + public StreamContent getStreamInfo() { + return streamInfo; + } + + public void setStreamInfo(StreamContent streamInfo) { + this.streamInfo = streamInfo; + } + + public String getSeverId() { + return severId; + } + + public void setSeverId(String severId) { + this.severId = severId; + } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } + + @Override + public String toString() { + return "OnStreamChangedHookParam{" + + "regist=" + regist + + ", app='" + app + '\'' + + ", stream='" + stream + '\'' + + ", severId='" + severId + '\'' + + '}'; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnStreamNoneReaderHookParam.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnStreamNoneReaderHookParam.java new file mode 100644 index 000000000..3b6284285 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnStreamNoneReaderHookParam.java @@ -0,0 +1,51 @@ +package com.genersoft.iot.vmp.media.zlm.dto.hook; + +public class OnStreamNoneReaderHookParam extends HookParam{ + + private String schema; + private String app; + private String stream; + private String vhost; + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + 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 getVhost() { + return vhost; + } + + public void setVhost(String vhost) { + this.vhost = vhost; + } + + @Override + public String toString() { + return "OnStreamNoneReaderHookParam{" + + "schema='" + schema + '\'' + + ", app='" + app + '\'' + + ", stream='" + stream + '\'' + + ", vhost='" + vhost + '\'' + + '}'; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnStreamNotFoundHookParam.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnStreamNotFoundHookParam.java new file mode 100644 index 000000000..76e6a7213 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnStreamNotFoundHookParam.java @@ -0,0 +1,95 @@ +package com.genersoft.iot.vmp.media.zlm.dto.hook; + +/** + * zlm hook事件中的on_stream_not_found事件的参数 + * @author lin + */ +public class OnStreamNotFoundHookParam extends HookParam{ + private String id; + private String app; + private String stream; + private String ip; + private String params; + private int port; + private String schema; + private String vhost; + + + 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 String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getParams() { + return params; + } + + public void setParams(String params) { + this.params = params; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public String getVhost() { + return vhost; + } + + public void setVhost(String vhost) { + this.vhost = vhost; + } + + @Override + public String toString() { + return "OnStreamNotFoundHookParam{" + + "id='" + id + '\'' + + ", app='" + app + '\'' + + ", stream='" + stream + '\'' + + ", ip='" + ip + '\'' + + ", params='" + params + '\'' + + ", port=" + port + + ", schema='" + schema + '\'' + + ", vhost='" + vhost + '\'' + + '}'; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OriginType.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OriginType.java new file mode 100644 index 000000000..926cf4d4d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OriginType.java @@ -0,0 +1,23 @@ +package com.genersoft.iot.vmp.media.zlm.dto.hook; + +public enum OriginType { + // 不可调整顺序 + UNKNOWN("UNKNOWN"), + RTMP_PUSH("PUSH"), + RTSP_PUSH("PUSH"), + RTP_PUSH("RTP"), + PULL("PULL"), + FFMPEG_PULL("PULL"), + MP4_VOD("MP4_VOD"), + DEVICE_CHN("DEVICE_CHN"), + RTC_PUSH("PUSH"); + + private final String type; + OriginType(String type) { + this.type = type; + } + + public String getType() { + return type; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMEventAbstract.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMEventAbstract.java new file mode 100644 index 000000000..8ffbddea0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMEventAbstract.java @@ -0,0 +1,25 @@ +package com.genersoft.iot.vmp.media.zlm.event; + +import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; +import org.springframework.context.ApplicationEvent; + +public abstract class ZLMEventAbstract extends ApplicationEvent { + + + private static final long serialVersionUID = 1L; + + private String mediaServerId; + + + public ZLMEventAbstract(Object source) { + super(source); + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMOfflineEvent.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMOfflineEvent.java new file mode 100644 index 000000000..8207bdd3b --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMOfflineEvent.java @@ -0,0 +1,11 @@ +package com.genersoft.iot.vmp.media.zlm.event; + +/** + * zlm离线事件类 + */ +public class ZLMOfflineEvent extends ZLMEventAbstract { + + public ZLMOfflineEvent(Object source) { + super(source); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMOnlineEvent.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMOnlineEvent.java new file mode 100644 index 000000000..612ff9d09 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMOnlineEvent.java @@ -0,0 +1,11 @@ +package com.genersoft.iot.vmp.media.zlm.event; + +/** + * zlm在线事件 + */ +public class ZLMOnlineEvent extends ZLMEventAbstract { + + public ZLMOnlineEvent(Object source) { + super(source); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMStatusEventListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMStatusEventListener.java new file mode 100644 index 000000000..bad8e5605 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMStatusEventListener.java @@ -0,0 +1,59 @@ +package com.genersoft.iot.vmp.media.zlm.event; + +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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + + +/** + * @description: 在线事件监听器,监听到离线后,修改设备离在线状态。 设备在线有两个来源: + * 1、设备主动注销,发送注销指令 + * 2、设备未知原因离线,心跳超时 + * @author: swwheihei + * @date: 2020年5月6日 下午1:51:23 + */ +@Component +public class ZLMStatusEventListener { + + private final static Logger logger = LoggerFactory.getLogger(ZLMStatusEventListener.class); + + @Autowired + private IStreamPushService streamPushService; + + @Autowired + private IStreamProxyService streamProxyService; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private IPlayService playService; + + @Async("taskExecutor") + @EventListener + public void onApplicationEvent(ZLMOnlineEvent event) { + logger.info("[ZLM] 上线 ID:" + event.getMediaServerId()); + streamPushService.zlmServerOnline(event.getMediaServerId()); + streamProxyService.zlmServerOnline(event.getMediaServerId()); + playService.zlmServerOnline(event.getMediaServerId()); + } + + @Async("taskExecutor") + @EventListener + public void onApplicationEvent(ZLMOfflineEvent event) { + + logger.info("[ZLM] 离线,ID:" + event.getMediaServerId()); + // 处理ZLM离线 + mediaServerService.zlmServerOffline(event.getMediaServerId()); + streamProxyService.zlmServerOffline(event.getMediaServerId()); + streamPushService.zlmServerOffline(event.getMediaServerId()); + playService.zlmServerOffline(event.getMediaServerId()); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IDeviceAlarmService.java b/src/main/java/com/genersoft/iot/vmp/service/IDeviceAlarmService.java new file mode 100644 index 000000000..51e53d266 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/IDeviceAlarmService.java @@ -0,0 +1,43 @@ +package com.genersoft.iot.vmp.service; + +import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; +import com.github.pagehelper.PageInfo; + +import java.util.List; + +/** + * 报警相关业务处理 + */ +public interface IDeviceAlarmService { + + /** + * 根据多个添加获取报警列表 + * @param page 当前页 + * @param count 每页数量 + * @param deviceId 设备id + * @param alarmPriority 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级 警情- + * @param alarmMethod 报警方式 , 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警, + * 7其他报警;可以为直接组合如12为电话报警或 设备报警- + * @param alarmType 报警类型 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 报警列表 + */ + PageInfo getAllAlarm(int page, int count, String deviceId, String alarmPriority, String alarmMethod, + String alarmType, String startTime, String endTime); + + /** + * 添加一个报警 + * @param deviceAlarm 添加报警 + */ + void add(DeviceAlarm deviceAlarm); + + /** + * 清空时间以前的报警 + * @param id 数据库id + * @param deviceIdList 制定需要清理的设备id + * @param time 不写时间则清空所有时间的 + */ + int clearAlarmBeforeTime(Integer id, List deviceIdList, String time); + +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IDeviceChannelService.java b/src/main/java/com/genersoft/iot/vmp/service/IDeviceChannelService.java new file mode 100644 index 000000000..cd402a109 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/IDeviceChannelService.java @@ -0,0 +1,90 @@ +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.vmanager.bean.ResourceBaseInfo; +import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; + +import java.util.List; + +/** + * 国标通道业务类 + * @author lin + */ +public interface IDeviceChannelService { + + /** + * 更新gps信息 + */ + DeviceChannel updateGps(DeviceChannel deviceChannel, Device device); + + /** + * 添加设备通道 + * + * @param deviceId 设备id + * @param channel 通道 + */ + void updateChannel(String deviceId, DeviceChannel channel); + + /** + * 批量添加设备通道 + * + * @param deviceId 设备id + * @param channels 多个通道 + */ + int updateChannels(String deviceId, List channels); + + /** + * 获取统计信息 + * @return + */ + ResourceBaseInfo getOverview(); + + /** + * 查询所有未分配的通道 + * @param platformId + * @return + */ + List queryAllChannelList(String platformId); + + /** + * 数据位置信息格式处理 + */ + boolean updateAllGps(Device device); + + /** + * 查询通道所属的设备 + */ + List getDeviceByChannelId(String channelId); + + /** + * 批量删除通道 + * @param deleteChannelList 待删除的通道列表 + */ + int deleteChannels(List deleteChannelList); + + /** + * 批量上线 + */ + int channelsOnline(List channels); + + /** + * 批量下线 + */ + int channelsOffline(List channels); + + /** + * 获取一个通道 + */ + DeviceChannel getOne(String deviceId, String channelId); + + /** + * 直接批量更新通道 + */ + void batchUpdateChannel(List channels); + + /** + * 直接批量添加 + */ + void batchAddChannel(List deviceChannels); +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java b/src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java new file mode 100644 index 000000000..e20c3bf22 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java @@ -0,0 +1,171 @@ +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.SipTransactionInfo; +import com.genersoft.iot.vmp.gb28181.bean.SyncStatus; +import com.genersoft.iot.vmp.vmanager.bean.BaseTree; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; + +import java.util.List; + +/** + * 设备相关业务处理 + * @author lin + */ +public interface IDeviceService { + + /** + * 设备上线 + * @param device 设备信息 + */ + void online(Device device, SipTransactionInfo sipTransactionInfo); + + /** + * 设备下线 + * @param deviceId 设备编号 + */ + void offline(String deviceId, String reason); + + /** + * 添加目录订阅 + * @param device 设备信息 + * @return 布尔 + */ + boolean addCatalogSubscribe(Device device); + + /** + * 移除目录订阅 + * @param device 设备信息 + * @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 + */ + ResourceBaseInfo getOverview(); + + /** + * 获取所有设备 + */ + List getAll(); +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IGbStreamService.java b/src/main/java/com/genersoft/iot/vmp/service/IGbStreamService.java new file mode 100644 index 000000000..314a38907 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/IGbStreamService.java @@ -0,0 +1,72 @@ +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; + +/** + * 级联国标平台关联流业务接口 + */ +public interface IGbStreamService { + + /** + * 分页获取所有 + * @param page + * @param count + * @return + */ + PageInfo getAll(Integer page, Integer count, String platFormId, String catalogId,String query,String mediaServerId); + + + /** + * 移除 + * @param app + * @param stream + */ + void del(String app, String stream); + + /** + * 保存国标关联 + * @param gbStreams + */ + boolean addPlatformInfo(List gbStreams, String platformId, String catalogId); + + /** + * 移除国标关联 + * @param gbStreams + * @param platformId + */ + boolean delPlatformInfo(String platformId, List gbStreams); + + DeviceChannel getDeviceChannelListByStream(GbStream gbStream, String catalogId, ParentPlatform platform); + + void sendCatalogMsg(GbStream gbStream, String type); + void sendCatalogMsgs(List gbStreams, String type); + + /** + * 修改gbId或name + * @param streamPushItemForUpdate + * @return + */ + int updateGbIdOrName(List streamPushItemForUpdate); + + DeviceChannel getDeviceChannelListByStreamWithStatus(GbStream gbStream, String catalogId, ParentPlatform platform); + + /** + * 查询所有未分配的通道 + * @param platformId + * @return + */ + List getAllGBChannels(String platformId); + + /** + * 移除所有关联的通道 + * @param platformId + * @param catalogId + */ + void delAllPlatformInfo(String platformId, String catalogId); +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IInviteStreamService.java b/src/main/java/com/genersoft/iot/vmp/service/IInviteStreamService.java new file mode 100644 index 000000000..ae30f26e7 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/IInviteStreamService.java @@ -0,0 +1,78 @@ +package com.genersoft.iot.vmp.service; + +import com.genersoft.iot.vmp.common.InviteInfo; +import com.genersoft.iot.vmp.common.InviteSessionType; +import com.genersoft.iot.vmp.service.bean.ErrorCallback; + +/** + * 记录国标点播的状态,包括实时预览,下载,录像回放 + */ +public interface IInviteStreamService { + + /** + * 更新点播的状态信息 + */ + void updateInviteInfo(InviteInfo inviteInfo); + + InviteInfo updateInviteInfoForStream(InviteInfo inviteInfo, String stream); + + /** + * 获取点播的状态信息 + */ + InviteInfo getInviteInfo(InviteSessionType type, + String deviceId, + String channelId, + String stream); + + /** + * 移除点播的状态信息 + */ + void removeInviteInfo(InviteSessionType type, + String deviceId, + String channelId, + String stream); + /** + * 移除点播的状态信息 + */ + void removeInviteInfo(InviteInfo inviteInfo); + /** + * 移除点播的状态信息 + */ + void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, String deviceId, String channelId); + + /** + * 获取点播的状态信息 + */ + InviteInfo getInviteInfoByDeviceAndChannel(InviteSessionType type, + String deviceId, + String channelId); + + /** + * 获取点播的状态信息 + */ + InviteInfo getInviteInfoByStream(InviteSessionType type, String stream); + + + /** + * 添加一个invite回调 + */ + void once(InviteSessionType type, String deviceId, String channelId, String stream, ErrorCallback callback); + + /** + * 调用一个invite回调 + */ + void call(InviteSessionType type, String deviceId, String channelId, String stream, int code, String msg, Object data); + + /** + * 清空一个设备的所有invite信息 + */ + void clearInviteInfo(String deviceId); + + /** + * 统计同一个zlm下的国标收流个数 + */ + int getStreamInfoCount(String mediaServerId); + + + +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/ILogService.java b/src/main/java/com/genersoft/iot/vmp/service/ILogService.java new file mode 100644 index 000000000..b7a67ede0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/ILogService.java @@ -0,0 +1,34 @@ +package com.genersoft.iot.vmp.service; + +import com.genersoft.iot.vmp.storager.dao.dto.LogDto; +import com.github.pagehelper.PageInfo; + +/** + * 系统日志 + */ +public interface ILogService { + + /** + * 查询日志 + * @param page 当前页 + * @param count 每页数量 + * @param query 搜索内容 + * @param type 类型 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 日志列表 + */ + PageInfo getAll(int page, int count, String query, String type, String startTime, String endTime); + + /** + * 添加日志 + * @param logDto 日志 + */ + void add(LogDto logDto); + + /** + * 清空 + */ + int clear(); + +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java b/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java new file mode 100644 index 000000000..530cd6dfd --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java @@ -0,0 +1,96 @@ +package com.genersoft.iot.vmp.service; + +import com.genersoft.iot.vmp.common.CommonCallback; +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 java.util.List; + +/** + * 媒体服务节点 + */ +public interface IMediaServerService { + + List getAll(); + + List getAllFromDatabase(); + + List getAllOnline(); + + MediaServerItem getOne(String generalMediaServerId); + + void syncCatchFromDatabase(); + + /** + * 新的节点加入 + * @param zlmServerConfig + * @return + */ + void zlmServerOnline(ZLMServerConfig zlmServerConfig); + + /** + * 节点离线 + * @param mediaServerId + * @return + */ + void zlmServerOffline(String mediaServerId); + + MediaServerItem getMediaServerForMinimumLoad(Boolean hasAssist); + + void setZLMConfig(MediaServerItem mediaServerItem, boolean restart); + + void updateVmServer(List mediaServerItemList); + + SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, + boolean isPlayback, Integer port, Boolean reUsePort, Integer tcpMode); + + void closeRTPServer(MediaServerItem mediaServerItem, String streamId); + + void closeRTPServer(MediaServerItem mediaServerItem, String streamId, CommonCallback callback); + Boolean updateRtpServerSSRC(MediaServerItem mediaServerItem, String streamId, String ssrc); + + void closeRTPServer(String mediaServerId, String streamId); + + void clearRTPServer(MediaServerItem mediaServerItem); + + void update(MediaServerItem mediaSerItem); + + void addCount(String mediaServerId); + + void removeCount(String mediaServerId); + + void releaseSsrc(String mediaServerItemId, String ssrc); + + void clearMediaServerForOnline(); + + void add(MediaServerItem mediaSerItem); + + int addToDatabase(MediaServerItem mediaSerItem); + + int updateToDatabase(MediaServerItem mediaSerItem); + + void resetOnlineServerItem(MediaServerItem serverItem); + + 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 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 new file mode 100644 index 000000000..c05f1977d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/IMediaService.java @@ -0,0 +1,44 @@ +package com.genersoft.iot.vmp.service; + +import com.alibaba.fastjson2.JSONArray; +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; + +/** + * 媒体信息业务 + */ +public interface IMediaService { + + /** + * 根据应用名和流ID获取播放地址, 通过zlm接口检查是否存在 + * @param app + * @param stream + * @return + */ + StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId,String addr, boolean authority); + + + /** + * 根据应用名和流ID获取播放地址, 通过zlm接口检查是否存在, 返回的ip使用远程访问ip,适用与zlm与wvp在一台主机的情况 + * @param app + * @param stream + * @return + */ + StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, boolean authority); + + /** + * 根据应用名和流ID获取播放地址, 只是地址拼接 + * @param app + * @param stream + * @return + */ + StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaServerItem, String app, String stream, Object tracks, String callId); + + /** + * 根据应用名和流ID获取播放地址, 只是地址拼接,返回的ip使用远程访问ip,适用与zlm与wvp在一台主机的情况 + * @param app + * @param stream + * @return + */ + 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 000000000..49d74282c --- /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 000000000..e9cddff83 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java @@ -0,0 +1,58 @@ +package com.genersoft.iot.vmp.service; + +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo; +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 级联平台 + */ + boolean update(ParentPlatform parentPlatform); + + /** + * 平台上线 + * @param parentPlatform 平台信息 + */ + void online(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo); + + /** + * 平台离线 + * @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 new file mode 100644 index 000000000..51624112d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java @@ -0,0 +1,47 @@ +package com.genersoft.iot.vmp.service; + +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.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.service.bean.ErrorCallback; +import com.genersoft.iot.vmp.service.bean.SSRCInfo; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; + +/** + * 点播处理 + */ +public interface IPlayService { + + void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + ErrorCallback callback); + SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, ErrorCallback callback); + + MediaServerItem getNewMediaServerItem(Device device); + + /** + * 获取包含assist服务的节点 + */ + MediaServerItem getNewMediaServerItemHasAssist(Device device); + + void playBack(String deviceId, String channelId, String startTime, String endTime, ErrorCallback callback); + void playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, ErrorCallback callback); + + void zlmServerOffline(String mediaServerId); + + void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback callback); + void download(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo,String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback callback); + + StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream); + + void zlmServerOnline(String mediaServerId); + + void pauseRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; + + void resumeRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; + + void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback); +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IRoleService.java b/src/main/java/com/genersoft/iot/vmp/service/IRoleService.java new file mode 100644 index 000000000..d207c6ab2 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/IRoleService.java @@ -0,0 +1,18 @@ +package com.genersoft.iot.vmp.service; + +import com.genersoft.iot.vmp.storager.dao.dto.Role; + +import java.util.List; + +public interface IRoleService { + + Role getRoleById(int id); + + int add(Role role); + + int delete(int id); + + List getAll(); + + int update(Role role); +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java b/src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java new file mode 100644 index 000000000..c4968a745 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java @@ -0,0 +1,114 @@ +package com.genersoft.iot.vmp.service; + +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.common.GeneralCallback; +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; +import com.github.pagehelper.PageInfo; + +public interface IStreamProxyService { + + /** + * 保存视频代理 + * @param param + */ + void save(StreamProxyItem param, GeneralCallback callback); + + /** + * 添加视频代理到zlm + * @param param + * @return + */ + JSONObject addStreamProxyToZlm(StreamProxyItem param); + + /** + * 从zlm移除视频代理 + * @param param + * @return + */ + JSONObject removeStreamProxyFromZlm(StreamProxyItem param); + + /** + * 分页查询 + * @param page + * @param count + * @return + */ + PageInfo getAll(Integer page, Integer count); + + /** + * 删除视频代理 + * @param app + * @param stream + */ + void del(String app, String stream); + + /** + * 启用视频代理 + * @param app + * @param stream + * @return + */ + boolean start(String app, String stream); + + /** + * 更新状态 + * @param status 状态 + * @param app + * @param stream + */ + int updateStatus(boolean status, String app, String stream); + + + + /** + * 停用用视频代理 + * @param app + * @param stream + * @return + */ + boolean stop(String app, String stream); + + /** + * 获取ffmpeg.cmd模板 + * @return + */ + JSONObject getFFmpegCMDs(MediaServerItem mediaServerItem); + + /** + * 根据app与stream获取streamProxy + * @return + */ + StreamProxyItem getStreamProxyByAppAndStream(String app, String streamId); + + + /** + * 新的节点加入 + * @param mediaServerId + * @return + */ + void zlmServerOnline(String mediaServerId); + + /** + * 节点离线 + * @param mediaServerId + * @return + */ + void zlmServerOffline(String mediaServerId); + + void clean(); + + /** + * 更新代理流 + */ + boolean updateStreamProxy(StreamProxyItem streamProxyItem); + + /** + * 获取统计信息 + * @return + */ + ResourceBaseInfo 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 new file mode 100644 index 000000000..8d9200216 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java @@ -0,0 +1,117 @@ +package com.genersoft.iot.vmp.service; + +import com.genersoft.iot.vmp.gb28181.bean.GbStream; +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.ResourceBaseInfo; +import com.github.pagehelper.PageInfo; + +import java.util.List; +import java.util.Map; + +/** + * @author lin + */ +public interface IStreamPushService { + + List handleJSON(String json, MediaServerItem mediaServerItem); + + /** + * 将应用名和流ID加入国标关联 + * @param stream + * @return + */ + boolean saveToGB(GbStream stream); + + /** + * 将应用名和流ID移出国标关联 + * @param stream + * @return + */ + boolean removeFromGB(GbStream stream); + + /** + * 获取 + */ + PageInfo getPushList(Integer page, Integer count, String query, Boolean pushing, String mediaServerId); + + List getPushList(String mediaSererId); + + StreamPushItem transform(OnStreamChangedHookParam item); + + StreamPushItem getPush(String app, String streamId); + + /** + * 停止一路推流 + * @param app 应用名 + * @param streamId 流ID + */ + boolean stop(String app, String streamId); + + /** + * 新的节点加入 + */ + void zlmServerOnline(String mediaServerId); + + /** + * 节点离线 + */ + void zlmServerOffline(String mediaServerId); + + /** + * 清空 + */ + void clean(); + + + boolean saveToRandomGB(); + + /** + * 批量添加 + */ + void batchAdd(List streamPushExcelDtoList); + + /** + * 中止多个推流 + */ + boolean batchStop(List streamPushItems); + + /** + * 导入时批量增加 + */ + void batchAddForUpload(List streamPushItems, Map> streamPushItemsForAll); + + /** + * 全部离线 + */ + void allStreamOffline(); + + /** + * 推流离线 + */ + void offline(List offlineStreams); + + /** + * 推流上线 + */ + void online(List onlineStreams); + + /** + * 增加推流 + */ + boolean add(StreamPushItem stream); + + /** + * 获取全部的app+Streanm 用于判断推流列表是新增还是修改 + * @return + */ + List getAllAppAndStream(); + + /** + * 获取统计信息 + * @return + */ + ResourceBaseInfo getOverview(); +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IUserService.java b/src/main/java/com/genersoft/iot/vmp/service/IUserService.java new file mode 100644 index 000000000..7e2a8395b --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/IUserService.java @@ -0,0 +1,29 @@ +package com.genersoft.iot.vmp.service; + +import com.genersoft.iot.vmp.storager.dao.dto.User; +import com.github.pagehelper.PageInfo; + +import java.util.List; + +public interface IUserService { + + User getUser(String username, String password); + + boolean changePassword(int id, String password); + + User getUserByUsername(String username); + + int addUser(User user); + + int deleteUser(int id); + + 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/ErrorCallback.java b/src/main/java/com/genersoft/iot/vmp/service/bean/ErrorCallback.java new file mode 100644 index 000000000..6211d0037 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/ErrorCallback.java @@ -0,0 +1,6 @@ +package com.genersoft.iot.vmp.service.bean; + +public interface ErrorCallback { + + void run(int code, String msg, T data); +} 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 000000000..b814c18f4 --- /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/InviteErrorCode.java b/src/main/java/com/genersoft/iot/vmp/service/bean/InviteErrorCode.java new file mode 100644 index 000000000..d43792e79 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/InviteErrorCode.java @@ -0,0 +1,37 @@ +package com.genersoft.iot.vmp.service.bean; + +/** + * 全局错误码 + */ +public enum InviteErrorCode { + SUCCESS(0, "成功"), + FAIL(-100, "失败"), + ERROR_FOR_SIGNALLING_TIMEOUT(-1, "信令超时"), + ERROR_FOR_STREAM_TIMEOUT(-2, "收流超时"), + ERROR_FOR_RESOURCE_EXHAUSTION(-3, "资源耗尽"), + ERROR_FOR_CATCH_DATA(-4, "缓存数据异常"), + ERROR_FOR_SIGNALLING_ERROR(-5, "收到信令错误"), + ERROR_FOR_STREAM_PARSING_EXCEPTIONS(-6, "流地址解析错误"), + ERROR_FOR_SDP_PARSING_EXCEPTIONS(-7, "SDP信息解析失败"), + ERROR_FOR_SSRC_UNAVAILABLE(-8, "SSRC不可用"), + ERROR_FOR_RESET_SSRC(-9, "重新设置收流信息失败"), + ERROR_FOR_SIP_SENDING_FAILED(-10, "命令发送失败"), + ERROR_FOR_ASSIST_NOT_READY(-11, "没有可用的assist服务"), + ERROR_FOR_PARAMETER_ERROR(-13, "参数异常"); + + private final int code; + private final String msg; + + InviteErrorCode(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/service/bean/InviteTimeOutCallback.java b/src/main/java/com/genersoft/iot/vmp/service/bean/InviteTimeOutCallback.java new file mode 100644 index 000000000..e30db5d99 --- /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 000000000..cb30f67af --- /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 000000000..1a9e3e5cf --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/MessageForPushChannel.java @@ -0,0 +1,142 @@ +package com.genersoft.iot.vmp.service.bean; + +/** + * 当上级平台 + * @author lin + */ +public class MessageForPushChannel { + /** + * 消息类型 + * 0 流注销 1 流注册 + */ + private int type; + + /** + * 流应用名 + */ + private String app; + + /** + * 流Id + */ + private String stream; + + /** + * 国标ID + */ + private String gbId; + + /** + * 请求的平台国标编号 + */ + private String platFormId; + + /** + * 请求的平台自增ID + */ + private int platFormIndex; + + /** + * 请求平台名称 + */ + 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; + } + + public int getPlatFormIndex() { + return platFormIndex; + } + + public void setPlatFormIndex(int platFormIndex) { + this.platFormIndex = platFormIndex; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/MessageForPushChannelResponse.java b/src/main/java/com/genersoft/iot/vmp/service/bean/MessageForPushChannelResponse.java new file mode 100644 index 000000000..10d1b43cf --- /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 000000000..33a09bd40 --- /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 000000000..0a332ef09 --- /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 000000000..5bb7b77f8 --- /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 000000000..5827d0132 --- /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 000000000..0c6502d19 --- /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 000000000..501621bb4 --- /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 new file mode 100644 index 000000000..1723bc594 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java @@ -0,0 +1,38 @@ +package com.genersoft.iot.vmp.service.bean; + +public class SSRCInfo { + + private int port; + private String ssrc; + private String Stream; + + public SSRCInfo(int port, String ssrc, String stream) { + this.port = port; + this.ssrc = ssrc; + Stream = stream; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getSsrc() { + return ssrc; + } + + public void setSsrc(String ssrc) { + this.ssrc = ssrc; + } + + public String getStream() { + return Stream; + } + + public void setStream(String stream) { + Stream = stream; + } +} 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 000000000..ff32d7914 --- /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/ThirdPartyGB.java b/src/main/java/com/genersoft/iot/vmp/service/bean/ThirdPartyGB.java new file mode 100644 index 000000000..9d6b06a4d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/ThirdPartyGB.java @@ -0,0 +1,23 @@ +package com.genersoft.iot.vmp.service.bean; + +public class ThirdPartyGB { + + private String name; + private String nationalStandardNo; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getNationalStandardNo() { + return nationalStandardNo; + } + + public void setNationalStandardNo(String nationalStandardNo) { + this.nationalStandardNo = nationalStandardNo; + } +} 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 000000000..12d79cb23 --- /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 000000000..cb118865c --- /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 new file mode 100644 index 000000000..8c55986ea --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImpl.java @@ -0,0 +1,36 @@ +package com.genersoft.iot.vmp.service.impl; + +import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; +import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; +import com.genersoft.iot.vmp.service.IDeviceAlarmService; +import com.genersoft.iot.vmp.storager.dao.DeviceAlarmMapper; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +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); + List all = deviceAlarmMapper.query(deviceId, alarmPriority, alarmMethod, alarmType, startTime, endTime); + return new PageInfo<>(all); + } + + @Override + public void add(DeviceAlarm deviceAlarm) { + deviceAlarmMapper.add(deviceAlarm); + } + + @Override + public int clearAlarmBeforeTime(Integer id, List deviceIdList, String time) { + return deviceAlarmMapper.clearAlarmBeforeTime(id, deviceIdList, time); + } +} 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 000000000..76a508727 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java @@ -0,0 +1,265 @@ +package com.genersoft.iot.vmp.service.impl; + +import com.genersoft.iot.vmp.common.InviteInfo; +import com.genersoft.iot.vmp.common.InviteSessionType; +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.service.IInviteStreamService; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; +import com.genersoft.iot.vmp.storager.dao.DeviceMapper; +import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; +import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * @author lin + */ +@Service +public class DeviceChannelServiceImpl implements IDeviceChannelService { + + private final static Logger logger = LoggerFactory.getLogger(DeviceChannelServiceImpl.class); + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IInviteStreamService inviteStreamService; + + @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); + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); + if (inviteInfo != null && inviteInfo.getStreamInfo() != null) { + channel.setStreamId(inviteInfo.getStreamInfo().getStream()); + } + String now = DateUtil.getNow(); + channel.setUpdateTime(now); + DeviceChannel deviceChannel = channelMapper.queryChannel(deviceId, channelId); + channel = updateGps(channel, null); + if (deviceChannel == null) { + channel.setCreateTime(now); + channelMapper.add(channel); + }else { + channelMapper.update(channel); + } + channelMapper.updateChannelSubCount(deviceId,channel.getParentId()); + } + + @Override + public int updateChannels(String deviceId, List channels) { + List addChannels = new ArrayList<>(); + List updateChannels = new ArrayList<>(); + HashMap channelsInStore = new HashMap<>(); + Device device = deviceMapper.getDeviceByDeviceId(deviceId); + if (channels != null && channels.size() > 0) { + List channelList = channelMapper.queryChannels(deviceId, null, null, null, null,null); + if (channelList.size() == 0) { + for (DeviceChannel channel : channels) { + channel.setDeviceId(deviceId); + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channel.getChannelId()); + if (inviteInfo != null && inviteInfo.getStreamInfo() != null) { + channel.setStreamId(inviteInfo.getStreamInfo().getStream()); + } + String now = DateUtil.getNow(); + channel.setUpdateTime(now); + channel.setCreateTime(now); + channel = updateGps(channel, device); + addChannels.add(channel); + } + }else { + for (DeviceChannel deviceChannel : channelList) { + channelsInStore.put(deviceChannel.getChannelId(), deviceChannel); + } + for (DeviceChannel channel : channels) { + channel.setDeviceId(deviceId); + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channel.getChannelId()); + if (inviteInfo != null && inviteInfo.getStreamInfo() != null) { + channel.setStreamId(inviteInfo.getStreamInfo().getStream()); + } + String now = DateUtil.getNow(); + channel.setUpdateTime(now); + channel = updateGps(channel, device); + if (channelsInStore.get(channel.getChannelId()) != null) { + updateChannels.add(channel); + }else { + addChannels.add(channel); + channel.setCreateTime(now); + } + } + } + int limitCount = 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 ResourceBaseInfo getOverview() { + + int online = channelMapper.getOnlineCount(); + int total = channelMapper.getAllChannelCount(); + + return new ResourceBaseInfo(total, online); + } + + + @Override + public List queryAllChannelList(String platformId) { + return channelMapper.queryChannelListInAll(null, null, null, platformId, null); + } + + @Override + public boolean updateAllGps(Device device) { + List deviceChannels = channelMapper.getChannelsWithoutTransform(device.getDeviceId()); + List result = new CopyOnWriteArrayList<>(); + if (deviceChannels.size() == 0) { + return true; + } + String now = DateUtil.getNow(); + deviceChannels.parallelStream().forEach(deviceChannel -> { + deviceChannel.setUpdateTime(now); + result.add(updateGps(deviceChannel, device)); + }); + int limitCount = 300; + if (result.size() > limitCount) { + for (int i = 0; i < result.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > result.size()) { + toIndex = result.size(); + } + channelMapper.batchUpdate(result.subList(i, toIndex)); + } + }else { + channelMapper.batchUpdate(result); + } + + return true; + } + + @Override + public List getDeviceByChannelId(String channelId) { + + return channelMapper.getDeviceByChannelId(channelId); + } + + @Override + public int deleteChannels(List deleteChannelList) { + return channelMapper.batchDel(deleteChannelList); + } + + @Override + public int channelsOnline(List channels) { + return channelMapper.batchOnline(channels); + } + + @Override + public int channelsOffline(List channels) { + return channelMapper.batchOffline(channels); + } + + @Override + public DeviceChannel getOne(String deviceId, String channelId){ + return channelMapper.queryChannel(deviceId, channelId); + } + + @Override + public void batchUpdateChannel(List channels) { + channelMapper.batchUpdate(channels); + for (DeviceChannel channel : channels) { + if (channel.getParentId() != null) { + channelMapper.updateChannelSubCount(channel.getDeviceId(), channel.getParentId()); + } + } + } + + @Override + public void batchAddChannel(List channels) { + channelMapper.batchAdd(channels); + for (DeviceChannel channel : channels) { + if (channel.getParentId() != null) { + channelMapper.updateChannelSubCount(channel.getDeviceId(), channel.getParentId()); + } + } + } + + +} 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 new file mode 100644 index 000000000..6e748f4f3 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java @@ -0,0 +1,584 @@ +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.conf.UserSetting; +import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; +import com.genersoft.iot.vmp.gb28181.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.cmd.impl.SIPCommander; +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.IInviteStreamService; +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.ResourceBaseInfo; +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; + +/** + * 设备业务(目录订阅) + */ +@Service +public class DeviceServiceImpl implements IDeviceService { + + private final static Logger logger = LoggerFactory.getLogger(DeviceServiceImpl.class); + + @Autowired + private SIPCommander cmder; + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private ISIPCommander sipCommander; + + @Autowired + private CatalogResponseMessageHandler catalogResponseMessageHandler; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IInviteStreamService inviteStreamService; + + @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, SipTransactionInfo sipTransactionInfo) { + 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 存在脏数据 + inviteStreamService.clearInviteInfo(device.getDeviceId()); + } + device.setUpdateTime(now); + if (device.getKeepaliveIntervalTime() == 0) { + // 默认心跳间隔60 + device.setKeepaliveIntervalTime(60); + } + if (sipTransactionInfo != null) { + device.setSipTransactionInfo(sipTransactionInfo); + }else { + if (deviceInRedis != null) { + device.setSipTransactionInfo(deviceInRedis.getSipTransactionInfo()); + } + } + + // 第一次上线 或则设备之前是离线状态--进行通道同步和设备信息查询 + if (deviceInDb == null) { + device.setOnLine(true); + device.setCreateTime(now); + device.setUpdateTime(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 (deviceInDb != null) { + device.setSwitchPrimarySubStream(deviceInDb.isSwitchPrimarySubStream()); + } + if(!device.isOnLine()){ + device.setOnLine(true); + 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); + if (userSetting.getDeviceStatusNotify()) { + // 发送redis消息 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), null, true); + } + + } + + @Override + public void offline(String deviceId, String reason) { + logger.warn("[设备离线],{}, device:{}", reason, deviceId); + Device device = deviceMapper.getDeviceByDeviceId(deviceId); + if (device == null) { + return; + } + String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + deviceId; + dynamicTask.stop(registerExpireTaskKey); + device.setOnLine(false); + 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); + if (userSetting.getDeviceStatusNotify()) { + // 发送redis消息 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), null, false); + } + + } + + @Override + public boolean addCatalogSubscribe(Device device) { + if (device == null || device.getSubscribeCycleForCatalog() < 0) { + return false; + } + logger.info("[添加目录订阅] 设备{}", device.getDeviceId()); + // 添加目录订阅 + CatalogSubscribeTask catalogSubscribeTask = new CatalogSubscribeTask(device, sipCommander, dynamicTask); + // 刷新订阅 + int subscribeCycleForCatalog = Math.max(device.getSubscribeCycleForCatalog(),30); + // 设置最小值为30 + dynamicTask.startCron(device.getDeviceId() + "catalog", catalogSubscribeTask, (subscribeCycleForCatalog -1) * 1000); + return true; + } + + @Override + public boolean removeCatalogSubscribe(Device device) { + if (device == null || device.getSubscribeCycleForCatalog() < 0) { + return false; + } + logger.info("[移除目录订阅]: {}", device.getDeviceId()); + String taskKey = device.getDeviceId() + "catalog"; + if (device.isOnLine()) { + 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.isOnLine()) { + 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.isOnLine()) { + 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 (ObjectUtils.isEmpty(parentId) ) { + parentId = deviceId; + } + List rootNodes = deviceChannelMapper.getSubChannelsByDeviceId(deviceId, parentId, onlyCatalog); + return transportChannelsToTree(rootNodes, ""); + } + + @Override + public List queryVideoDeviceInTreeNode(String deviceId, String parentId) { + Device device = deviceMapper.getDeviceByDeviceId(deviceId); + if (device == null) { + return null; + } + if (ObjectUtils.isEmpty(parentId) || parentId.equals(deviceId)) { + return deviceChannelMapper.getSubChannelsByDeviceId(deviceId, null, false); + }else { + return deviceChannelMapper.getSubChannelsByDeviceId(deviceId, parentId, false); + } + } + + private List> transportChannelsToTree(List channels, String parentId) { + if (channels == null) { + return null; + } + List> treeNotes = new ArrayList<>(); + if (channels.size() == 0) { + return treeNotes; + } + for (DeviceChannel channel : channels) { + + BaseTree node = new BaseTree<>(); + node.setId(channel.getChannelId()); + node.setDeviceId(channel.getDeviceId()); + node.setName(channel.getName()); + node.setPid(parentId); + node.setBasicData(channel); + node.setParent(false); + if (channel.getChannelId().length() <= 8) { + node.setParent(true); + }else { + if (channel.getChannelId().length() != 20) { + node.setParent(channel.getParental() == 1); + }else { + try { + int type = Integer.parseInt(channel.getChannelId().substring(10, 13)); + if (type == 215 || type == 216 || type == 200) { + node.setParent(true); + } + }catch (NumberFormatException e) { + node.setParent(false); + } + } + } + treeNotes.add(node); + } + Collections.sort(treeNotes); + return treeNotes; + } + + @Override + public boolean isExist(String deviceId) { + return deviceMapper.getDeviceByDeviceId(deviceId) != null; + } + + @Override + public void addDevice(Device device) { + device.setOnLine(false); + 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(deviceInStore.isSwitchPrimarySubStream() != device.isSwitchPrimarySubStream()){ + //当修改设备的主子码流开关时,需要校验是否存在流,如果存在流则直接关闭 + List ssrcTransactionForAll = streamSession.getSsrcTransactionForAll(device.getDeviceId(), null, null, null); + if(ssrcTransactionForAll != null){ + for (SsrcTransaction ssrcTransaction: ssrcTransactionForAll) { + try { + cmder.streamByeCmd(device, ssrcTransaction.getChannelId(), ssrcTransaction.getStream(), null, null); + } catch (InvalidArgumentException | SsrcTransactionNotFoundException | ParseException | SipException e) { + throw new RuntimeException(e); + } + } + } + deviceChannelMapper.clearPlay(device.getDeviceId()); + inviteStreamService.clearInviteInfo(device.getDeviceId()); + } + + 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()); + } + if (!ObjectUtils.isEmpty(device.getCharset())) { + deviceInStore.setCharset(device.getCharset()); + } + if (!ObjectUtils.isEmpty(device.getSdpIp())) { + deviceInStore.setSdpIp(device.getSdpIp()); + } + + // 目录订阅相关的信息 + 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); + } + } + if (deviceInStore.getGeoCoordSys() != null) { + // 坐标系变化,需要重新计算GCJ02坐标和WGS84坐标 + if (!deviceInStore.getGeoCoordSys().equals(device.getGeoCoordSys())) { + updateDeviceChannelGeoCoordSys(device); + } + }else { + device.setGeoCoordSys("WGS84"); + } + if (device.getCharset() == null) { + device.setCharset("GB2312"); + } + + // 更新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); + } + if (result) { + redisCatchStorage.removeDevice(deviceId); + } + return result; + } + + @Override + public ResourceBaseInfo getOverview() { + List onlineDevices = deviceMapper.getOnlineDevices(); + List all = deviceMapper.getAll(); + return new ResourceBaseInfo(all.size(), onlineDevices.size()); + } + + @Override + public List getAll() { + return deviceMapper.getAll(); + } + + +} 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 new file mode 100644 index 000000000..3fb2f93c2 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java @@ -0,0 +1,266 @@ +package com.genersoft.iot.vmp.service.impl; + +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; +import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; +import com.genersoft.iot.vmp.service.IGbStreamService; +import com.genersoft.iot.vmp.storager.dao.GbStreamMapper; +import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper; +import com.genersoft.iot.vmp.storager.dao.PlatformCatalogMapper; +import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.stereotype.Service; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.util.ObjectUtils; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class GbStreamServiceImpl implements IGbStreamService { + + private final static Logger logger = LoggerFactory.getLogger(GbStreamServiceImpl.class); + + @Autowired + DataSourceTransactionManager dataSourceTransactionManager; + + @Autowired + TransactionDefinition transactionDefinition; + + @Autowired + private GbStreamMapper gbStreamMapper; + + @Autowired + private PlatformGbStreamMapper platformGbStreamMapper; + + @Autowired + private SubscribeHolder subscribeHolder; + + @Autowired + private ParentPlatformMapper platformMapper; + + @Autowired + private PlatformCatalogMapper catalogMapper; + + @Autowired + private EventPublisher eventPublisher; + + @Override + public PageInfo getAll(Integer page, Integer count, String platFormId, String catalogId, String query, String mediaServerId) { + PageHelper.startPage(page, count); + List all = gbStreamMapper.selectAll(platFormId, catalogId, query, mediaServerId); + return new PageInfo<>(all); + } + + @Override + public void del(String app, String stream) { + gbStreamMapper.del(app, stream); + } + + + @Override + public boolean addPlatformInfo(List gbStreams, String platformId, String catalogId) { + // 放在事务内执行 + boolean result = false; + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + ParentPlatform parentPlatform = platformMapper.getParentPlatByServerGBId(platformId); + if (catalogId == null) { + catalogId = parentPlatform.getCatalogId(); + } + try { + List deviceChannelList = new ArrayList<>(); + + + for (int i = 0; i < gbStreams.size(); i++) { + GbStream gbStream = gbStreams.get(i); + gbStream.setCatalogId(catalogId); + gbStream.setPlatformId(platformId); + // TODO 修改为批量提交 + platformGbStreamMapper.add(gbStream); + logger.info("[关联通道]直播流通道 平台:{}, 共需关联通道数:{}, 已关联:{}", platformId, gbStreams.size(), i + 1); + DeviceChannel deviceChannelListByStream = getDeviceChannelListByStreamWithStatus(gbStream, catalogId, parentPlatform); + deviceChannelList.add(deviceChannelListByStream); + } + dataSourceTransactionManager.commit(transactionStatus); //手动提交 + if (subscribeHolder.getCatalogSubscribe(platformId) != null) { + eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD); + } + + result = true; + }catch (Exception e) { + logger.error("批量保存流与平台的关系时错误", e); + dataSourceTransactionManager.rollback(transactionStatus); + } + return result; + } + + @Override + public DeviceChannel getDeviceChannelListByStream(GbStream gbStream, String catalogId, ParentPlatform platform) { + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setChannelId(gbStream.getGbId()); + deviceChannel.setName(gbStream.getName()); + deviceChannel.setLongitude(gbStream.getLongitude()); + deviceChannel.setLatitude(gbStream.getLatitude()); + deviceChannel.setDeviceId(platform.getDeviceGBId()); + deviceChannel.setManufacture("wvp-pro"); + deviceChannel.setStatus(gbStream.isStatus()); + + deviceChannel.setRegisterWay(1); + + PlatformCatalog catalog = catalogMapper.select(catalogId); + if (catalog != null) { + deviceChannel.setCivilCode(catalog.getCivilCode()); + deviceChannel.setParentId(catalog.getParentId()); + deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId()); + }else { + deviceChannel.setCivilCode(platform.getAdministrativeDivision()); + deviceChannel.setParentId(platform.getDeviceGBId()); + } + + deviceChannel.setModel("live"); + deviceChannel.setOwner("wvp-pro"); + deviceChannel.setParental(0); + deviceChannel.setSecrecy("0"); + return deviceChannel; + } + + @Override + public boolean delPlatformInfo(String platformId, List gbStreams) { + // 放在事务内执行 + boolean result = false; + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + try { + List deviceChannelList = new ArrayList<>(); + platformGbStreamMapper.delByAppAndStreamsByPlatformId(gbStreams, platformId); + for (GbStream gbStream : gbStreams) { + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setChannelId(gbStream.getGbId()); + deviceChannelList.add(deviceChannel); + } + + eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.DEL); + dataSourceTransactionManager.commit(transactionStatus); //手动提交 + result = true; + }catch (Exception e) { + logger.error("批量移除流与平台的关系时错误", e); + dataSourceTransactionManager.rollback(transactionStatus); + } + return result; + } + + @Override + public void sendCatalogMsg(GbStream gbStream, String type) { + if (gbStream == null || type == null) { + logger.warn("[发送目录订阅]类型:流信息或类型为NULL"); + return; + } + List gbStreams = new ArrayList<>(); + if (gbStream.getGbId() != null) { + gbStreams.add(gbStream); + }else { + GbStream gbStreamIndb = gbStreamMapper.selectOne(gbStream.getApp(), gbStream.getStream()); + if (gbStreamIndb != null && gbStreamIndb.getGbId() != null){ + gbStreams.add(gbStreamIndb); + } + } + sendCatalogMsgs(gbStreams, type); + } + + @Override + public void sendCatalogMsgs(List gbStreams, String type) { + if (gbStreams.size() > 0) { + for (GbStream gs : gbStreams) { + if (ObjectUtils.isEmpty(gs.getGbId())){ + continue; + } + List parentPlatforms = platformGbStreamMapper.selectByAppAndStream(gs.getApp(), gs.getStream()); + if (parentPlatforms.size() > 0) { + for (ParentPlatform parentPlatform : parentPlatforms) { + if (parentPlatform != null) { + eventPublisher.catalogEventPublishForStream(parentPlatform.getServerGBId(), gs, type); + } + } + } + } + } + } + + @Override + public int updateGbIdOrName(List streamPushItemForUpdate) { + return gbStreamMapper.updateGbIdOrName(streamPushItemForUpdate); + } + + @Override + public DeviceChannel getDeviceChannelListByStreamWithStatus(GbStream gbStream, String catalogId, ParentPlatform platform) { + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setChannelId(gbStream.getGbId()); + deviceChannel.setName(gbStream.getName()); + deviceChannel.setLongitude(gbStream.getLongitude()); + deviceChannel.setLatitude(gbStream.getLatitude()); + deviceChannel.setDeviceId(platform.getDeviceGBId()); + deviceChannel.setManufacture("wvp-pro"); + // todo 目前是每一条查询一次,需要优化 + Boolean status = null; + if ("proxy".equals(gbStream.getStreamType())) { + status = gbStreamMapper.selectStatusForProxy(gbStream.getApp(), gbStream.getStream()); + }else { + status = gbStreamMapper.selectStatusForPush(gbStream.getApp(), gbStream.getStream()); + } + deviceChannel.setStatus(status != null && status); + + deviceChannel.setRegisterWay(1); + PlatformCatalog catalog = catalogMapper.select(catalogId); + if (catalog != null) { + deviceChannel.setCivilCode(catalog.getCivilCode()); + deviceChannel.setParentId(catalog.getParentId()); + deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId()); + }else { + deviceChannel.setCivilCode(platform.getAdministrativeDivision()); + deviceChannel.setParentId(platform.getDeviceGBId()); + } + + deviceChannel.setModel("live"); + deviceChannel.setOwner("wvp-pro"); + deviceChannel.setParental(0); + deviceChannel.setSecrecy("0"); + return deviceChannel; + } + + @Override + public List getAllGBChannels(String platformId) { + + return gbStreamMapper.selectAll(platformId, null, null, null); + + } + + @Override + public void delAllPlatformInfo(String platformId, String catalogId) { + if (platformId == null) { + return ; + } + ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformId); + if (platform == null) { + return ; + } + if (ObjectUtils.isEmpty(catalogId)) { + catalogId = 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/InviteStreamServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java new file mode 100644 index 000000000..f41281633 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java @@ -0,0 +1,229 @@ +package com.genersoft.iot.vmp.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.genersoft.iot.vmp.common.InviteInfo; +import com.genersoft.iot.vmp.common.InviteSessionStatus; +import com.genersoft.iot.vmp.common.InviteSessionType; +import com.genersoft.iot.vmp.common.VideoManagerConstants; +import com.genersoft.iot.vmp.service.IInviteStreamService; +import com.genersoft.iot.vmp.service.bean.ErrorCallback; +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.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +@Service +public class InviteStreamServiceImpl implements IInviteStreamService { + + private final Logger logger = LoggerFactory.getLogger(InviteStreamServiceImpl.class); + + private final Map>> inviteErrorCallbackMap = new ConcurrentHashMap<>(); + + @Autowired + private RedisTemplate redisTemplate; + + @Override + public void updateInviteInfo(InviteInfo inviteInfo) { + if (inviteInfo == null || (inviteInfo.getDeviceId() == null || inviteInfo.getChannelId() == null)) { + logger.warn("[更新Invite信息],参数不全: {}", JSON.toJSON(inviteInfo)); + return; + } + InviteInfo inviteInfoForUpdate = null; + + if (InviteSessionStatus.ready == inviteInfo.getStatus()) { + if (inviteInfo.getDeviceId() == null + || inviteInfo.getChannelId() == null + || inviteInfo.getType() == null + || inviteInfo.getStream() == null + ) { + return; + } + inviteInfoForUpdate = inviteInfo; + } else { + InviteInfo inviteInfoInRedis = getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), + inviteInfo.getChannelId(), inviteInfo.getStream()); + if (inviteInfoInRedis == null) { + logger.warn("[更新Invite信息],未从缓存中读取到Invite信息: deviceId: {}, channel: {}, stream: {}", + inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()); + return; + } + if (inviteInfo.getStreamInfo() != null) { + inviteInfoInRedis.setStreamInfo(inviteInfo.getStreamInfo()); + } + if (inviteInfo.getSsrcInfo() != null) { + inviteInfoInRedis.setSsrcInfo(inviteInfo.getSsrcInfo()); + } + if (inviteInfo.getStreamMode() != null) { + inviteInfoInRedis.setStreamMode(inviteInfo.getStreamMode()); + } + if (inviteInfo.getReceiveIp() != null) { + inviteInfoInRedis.setReceiveIp(inviteInfo.getReceiveIp()); + } + if (inviteInfo.getReceivePort() != null) { + inviteInfoInRedis.setReceivePort(inviteInfo.getReceivePort()); + } + if (inviteInfo.getStatus() != null) { + inviteInfoInRedis.setStatus(inviteInfo.getStatus()); + } + + inviteInfoForUpdate = inviteInfoInRedis; + + } + String key = VideoManagerConstants.INVITE_PREFIX + + "_" + inviteInfoForUpdate.getType() + + "_" + inviteInfoForUpdate.getDeviceId() + + "_" + inviteInfoForUpdate.getChannelId() + + "_" + inviteInfoForUpdate.getStream(); + redisTemplate.opsForValue().set(key, inviteInfoForUpdate); + } + + @Override + public InviteInfo updateInviteInfoForStream(InviteInfo inviteInfo, String stream) { + + InviteInfo inviteInfoInDb = getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()); + if (inviteInfoInDb == null) { + return null; + } + removeInviteInfo(inviteInfoInDb); + String key = VideoManagerConstants.INVITE_PREFIX + + "_" + inviteInfo.getType() + + "_" + inviteInfo.getDeviceId() + + "_" + inviteInfo.getChannelId() + + "_" + stream; + inviteInfoInDb.setStream(stream); + redisTemplate.opsForValue().set(key, inviteInfoInDb); + return inviteInfoInDb; + } + + @Override + public InviteInfo getInviteInfo(InviteSessionType type, String deviceId, String channelId, String stream) { + String key = VideoManagerConstants.INVITE_PREFIX + + "_" + (type != null ? type : "*") + + "_" + (deviceId != null ? deviceId : "*") + + "_" + (channelId != null ? channelId : "*") + + "_" + (stream != null ? stream : "*"); + List scanResult = RedisUtil.scan(redisTemplate, key); + if (scanResult.size() != 1) { + return null; + } + + return (InviteInfo) redisTemplate.opsForValue().get(scanResult.get(0)); + } + + @Override + public InviteInfo getInviteInfoByDeviceAndChannel(InviteSessionType type, String deviceId, String channelId) { + return getInviteInfo(type, deviceId, channelId, null); + } + + @Override + public InviteInfo getInviteInfoByStream(InviteSessionType type, String stream) { + return getInviteInfo(type, null, null, stream); + } + + @Override + public void removeInviteInfo(InviteSessionType type, String deviceId, String channelId, String stream) { + String scanKey = VideoManagerConstants.INVITE_PREFIX + + "_" + (type != null ? type : "*") + + "_" + (deviceId != null ? deviceId : "*") + + "_" + (channelId != null ? channelId : "*") + + "_" + (stream != null ? stream : "*"); + List scanResult = RedisUtil.scan(redisTemplate, scanKey); + if (scanResult.size() > 0) { + for (Object keyObj : scanResult) { + String key = (String) keyObj; + InviteInfo inviteInfo = (InviteInfo) redisTemplate.opsForValue().get(key); + if (inviteInfo == null) { + continue; + } + redisTemplate.delete(key); + inviteErrorCallbackMap.remove(buildKey(type, deviceId, channelId, inviteInfo.getStream())); + } + } + } + + @Override + public void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, String deviceId, String channelId) { + removeInviteInfo(inviteSessionType, deviceId, channelId, null); + } + + @Override + public void removeInviteInfo(InviteInfo inviteInfo) { + removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()); + } + + @Override + public void once(InviteSessionType type, String deviceId, String channelId, String stream, ErrorCallback callback) { + String key = buildKey(type, deviceId, channelId, stream); + List> callbacks = inviteErrorCallbackMap.get(key); + if (callbacks == null) { + callbacks = new CopyOnWriteArrayList<>(); + inviteErrorCallbackMap.put(key, callbacks); + } + callbacks.add(callback); + + } + + private String buildKey(InviteSessionType type, String deviceId, String channelId, String stream) { + String key = type + "_" + deviceId + "_" + channelId; + // 如果ssrc未null那么可以实现一个通道只能一次操作,ssrc不为null则可以支持一个通道多次invite + if (stream != null) { + key += ("_" + stream); + } + return key; + } + + + @Override + public void clearInviteInfo(String deviceId) { + removeInviteInfo(null, deviceId, null, null); + } + + @Override + public int getStreamInfoCount(String mediaServerId) { + int count = 0; + String key = VideoManagerConstants.INVITE_PREFIX + "_*_*_*_*"; + List scanResult = RedisUtil.scan(redisTemplate, key); + if (scanResult.size() == 0) { + return 0; + }else { + for (Object keyObj : scanResult) { + String keyStr = (String) keyObj; + InviteInfo inviteInfo = (InviteInfo) redisTemplate.opsForValue().get(keyStr); + if (inviteInfo != null && inviteInfo.getStreamInfo() != null && inviteInfo.getStreamInfo().getMediaServerId().equals(mediaServerId)) { + count++; + } + } + } + return count; + } + + @Override + public void call(InviteSessionType type, String deviceId, String channelId, String stream, int code, String msg, Object data) { + String key = buildSubStreamKey(type, deviceId, channelId, stream); + List> callbacks = inviteErrorCallbackMap.get(key); + if (callbacks == null) { + return; + } + for (ErrorCallback callback : callbacks) { + callback.run(code, msg, data); + } + inviteErrorCallbackMap.remove(key); + } + + + private String buildSubStreamKey(InviteSessionType type, String deviceId, String channelId, String stream) { + String key = type + "_" + "_" + deviceId + "_" + channelId; + // 如果ssrc为null那么可以实现一个通道只能一次操作,ssrc不为null则可以支持一个通道多次invite + if (stream != null) { + key += ("_" + stream); + } + return key; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/LogServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/LogServiceImpl.java new file mode 100644 index 000000000..92fa5960d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/LogServiceImpl.java @@ -0,0 +1,36 @@ +package com.genersoft.iot.vmp.service.impl; + +import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; +import com.genersoft.iot.vmp.service.ILogService; +import com.genersoft.iot.vmp.storager.dao.LogMapper; +import com.genersoft.iot.vmp.storager.dao.dto.LogDto; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class LogServiceImpl implements ILogService { + + @Autowired + private LogMapper logMapper; + + @Override + public PageInfo getAll(int page, int count, String query, String type, String startTime, String endTime) { + PageHelper.startPage(page, count); + List all = logMapper.query(query, type, startTime, endTime); + return new PageInfo<>(all); + } + + @Override + public void add(LogDto logDto) { + logMapper.add(logDto); + } + + @Override + public int clear() { + return logMapper.clear(); + } +} 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 new file mode 100644 index 000000000..a79b4108a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java @@ -0,0 +1,775 @@ +package com.genersoft.iot.vmp.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.common.CommonCallback; +import com.genersoft.iot.vmp.common.VideoManagerConstants; +import com.genersoft.iot.vmp.conf.DynamicTask; +import com.genersoft.iot.vmp.conf.SipConfig; +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.SSRCFactory; +import com.genersoft.iot.vmp.media.zlm.*; +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.media.zlm.dto.ServerKeepaliveData; +import com.genersoft.iot.vmp.service.IInviteStreamService; +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.DateUtil; +import com.genersoft.iot.vmp.utils.JsonUtil; +import com.genersoft.iot.vmp.utils.redis.RedisUtil; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import 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.data.redis.core.RedisTemplate; +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.io.File; +import java.time.LocalDateTime; +import java.util.*; + +/** + * 媒体服务器节点管理 + */ +@Service +public class MediaServerServiceImpl implements IMediaServerService { + + private final static Logger logger = LoggerFactory.getLogger(MediaServerServiceImpl.class); + + private final String zlmKeepaliveKeyPrefix = "zlm-keepalive_"; + + @Autowired + private SipConfig sipConfig; + + @Autowired + private SSRCFactory ssrcFactory; + + @Value("${server.ssl.enabled:false}") + private boolean sslEnabled; + + @Value("${server.port}") + private Integer serverPort; + + @Autowired + private UserSetting userSetting; + + @Autowired + private SendRtpPortManager sendRtpPortManager; + + @Autowired + private AssistRESTfulUtils assistRESTfulUtils; + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private MediaServerMapper mediaServerMapper; + + @Autowired + private DataSourceTransactionManager dataSourceTransactionManager; + + @Autowired + private TransactionDefinition transactionDefinition; + + + @Autowired + private ZLMServerFactory zlmServerFactory; + + @Autowired + private EventPublisher publisher; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IInviteStreamService inviteStreamService; + + @Autowired + private RedisTemplate redisTemplate; + + + /** + * 初始化 + */ + @Override + public void updateVmServer(List mediaServerItemList) { + logger.info("[zlm] 缓存初始化 "); + for (MediaServerItem mediaServerItem : mediaServerItemList) { + if (ObjectUtils.isEmpty(mediaServerItem.getId())) { + continue; + } + // 更新 + if (ssrcFactory.hasMediaServerSSRC(mediaServerItem.getId())) { + ssrcFactory.initMediaServerSSRC(mediaServerItem.getId(), null); + } + if (userSetting.getGbSendStreamStrict()) { + int startPort = 50000; + int endPort = 60000; + String sendRtpPortRange = mediaServerItem.getSendRtpPortRange(); + if (sendRtpPortRange == null) { + logger.warn("[zlm] ] 未配置发流端口范围,默认使用50000到60000"); + }else { + String[] sendRtpPortRangeArray = sendRtpPortRange.trim().split(","); + if (sendRtpPortRangeArray.length != 2) { + logger.warn("[zlm] ] 发流端口范围错误,默认使用50000到60000"); + }else { + try { + startPort = Integer.parseInt(sendRtpPortRangeArray[0]); + endPort = Integer.parseInt(sendRtpPortRangeArray[1]); + if (endPort <= startPort) { + logger.warn("[zlm] ] 发流端口范围错误,结束端口应大于开始端口,使用默认端口"); + startPort = 50000; + endPort = 60000; + } + + }catch (NumberFormatException e) { + logger.warn("[zlm] ] 发流端口范围错误,默认使用50000到60000"); + } + } + } + logger.info("[[zlm] ] 配置发流端口范围,{}-{}", startPort, endPort); + sendRtpPortManager.initServerPort(mediaServerItem.getId(), startPort, endPort); + } + // 查询redis是否存在此mediaServer + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId(); + Boolean hasKey = redisTemplate.hasKey(key); + if (hasKey != null && ! hasKey) { + redisTemplate.opsForValue().set(key, mediaServerItem); + } + } + } + + + @Override + public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck, + boolean isPlayback, Integer port, Boolean reUsePort, Integer tcpMode) { + if (mediaServerItem == null || mediaServerItem.getId() == null) { + logger.info("[openRTPServer] 失败, mediaServerItem == null || mediaServerItem.getId() == null"); + return null; + } + // 获取mediaServer可用的ssrc + String ssrc; + if (presetSsrc != null) { + ssrc = presetSsrc; + }else { + if (isPlayback) { + ssrc = ssrcFactory.getPlayBackSsrc(mediaServerItem.getId()); + }else { + ssrc = ssrcFactory.getPlaySsrc(mediaServerItem.getId()); + } + } + + if (streamId == null) { + streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); + } + int rtpServerPort; + if (mediaServerItem.isRtpEnable()) { + rtpServerPort = zlmServerFactory.createRTPServer(mediaServerItem, streamId, ssrcCheck?Integer.parseInt(ssrc):0, port, reUsePort, tcpMode); + } else { + rtpServerPort = mediaServerItem.getRtpProxyPort(); + } + return new SSRCInfo(rtpServerPort, ssrc, streamId); + } + + @Override + public void closeRTPServer(MediaServerItem mediaServerItem, String streamId) { + if (mediaServerItem == null) { + return; + } + zlmServerFactory.closeRtpServer(mediaServerItem, streamId); + } + + @Override + public void closeRTPServer(MediaServerItem mediaServerItem, String streamId, CommonCallback callback) { + if (mediaServerItem == null) { + callback.run(false); + return; + } + zlmServerFactory.closeRtpServer(mediaServerItem, streamId, callback); + } + + @Override + public void closeRTPServer(String mediaServerId, String streamId) { + MediaServerItem mediaServerItem = this.getOne(mediaServerId); + closeRTPServer(mediaServerItem, streamId); + } + + @Override + public Boolean updateRtpServerSSRC(MediaServerItem mediaServerItem, String streamId, String ssrc) { + return zlmServerFactory.updateRtpServerSSRC(mediaServerItem, streamId, ssrc); + } + + @Override + public void releaseSsrc(String mediaServerItemId, String ssrc) { + MediaServerItem mediaServerItem = getOne(mediaServerItemId); + if (mediaServerItem == null || ssrc == null) { + return; + } + ssrcFactory.releaseSsrc(mediaServerItemId, ssrc); + } + + /** + * zlm 重启后重置他的推流信息, TODO 给正在使用的设备发送停止命令 + */ + @Override + public void clearRTPServer(MediaServerItem mediaServerItem) { + ssrcFactory.reset(mediaServerItem.getId()); + + } + + + @Override + public void update(MediaServerItem mediaSerItem) { + mediaServerMapper.update(mediaSerItem); + MediaServerItem mediaServerItemInRedis = getOne(mediaSerItem.getId()); + MediaServerItem mediaServerItemInDataBase = mediaServerMapper.queryOne(mediaSerItem.getId()); + if (mediaServerItemInRedis == null || ssrcFactory.hasMediaServerSSRC(mediaSerItem.getId())) { + ssrcFactory.initMediaServerSSRC(mediaServerItemInDataBase.getId(),null); + } + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItemInDataBase.getId(); + redisTemplate.opsForValue().set(key, mediaServerItemInDataBase); + } + + @Override + public List getAll() { + List result = new ArrayList<>(); + List mediaServerKeys = RedisUtil.scan(redisTemplate, 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 = JsonUtil.redisJsonToObject(redisTemplate, key, MediaServerItem.class); + if (Objects.isNull(mediaServerItem)) { + continue; + } + // 检查状态 + Double aDouble = redisTemplate.opsForZSet().score(onlineKey, mediaServerItem.getId()); + if (aDouble != null) { + mediaServerItem.setStatus(true); + } + result.add(mediaServerItem); + } + result.sort((serverItem1, serverItem2)->{ + int sortResult = 0; + LocalDateTime localDateTime1 = LocalDateTime.parse(serverItem1.getCreateTime(), DateUtil.formatter); + LocalDateTime localDateTime2 = LocalDateTime.parse(serverItem2.getCreateTime(), DateUtil.formatter); + + sortResult = localDateTime1.compareTo(localDateTime2); + return sortResult; + }); + return result; + } + + + @Override + public List getAllFromDatabase() { + return mediaServerMapper.queryAll(); + } + + @Override + public List getAllOnline() { + String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); + Set mediaServerIdSet = redisTemplate.opsForZSet().reverseRange(key, 0, -1); + + List result = new ArrayList<>(); + if (mediaServerIdSet != null && mediaServerIdSet.size() > 0) { + for (Object mediaServerId : mediaServerIdSet) { + String mediaServerIdStr = (String) mediaServerId; + String serverKey = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerIdStr; + result.add((MediaServerItem) redisTemplate.opsForValue().get(serverKey)); + } + } + Collections.reverse(result); + return result; + } + + /** + * 获取单个zlm服务器 + * @param mediaServerId 服务id + * @return MediaServerItem + */ + @Override + public MediaServerItem getOne(String mediaServerId) { + if (mediaServerId == null) { + return null; + } + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerId; + return JsonUtil.redisJsonToObject(redisTemplate, key, MediaServerItem.class); + } + + @Override + public MediaServerItem getDefaultMediaServer() { + + return mediaServerMapper.queryDefault(); + } + + @Override + public void clearMediaServerForOnline() { + String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); + redisTemplate.delete(key); + } + + @Override + 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) { + throw new ControllerException(ErrorCode.ERROR100.getCode(),"保存失败,媒体服务ID [ " + zlmServerConfig.getGeneralMediaServerId() + " ] 已存在,请修改媒体服务器配置"); + } + mediaServerItem.setId(zlmServerConfig.getGeneralMediaServerId()); + zlmServerConfig.setIp(mediaServerItem.getIp()); + mediaServerMapper.add(mediaServerItem); + zlmServerOnline(zlmServerConfig); + }else { + throw new ControllerException(ErrorCode.ERROR100.getCode(),"连接失败"); + } + + }else { + throw new ControllerException(ErrorCode.ERROR100.getCode(),"连接失败"); + } + } + + @Override + public int addToDatabase(MediaServerItem mediaSerItem) { + return mediaServerMapper.add(mediaSerItem); + } + + @Override + public int updateToDatabase(MediaServerItem 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; + } + + /** + * 处理zlm上线 + * @param zlmServerConfig zlm上线携带的参数 + */ + @Override + public void zlmServerOnline(ZLMServerConfig zlmServerConfig) { + + MediaServerItem serverItem = mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId()); + if (serverItem == null) { + 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) { + serverItem.setHttpPort(zlmServerConfig.getHttpPort()); + } + if (serverItem.getHttpSSlPort() == 0) { + serverItem.setHttpSSlPort(zlmServerConfig.getHttpSSLport()); + } + if (serverItem.getRtmpPort() == 0) { + serverItem.setRtmpPort(zlmServerConfig.getRtmpPort()); + } + if (serverItem.getRtmpSSlPort() == 0) { + serverItem.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort()); + } + if (serverItem.getRtspPort() == 0) { + serverItem.setRtspPort(zlmServerConfig.getRtspPort()); + } + if (serverItem.getRtspSSLPort() == 0) { + serverItem.setRtspSSLPort(zlmServerConfig.getRtspSSlport()); + } + if (serverItem.getRtpProxyPort() == 0) { + serverItem.setRtpProxyPort(zlmServerConfig.getRtpProxyPort()); + } + serverItem.setStatus(true); + + if (ObjectUtils.isEmpty(serverItem.getId())) { + logger.warn("[未注册的zlm] serverItem缺少ID, 无法接入:{}:{}", zlmServerConfig.getIp(),zlmServerConfig.getHttpPort() ); + return; + } + mediaServerMapper.update(serverItem); + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + zlmServerConfig.getGeneralMediaServerId(); + if (ssrcFactory.hasMediaServerSSRC(serverItem.getId())) { + ssrcFactory.initMediaServerSSRC(zlmServerConfig.getGeneralMediaServerId(), null); + } + redisTemplate.opsForValue().set(key, serverItem); + resetOnlineServerItem(serverItem); + + + if (serverItem.isAutoConfig()) { + // 查看assist服务的录像路径配置 + if (serverItem.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) { + JSONObject info = assistRESTfulUtils.getInfo(serverItem, null); + if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0 ) { + JSONObject dataJson = info.getJSONObject("data"); + if (dataJson != null) { + String recordPath = dataJson.getString("record"); + userSetting.setRecordPath(recordPath); + } + } + } + setZLMConfig(serverItem, "0".equals(zlmServerConfig.getHookEnable())); + } + final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + serverItem.getId(); + dynamicTask.stop(zlmKeepaliveKey); + dynamicTask.startDelay(zlmKeepaliveKey, new KeepAliveTimeoutRunnable(serverItem), (serverItem.getHookAliveInterval().intValue() + 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 + userSetting.getServerId(); + // 使用zset的分数作为当前并发量, 默认值设置为0 + if (redisTemplate.opsForZSet().score(key, serverItem.getId()) == null) { // 不存在则设置默认值 已存在则重置 + redisTemplate.opsForZSet().add(key, serverItem.getId(), 0L); + // 查询服务流数量 + zlmresTfulUtils.getMediaList(serverItem, null, null, "rtsp",(mediaList ->{ + Integer code = mediaList.getInteger("code"); + if (code == 0) { + JSONArray data = mediaList.getJSONArray("data"); + if (data != null) { + redisTemplate.opsForZSet().add(key, serverItem.getId(), data.size()); + } + } + })); + }else { + clearRTPServer(serverItem); + } + } + + + @Override + public void addCount(String mediaServerId) { + if (mediaServerId == null) { + return; + } + String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); + redisTemplate.opsForZSet().incrementScore(key, mediaServerId, 1); + + } + + @Override + public void removeCount(String mediaServerId) { + String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); + redisTemplate.opsForZSet().incrementScore(key, mediaServerId, - 1); + } + + /** + * 获取负载最低的节点 + * @return MediaServerItem + */ + @Override + public MediaServerItem getMediaServerForMinimumLoad(Boolean hasAssist) { + String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); + Long size = redisTemplate.opsForZSet().zCard(key); + if (size == null || size == 0) { + logger.info("获取负载最低的节点时无在线节点"); + return null; + } + + // 获取分数最低的,及并发最低的 + Set objects = redisTemplate.opsForZSet().range(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; + } + } + } + + return mediaServerItem; + } + + /** + * 对zlm服务器进行基础配置 + * @param mediaServerItem 服务ID + * @param restart 是否重启zlm + */ + @Override + 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); + + Map param = new HashMap<>(); + param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline + if (mediaServerItem.getRtspPort() != 0) { + param.put("ffmpeg.snap", "%s -rtsp_transport tcp -i %s -y -f mjpeg -t 0.001 %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_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",""); + 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"); + // 推流断开后可以在超时时间内重新连接上继续推流,这样播放器会接着播放。 + // 置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(",", "-")); + } + + if (userSetting.getRecordPath() != null) { + File recordPathFile = new File(userSetting.getRecordPath()); + File mp4SavePathFile = recordPathFile.getParentFile().getAbsoluteFile(); + param.put("protocol.mp4_save_path", mp4SavePathFile.getAbsoluteFile()); + param.put("record.appName", recordPathFile.getName()); + } + + JSONObject responseJSON = zlmresTfulUtils.setServerConfig(mediaServerItem, param); + + if (responseJSON != null && responseJSON.getInteger("code") == 0) { + 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失败 {} -> {}:{}", + mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); + } + + + } + + + @Override + public MediaServerItem checkMediaServer(String ip, int port, String secret) { + if (mediaServerMapper.queryOneByHostAndPort(ip, port) != null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "此连接已存在"); + } + MediaServerItem mediaServerItem = new MediaServerItem(); + mediaServerItem.setIp(ip); + mediaServerItem.setHttpPort(port); + mediaServerItem.setSecret(secret); + JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem); + if (responseJSON == null) { + 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) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "读取配置失败"); + } + if (mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId()) != null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "媒体服务ID [" + zlmServerConfig.getGeneralMediaServerId() + " ] 已存在,请修改媒体服务器配置"); + } + mediaServerItem.setHttpSSlPort(zlmServerConfig.getHttpPort()); + mediaServerItem.setRtmpPort(zlmServerConfig.getRtmpPort()); + mediaServerItem.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort()); + mediaServerItem.setRtspPort(zlmServerConfig.getRtspPort()); + mediaServerItem.setRtspSSLPort(zlmServerConfig.getRtspSSlport()); + mediaServerItem.setRtpProxyPort(zlmServerConfig.getRtpProxyPort()); + mediaServerItem.setStreamIp(ip); + mediaServerItem.setHookIp(sipConfig.getIp().split(",")[0]); + mediaServerItem.setSdpIp(ip); + return mediaServerItem; + } + + @Override + public boolean checkMediaRecordServer(String ip, int port) { + boolean result = false; + OkHttpClient client = new OkHttpClient(); + String url = String.format("http://%s:%s/index/api/record", ip, port); + Request request = new Request.Builder() + .get() + .url(url) + .build(); + try { + Response response = client.newCall(request).execute(); + if (response != null) { + result = true; + } + } catch (Exception e) {} + + return result; + } + + @Override + public void delete(String id) { + redisTemplate.opsForZSet().remove(VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(), id); + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + id; + redisTemplate.delete(key); + } + @Override + public void deleteDb(String id){ + //同步删除数据库中的数据 + mediaServerMapper.delOne(id); + } + + @Override + public void updateMediaServerKeepalive(String mediaServerId, ServerKeepaliveData data) { + MediaServerItem mediaServerItem = getOne(mediaServerId); + if (mediaServerItem == null) { + // 缓存不存在,从数据库查询,如果数据库不存在则是错误的 + mediaServerItem = getOneFromDatabase(mediaServerId); + if (mediaServerItem == null) { + logger.warn("[更新ZLM 保活信息] 流媒体{}尚未加入使用,请检查节点中是否含有此流媒体 ", mediaServerId); + return; + } + // zlm连接重试 + logger.warn("[更新ZLM 保活信息]尝试链接zml id {}", mediaServerId); + ssrcFactory.initMediaServerSSRC(mediaServerItem.getId(), null); + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId(); + redisTemplate.opsForValue().set(key, mediaServerItem); + clearRTPServer(mediaServerItem); + } + 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(inviteStreamService.getStreamInfoCount(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 new file mode 100644 index 000000000..b8241d7a1 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java @@ -0,0 +1,113 @@ +package com.genersoft.iot.vmp.service.impl; + +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.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.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 { + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + 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, String callId) { + return getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, null, callId); + } + + @Override + public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, String addr, boolean authority) { + StreamInfo streamInfo = null; + if (mediaServerId == null) { + mediaServerId = mediaConfig.getId(); + } + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); + if (mediaInfo == null) { + 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; + } + JSONObject mediaJSON = JSON.parseObject(JSON.toJSONString(data.get(0)), JSONObject.class); + JSONArray tracks = mediaJSON.getJSONArray("tracks"); + if (authority) { + streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, addr, calld); + }else { + streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, addr,null); + } + } + } + return streamInfo; + } + + + + @Override + 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, String callId) { + StreamInfo streamInfoResult = new StreamInfo(); + streamInfoResult.setStream(stream); + streamInfoResult.setApp(app); + if (addr == null) { + addr = mediaInfo.getStreamIp(); + } + + streamInfoResult.setIp(addr); + streamInfoResult.setMediaServerId(mediaInfo.getId()); + 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 000000000..eeea29a72 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java @@ -0,0 +1,166 @@ +package com.genersoft.iot.vmp.service.impl; + +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; +import com.genersoft.iot.vmp.service.IPlatformChannelService; +import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; +import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper; +import com.genersoft.iot.vmp.storager.dao.PlatformCatalogMapper; +import com.genersoft.iot.vmp.storager.dao.PlatformChannelMapper; +import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.stereotype.Service; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.util.ObjectUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author lin + */ +@Service +public class PlatformChannelServiceImpl implements IPlatformChannelService { + + private final static Logger logger = LoggerFactory.getLogger(PlatformChannelServiceImpl.class); + + @Autowired + private PlatformChannelMapper platformChannelMapper; + + @Autowired + TransactionDefinition transactionDefinition; + + @Autowired + DataSourceTransactionManager dataSourceTransactionManager; + + @Autowired + private SubscribeHolder subscribeHolder; + + + @Autowired + private DeviceChannelMapper deviceChannelMapper; + + @Autowired + private PlatformCatalogMapper catalogManager; + + @Autowired + private ParentPlatformMapper platformMapper; + + @Autowired + EventPublisher eventPublisher; + + @Override + public int updateChannelForGB(String platformId, List channelReduces, String catalogId) { + ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformId); + if (platform == null) { + logger.warn("更新级联通道信息时未找到平台{}的信息", platformId); + return 0; + } + Map deviceAndChannels = new HashMap<>(); + for (ChannelReduce channelReduce : channelReduces) { + channelReduce.setCatalogId(catalogId); + deviceAndChannels.put(channelReduce.getId(), channelReduce); + } + List deviceAndChannelList = new ArrayList<>(deviceAndChannels.keySet()); + // 查询当前已经存在的 + List channelIds = platformChannelMapper.findChannelRelatedPlatform(platformId, channelReduces); + if (deviceAndChannelList != null) { + deviceAndChannelList.removeAll(channelIds); + } + for (Integer channelId : channelIds) { + deviceAndChannels.remove(channelId); + } + List channelReducesToAdd = new ArrayList<>(deviceAndChannels.values()); + // 对剩下的数据进行存储 + int allCount = 0; + boolean result = false; + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + int limitCount = 300; + if (channelReducesToAdd.size() > 0) { + if (channelReducesToAdd.size() > limitCount) { + for (int i = 0; i < channelReducesToAdd.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > channelReducesToAdd.size()) { + toIndex = channelReducesToAdd.size(); + } + int count = platformChannelMapper.addChannels(platformId, channelReducesToAdd.subList(i, toIndex)); + result = result || count < 0; + allCount += count; + logger.info("[关联通道]国标通道 平台:{}, 共需关联通道数:{}, 已关联:{}", platformId, channelReducesToAdd.size(), toIndex); + } + }else { + allCount = platformChannelMapper.addChannels(platformId, channelReducesToAdd); + result = result || allCount < 0; + logger.info("[关联通道]国标通道 平台:{}, 关联通道数:{}", platformId, channelReducesToAdd.size()); + } + + if (result) { + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + allCount = 0; + }else { + logger.info("[关联通道]国标通道 平台:{}, 正在存入数据库", platformId); + dataSourceTransactionManager.commit(transactionStatus); + + } + SubscribeInfo catalogSubscribe = subscribeHolder.getCatalogSubscribe(platformId); + if (catalogSubscribe != null) { + List deviceChannelList = getDeviceChannelListByChannelReduceList(channelReducesToAdd, catalogId, platform); + if (deviceChannelList != null) { + eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD); + } + } + logger.info("[关联通道]国标通道 平台:{}, 存入数据库成功", platformId); + } + return allCount; + } + + private List getDeviceChannelListByChannelReduceList(List channelReduces, String catalogId, ParentPlatform platform) { + List deviceChannelList = new ArrayList<>(); + if (channelReduces.size() > 0){ + PlatformCatalog catalog = catalogManager.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); + deviceChannel.setCivilCode(catalog.getCivilCode()); + deviceChannel.setParentId(catalog.getParentId()); + deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId()); + deviceChannelList.add(deviceChannel); + } + } + return deviceChannelList; + } + + @Override + public int delAllChannelForGB(String platformId, String catalogId) { + + int result; + if (platformId == null) { + return 0; + } + ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformId); + if (platform == null) { + return 0; + } + if (ObjectUtils.isEmpty(catalogId)) { + catalogId = 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 000000000..b75b47710 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java @@ -0,0 +1,397 @@ +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.session.SSRCFactory; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory; +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.*; +import com.genersoft.iot.vmp.utils.DateUtil; +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 REGISTER_FAIL_AGAIN_KEY_PREFIX = "platform_register_fail_again_"; + private final static String KEEPALIVE_KEY_PREFIX = "platform_keepalive_"; + + private final static Logger logger = LoggerFactory.getLogger(PlatformServiceImpl.class); + + @Autowired + private ParentPlatformMapper platformMapper; + + @Autowired + private PlatformCatalogMapper catalogMapper; + + @Autowired + private PlatformChannelMapper platformChannelMapper; + + @Autowired + private PlatformGbStreamMapper platformGbStreamMapper; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private SSRCFactory ssrcFactory; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private SIPCommanderFroPlatform commanderForPlatform; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private ZLMServerFactory zlmServerFactory; + + @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 boolean update(ParentPlatform parentPlatform) { + logger.info("[国标级联]更新平台 {}", parentPlatform.getDeviceGBId()); + parentPlatform.setCharacterSet(parentPlatform.getCharacterSet().toUpperCase()); + ParentPlatform parentPlatformOld = platformMapper.getParentPlatById(parentPlatform.getId()); + ParentPlatformCatch parentPlatformCatchOld = redisCatchStorage.queryPlatformCatchInfo(parentPlatformOld.getServerGBId()); + parentPlatform.setUpdateTime(DateUtil.getNow()); + + // 停止心跳定时 + final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatformOld.getServerGBId(); + dynamicTask.stop(keepaliveTaskKey); + // 停止注册定时 + final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatformOld.getServerGBId(); + dynamicTask.stop(registerTaskKey); + // 注销旧的 + try { + if (parentPlatformOld.isStatus()) { + logger.info("保存平台{}时发现旧平台在线,发送注销命令", parentPlatformOld.getServerGBId()); + commanderForPlatform.unregister(parentPlatformOld, parentPlatformCatchOld.getSipTransactionInfo(), null, eventResult -> { + logger.info("[国标级联] 注销成功, 平台:{}", parentPlatformOld.getServerGBId()); + }); + } + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage()); + } + + // 更新数据库 + if (parentPlatform.getCatalogGroup() == 0) { + parentPlatform.setCatalogGroup(1); + } + if (parentPlatform.getAdministrativeDivision() == null) { + parentPlatform.setAdministrativeDivision(parentPlatform.getAdministrativeDivision()); + } + + platformMapper.updateParentPlatform(parentPlatform); + // 更新redis + redisCatchStorage.delPlatformCatchInfo(parentPlatformOld.getServerGBId()); + ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch(); + parentPlatformCatch.setParentPlatform(parentPlatform); + parentPlatformCatch.setId(parentPlatform.getServerGBId()); + redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); + // 注册 + if (parentPlatform.isEnable()) { + // 保存时启用就发送注册 + // 注册成功时由程序直接调用了online方法 + try { + logger.info("[国标级联] 平台注册 {}", parentPlatform.getDeviceGBId()); + commanderForPlatform.register(parentPlatform, eventResult -> { + logger.info("[国标级联] {},添加向上级注册失败,请确定上级平台可用时重新保存", parentPlatform.getServerGBId()); + }, null); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 国标级联: {}", e.getMessage()); + } + } + + return false; + } + + + @Override + public void online(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo) { + logger.info("[国标级联]:{}, 平台上线", parentPlatform.getServerGBId()); + final String registerFailAgainTaskKey = REGISTER_FAIL_AGAIN_KEY_PREFIX + parentPlatform.getServerGBId(); + dynamicTask.stop(registerFailAgainTaskKey); + + platformMapper.updateParentPlatformStatus(parentPlatform.getServerGBId(), true); + ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); + if (parentPlatformCatch == null) { + parentPlatformCatch = new ParentPlatformCatch(); + parentPlatformCatch.setParentPlatform(parentPlatform); + parentPlatformCatch.setId(parentPlatform.getServerGBId()); + parentPlatform.setStatus(true); + parentPlatformCatch.setParentPlatform(parentPlatform); + } + + parentPlatformCatch.getParentPlatform().setStatus(true); + parentPlatformCatch.setSipTransactionInfo(sipTransactionInfo); + redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); + + final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatform.getServerGBId(); + if (!dynamicTask.isAlive(registerTaskKey)) { + logger.info("[国标级联]:{}, 添加定时注册任务", parentPlatform.getServerGBId()); + // 添加注册任务 + dynamicTask.startCron(registerTaskKey, + // 注册失败(注册成功时由程序直接调用了online方法) + ()-> registerTask(parentPlatform, sipTransactionInfo), + parentPlatform.getExpires() * 1000); + } + + + final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatform.getServerGBId(); + if (!dynamicTask.contains(keepaliveTaskKey)) { + logger.info("[国标级联]:{}, 添加定时心跳任务", parentPlatform.getServerGBId()); + // 添加心跳任务 + 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.getName(), parentPlatform.getServerGBId()); + offline(parentPlatform, false); + + } + + }else { + logger.warn("[国标级联]发送心跳收到错误,code: {}, msg: {}", eventResult.statusCode, eventResult.msg); + } + + }, eventResult -> { + // 心跳成功 + // 清空之前的心跳超时计数 + ParentPlatformCatch platformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); + if (platformCatch != null && platformCatch.getKeepAliveReply() > 0) { + platformCatch.setKeepAliveReply(0); + redisCatchStorage.updatePlatformCatchInfo(platformCatch); + } + }); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 发送心跳: {}", e.getMessage()); + } + }, + (parentPlatform.getKeepTimeout())*1000); + } + } + + private void registerTask(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo){ + try { + // 不在同一个会话中续订则每次全新注册 + if (!userSetting.isRegisterKeepIntDialog()) { + sipTransactionInfo = null; + } + + if (sipTransactionInfo == null) { + logger.info("[国标级联] 平台:{}注册即将到期,开始重新注册", parentPlatform.getServerGBId()); + }else { + logger.info("[国标级联] 平台:{}注册即将到期,开始续订", parentPlatform.getServerGBId()); + } + + commanderForPlatform.register(parentPlatform, sipTransactionInfo, eventResult -> { + logger.info("[国标级联] 平台:{}注册失败,{}:{}", parentPlatform.getServerGBId(), + eventResult.statusCode, eventResult.msg); + offline(parentPlatform, false); + }, null); + } 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()); + + // 清除注册定时 + 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()); + // 发起定时自动重新注册 + if (!stopRegister) { + // 设置为60秒自动尝试重新注册 + final String registerFailAgainTaskKey = REGISTER_FAIL_AGAIN_KEY_PREFIX + parentPlatform.getServerGBId(); + ParentPlatform platform = platformMapper.getParentPlatById(parentPlatform.getId()); + if (platform.isEnable()) { + dynamicTask.startCron(registerFailAgainTaskKey, + ()-> registerTask(platform, null), + userSetting.getRegisterAgainAfterTime() * 1000); + } + + } + } + + private void stopAllPush(String platformId) { + List sendRtpItems = redisCatchStorage.querySendRTPServer(platformId); + if (sendRtpItems != null && sendRtpItems.size() > 0) { + for (SendRtpItem sendRtpItem : sendRtpItems) { + ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc()); + 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()); + zlmServerFactory.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 new file mode 100644 index 000000000..519013920 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java @@ -0,0 +1,1171 @@ +package com.genersoft.iot.vmp.service.impl; + +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.common.InviteInfo; +import com.genersoft.iot.vmp.common.InviteSessionStatus; +import com.genersoft.iot.vmp.common.InviteSessionType; +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.DynamicTask; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.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.SSRCFactory; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; +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.ZLMServerFactory; +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam; +import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; +import com.genersoft.iot.vmp.service.*; +import com.genersoft.iot.vmp.service.bean.ErrorCallback; +import com.genersoft.iot.vmp.service.bean.InviteErrorCode; +import com.genersoft.iot.vmp.service.bean.SSRCInfo; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.util.ObjectUtils; + +import javax.sdp.*; +import javax.sip.InvalidArgumentException; +import javax.sip.ResponseEvent; +import javax.sip.SipException; +import java.io.File; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.ParseException; +import java.util.List; +import java.util.UUID; +import java.util.Vector; + +@SuppressWarnings(value = {"rawtypes", "unchecked"}) +@Service +public class PlayServiceImpl implements IPlayService { + + private final static Logger logger = LoggerFactory.getLogger(PlayServiceImpl.class); + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommander cmder; + + @Autowired + private SIPCommanderFroPlatform sipCommanderFroPlatform; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IInviteStreamService inviteStreamService; + + @Autowired + private DeferredResultHolder resultHolder; + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private ZLMServerFactory zlmServerFactory; + + @Autowired + private AssistRESTfulUtils assistRESTfulUtils; + + @Autowired + private IMediaService mediaService; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private VideoStreamSessionManager streamSession; + + + @Autowired + private IDeviceService deviceService; + + @Autowired + private UserSetting userSetting; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private ZlmHttpHookSubscribe subscribe; + + @Autowired + private SSRCFactory ssrcFactory; + + @Autowired + private RedisTemplate redisTemplate; + + + @Override + public SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, ErrorCallback callback) { + if (mediaServerItem == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的zlm"); + } + + Device device = redisCatchStorage.getDevice(deviceId); + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); + if (inviteInfo != null ) { + if (inviteInfo.getStreamInfo() == null) { + // 点播发起了但是尚未成功, 仅注册回调等待结果即可 + inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback); + return inviteInfo.getSsrcInfo(); + }else { + StreamInfo streamInfo = inviteInfo.getStreamInfo(); + String streamId = streamInfo.getStream(); + if (streamId == null) { + callback.run(InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), "点播失败, redis缓存streamId等于null", null); + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), + "点播失败, redis缓存streamId等于null", + null); + return inviteInfo.getSsrcInfo(); + } + String mediaServerId = streamInfo.getMediaServerId(); + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); + + Boolean ready = zlmServerFactory.isStreamReady(mediaInfo, "rtp", streamId); + if (ready != null && ready) { + callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.SUCCESS.getCode(), + InviteErrorCode.SUCCESS.getMsg(), + streamInfo); + return inviteInfo.getSsrcInfo(); + }else { + // 点播发起了但是尚未成功, 仅注册回调等待结果即可 + inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback); + storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); + } + } + } + String streamId = null; + if (mediaServerItem.isRtpEnable()) { + streamId = String.format("%s_%s", device.getDeviceId(), channelId); + } + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, device.isSsrcCheck(), false, 0, false, device.getStreamModeForParam()); + if (ssrcInfo == null) { + callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), null); + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), + null); + return null; + } + // TODO 记录点播的状态 + play(mediaServerItem, ssrcInfo, device, channelId, callback); + return ssrcInfo; + } + + + @Override + public void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + ErrorCallback callback) { + + if (mediaServerItem == null || ssrcInfo == null) { + callback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(), + InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getMsg(), + null); + return; + } + logger.info("[点播开始] deviceId: {}, channelId: {},码流类型:{}, 收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", + device.getDeviceId(), channelId, device.isSwitchPrimarySubStream() ? "辅码流" : "主码流", ssrcInfo.getPort(), + device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); + //端口获取失败的ssrcInfo 没有必要发送点播指令 + if (ssrcInfo.getPort() <= 0) { + logger.info("[点播端口分配异常],deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channelId, ssrcInfo); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + + callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "点播端口分配异常", null); + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "点播端口分配异常", null); + return; + } + + // 初始化redis中的invite消息状态 + InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo, + mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAY, + InviteSessionStatus.ready); + inviteInfo.setSubStream(device.isSwitchPrimarySubStream()); + inviteStreamService.updateInviteInfo(inviteInfo); + // 超时处理 + String timeOutTaskKey = UUID.randomUUID().toString(); + dynamicTask.startDelay(timeOutTaskKey, () -> { + // 执行超时任务时查询是否已经成功,成功了则不执行超时任务,防止超时任务取消失败的情况 + InviteInfo inviteInfoForTimeOut = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); + if (inviteInfoForTimeOut == null || inviteInfoForTimeOut.getStreamInfo() == null) { + logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},码流类型:{},端口:{}, SSRC: {}", + device.getDeviceId(), channelId, device.isSwitchPrimarySubStream() ? "辅码流" : "主码流", + ssrcInfo.getPort(), ssrcInfo.getSsrc()); + + // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 +// InviteInfo inviteInfoForTimeout = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.play, device.getDeviceId(), channelId); +// if (inviteInfoForTimeout == null) { +// return; +// } +// if (InviteSessionStatus.ok == inviteInfoForTimeout.getStatus() ) { +// // TODO 发送bye +// }else { +// // TODO 发送cancel +// } + callback.run(InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null); + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null); + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); + + try { + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null); + } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { + logger.error("[点播超时], 发送BYE失败 {}", e.getMessage()); + } finally { + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); + streamSession.remove(device.getDeviceId(), 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()); + + try { + cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (mediaServerItemInuse, hookParam ) -> { + logger.info("收到订阅消息: " + hookParam); + dynamicTask.stop(timeOutTaskKey); + // hook响应 + StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInuse, hookParam, device.getDeviceId(), channelId); + if (streamInfo == null){ + callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); + return; + } + callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.SUCCESS.getCode(), + InviteErrorCode.SUCCESS.getMsg(), + streamInfo); + logger.info("[点播成功] deviceId: {}, channelId:{}, 码流类型:{}", device.getDeviceId(), + device.isSwitchPrimarySubStream() ? "辅码流" : "主码流"); + String streamUrl; + if (mediaServerItemInuse.getRtspPort() != 0) { + streamUrl = String.format("rtsp://127.0.0.1:%s/%s/%s", mediaServerItemInuse.getRtspPort(), "rtp", ssrcInfo.getStream()); + }else { + streamUrl = String.format("http://127.0.0.1:%s/%s/%s.live.mp4", 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) -> { + inviteInfo.setStatus(InviteSessionStatus.ok); + + 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).trim(); + // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 + if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { + if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { + String substring = contentString.substring(0, contentString.indexOf("y=")); + try { + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); + int port = -1; + Vector mediaDescriptions = sdp.getMediaDescriptions(true); + for (Object description : mediaDescriptions) { + MediaDescription mediaDescription = (MediaDescription) description; + Media media = mediaDescription.getMedia(); + + Vector mediaFormats = media.getMediaFormats(false); + if (mediaFormats.contains("96")) { + port = media.getMediaPort(); + break; + } + } + logger.info("[点播-TCP主动连接对方] deviceId: {}, channelId: {}, 连接对方的地址:{}:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, sdp.getConnection().getAddress(), port, device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); + JSONObject jsonObject = zlmresTfulUtils.connectRtpServer(mediaServerItem, sdp.getConnection().getAddress(), port, ssrcInfo.getStream()); + logger.info("[点播-TCP主动连接对方] 结果: {}", jsonObject); + } catch (SdpException e) { + logger.error("[点播-TCP主动连接对方] deviceId: {}, channelId: {}, 解析200OK的SDP信息失败", device.getDeviceId(), channelId, e); + dynamicTask.stop(timeOutTaskKey); + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + + callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); + } + } + return; + } + logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); + if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { + logger.info("[点播消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + // 单端口模式streamId也有变化,重新设置监听即可 + if (!mediaServerItem.isRtpEnable()) { + // 添加订阅 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); + subscribe.removeSubscribe(hookSubscribe); + String stream = String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase(); + hookSubscribe.getContent().put("stream", stream); + inviteStreamService.updateInviteInfoForStream(inviteInfo, stream); + subscribe.addSubscribe(hookSubscribe, (mediaServerItemInUse, hookParam) -> { + logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + hookParam); + dynamicTask.stop(timeOutTaskKey); + // hook响应 + StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInUse, hookParam, device.getDeviceId(), channelId); + if (streamInfo == null){ + callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); + return; + } + callback.run(InviteErrorCode.SUCCESS.getCode(), + InviteErrorCode.SUCCESS.getMsg(), streamInfo); + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.SUCCESS.getCode(), + InviteErrorCode.SUCCESS.getMsg(), + streamInfo); + }); + return; + } + + // 更新ssrc + Boolean result = mediaServerService.updateRtpServerSSRC(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse); + if (!result) { + try { + logger.warn("[点播] 更新ssrc失败,停止点播 {}/{}", device.getDeviceId(), channelId); + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null); + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { + logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage()); + } + + dynamicTask.stop(timeOutTaskKey); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + + callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), + "下级自定义了ssrc,重新设置收流信息失败", null); + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), + "下级自定义了ssrc,重新设置收流信息失败", null); + + }else { + if (ssrcInfo.getStream()!= null && !ssrcInfo.getStream().equals(inviteInfo.getStream())) { + inviteStreamService.removeInviteInfo(inviteInfo); + } + ssrcInfo.setSsrc(ssrcInResponse); + inviteInfo.setSsrcInfo(ssrcInfo); + inviteInfo.setStream(ssrcInfo.getStream()); + } + }else { + logger.info("[点播消息] 收到invite 200, 下级自定义了ssrc, 但是当前模式无需修正"); + } + } + inviteStreamService.updateInviteInfo(inviteInfo); + }, (event) -> { + dynamicTask.stop(timeOutTaskKey); + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + + callback.run(InviteErrorCode.ERROR_FOR_SIGNALLING_ERROR.getCode(), + String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg), null); + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), + String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg), null); + + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); + }); + } 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()); + + callback.run(InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), + InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), + InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); + + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); + } + } + + private StreamInfo onPublishHandlerForPlay(MediaServerItem mediaServerItem, HookParam hookParam, String deviceId, String channelId) { + StreamInfo streamInfo = null; + Device device = redisCatchStorage.getDevice(deviceId); + OnStreamChangedHookParam streamChangedHookParam = (OnStreamChangedHookParam)hookParam; + streamInfo = onPublishHandler(mediaServerItem, streamChangedHookParam, deviceId, channelId); + if (streamInfo != null) { + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); + if (deviceChannel != null) { + deviceChannel.setStreamId(streamInfo.getStream()); + storager.startPlay(deviceId, channelId, streamInfo.getStream()); + } + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); + if (inviteInfo != null) { + inviteInfo.setStatus(InviteSessionStatus.ok); + inviteInfo.setStreamInfo(streamInfo); + inviteStreamService.updateInviteInfo(inviteInfo); + } + } + return streamInfo; + + } + + private StreamInfo onPublishHandlerForPlayback(MediaServerItem mediaServerItem, HookParam param, String deviceId, String channelId, String startTime, String endTime) { + OnStreamChangedHookParam streamChangedHookParam = (OnStreamChangedHookParam) param; + StreamInfo streamInfo = onPublishHandler(mediaServerItem, streamChangedHookParam, deviceId, channelId); + if (streamInfo != null) { + streamInfo.setStartTime(startTime); + streamInfo.setEndTime(endTime); + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); + if (deviceChannel != null) { + deviceChannel.setStreamId(streamInfo.getStream()); + storager.startPlay(deviceId, channelId, streamInfo.getStream()); + } + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAYBACK, deviceId, channelId); + if (inviteInfo != null) { + inviteInfo.setStatus(InviteSessionStatus.ok); + + inviteInfo.setStreamInfo(streamInfo); + inviteStreamService.updateInviteInfo(inviteInfo); + } + + } + return streamInfo; + } + + @Override + public MediaServerItem getNewMediaServerItem(Device device) { + if (device == null) { + return null; + } + MediaServerItem mediaServerItem; + if (ObjectUtils.isEmpty(device.getMediaServerId()) || "auto".equals(device.getMediaServerId())) { + mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(null); + } else { + mediaServerItem = mediaServerService.getOne(device.getMediaServerId()); + } + if (mediaServerItem == null) { + logger.warn("点播时未找到可使用的ZLM..."); + } + return mediaServerItem; + } + + @Override + public MediaServerItem getNewMediaServerItemHasAssist(Device device) { + if (device == null) { + return null; + } + MediaServerItem mediaServerItem; + if (ObjectUtils.isEmpty(device.getMediaServerId()) || "auto".equals(device.getMediaServerId())) { + mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(true); + } else { + mediaServerItem = mediaServerService.getOne(device.getMediaServerId()); + } + if (mediaServerItem == null) { + logger.warn("[获取可用的ZLM节点]未找到可使用的ZLM..."); + } + return mediaServerItem; + } + + @Override + public void playBack(String deviceId, String channelId, String startTime, + String endTime, ErrorCallback callback) { + Device device = storager.queryVideoDevice(deviceId); + if (device == null) { + return; + } + MediaServerItem newMediaServerItem = getNewMediaServerItem(device); + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, null, device.isSsrcCheck(), true, 0, false, device.getStreamModeForParam()); + playBack(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, callback); + } + + @Override + public void playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, + String deviceId, String channelId, String startTime, + String endTime, ErrorCallback callback) { + if (mediaServerItem == null || ssrcInfo == null) { + callback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(), + InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getMsg(), + null); + return; + } + + Device device = storager.queryVideoDevice(deviceId); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备: " + deviceId + "不存在"); + } + logger.info("[录像回放] deviceId: {}, channelId: {}, 开始时间: {}, 结束时间: {}, 收流端口:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", + device.getDeviceId(), channelId, startTime, endTime, ssrcInfo.getPort(), device.getStreamMode(), + ssrcInfo.getSsrc(), device.isSsrcCheck()); + // 初始化redis中的invite消息状态 + InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo, + mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAYBACK, + InviteSessionStatus.ready); + inviteStreamService.updateInviteInfo(inviteInfo); + String playBackTimeOutTaskKey = UUID.randomUUID().toString(); + dynamicTask.startDelay(playBackTimeOutTaskKey, () -> { + logger.warn("[录像回放] 超时,deviceId:{} ,channelId:{}", deviceId, channelId); + inviteStreamService.removeInviteInfo(inviteInfo); + callback.run(InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getMsg(), null); + + try { + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[录像回放] 超时 发送BYE失败 {}", e.getMessage()); + } catch (SsrcTransactionNotFoundException e) { + // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); + streamSession.remove(deviceId, channelId, ssrcInfo.getStream()); + } + }, userSetting.getPlayTimeout()); + + SipSubscribe.Event errorEvent = event -> { + logger.info("[录像回放] 失败,{} {}", event.statusCode, event.msg); + dynamicTask.stop(playBackTimeOutTaskKey); + callback.run(InviteErrorCode.ERROR_FOR_SIGNALLING_ERROR.getCode(), + String.format("回放失败, 错误码: %s, %s", event.statusCode, event.msg), null); + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + inviteStreamService.removeInviteInfo(inviteInfo); + }; + + ZlmHttpHookSubscribe.Event hookEvent = (mediaServerItemInuse, hookParam) -> { + logger.info("收到回放订阅消息: " + hookParam); + dynamicTask.stop(playBackTimeOutTaskKey); + StreamInfo streamInfo = onPublishHandlerForPlayback(mediaServerItemInuse, hookParam, deviceId, channelId, startTime, endTime); + if (streamInfo == null) { + logger.warn("设备回放API调用失败!"); + callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); + return; + } + callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); + logger.info("[录像回放] 成功 deviceId: {}, channelId: {}, 开始时间: {}, 结束时间: {}", device.getDeviceId(), channelId, startTime, endTime); + }; + + try { + cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, + hookEvent, eventResult -> { + inviteInfo.setStatus(InviteSessionStatus.ok); + 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)) { + if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { + String substring = contentString.substring(0, contentString.indexOf("y=")); + try { + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); + int port = -1; + Vector mediaDescriptions = sdp.getMediaDescriptions(true); + for (Object description : mediaDescriptions) { + MediaDescription mediaDescription = (MediaDescription) description; + Media media = mediaDescription.getMedia(); + + Vector mediaFormats = media.getMediaFormats(false); + if (mediaFormats.contains("96")) { + port = media.getMediaPort(); + break; + } + } + logger.info("[录像回放-TCP主动连接对方] deviceId: {}, channelId: {}, 连接对方的地址:{}:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, sdp.getConnection().getAddress(), port, device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); + JSONObject jsonObject = zlmresTfulUtils.connectRtpServer(mediaServerItem, sdp.getConnection().getAddress(), port, ssrcInfo.getStream()); + logger.info("[录像回放-TCP主动连接对方] 结果: {}", jsonObject); + } catch (SdpException e) { + logger.error("[录像回放-TCP主动连接对方] deviceId: {}, channelId: {}, 解析200OK的SDP信息失败", device.getDeviceId(), channelId, e); + dynamicTask.stop(playBackTimeOutTaskKey); + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + + callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); + } + } + return; + } + logger.info("[录像回放] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); + if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { + logger.info("[录像回放] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); + + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + + // 单端口模式streamId也有变化,需要重新设置监听 + if (!mediaServerItem.isRtpEnable()) { + // 添加订阅 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); + subscribe.removeSubscribe(hookSubscribe); + String stream = String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase(); + hookSubscribe.getContent().put("stream", stream); + inviteStreamService.updateInviteInfoForStream(inviteInfo, stream); + subscribe.addSubscribe(hookSubscribe, (mediaServerItemInUse, hookParam) -> { + logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + hookParam); + dynamicTask.stop(playBackTimeOutTaskKey); + // hook响应 + hookEvent.response(mediaServerItemInUse, hookParam); + }); + } + // 更新ssrc + Boolean result = mediaServerService.updateRtpServerSSRC(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse); + if (!result) { + try { + logger.warn("[录像回放] 更新ssrc失败,停止录像回放 {}/{}", device.getDeviceId(), channelId); + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null); + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { + logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage()); + + } + + dynamicTask.stop(playBackTimeOutTaskKey); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + + callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), + "下级自定义了ssrc,重新设置收流信息失败", null); + + }else { + if (ssrcInfo.getStream()!= null && !ssrcInfo.getStream().equals(inviteInfo.getStream())) { + inviteStreamService.removeInviteInfo(inviteInfo); + } + + ssrcInfo.setSsrc(ssrcInResponse); + inviteInfo.setSsrcInfo(ssrcInfo); + inviteInfo.setStream(ssrcInfo.getStream()); + } + }else { + logger.info("[点播消息] 收到invite 200, 下级自定义了ssrc, 但是当前模式无需修正"); + } + } + inviteStreamService.updateInviteInfo(inviteInfo); + }, errorEvent); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 回放: {}", e.getMessage()); + + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(); + eventResult.type = SipSubscribe.EventResultType.cmdSendFailEvent; + eventResult.statusCode = -1; + eventResult.msg = "命令发送失败"; + errorEvent.response(eventResult); + } + } + + + + @Override + public void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback callback) { + Device device = storager.queryVideoDevice(deviceId); + if (device == null) { + return; + } + MediaServerItem newMediaServerItem = getNewMediaServerItemHasAssist(device); + if (newMediaServerItem == null) { + callback.run(InviteErrorCode.ERROR_FOR_ASSIST_NOT_READY.getCode(), + InviteErrorCode.ERROR_FOR_ASSIST_NOT_READY.getMsg(), + null); + return; + } + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, null, device.isSsrcCheck(), true, 0, false, device.getStreamModeForParam()); + download(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, downloadSpeed, callback); + } + + + @Override + public void download(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback callback) { + if (mediaServerItem == null || ssrcInfo == null) { + callback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(), + InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getMsg(), + null); + return; + } + Device device = storager.queryVideoDevice(deviceId); + if (device == null) { + callback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(), + "设备:" + deviceId + "不存在", + null); + return; + } + logger.info("[录像下载] deviceId: {}, channelId: {}, 下载速度:{}, 收流端口:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, downloadSpeed, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); + // 初始化redis中的invite消息状态 + InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo, + mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.DOWNLOAD, + InviteSessionStatus.ready); + inviteStreamService.updateInviteInfo(inviteInfo); + String downLoadTimeOutTaskKey = UUID.randomUUID().toString(); + dynamicTask.startDelay(downLoadTimeOutTaskKey, () -> { + logger.warn(String.format("录像下载请求超时,deviceId:%s ,channelId:%s", deviceId, channelId)); + inviteStreamService.removeInviteInfo(inviteInfo); + callback.run(InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode(), + InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getMsg(), null); + + // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 + try { + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[录像流]录像下载请求超时, 发送BYE失败 {}", e.getMessage()); + } catch (SsrcTransactionNotFoundException e) { + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); + streamSession.remove(deviceId, channelId, ssrcInfo.getStream()); + } + }, userSetting.getPlayTimeout()); + + SipSubscribe.Event errorEvent = event -> { + dynamicTask.stop(downLoadTimeOutTaskKey); + callback.run(InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode(), + String.format("录像下载失败, 错误码: %s, %s", event.statusCode, event.msg), null); + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + inviteStreamService.removeInviteInfo(inviteInfo); + }; + ZlmHttpHookSubscribe.Event hookEvent = (mediaServerItemInuse, hookParam) -> { + logger.info("[录像下载]收到订阅消息: " + hookParam); + dynamicTask.stop(downLoadTimeOutTaskKey); + StreamInfo streamInfo = onPublishHandlerForDownload(mediaServerItemInuse, hookParam, deviceId, channelId, startTime, endTime); + if (streamInfo == null) { + logger.warn("[录像下载] 获取流地址信息失败"); + callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); + return; + } + callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); + logger.info("[录像下载] 调用成功 deviceId: {}, channelId: {}, 开始时间: {}, 结束时间: {}", device.getDeviceId(), channelId, startTime, endTime); + }; + try { + cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, + hookEvent, errorEvent, eventResult ->{ + inviteInfo.setStatus(InviteSessionStatus.ok); + 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)) { + if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { + String substring = contentString.substring(0, contentString.indexOf("y=")); + try { + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); + int port = -1; + Vector mediaDescriptions = sdp.getMediaDescriptions(true); + for (Object description : mediaDescriptions) { + MediaDescription mediaDescription = (MediaDescription) description; + Media media = mediaDescription.getMedia(); + + Vector mediaFormats = media.getMediaFormats(false); + if (mediaFormats.contains("96")) { + port = media.getMediaPort(); + break; + } + } + logger.info("[录像下载-TCP主动连接对方] deviceId: {}, channelId: {}, 连接对方的地址:{}:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, sdp.getConnection().getAddress(), port, device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); + JSONObject jsonObject = zlmresTfulUtils.connectRtpServer(mediaServerItem, sdp.getConnection().getAddress(), port, ssrcInfo.getStream()); + logger.info("[录像下载-TCP主动连接对方] 结果: {}", jsonObject); + } catch (SdpException e) { + logger.error("[录像下载-TCP主动连接对方] deviceId: {}, channelId: {}, 解析200OK的SDP信息失败", device.getDeviceId(), channelId, e); + dynamicTask.stop(downLoadTimeOutTaskKey); + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + + callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); + } + } + return; + } + logger.info("[录像下载] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); + if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { + logger.info("[录像下载] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); + + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + + // 单端口模式streamId也有变化,需要重新设置监听 + if (!mediaServerItem.isRtpEnable()) { + // 添加订阅 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); + subscribe.removeSubscribe(hookSubscribe); + String stream = String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase(); + hookSubscribe.getContent().put("stream", stream); + inviteStreamService.updateInviteInfoForStream(inviteInfo, stream); + subscribe.addSubscribe(hookSubscribe, (mediaServerItemInUse, hookParam) -> { + logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + hookParam); + dynamicTask.stop(downLoadTimeOutTaskKey); + hookEvent.response(mediaServerItemInUse, hookParam); + }); + } + + // 更新ssrc + Boolean result = mediaServerService.updateRtpServerSSRC(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse); + if (!result) { + try { + logger.warn("[录像下载] 更新ssrc失败,停止录像回放 {}/{}", device.getDeviceId(), channelId); + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null); + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { + logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage()); + } + + dynamicTask.stop(downLoadTimeOutTaskKey); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + + callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), + "下级自定义了ssrc,重新设置收流信息失败", null); + + }else { + if (ssrcInfo.getStream()!= null && !ssrcInfo.getStream().equals(inviteInfo.getStream())) { + inviteStreamService.removeInviteInfo(inviteInfo); + } + ssrcInfo.setSsrc(ssrcInResponse); + inviteInfo.setSsrcInfo(ssrcInfo); + inviteInfo.setStream(ssrcInfo.getStream()); + } + }else { + logger.info("[录像下载] 收到invite 200, 下级自定义了ssrc, 但是当前模式无需修正"); + } + } + inviteStreamService.updateInviteInfo(inviteInfo); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 录像下载: {}", e.getMessage()); + + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(); + eventResult.type = SipSubscribe.EventResultType.cmdSendFailEvent; + eventResult.statusCode = -1; + eventResult.msg = "命令发送失败"; + errorEvent.response(eventResult); + } + } + + @Override + public StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream) { + InviteInfo inviteInfo = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, stream); + + if (inviteInfo != null && inviteInfo.getStreamInfo() != null) { + if (inviteInfo.getStreamInfo().getProgress() == 1) { + return inviteInfo.getStreamInfo(); + } + + // 获取当前已下载时长 + String mediaServerId = inviteInfo.getStreamInfo().getMediaServerId(); + MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); + if (mediaServerItem == null) { + logger.warn("查询录像信息时发现节点已离线"); + return null; + } + if (mediaServerItem.getRecordAssistPort() > 0) { + JSONObject jsonObject = assistRESTfulUtils.fileDuration(mediaServerItem, inviteInfo.getStreamInfo().getApp(), inviteInfo.getStreamInfo().getStream(), null); + if (jsonObject == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "连接Assist服务失败"); + } + if (jsonObject.getInteger("code") == 0) { + long duration = jsonObject.getLong("data"); + + if (duration == 0) { + inviteInfo.getStreamInfo().setProgress(0); + } else { + String startTime = inviteInfo.getStreamInfo().getStartTime(); + String endTime = inviteInfo.getStreamInfo().getEndTime(); + long start = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime); + long end = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime); + + BigDecimal currentCount = new BigDecimal(duration / 1000); + BigDecimal totalCount = new BigDecimal(end - start); + BigDecimal divide = currentCount.divide(totalCount, 2, RoundingMode.HALF_UP); + double process = divide.doubleValue(); + inviteInfo.getStreamInfo().setProgress(process); + } + inviteStreamService.updateInviteInfo(inviteInfo); + } + } + return inviteInfo.getStreamInfo(); + } + return null; + } + + private StreamInfo onPublishHandlerForDownload(MediaServerItem mediaServerItemInuse, HookParam hookParam, String deviceId, String channelId, String startTime, String endTime) { + OnStreamChangedHookParam streamChangedHookParam = (OnStreamChangedHookParam) hookParam; + StreamInfo streamInfo = onPublishHandler(mediaServerItemInuse, streamChangedHookParam, deviceId, channelId); + if (streamInfo != null) { + streamInfo.setProgress(0); + streamInfo.setStartTime(startTime); + streamInfo.setEndTime(endTime); + InviteInfo inviteInfo = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, streamInfo.getStream()); + if (inviteInfo != null) { + logger.info("[录像下载] 更新invite消息中的stream信息"); + inviteInfo.setStatus(InviteSessionStatus.ok); + inviteInfo.setStreamInfo(streamInfo); + inviteStreamService.updateInviteInfo(inviteInfo); + } + } + return streamInfo; + } + + + public StreamInfo onPublishHandler(MediaServerItem mediaServerItem, OnStreamChangedHookParam hookParam, String deviceId, String channelId) { + StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(mediaServerItem, "rtp", hookParam.getStream(), hookParam.getTracks(), 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 { + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId); + if (null == inviteInfo || inviteInfo.getStreamInfo() == null) { + logger.warn("streamId不存在!"); + throw new ServiceException("streamId不存在"); + } + inviteInfo.getStreamInfo().setPause(true); + inviteStreamService.updateInviteInfo(inviteInfo); + MediaServerItem mediaServerItem = mediaServerService.getOne(inviteInfo.getStreamInfo().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(inviteInfo.getDeviceId()); + cmder.playPauseCmd(device, inviteInfo.getStreamInfo()); + } + + @Override + public void resumeRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException { + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId); + if (null == inviteInfo || inviteInfo.getStreamInfo() == null) { + logger.warn("streamId不存在!"); + throw new ServiceException("streamId不存在"); + } + inviteInfo.getStreamInfo().setPause(false); + inviteStreamService.updateInviteInfo(inviteInfo); + MediaServerItem mediaServerItem = mediaServerService.getOne(inviteInfo.getStreamInfo().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(inviteInfo.getDeviceId()); + cmder.playResumeCmd(device, inviteInfo.getStreamInfo()); + } + + @Override + public void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback) { + Device device = deviceService.getDevice(deviceId); + if (device == null) { + errorCallback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(), InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getMsg(), null); + return; + } + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); + if (inviteInfo != null) { + if (inviteInfo.getStreamInfo() != null) { + // 已存在线直接截图 + MediaServerItem mediaServerItemInuse = mediaServerService.getOne(inviteInfo.getStreamInfo().getMediaServerId()); + String streamUrl; + if (mediaServerItemInuse.getRtspPort() != 0) { + streamUrl = String.format("rtsp://127.0.0.1:%s/%s/%s", mediaServerItemInuse.getRtspPort(), "rtp", inviteInfo.getStreamInfo().getStream()); + }else { + streamUrl = String.format("http://127.0.0.1:%s/%s/%s.live.mp4", mediaServerItemInuse.getHttpPort(), "rtp", inviteInfo.getStreamInfo().getStream()); + } + String path = "snap"; + // 请求截图 + logger.info("[请求截图]: " + fileName); + zlmresTfulUtils.getSnap(mediaServerItemInuse, streamUrl, 15, 1, path, fileName); + File snapFile = new File(path + File.separator + fileName); + if (snapFile.exists()) { + errorCallback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), snapFile.getAbsoluteFile()); + }else { + errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null); + } + return; + } + } + + MediaServerItem newMediaServerItem = getNewMediaServerItem(device); + play(newMediaServerItem, deviceId, channelId, (code, msg, data)->{ + if (code == InviteErrorCode.SUCCESS.getCode()) { + InviteInfo inviteInfoForPlay = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); + if (inviteInfoForPlay != null && inviteInfoForPlay.getStreamInfo() != null) { + getSnap(deviceId, channelId, fileName, errorCallback); + }else { + errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null); + } + }else { + errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null); + } + }); + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/RoleServerImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/RoleServerImpl.java new file mode 100644 index 000000000..d31bbcefb --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/RoleServerImpl.java @@ -0,0 +1,41 @@ +package com.genersoft.iot.vmp.service.impl; + +import com.genersoft.iot.vmp.service.IRoleService; +import com.genersoft.iot.vmp.storager.dao.RoleMapper; +import com.genersoft.iot.vmp.storager.dao.dto.Role; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class RoleServerImpl implements IRoleService { + + @Autowired + private RoleMapper roleMapper; + + @Override + public Role getRoleById(int id) { + return roleMapper.selectById(id); + } + + @Override + public int add(Role role) { + return roleMapper.add(role); + } + + @Override + public int delete(int id) { + return roleMapper.delete(id); + } + + @Override + public List getAll() { + return roleMapper.selectAll(); + } + + @Override + public int update(Role role) { + return roleMapper.update(role); + } +} 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 new file mode 100644 index 000000000..71711389c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java @@ -0,0 +1,516 @@ +package com.genersoft.iot.vmp.service.impl; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.common.GeneralCallback; +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.gb28181.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.ZlmHttpHookSubscribe; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; +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.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.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; +import com.github.pagehelper.PageInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.stereotype.Service; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.util.ObjectUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 视频代理业务 + */ +@Service +public class StreamProxyServiceImpl implements IStreamProxyService { + + private final static Logger logger = LoggerFactory.getLogger(StreamProxyServiceImpl.class); + + @Autowired + private IVideoManagerStorage videoManagerStorager; + + @Autowired + private IMediaService mediaService; + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private StreamProxyMapper streamProxyMapper; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private UserSetting userSetting; + + @Autowired + private GbStreamMapper gbStreamMapper; + + @Autowired + private PlatformGbStreamMapper platformGbStreamMapper; + + @Autowired + private EventPublisher eventPublisher; + + @Autowired + private ParentPlatformMapper parentPlatformMapper; + + @Autowired + private IGbStreamService gbStreamService; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private ZlmHttpHookSubscribe hookSubscribe; + + @Autowired + DataSourceTransactionManager dataSourceTransactionManager; + + @Autowired + TransactionDefinition transactionDefinition; + + + @Override + public void save(StreamProxyItem param, GeneralCallback callback) { + MediaServerItem mediaInfo; + 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..."); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "保存代理未找到在线的ZLM"); + } + String dstUrl; + if ("ffmpeg".equalsIgnoreCase(param.getType())) { + JSONObject jsonObject = zlmresTfulUtils.getMediaServerConfig(mediaInfo); + if (jsonObject.getInteger("code") != 0) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "获取流媒体配置失败"); + } + JSONArray dataArray = jsonObject.getJSONArray("data"); + JSONObject mediaServerConfig = dataArray.getJSONObject(0); + String ffmpegCmd = mediaServerConfig.getString(param.getFfmpegCmdKey()); + String schema = getSchemaFromFFmpegCmd(ffmpegCmd); + if (schema == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "ffmpeg拉流代理无法从ffmpeg cmd中获取到输出格式"); + } + int port; + String schemaForUri; + if (schema.equalsIgnoreCase("rtsp")) { + port = mediaInfo.getRtspPort(); + schemaForUri = schema; + }else if (schema.equalsIgnoreCase("flv")) { + port = mediaInfo.getRtmpPort(); + schemaForUri = schema; + }else { + port = mediaInfo.getRtmpPort(); + schemaForUri = schema; + } + + dstUrl = String.format("%s://%s:%s/%s/%s", schemaForUri, "127.0.0.1", port, param.getApp(), + param.getStream()); + }else { + dstUrl = String.format("rtmp://%s:%s/%s/%s", "127.0.0.1", mediaInfo.getRtmpPort(), param.getApp(), + param.getStream()); + } + param.setDstUrl(dstUrl); + logger.info("[拉流代理] 输出地址为:{}", dstUrl); + param.setMediaServerId(mediaInfo.getId()); + boolean saveResult; + // 更新 + if (videoManagerStorager.queryStreamProxy(param.getApp(), param.getStream()) != null) { + saveResult = updateStreamProxy(param); + }else { // 新增 + saveResult = addStreamProxy(param); + } + if (!saveResult) { + callback.run(ErrorCode.ERROR100.getCode(), "保存失败", null); + return; + } + + HookSubscribeForStreamChange hookSubscribeForStreamChange = HookSubscribeFactory.on_stream_changed(param.getApp(), param.getStream(), true, "rtsp", mediaInfo.getId()); + hookSubscribe.addSubscribe(hookSubscribeForStreamChange, (mediaServerItem, response) -> { + StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream( + mediaInfo, param.getApp(), param.getStream(), null, null); + callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo); + }); + + if (param.isEnable()) { + JSONObject jsonObject = addStreamProxyToZlm(param); + if (jsonObject != null && jsonObject.getInteger("code") == 0) { + hookSubscribe.removeSubscribe(hookSubscribeForStreamChange); + StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream( + mediaInfo, param.getApp(), param.getStream(), null, null); + callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo); + }else { + param.setEnable(false); + // 直接移除 + if (param.isEnableRemoveNoneReader()) { + del(param.getApp(), param.getStream()); + }else { + updateStreamProxy(param); + } + if (jsonObject == null){ + callback.run(ErrorCode.ERROR100.getCode(), "记录已保存,启用失败", null); + return; + }else { + callback.run(ErrorCode.ERROR100.getCode(), jsonObject.getString("msg"), null); + return; + } + } + } + } + + private String getSchemaFromFFmpegCmd(String ffmpegCmd) { + ffmpegCmd = ffmpegCmd.replaceAll(" + ", " "); + String[] paramArray = ffmpegCmd.split(" "); + if (paramArray.length == 0) { + return null; + } + for (int i = 0; i < paramArray.length; i++) { + if (paramArray[i].equalsIgnoreCase("-f")) { + if (i + 1 < paramArray.length - 1) { + return paramArray[i+1]; + }else { + return null; + } + + } + } + return null; + } + + /** + * 新增代理流 + * @param streamProxyItem + * @return + */ + private boolean addStreamProxy(StreamProxyItem streamProxyItem) { + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + boolean result = false; + streamProxyItem.setStreamType("proxy"); + streamProxyItem.setStatus(true); + String now = DateUtil.getNow(); + streamProxyItem.setCreateTime(now); + try { + if (streamProxyMapper.add(streamProxyItem) > 0) { + if (!ObjectUtils.isEmpty(streamProxyItem.getGbId())) { + if (gbStreamMapper.add(streamProxyItem) < 0) { + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + return false; + } + } + }else { + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + return false; + } + result = true; + dataSourceTransactionManager.commit(transactionStatus); //手动提交 + }catch (Exception e) { + logger.error("向数据库添加流代理失败:", e); + dataSourceTransactionManager.rollback(transactionStatus); + } + + + return result; + } + + /** + * 更新代理流 + * @param streamProxyItem + * @return + */ + @Override + public boolean updateStreamProxy(StreamProxyItem streamProxyItem) { + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + boolean result = false; + streamProxyItem.setStreamType("proxy"); + try { + if (streamProxyMapper.update(streamProxyItem) > 0) { + if (!ObjectUtils.isEmpty(streamProxyItem.getGbId())) { + if (gbStreamMapper.updateByAppAndStream(streamProxyItem) == 0) { + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + return false; + } + } + } else { + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + return false; + } + + dataSourceTransactionManager.commit(transactionStatus); //手动提交 + result = true; + }catch (Exception e) { + logger.error("未处理的异常 ", e); + dataSourceTransactionManager.rollback(transactionStatus); + } + return result; + } + + @Override + public JSONObject addStreamProxyToZlm(StreamProxyItem param) { + JSONObject result = null; + MediaServerItem mediaServerItem = null; + if (param.getMediaServerId() == null) { + logger.warn("添加代理时MediaServerId 为null"); + return null; + }else { + mediaServerItem = mediaServerService.getOne(param.getMediaServerId()); + } + if (mediaServerItem == null) { + return null; + } + if ("default".equals(param.getType())){ + result = zlmresTfulUtils.addStreamProxy(mediaServerItem, param.getApp(), param.getStream(), param.getUrl(), + param.isEnableAudio(), param.isEnableMp4(), param.getRtpType()); + }else if ("ffmpeg".equals(param.getType())) { + result = zlmresTfulUtils.addFFmpegSource(mediaServerItem, param.getSrcUrl(), param.getDstUrl(), + param.getTimeoutMs() + "", param.isEnableAudio(), param.isEnableMp4(), + param.getFfmpegCmdKey()); + } + return result; + } + + @Override + public JSONObject removeStreamProxyFromZlm(StreamProxyItem param) { + if (param ==null) { + return null; + } + MediaServerItem mediaServerItem = mediaServerService.getOne(param.getMediaServerId()); + JSONObject result = zlmresTfulUtils.closeStreams(mediaServerItem, param.getApp(), param.getStream()); + return result; + } + + @Override + public PageInfo getAll(Integer page, Integer count) { + return videoManagerStorager.queryStreamProxyList(page, count); + } + + @Override + public void del(String app, String stream) { + StreamProxyItem streamProxyItem = videoManagerStorager.queryStreamProxy(app, stream); + if (streamProxyItem != null) { + gbStreamService.sendCatalogMsg(streamProxyItem, CatalogEvent.DEL); + videoManagerStorager.deleteStreamProxy(app, stream); + JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyItem); + if (jsonObject != null && jsonObject.getInteger("code") == 0) { + // 如果关联了国标那么移除关联 + gbStreamMapper.del(app, stream); + platformGbStreamMapper.delByAppAndStream(app, stream); + // TODO 如果关联的推流, 那么状态设置为离线 + } + redisCatchStorage.removeStream(streamProxyItem.getMediaServerId(), "PULL", app, stream); + } + + + } + + @Override + public boolean start(String app, String stream) { + boolean result = false; + StreamProxyItem streamProxy = videoManagerStorager.queryStreamProxy(app, stream); + if (streamProxy != null && !streamProxy.isEnable() ) { + JSONObject jsonObject = addStreamProxyToZlm(streamProxy); + if (jsonObject == null) { + return false; + } + if (jsonObject.getInteger("code") == 0) { + result = true; + streamProxy.setEnable(true); + updateStreamProxy(streamProxy); + }else { + logger.info("启用代理失败: {}/{}->{}({})", app, stream, jsonObject.getString("msg"), + streamProxy.getSrcUrl() == null? streamProxy.getUrl():streamProxy.getSrcUrl()); + } + } + return result; + } + + @Override + public boolean stop(String app, String stream) { + boolean result = false; + StreamProxyItem streamProxyDto = videoManagerStorager.queryStreamProxy(app, stream); + if (streamProxyDto != null && streamProxyDto.isEnable()) { + JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyDto); + if (jsonObject != null && jsonObject.getInteger("code") == 0) { + streamProxyDto.setEnable(false); + result = updateStreamProxy(streamProxyDto); + } + } + return result; + } + + @Override + public JSONObject getFFmpegCMDs(MediaServerItem mediaServerItem) { + JSONObject result = new JSONObject(); + JSONObject mediaServerConfigResuly = zlmresTfulUtils.getMediaServerConfig(mediaServerItem); + if (mediaServerConfigResuly != null && mediaServerConfigResuly.getInteger("code") == 0 + && mediaServerConfigResuly.getJSONArray("data").size() > 0){ + JSONObject mediaServerConfig = mediaServerConfigResuly.getJSONArray("data").getJSONObject(0); + + for (String key : mediaServerConfig.keySet()) { + if (key.startsWith("ffmpeg.cmd")){ + result.put(key, mediaServerConfig.getString(key)); + } + } + } + return result; + } + + + @Override + public StreamProxyItem getStreamProxyByAppAndStream(String app, String streamId) { + return videoManagerStorager.getStreamProxyByAppAndStream(app, streamId); + } + + @Override + public void zlmServerOnline(String mediaServerId) { + // 移除开启了无人观看自动移除的流 + List streamProxyItemList = streamProxyMapper.selectAutoRemoveItemByMediaServerId(mediaServerId); + if (streamProxyItemList.size() > 0) { + gbStreamMapper.batchDel(streamProxyItemList); + } + streamProxyMapper.deleteAutoRemoveItemByMediaServerId(mediaServerId); + + // 移除拉流代理生成的流信息 +// syncPullStream(mediaServerId); + + // 恢复流代理, 只查找这个这个流媒体 + List streamProxyListForEnable = storager.getStreamProxyListForEnableInMediaServer( + mediaServerId, true); + for (StreamProxyItem streamProxyDto : streamProxyListForEnable) { + logger.info("恢复流代理," + streamProxyDto.getApp() + "/" + streamProxyDto.getStream()); + 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 + public void zlmServerOffline(String mediaServerId) { + // 移除开启了无人观看自动移除的流 + List streamProxyItemList = streamProxyMapper.selectAutoRemoveItemByMediaServerId(mediaServerId); + if (streamProxyItemList.size() > 0) { + gbStreamMapper.batchDel(streamProxyItemList); + } + streamProxyMapper.deleteAutoRemoveItemByMediaServerId(mediaServerId); + // 其他的流设置离线 + streamProxyMapper.updateStatusByMediaServerId(mediaServerId, false); + String type = "PULL"; + + // 发送redis消息 + List onStreamChangedHookParams = redisCatchStorage.getStreams(mediaServerId, type); + if (onStreamChangedHookParams.size() > 0) { + for (OnStreamChangedHookParam onStreamChangedHookParam : onStreamChangedHookParams) { + JSONObject jsonObject = new JSONObject(); + 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, onStreamChangedHookParam.getApp(), onStreamChangedHookParam.getStream()); + } + } + } + + @Override + 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 ResourceBaseInfo getOverview() { + + int total = streamProxyMapper.getAllCount(); + int online = streamProxyMapper.getOnline(); + + return new ResourceBaseInfo(total, online); + } +} 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 new file mode 100644 index 000000000..dcaab9e35 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java @@ -0,0 +1,541 @@ +package com.genersoft.iot.vmp.service.impl; + +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.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.*; +import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.stereotype.Service; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.util.ObjectUtils; + +import java.util.*; +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; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + 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; + } + + Map result = new HashMap<>(); + + List onStreamChangedHookParams = JSON.parseObject(jsonData, new TypeReference>() {}); + for (OnStreamChangedHookParam item : onStreamChangedHookParams) { + + // 不保存国标推理以及拉流代理的流 + if (item.getOriginType() == OriginType.RTSP_PUSH.ordinal() + || item.getOriginType() == OriginType.RTMP_PUSH.ordinal() + || item.getOriginType() == OriginType.RTC_PUSH.ordinal() ) { + String key = item.getApp() + "_" + item.getStream(); + StreamPushItem streamPushItem = result.get(key); + if (streamPushItem == null) { + streamPushItem = transform(item); + result.put(key, streamPushItem); + } + } + } + + return new ArrayList<>(result.values()); + } + @Override + public StreamPushItem transform(OnStreamChangedHookParam item) { + StreamPushItem streamPushItem = new StreamPushItem(); + streamPushItem.setApp(item.getApp()); + streamPushItem.setMediaServerId(item.getMediaServerId()); + streamPushItem.setStream(item.getStream()); + streamPushItem.setAliveSecond(item.getAliveSecond()); + streamPushItem.setOriginSock(item.getOriginSock()); + streamPushItem.setTotalReaderCount(item.getTotalReaderCount()); + streamPushItem.setOriginType(item.getOriginType()); + streamPushItem.setOriginTypeStr(item.getOriginTypeStr()); + streamPushItem.setOriginUrl(item.getOriginUrl()); + streamPushItem.setCreateTime(DateUtil.getNow()); + streamPushItem.setAliveSecond(item.getAliveSecond()); + streamPushItem.setStatus(true); + streamPushItem.setStreamType("push"); + streamPushItem.setVhost(item.getVhost()); + streamPushItem.setServerId(item.getSeverId()); + return streamPushItem; + } + + @Override + public PageInfo getPushList(Integer page, Integer count, String query, Boolean pushing, String mediaServerId) { + PageHelper.startPage(page, count); + List all = streamPushMapper.selectAllForList(query, pushing, mediaServerId); + return new PageInfo<>(all); + } + + @Override + public List getPushList(String mediaServerId) { + return streamPushMapper.selectAllByMediaServerIdWithOutGbID(mediaServerId); + } + + @Override + public boolean saveToGB(GbStream stream) { + stream.setStreamType("push"); + stream.setStatus(true); + stream.setCreateTime(DateUtil.getNow()); + stream.setStreamType("push"); + stream.setMediaServerId(mediaConfig.getId()); + int add = gbStreamMapper.add(stream); + return add > 0; + } + + @Override + public boolean removeFromGB(GbStream stream) { + // 判断是否需要发送事件 + gbStreamService.sendCatalogMsg(stream, CatalogEvent.DEL); + platformGbStreamMapper.delByAppAndStream(stream.getApp(), stream.getStream()); + int del = gbStreamMapper.del(stream.getApp(), stream.getStream()); + MediaServerItem mediaInfo = mediaServerService.getOne(stream.getMediaServerId()); + JSONObject mediaList = zlmresTfulUtils.getMediaList(mediaInfo, 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; + } + + + @Override + public StreamPushItem getPush(String app, String streamId) { + return streamPushMapper.selectOne(app, streamId); + } + + @Override + public boolean stop(String app, String streamId) { + logger.info("[推流 ] 停止流: {}/{}", app, streamId); + StreamPushItem streamPushItem = streamPushMapper.selectOne(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); + } + return true; + } + + @Override + public void zlmServerOnline(String mediaServerId) { + // 同步zlm推流信息 + MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); + if (mediaServerItem == null) { + return; + } + // 数据库记录 + List pushList = getPushList(mediaServerId); + Map pushItemMap = new HashMap<>(); + // redis记录 + List onStreamChangedHookParams = redisCatchStorage.getStreams(mediaServerId, "PUSH"); + Map streamInfoPushItemMap = new HashMap<>(); + if (pushList.size() > 0) { + for (StreamPushItem streamPushItem : pushList) { + if (ObjectUtils.isEmpty(streamPushItem.getGbId())) { + pushItemMap.put(streamPushItem.getApp() + streamPushItem.getStream(), streamPushItem); + } + } + } + if (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; + } + String dataStr = mediaList.getString("data"); + + Integer code = mediaList.getInteger("code"); + List streamPushItems = null; + if (code == 0 ) { + if (dataStr != null) { + streamPushItems = handleJSON(dataStr, mediaServerItem); + } + } + + if (streamPushItems != null) { + for (StreamPushItem streamPushItem : streamPushItems) { + pushItemMap.remove(streamPushItem.getApp() + streamPushItem.getStream()); + streamInfoPushItemMap.remove(streamPushItem.getApp() + streamPushItem.getStream()); + streamAuthorityInfoInfoMap.remove(streamPushItem.getApp() + streamPushItem.getStream()); + } + } + List offlinePushItems = new ArrayList<>(pushItemMap.values()); + if (offlinePushItems.size() > 0) { + String type = "PUSH"; + int runLimit = 300; + if (offlinePushItems.size() > runLimit) { + for (int i = 0; i < offlinePushItems.size(); i += runLimit) { + int toIndex = i + runLimit; + if (i + runLimit > offlinePushItems.size()) { + toIndex = offlinePushItems.size(); + } + List streamPushItemsSub = offlinePushItems.subList(i, toIndex); + streamPushMapper.delAll(streamPushItemsSub); + } + }else { + streamPushMapper.delAll(offlinePushItems); + } + + } + Collection offlineOnStreamChangedHookParamList = streamInfoPushItemMap.values(); + if (offlineOnStreamChangedHookParamList.size() > 0) { + String type = "PUSH"; + for (OnStreamChangedHookParam offlineOnStreamChangedHookParam : offlineOnStreamChangedHookParamList) { + JSONObject jsonObject = new JSONObject(); + 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", offlineOnStreamChangedHookParam.getApp(), offlineOnStreamChangedHookParam.getStream()); + } + } + + Collection streamAuthorityInfos = streamAuthorityInfoInfoMap.values(); + if (streamAuthorityInfos.size() > 0) { + for (StreamAuthorityInfo streamAuthorityInfo : streamAuthorityInfos) { + // 移除redis内流的信息 + redisCatchStorage.removeStreamAuthorityInfo(streamAuthorityInfo.getApp(), streamAuthorityInfo.getStream()); + } + } + })); + } + + @Override + public void zlmServerOffline(String mediaServerId) { + List streamPushItems = streamPushMapper.selectAllByMediaServerIdWithOutGbID(mediaServerId); + // 移除没有GBId的推流 + streamPushMapper.deleteWithoutGBId(mediaServerId); + gbStreamMapper.deleteWithoutGBId("push", mediaServerId); + // 其他的流设置未启用 + streamPushMapper.updateStatusByMediaServerId(mediaServerId, false); + streamProxyMapper.updateStatusByMediaServerId(mediaServerId, false); + // 发送流停止消息 + String type = "PUSH"; + // 发送redis消息 + List streamInfoList = redisCatchStorage.getStreams(mediaServerId, type); + if (streamInfoList.size() > 0) { + for (OnStreamChangedHookParam onStreamChangedHookParam : streamInfoList) { + // 移除redis内流的信息 + redisCatchStorage.removeStream(mediaServerId, type, onStreamChangedHookParam.getApp(), onStreamChangedHookParam.getStream()); + JSONObject jsonObject = new JSONObject(); + 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); + } + } + } + + @Override + public void clean() { + + } + + @Override + public boolean saveToRandomGB() { + List streamPushItems = streamPushMapper.selectAll(); + long gbId = 100001; + for (StreamPushItem streamPushItem : streamPushItems) { + streamPushItem.setStreamType("push"); + streamPushItem.setStatus(true); + streamPushItem.setGbId("34020000004111" + gbId); + streamPushItem.setCreateTime(DateUtil.getNow()); + gbId ++; + } + int limitCount = 30; + + if (streamPushItems.size() > limitCount) { + for (int i = 0; i < streamPushItems.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > streamPushItems.size()) { + toIndex = streamPushItems.size(); + } + gbStreamMapper.batchAdd(streamPushItems.subList(i, toIndex)); + } + }else { + gbStreamMapper.batchAdd(streamPushItems); + } + return true; + } + + @Override + public void batchAdd(List streamPushItems) { + streamPushMapper.addAll(streamPushItems); + gbStreamMapper.batchAdd(streamPushItems); + } + + + @Override + public void batchAddForUpload(List streamPushItems, Map> streamPushItemsForAll ) { + // 存储数据到stream_push表 + streamPushMapper.addAll(streamPushItems); + List streamPushItemForGbStream = streamPushItems.stream() + .filter(streamPushItem-> streamPushItem.getGbId() != null) + .collect(Collectors.toList()); + // 存储数据到gb_stream表, id会返回到streamPushItemForGbStream里 + if (streamPushItemForGbStream.size() > 0) { + gbStreamMapper.batchAdd(streamPushItemForGbStream); + } + // 去除没有ID也就是没有存储到数据库的数据 + List streamPushItemsForPlatform = streamPushItemForGbStream.stream() + .filter(streamPushItem-> streamPushItem.getGbStreamId() != null) + .collect(Collectors.toList()); + + if (streamPushItemsForPlatform.size() > 0) { + // 获取所有平台,平台和目录信息一般不会特别大量。 + List parentPlatformList = parentPlatformMapper.getParentPlatformList(); + Map> platformInfoMap = new HashMap<>(); + if (parentPlatformList.size() == 0) { + return; + } + for (ParentPlatform platform : parentPlatformList) { + Map catalogMap = new HashMap<>(); + + // 创建根节点 + PlatformCatalog platformCatalog = new PlatformCatalog(); + platformCatalog.setId(platform.getServerGBId()); + catalogMap.put(platform.getServerGBId(), platformCatalog); + + // 查询所有节点信息 + List platformCatalogs = platformCatalogMapper.selectByPlatForm(platform.getServerGBId()); + if (platformCatalogs.size() > 0) { + for (PlatformCatalog catalog : platformCatalogs) { + catalogMap.put(catalog.getId(), catalog); + } + } + platformInfoMap.put(platform.getServerGBId(), catalogMap); + } + List streamPushItemListFroPlatform = new ArrayList<>(); + Map> platformForEvent = new HashMap<>(); + // 遍历存储结果,查找app+Stream->platformId+catalogId的对应关系,然后执行批量写入 + for (StreamPushItem streamPushItem : streamPushItemsForPlatform) { + List platFormInfoList = streamPushItemsForAll.get(streamPushItem.getApp() + streamPushItem.getStream()); + if (platFormInfoList != null && platFormInfoList.size() > 0) { + for (String[] platFormInfoArray : platFormInfoList) { + StreamPushItem streamPushItemForPlatform = new StreamPushItem(); + streamPushItemForPlatform.setGbStreamId(streamPushItem.getGbStreamId()); + if (platFormInfoArray.length > 0) { + // 数组 platFormInfoArray 0 为平台ID。 1为目录ID + // 不存在这个平台,则忽略导入此关联关系 + if (platformInfoMap.get(platFormInfoArray[0]) == null + || platformInfoMap.get(platFormInfoArray[0]).get(platFormInfoArray[1]) == null) { + logger.info("导入数据时不存在平台或目录{}/{},已导入未分配", platFormInfoArray[0], platFormInfoArray[1] ); + continue; + } + streamPushItemForPlatform.setPlatformId(platFormInfoArray[0]); + List gbStreamList = platformForEvent.get(platFormInfoArray[0]); + if (gbStreamList == null) { + gbStreamList = new ArrayList<>(); + platformForEvent.put(platFormInfoArray[0], gbStreamList); + } + // 为发送通知整理数据 + streamPushItemForPlatform.setName(streamPushItem.getName()); + streamPushItemForPlatform.setApp(streamPushItem.getApp()); + streamPushItemForPlatform.setStream(streamPushItem.getStream()); + streamPushItemForPlatform.setGbId(streamPushItem.getGbId()); + gbStreamList.add(streamPushItemForPlatform); + } + if (platFormInfoArray.length > 1) { + streamPushItemForPlatform.setCatalogId(platFormInfoArray[1]); + } + streamPushItemListFroPlatform.add(streamPushItemForPlatform); + } + + } + } + if (streamPushItemListFroPlatform.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 ResourceBaseInfo getOverview() { + int total = streamPushMapper.getAllCount(); + int online = streamPushMapper.getAllOnline(userSetting.isUsePushingAsStatus()); + + return new ResourceBaseInfo(total, online); + } +} 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 000000000..a12715939 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushUploadFileHandler.java @@ -0,0 +1,192 @@ +package com.genersoft.iot.vmp.service.impl; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; +import com.genersoft.iot.vmp.service.IStreamPushService; +import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.StreamPushExcelDto; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import org.springframework.util.ObjectUtils; + +import java.util.*; + +public class StreamPushUploadFileHandler extends AnalysisEventListener { + + /** + * 错误数据的回调,用于将错误数据发送给页面 + */ + private ErrorDataHandler errorDataHandler; + + /** + * 推流的业务类用于存储数据 + */ + private IStreamPushService pushService; + + /** + * 默认流媒体节点ID + */ + private String defaultMediaServerId; + + /** + * 用于存储不加过滤的所有数据 + */ + private final List streamPushItems = new ArrayList<>(); + + /** + * 用于存储更具APP+Stream过滤后的数据,可以直接存入stream_push表与gb_stream表 + */ + private final Map streamPushItemForSave = new HashMap<>(); + + /** + * 用于存储按照APP+Stream为KEY, 平台ID+目录Id 为value的数据,用于存储到gb_stream表后获取app+Stream对应的平台与目录信息,然后存入关联表 + */ + private final Map> streamPushItemsForPlatform = new HashMap<>(); + + /** + * 用于判断文件是否存在重复的app+Stream+平台ID + */ + private final Set streamPushStreamSet = new HashSet<>(); + + /** + * 用于存储APP+Stream->国标ID 的数据结构, 数据一一对应,全局判断APP+Stream->国标ID是否存在不对应 + */ + private final BiMap gBMap = HashBiMap.create(); + + /** + * 用于存储APP+Stream-> 在数据库中的数据 + */ + private final BiMap pushMapInDb = HashBiMap.create(); + + /** + * 记录错误的APP+Stream + */ + private final List errorStreamList = new ArrayList<>(); + + + /** + * 记录错误的国标ID + */ + private final List errorInfoList = new ArrayList<>(); + + /** + * 读取数量计数器 + */ + private int loadedSize = 0; + + public StreamPushUploadFileHandler(IStreamPushService pushService, String defaultMediaServerId, ErrorDataHandler errorDataHandler) { + this.pushService = pushService; + this.defaultMediaServerId = defaultMediaServerId; + this.errorDataHandler = errorDataHandler; + // 获取数据库已有的数据,已经存在的则忽略 + List allAppAndStreams = pushService.getAllAppAndStream(); + if (allAppAndStreams.size() > 0) { + for (String allAppAndStream : allAppAndStreams) { + pushMapInDb.put(allAppAndStream, allAppAndStream); + } + } + } + + public interface ErrorDataHandler{ + void handle(List streams, List gbId); + } + + @Override + public void invoke(StreamPushExcelDto streamPushExcelDto, AnalysisContext analysisContext) { + if (ObjectUtils.isEmpty(streamPushExcelDto.getApp()) + || ObjectUtils.isEmpty(streamPushExcelDto.getStream()) + || ObjectUtils.isEmpty(streamPushExcelDto.getGbId())) { + return; + } + Integer rowIndex = analysisContext.readRowHolder().getRowIndex(); + + if (gBMap.get(streamPushExcelDto.getApp() + streamPushExcelDto.getStream()) == null) { + try { + gBMap.put(streamPushExcelDto.getApp() + streamPushExcelDto.getStream(), streamPushExcelDto.getGbId()); + }catch (IllegalArgumentException e) { + errorInfoList.add("行:" + rowIndex + ", " + streamPushExcelDto.getGbId() + " 国标ID重复使用"); + return; + } + }else { + if (!gBMap.get(streamPushExcelDto.getApp() + streamPushExcelDto.getStream()).equals(streamPushExcelDto.getGbId())) { + errorInfoList.add("行:" + rowIndex + ", " + streamPushExcelDto.getGbId() + " 同样的应用名和流ID使用了不同的国标ID"); + return; + } + } + + if (streamPushStreamSet.contains(streamPushExcelDto.getApp() + streamPushExcelDto.getStream() + streamPushExcelDto.getPlatformId())) { + errorStreamList.add("行:" + rowIndex + ", " + streamPushExcelDto.getApp() + "/" + streamPushExcelDto.getStream()+ " 平台信息重复"); + return; + }else { + if (pushMapInDb.get(streamPushExcelDto.getApp()+streamPushExcelDto.getStream()) != null) { + errorStreamList.add("行:" + rowIndex + ", " + streamPushExcelDto.getApp() + "/" + streamPushExcelDto.getStream()+ " 数据已存在"); + return; + } + streamPushStreamSet.add(streamPushExcelDto.getApp()+streamPushExcelDto.getStream() + streamPushExcelDto.getPlatformId()); + } + + StreamPushItem streamPushItem = new StreamPushItem(); + streamPushItem.setApp(streamPushExcelDto.getApp()); + streamPushItem.setStream(streamPushExcelDto.getStream()); + streamPushItem.setGbId(streamPushExcelDto.getGbId()); + streamPushItem.setStatus(streamPushExcelDto.getStatus()); + streamPushItem.setStreamType("push"); + streamPushItem.setCreateTime(DateUtil.getNow()); + streamPushItem.setMediaServerId(defaultMediaServerId); + streamPushItem.setName(streamPushExcelDto.getName()); + streamPushItem.setOriginType(2); + streamPushItem.setOriginTypeStr("rtsp_push"); + streamPushItem.setTotalReaderCount("0"); + streamPushItem.setPlatformId(streamPushExcelDto.getPlatformId()); + streamPushItem.setCatalogId(streamPushExcelDto.getCatalogId()); + + // 存入所有的通道信息 + streamPushItems.add(streamPushItem); + streamPushItemForSave.put(streamPushItem.getApp() + streamPushItem.getStream(), streamPushItem); + + if (!ObjectUtils.isEmpty(streamPushExcelDto.getPlatformId())) { + List platformList = streamPushItemsForPlatform.get(streamPushItem.getApp() + streamPushItem.getStream()); + if (platformList == null) { + platformList = new ArrayList<>(); + streamPushItemsForPlatform.put(streamPushItem.getApp() + streamPushItem.getStream(), platformList); + } + String platformId = streamPushExcelDto.getPlatformId(); + String catalogId = streamPushExcelDto.getCatalogId(); + if (ObjectUtils.isEmpty(streamPushExcelDto.getCatalogId())) { + catalogId = null; + } + String[] platFormInfoArray = new String[]{platformId, catalogId}; + platformList.add(platFormInfoArray); + } + + loadedSize ++; + if (loadedSize > 1000) { + saveData(); + streamPushItems.clear(); + streamPushItemForSave.clear(); + streamPushItemsForPlatform.clear(); + loadedSize = 0; + } + + } + + @Override + public void doAfterAllAnalysed(AnalysisContext analysisContext) { + // 这里也要保存数据,确保最后遗留的数据也存储到数据库 + saveData(); + streamPushItems.clear(); + streamPushItemForSave.clear(); + gBMap.clear(); + streamPushStreamSet.clear(); + streamPushItemsForPlatform.clear(); + errorDataHandler.handle(errorStreamList, errorInfoList); + } + + private void saveData(){ + if (streamPushItemForSave.size() > 0) { + // 向数据库查询是否存在重复的app + pushService.batchAddForUpload(new ArrayList<>(streamPushItemForSave.values()), streamPushItemsForPlatform); + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java new file mode 100644 index 000000000..30d9c1304 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java @@ -0,0 +1,92 @@ +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.DigestUtils; + +import java.util.List; + +@Service +public class UserServiceImpl implements IUserService { + + @Autowired + private UserMapper userMapper; + + @Override + public User getUser(String username, String password) { + return userMapper.select(username, password); + } + + @Override + public boolean changePassword(int id, String password) { + User user = userMapper.selectById(id); + user.setPassword(password); + return userMapper.update(user) > 0; + } + + @Override + public User getUserByUsername(String username) { + return userMapper.getUserByUsername(username); + } + + @Override + public int addUser(User user) { + User userByUsername = userMapper.getUserByUsername(user.getUsername()); + if (userByUsername != null) { + return 0; + } + return userMapper.add(user); + } + @Override + public int deleteUser(int id) { + return userMapper.delete(id); + } + + @Override + public List getAllUsers() { + return userMapper.selectAll(); + } + + @Override + public int updateUsers(User user) { + return userMapper.update(user); + } + + + @Override + public boolean checkPushAuthority(String callId, String sign) { + + List users = userMapper.getUsers(); + if (users.size() == 0) { + return false; + } + for (User user : users) { + if (user.getPushKey() == null) { + continue; + } + String checkStr = callId == null? user.getPushKey():(callId + "_" + user.getPushKey()) ; + String checkSign = DigestUtils.md5DigestAsHex(checkStr.getBytes()); + if (checkSign.equals(sign)) { + return true; + } + } + return false; + } + + @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 000000000..194e90fb9 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java @@ -0,0 +1,150 @@ +package com.genersoft.iot.vmp.service.redisMsg; + +import com.alibaba.fastjson2.JSON; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.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; + + @Autowired + private UserSetting userSetting; + + @Override + public void onMessage(@NotNull Message message, byte[] bytes) { + // 消息示例: PUBLISH alarm_receive '{ "gbId": "", "alarmSn": 1, "alarmType": "111", "alarmDescription": "222", }' + logger.info("收到来自REDIS的ALARM通知: {}", new String(message.getBody())); + 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.setAlarmType("" + alarmChannelMessage.getAlarmType()); + deviceAlarm.setAlarmPriority("1"); + deviceAlarm.setAlarmTime(DateUtil.getNow()); + deviceAlarm.setLongitude(0); + deviceAlarm.setLatitude(0); + + if (ObjectUtils.isEmpty(gbId)) { + if (userSetting.getSendToPlatformsWhenIdLost()) { + // 发送给所有的上级 + List parentPlatforms = storage.queryEnableParentPlatformList(true); + if (parentPlatforms.size() > 0) { + for (ParentPlatform parentPlatform : parentPlatforms) { + try { + deviceAlarm.setChannelId(parentPlatform.getDeviceGBId()); + commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage()); + } + } + } + }else { + // 获取开启了消息推送的设备和平台 + List parentPlatforms = storage.queryEnablePlatformListWithAsMessageChannel(); + if (parentPlatforms.size() > 0) { + for (ParentPlatform parentPlatform : parentPlatforms) { + try { + deviceAlarm.setChannelId(parentPlatform.getDeviceGBId()); + commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage()); + } + } + } + + } + // 获取开启了消息推送的设备和平台 + List devices = storage.queryDeviceWithAsMessageChannel(); + if (devices.size() > 0) { + for (Device device : devices) { + try { + deviceAlarm.setChannelId(device.getDeviceId()); + commander.sendAlarmMessage(device, deviceAlarm); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 发送报警: {}", e.getMessage()); + } + } + } + + }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.error("未处理的异常 ", e); + logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage()); + } + } + }); + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisCloseStreamMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisCloseStreamMsgListener.java new file mode 100644 index 000000000..d01047566 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisCloseStreamMsgListener.java @@ -0,0 +1,59 @@ +package com.genersoft.iot.vmp.service.redisMsg; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.service.IStreamPushService; +import 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.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; + +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * 接收来自redis的关闭流更新通知 + * @author lin + */ +@Component +public class RedisCloseStreamMsgListener implements MessageListener { + + private final static Logger logger = LoggerFactory.getLogger(RedisCloseStreamMsgListener.class); + + + @Autowired + private IStreamPushService pushService; + + 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 { + JSONObject jsonObject = JSON.parseObject(msg.getBody()); + String app = jsonObject.getString("app"); + String stream = jsonObject.getString("stream"); + pushService.stop(app, stream); + + }catch (Exception e) { + logger.warn("[REDIS的关闭推流通知] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); + logger.error("[REDIS的关闭推流通知] 异常内容: ", e); + } + } + }); + } + } +} 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 000000000..2b0f366bd --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGbPlayMsgListener.java @@ -0,0 +1,421 @@ +package com.genersoft.iot.vmp.service.redisMsg; + +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.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory; +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.service.IMediaServerService; +import com.genersoft.iot.vmp.service.bean.*; +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.data.redis.core.RedisTemplate; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; + +import java.text.ParseException; +import java.util.HashMap; +import java.util.List; +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 RedisTemplate redisTemplate; + + @Autowired + private ZLMServerFactory zlmServerFactory; + + @Autowired + private IMediaServerService mediaServerService; + + + @Autowired + private DynamicTask dynamicTask; + + + @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] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); + logger.error("[RedisGbPlayMsg] 异常内容: ", e); + } + } + }); + } + } + + /** + * 处理收到的请求推流的请求 + */ + 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 = zlmServerFactory.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); + redisTemplate.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); + redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); + return; + } + // 确定流是否在线 + Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, content.getApp(), content.getStream()); + if (streamReady != null && 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); + redisTemplate.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, (mediaServerItemInUse, hookParam)->{ + 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()); + + String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_REQUESTED; + logger.info("[redis发送通知] 推流被请求 {}: {}/{}", key, messageForPushChannel.getApp(), messageForPushChannel.getStream()); + redisTemplate.convertAndSend(key, JSON.toJSON(messageForPushChannel)); + } + } + + /** + * 将获取到的sendItem发送出去 + */ + private void responseSendItem(MediaServerItem mediaServerItem, RequestSendItemMsg content, String toId, String serial) { + SendRtpItem sendRtpItem = zlmServerFactory.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); + redisTemplate.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()); + redisTemplate.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); + }); + redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); + } + + private 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(redisTemplate, key); + if (scan.size() > 0) { + return (SendRtpItem)redisTemplate.opsForValue().get(scan.get(0)); + }else { + return null; + } + } +} 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 000000000..4e81a591d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGpsMsgListener.java @@ -0,0 +1,78 @@ +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通知] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); + logger.error("[REDIS的ALARM通知] 异常内容: ", e); + } + } + }); + } + } + + /** + * 定时将经纬度更新到数据库 + */ + @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/RedisPushStreamCloseResponseListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamCloseResponseListener.java new file mode 100644 index 000000000..ad00baf7e --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamCloseResponseListener.java @@ -0,0 +1,120 @@ +package com.genersoft.iot.vmp.service.redisMsg; + +import com.alibaba.fastjson2.JSON; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.InviteStreamType; +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.ZLMServerFactory; +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; +import com.genersoft.iot.vmp.service.IMediaServerService; +import com.genersoft.iot.vmp.service.IStreamPushService; +import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; +import com.genersoft.iot.vmp.service.bean.MessageForPushChannelResponse; +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.data.redis.connection.Message; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.stereotype.Component; + +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.concurrent.ConcurrentHashMap; + +/** + * 接收redis发送的结束推流请求 + * @author lin + */ +@Component +public class RedisPushStreamCloseResponseListener implements MessageListener { + + private final static Logger logger = LoggerFactory.getLogger(RedisPushStreamCloseResponseListener.class); + + @Autowired + private IStreamPushService streamPushService; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private ISIPCommanderForPlatform commanderFroPlatform; + + @Autowired + private UserSetting userSetting; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private ZLMServerFactory zlmServerFactory; + + + 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())); + MessageForPushChannel pushChannel = JSON.parseObject(message.getBody(), MessageForPushChannel.class); + StreamPushItem push = streamPushService.getPush(pushChannel.getApp(), pushChannel.getStream()); + if (push != null) { + if (redisCatchStorage.isChannelSendingRTP(push.getGbId())) { + List sendRtpItems = redisCatchStorage.querySendRTPServerByChnnelId( + push.getGbId()); + if (sendRtpItems.size() > 0) { + for (SendRtpItem sendRtpItem : sendRtpItems) { + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId()); + // 停止向上级推流 + 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("[REDIS消息-推流结束] 停止向上级推流:{}", streamId); + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); + redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(), sendRtpItem.getCallId(), sendRtpItem.getStreamId()); + zlmServerFactory.stopSendRtpStream(mediaInfo, param); + + try { + commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); + } + if (InviteStreamType.PUSH == sendRtpItem.getPlayType()) { + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, + sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getChannelId(), + sendRtpItem.getPlatformId(), parentPlatform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId()); + messageForPushChannel.setPlatFormIndex(parentPlatform.getId()); + redisCatchStorage.sendPlatformStopPlayMsg(messageForPushChannel); + } + } + } + } + } + + } + + 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/RedisPushStreamResponseListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamResponseListener.java new file mode 100644 index 000000000..c90771be8 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamResponseListener.java @@ -0,0 +1,76 @@ +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消息-请求推流结果] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); + logger.error("[REDIS消息-请求推流结果] 异常内容: ", e); + } + } + }); + } + } + + 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 000000000..cb34ff591 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusListMsgListener.java @@ -0,0 +1,105 @@ +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消息-推流设备列表更新] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); + logger.error("[REDIS消息-推流设备列表更新] 异常内容: ", e); + } + } + }); + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusMsgListener.java new file mode 100644 index 000000000..c8f4b2ae8 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusMsgListener.java @@ -0,0 +1,107 @@ +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.conf.UserSetting; +import com.genersoft.iot.vmp.service.IStreamPushService; +import com.genersoft.iot.vmp.service.bean.PushStreamStatusChangeFromRedisDto; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import 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; + + @Autowired + private UserSetting userSetting; + + + + 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消息-推流设备状态变化] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); + logger.error("[REDIS消息-推流设备状态变化] 异常内容: ", e); + } + } + }); + } + } + + @Override + public void run(ApplicationArguments args) throws Exception { + if (!userSetting.isUsePushingAsStatus()) { + // 启动时设置所有推流通道离线,发起查询请求 + 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 000000000..f5f29487e --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisStreamMsgListener.java @@ -0,0 +1,92 @@ +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消息-流变化] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); + logger.error("[REDIS消息-流变化] 异常内容: ", e); + } + } + }); + } + } +} 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 8bc78b938..a97e454c4 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java @@ -1,58 +1,211 @@ package com.genersoft.iot.vmp.storager; -import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.conf.MediaServerConfig; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.common.SystemAllInfo; +import com.genersoft.iot.vmp.gb28181.bean.AlarmChannelMessage; +import com.genersoft.iot.vmp.gb28181.bean.Device; +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.media.zlm.dto.StreamAuthorityInfo; +import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; +import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; +import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; +import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo; +import java.util.List; import java.util.Map; public interface IRedisCatchStorage { /** - * 开始播放时将流存入 - * - * @param stream 流信息 - * @return - */ - boolean startPlay(StreamInfo stream); - - - /** - * 停止播放时删除 + * 计数器。为cseq进行计数 * * @return */ - boolean stopPlay(StreamInfo streamInfo); + Long getCSEQ(); + + void updatePlatformCatchInfo(ParentPlatformCatch parentPlatformCatch); + + ParentPlatformCatch queryPlatformCatchInfo(String platformGbId); + + void delPlatformCatchInfo(String platformGbId); + + void delPlatformKeepalive(String platformGbId); + + void delPlatformRegister(String platformGbId); + + void updatePlatformRegisterInfo(String callId, PlatformRegisterInfo platformRegisterInfo); + + PlatformRegisterInfo queryPlatformRegisterInfo(String callId); + + void delPlatformRegisterInfo(String callId); + + void updateSendRTPSever(SendRtpItem sendRtpItem); /** - * 查询播放列表 - * @return + * 查询RTP推送信息缓存 + * @param platformGbId + * @param channelId + * @return sendRtpItem */ - StreamInfo queryPlay(StreamInfo streamInfo); + SendRtpItem querySendRTPServer(String platformGbId, String channelId, String streamId, String callId); - StreamInfo queryPlayByStreamId(String steamId); - - StreamInfo queryPlaybackByStreamId(String steamId); - - StreamInfo queryPlayByDevice(String deviceId, String code); + List querySendRTPServer(String platformGbId); /** - * 更新流媒体信息 - * @param mediaServerConfig - * @return + * 删除RTP推送信息缓存 + * @param platformGbId + * @param channelId */ - boolean updateMediaInfo(MediaServerConfig mediaServerConfig); + void deleteSendRTPServer(String platformGbId, String channelId, String callId, String streamId); /** - * 获取流媒体信息 + * 查询某个通道是否存在上级点播(RTP推送) + * @param channelId + */ + boolean isChannelSendingRTP(String channelId); + + /** + * 在redis添加wvp的信息 + */ + void updateWVPInfo(JSONObject jsonObject, int time); + + /** + * 发送推流生成与推流消失消息 + * @param jsonObject 消息内容 + */ + 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, OnStreamChangedHookParam item); + + /** + * 移除流信息从redis + * @param mediaServerId + * @param app + * @param streamId + */ + void removeStream(String mediaServerId, String type, String app, String streamId); + + + /** + * 移除流信息从redis + * @param mediaServerId + */ + void removeStream(String mediaServerId, String type); + + List getStreams(String mediaServerId, String pull); + + /** + * 将device信息写入redis + * @param device + */ + 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 */ - MediaServerConfig getMediaInfo(); + public boolean deviceIsOnline(String deviceId); - Map queryPlayByDeviceId(String deviceId); + /** + * 存储推流的鉴权信息 + * @param app 应用名 + * @param stream 流 + * @param streamAuthorityInfo 鉴权信息 + */ + void updateStreamAuthorityInfo(String app, String stream, StreamAuthorityInfo streamAuthorityInfo); - boolean startPlayback(StreamInfo stream); + /** + * 移除推流的鉴权信息 + * @param app 应用名 + * @param streamId 流 + */ + void removeStreamAuthorityInfo(String app, String streamId); - boolean stopPlayback(StreamInfo streamInfo); + /** + * 获取推流的鉴权信息 + * @param app 应用名 + * @param stream 流 + * @return + */ + StreamAuthorityInfo getStreamAuthorityInfo(String app, String stream); - StreamInfo queryPlaybackByDevice(String deviceId, String code); + List getAllStreamAuthorityInfo(); + + /** + * 发送redis消息 查询所有推流设备的状态 + */ + void sendStreamPushRequestedMsgForStatus(); + + List querySendRTPServerByChnnelId(String channelId); + + List querySendRTPServerByStream(String stream); + + SystemAllInfo getSystemInfo(); + + int getPushStreamCount(String id); + + int getProxyStreamCount(String id); + + int getGbSendCount(String id); + + void addDiskInfo(List> diskInfo); + + List queryAllSendRTPServer(); + + List getAllDevices(); + + void removeAllDevice(); + + void sendDeviceOrChannelStatus(String deviceId, String channelId, boolean online); + + void sendChannelAddOrDelete(String deviceId, String channelId, boolean add); + + void sendPlatformStartPlayMsg(MessageForPushChannel messageForPushChannel); + + void sendPlatformStopPlayMsg(MessageForPushChannel messageForPushChannel); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java new file mode 100644 index 000000000..b53c2d31f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java @@ -0,0 +1,384 @@ +package com.genersoft.iot.vmp.storager; + +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; +import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; +import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo; +import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; +import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend; +import com.github.pagehelper.PageInfo; + +import java.util.List; + +/** + * @description:视频设备数据存储接口 + * @author: swwheihei + * @date: 2020年5月6日 下午2:14:31 + */ +@SuppressWarnings("rawtypes") +public interface IVideoManagerStorage { + + /** + * 根据设备ID判断设备是否存在 + * + * @param deviceId 设备ID + * @return true:存在 false:不存在 + */ + public boolean exists(String deviceId); + + /** + * 开始播放 + * @param deviceId 设备id + * @param channelId 通道ID + * @param streamId 流地址 + */ + public void startPlay(String deviceId, String channelId, String streamId); + + /** + * 停止播放 + * @param deviceId 设备id + * @param channelId 通道ID + */ + public void stopPlay(String deviceId, String channelId); + + /** + * 获取设备 + * + * @param deviceId 设备ID + * @return DShadow 设备对象 + */ + public Device queryVideoDevice(String deviceId); + + /** + * 获取某个设备的通道列表 + * + * @param deviceId 设备ID + * @param page 分页 当前页 + * @param count 每页数量 + * @return + */ + public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, Boolean catalogUnderDevice, int page, int count); + + public List queryChannelsByDeviceIdWithStartAndLimit(String deviceId, List channelIds, String query, Boolean hasSubChannel, Boolean online, int start, int limit); + + + /** + * 获取某个设备的通道列表 + * + * @param deviceId 设备ID + * @return + */ + public List queryChannelsByDeviceId(String deviceId,Boolean online,List channelIds); + public List queryOnlineChannelsByDeviceId(String deviceId); + + /** + * 获取某个设备的通道 + * @param deviceId 设备ID + * @param channelId 通道ID + */ + public DeviceChannel queryChannel(String deviceId, String channelId); + + /** + * 删除通道 + * @param deviceId 设备ID + * @param channelId 通道ID + */ + public int delChannel(String deviceId, String channelId); + + /** + * 获取多个设备 + * @param page 当前页数 + * @param count 每页数量 + * @return List 设备对象数组 + */ + public PageInfo queryVideoDeviceList(int page, int count,Boolean online); + + /** + * 获取多个设备 + * + * @return List 设备对象数组 + */ + public List queryVideoDeviceList(Boolean online); + + + + /** + * 查询子设备 + * + * @param deviceId + * @param channelId + * @param page + * @param count + * @return + */ + PageInfo querySubChannels(String deviceId, String channelId, String query, Boolean hasSubChannel, Boolean online, int page, int count); + + + /** + * 清空通道 + * @param deviceId + */ + void cleanChannelsForDevice(String deviceId); + + + /** + * 更新上级平台 + * @param parentPlatform + */ + boolean updateParentPlatform(ParentPlatform parentPlatform); + + + /** + * 添加上级平台 + * @param parentPlatform + */ + boolean addParentPlatform(ParentPlatform parentPlatform); + + /** + * 删除上级平台 + * @param parentPlatform + */ + boolean deleteParentPlatform(ParentPlatform parentPlatform); + + /** + * 获取所有已启用的平台 + * @return + */ + List queryEnableParentPlatformList(boolean enable); + + /** + * 获取上级平台 + * @param platformGbId + * @return + */ + ParentPlatform queryParentPlatByServerGBId(String platformGbId); + + /** + * 所有平台离线 + */ + void outlineForAllParentPlatform(); + + /** + * 查询通道信息,不区分设备(已关联平台或全部) + */ + PageInfo queryAllChannelList(int page, int count, String query, Boolean online, Boolean channelType, String platformId, String catalogId); + + /** + * 查询设备的通道信息 + */ + List queryChannelListInParentPlatform(String platformId); + + + + /** + * 移除上级平台的通道信息 + * @param platformId + * @param channelReduces + * @return + */ + int delChannelForGB(String platformId, List channelReduces); + + + DeviceChannel queryChannelInParentPlatform(String platformId, String channelId); + + List queryChannelInParentPlatformAndCatalog(String platformId, String catalogId); + List queryStreamInParentPlatformAndCatalog(String platformId, String catalogId); + + Device queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId); + + /** + * 针对deviceinfo指令的查询接口 + * @param platformId 平台id + * @param channelId 通道id + * @return 设备信息 + */ + Device queryDeviceInfoByPlatformIdAndChannelId(String platformId, String channelId); + /** + * 添加Mobile Position设备移动位置 + * @param mobilePosition + * @return + */ + public boolean insertMobilePosition(MobilePosition mobilePosition); + + /** + * 查询移动位置轨迹 + * @param deviceId + * @param startTime + * @param endTime + */ + public List queryMobilePositions(String deviceId, String channelId, String startTime, String endTime); + + /** + * 查询最新移动位置 + * @param deviceId + */ + public MobilePosition queryLatestPosition(String deviceId); + + /** + * 删除指定设备的所有移动位置 + * @param deviceId + */ + public int clearMobilePositionsByDeviceId(String deviceId); + + /** + * 移除代理流 + * @param app + * @param stream + * @return + */ + public int deleteStreamProxy(String app, String stream); + + /** + * 按照是否启用获取代理流 + * @param enable + * @return + */ + public List getStreamProxyListForEnable(boolean enable); + + /** + * 按照是app和stream获取代理流 + * @param app + * @param stream + * @return + */ + public StreamProxyItem queryStreamProxy(String app, String stream); + + /** + * 获取代理流 + * @param page + * @param count + * @return + */ + PageInfo queryStreamProxyList(Integer page, Integer count); + + /** + * 根据国标ID获取平台关联的直播流 + * @param platformId + * @param channelId + * @return + */ + GbStream queryStreamInParentPlatform(String platformId, String channelId); + + /** + * 获取平台关联的直播流 + * @param platformId + * @return + */ + List queryGbStreamListInPlatform(String platformId); + + /** + * 移除单个推流 + * @param app + * @param stream + */ + int removeMedia(String app, String stream); + + /** + * 设置流离线 + */ + int mediaOffline(String app, String streamId); + + /** + * 设置流上线 + */ + int mediaOnline(String app, String streamId); + + /** + * 设置平台在线/离线 + */ + void updateParentPlatformStatus(String platformGbID, boolean online); + + /** + * 根据媒体ID获取启用/不启用的代理列表 + * @param id 媒体ID + * @param enable 启用/不启用 + * @return + */ + List getStreamProxyListForEnableInMediaServer(String id, boolean enable); + + /** + * 根据通道ID获取其所在设备 + * @param channelId 通道ID + * @return + */ + Device queryVideoDeviceByChannelId(String channelId); + + /** + * 通道上线 + * @param channelId 通道ID + */ + void deviceChannelOnline(String deviceId, String channelId); + + /** + * 通道离线 + * @param channelId 通道ID + */ + void deviceChannelOffline(String deviceId, String channelId); + + /** + * 通过app与stream获取StreamProxy + * @param app + * @param streamId + * @return + */ + StreamProxyItem getStreamProxyByAppAndStream(String app, String streamId); + + /** + * catlog查询结束后完全重写通道信息 + * @param deviceId + * @param deviceChannelList + */ + boolean resetChannels(String deviceId, List deviceChannelList); + + boolean updateChannels(String deviceId, List deviceChannelList); + + /** + * 获取目录信息 + * @param platformId + * @param parentId + * @return + */ + List getChildrenCatalogByPlatform(String platformId, String parentId); + + int addCatalog(PlatformCatalog platformCatalog); + + PlatformCatalog getCatalog(String 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); + + List queryChannelsByDeviceId(String serial, List channelIds, Boolean online); + + List queryEnablePlatformListWithAsMessageChannel(); + + List queryDeviceWithAsMessageChannel(); +} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java deleted file mode 100644 index 37f3eb18d..000000000 --- a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java +++ /dev/null @@ -1,182 +0,0 @@ -package com.genersoft.iot.vmp.storager; - -import java.util.List; - -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; -import com.github.pagehelper.PageInfo; - -/** - * @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 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 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); - - - /** - * 查询子设备 - * - * @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); - - /** - * 添加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); -} 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 new file mode 100644 index 000000000..28a2e913f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceAlarmMapper.java @@ -0,0 +1,49 @@ +package com.genersoft.iot.vmp.storager.dao; + +import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * 用于存储设备的报警信息 + */ +@Mapper +@Repository +public interface DeviceAlarmMapper { + + @Insert("INSERT INTO wvp_device_alarm (device_id, channel_id, alarm_priority, alarm_method, alarm_time, alarm_description, longitude, latitude, alarm_type , create_time ) " + + "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, + String alarmType, String startTime, String endTime); + + + @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 11b728a81..bdc45bf20 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,7 +1,12 @@ package com.genersoft.iot.vmp.storager.dao; +import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannelInPlatform; +import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; +import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend; import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; import java.util.List; @@ -9,69 +14,455 @@ import java.util.List; * 用于存储设备通道信息 */ @Mapper +@Repository 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) " + - "VALUES ('${channelId}', '${deviceId}', '${name}', '${manufacture}', '${model}', '${owner}', '${civilCode}', '${block}'," + - "'${address}', ${parental}, '${parentId}', ${safetyWay}, ${registerWay}, '${certNum}', ${certifiable}, ${errCode}, '${secrecy}', " + - "'${ipAddress}', ${port}, '${password}', ${PTZType}, ${status})") + @Insert("INSERT INTO wvp_device_channel (channel_id, device_id, name, manufacture, model, owner, civil_code, block, " + + "address, parental, parent_id, safety_way, register_way, cert_num, certifiable, err_code, secrecy, " + + "ip_address, port, password, ptz_type, status, stream_id, longitude, latitude, longitude_gcj02, latitude_gcj02, " + + "longitude_wgs84, latitude_wgs84, has_audio, create_time, update_time, business_group_id, gps_time) " + + "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 queryChannelsByDeviceId(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online); + 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}") + @Select(value = {" "}) + List queryChannelsWithDeviceInfo(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online, List channelIds); + + + @Select(value = {" "}) + List queryChannelsByDeviceIdWithStartAndLimit(String deviceId,List channelIds, String parentChannelId, String query, + Boolean hasSubChannel, Boolean online, int start, int limit); + + @Select("SELECT * FROM wvp_device_channel WHERE device_id=#{deviceId} AND channel_id=#{channelId}") DeviceChannel queryChannel(String deviceId, String channelId); - @Delete("DELETE FROM device_channel WHERE deviceId=#{deviceId}") + @Delete("DELETE FROM wvp_device_channel WHERE device_id=#{deviceId}") int cleanChannelsByDeviceId(String deviceId); - @Update(value = {"UPDATE device_channel SET streamId=null WHERE deviceId=#{deviceId} AND channelId=#{channelId}"}) + @Delete("DELETE FROM wvp_device_channel WHERE device_id=#{deviceId} AND channel_id=#{channelId}") + int del(String deviceId, String channelId); + + @Update(value = {"UPDATE wvp_device_channel SET stream_id=null WHERE device_id=#{deviceId} AND channel_id=#{channelId}"}) void stopPlay(String deviceId, String channelId); - @Update(value = {"UPDATE device_channel SET streamId=#{streamId} WHERE deviceId=#{deviceId} AND channelId=#{channelId}"}) + @Update(value = {"UPDATE wvp_device_channel SET stream_id=#{streamId} WHERE device_id=#{deviceId} AND channel_id=#{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); + + + @Select("SELECT * FROM wvp_device_channel WHERE channel_id=#{channelId}") + List queryChannelByChannelId( String channelId); + + @Update(value = {"UPDATE wvp_device_channel SET status=false WHERE device_id=#{deviceId} AND channel_id=#{channelId}"}) + void offline(String deviceId, String channelId); + + @Update(value = {"UPDATE wvp_device_channel SET status=fasle WHERE device_id=#{deviceId}"}) + void offlineByDeviceId(String deviceId); + + @Insert("") + int batchAdd(List addChannels); + + + @Insert("") + int batchAddOrUpdate(List addChannels); + + @Update(value = {"UPDATE wvp_device_channel SET status=true WHERE device_id=#{deviceId} AND channel_id=#{channelId}"}) + void online(String deviceId, String channelId); + + @Update({""}) + int batchUpdate(List updateChannels); + + + @Select("SELECT * FROM wvp_device_channel WHERE device_id=#{deviceId} AND status=true") + List queryOnlineChannelsByDeviceId(String deviceId); + + @Delete(value = {" "}) + int cleanChannelsNotInList(String deviceId, List channels); + + @Update(" update wvp_device_channel" + + " set sub_count = (select *" + + " from (select count(0)" + + " from wvp_device_channel" + + " where device_id = #{deviceId} and parent_id = #{channelId}) as temp)" + + " where device_id = #{deviceId} " + + " and channel_id = #{channelId}") + int updateChannelSubCount(String deviceId, String channelId); + + @Update(value = {" "}) + void updatePosition(DeviceChannel deviceChannel); + + @Select("SELECT * FROM wvp_device_channel WHERE length(trim(stream_id)) > 0") + List getAllChannelInPlay(); + + @Select("select * from wvp_device_channel where longitude*latitude > 0 and device_id = #{deviceId}") + List getAllChannelWithCoordinate(String deviceId); + + + @Select(value = {" "}) + List getChannelsWithCivilCodeAndLength(String deviceId, String parentId, Integer length); + + @Select(value = {" "}) + List getChannelsByCivilCode(String deviceId, String parentId); + + @Select("select min(length(channel_id)) as minLength " + + "from wvp_device_channel " + + "where device_id=#{deviceId}") + Integer getChannelMinLength(String deviceId); + + @Select("select * from wvp_device_channel where device_id=#{deviceId} and civil_code not in " + + "(select civil_code from wvp_device_channel where device_id=#{deviceId} group by civil_code)") + List getChannelWithoutCivilCode(String deviceId); + + @Select("select * from wvp_device_channel where device_id=#{deviceId} and SUBSTRING(channel_id, 11, 3)=#{typeCode}") + List getBusinessGroups(String deviceId, String typeCode); + + @Select("select dc.id, dc.channel_id, dc.device_id, dc.name, dc.manufacture,dc.model,dc.owner, pc.civil_code,dc.block, " + + " dc.address, '0' as parental,'0' as channel_type, pc.id as parent_id, dc.safety_way, dc.register_way,dc.cert_num, dc.certifiable, " + + " dc.err_code,dc.end_time, dc.secrecy, dc.ip_address, dc.port, dc.ptz_type, dc.password, dc.status, " + + " dc.longitude_wgs84 as longitude, dc.latitude_wgs84 as latitude, pc.business_group_id " + + " from wvp_device_channel dc" + + " LEFT JOIN wvp_platform_gb_channel pgc on dc.id = pgc.device_channel_id" + + " LEFT JOIN wvp_platform_catalog pc on pgc.catalog_id = pc.id and pgc.platform_id = pc.platform_id" + + " where pgc.platform_id=#{serverGBId}") + List queryChannelWithCatalog(String serverGBId); + + @Select("select * from wvp_device_channel where device_id = #{deviceId}") + List queryAllChannels(String deviceId); + + + @Select("select channelId" + + ", device_id" + + ", latitude" + + ", longitude"+ + ",latitude_wgs84"+ + ",longitude_wgs84"+ + ",latitude_gcj02"+ + ",longitude_gcj02"+ + "from wvp_device_channel where device_id = #{deviceId} " + + "and latitude != 0 " + + "and longitude != 0 " + + "and(latitude_gcj02=0 or latitude_wgs84=0 or longitude_wgs84= 0 or longitude_gcj02 = 0)") + List getChannelsWithoutTransform(String deviceId); + + @Select("select de.* from wvp_device de left join wvp_device_channel dc on de.device_id = dc.deviceId where dc.channel_id=#{channelId}") + List getDeviceByChannelId(String channelId); + + + @Delete({""}) + int batchDel(List deleteChannelList); + + @Update({""}) + int batchOnline(List channels); + + @Update({""}) + int batchOffline(List channels); + + + @Select("select count(1) from wvp_device_channel where status = true") + int getOnlineCount(); + + @Select("select count(1) from wvp_device_channel") + int getAllChannelCount(); + + // 设备主子码流逻辑START + @Update(value = {"UPDATE wvp_device_channel SET stream_id=null WHERE device_id=#{deviceId}"}) + void clearPlay(String deviceId); + // 设备主子码流逻辑END + @Select(value = {" "}) + List getSubChannelsByDeviceId(String deviceId, String parentId, boolean onlyCatalog); + } 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 f6e328bfc..96773fe95 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 @@ -13,21 +13,68 @@ import java.util.List; @Repository public interface DeviceMapper { - @Select("SELECT * FROM device WHERE deviceId = #{deviceId}") + @Select("SELECT " + + "device_id, " + + "coalesce(custom_name, name) as name, " + + "password, " + + "manufacturer, " + + "model, " + + "firmware, " + + "transport," + + "stream_mode," + + "ip," + + "sdp_ip," + + "local_ip," + + "port," + + "host_address," + + "expires," + + "register_time," + + "keepalive_time," + + "create_time," + + "update_time," + + "charset," + + "subscribe_cycle_for_catalog," + + "subscribe_cycle_for_mobile_position," + + "mobile_position_submission_interval," + + "subscribe_cycle_for_alarm," + + "ssrc_check," + + "as_message_channel," + + "geo_coord_sys," + + "on_line," + + "media_server_id," + + "switch_primary_sub_stream," + + "(SELECT count(0) FROM wvp_device_channel WHERE device_id=wvp_device.device_id) as channel_count "+ + " FROM wvp_device WHERE device_id = #{deviceId}") Device getDeviceByDeviceId(String deviceId); - @Insert("INSERT INTO device (" + - "deviceId, " + + @Insert("INSERT INTO wvp_device (" + + "device_id, " + "name, " + "manufacturer, " + "model, " + "firmware, " + "transport," + - "streamMode," + + "stream_mode," + "ip," + + "sdp_ip," + + "local_ip," + "port," + - "hostAddress," + - "online" + + "host_address," + + "expires," + + "register_time," + + "keepalive_time," + + "keepalive_interval_time," + + "create_time," + + "update_time," + + "charset," + + "subscribe_cycle_for_catalog," + + "subscribe_cycle_for_mobile_position,"+ + "mobile_position_submission_interval,"+ + "subscribe_cycle_for_alarm,"+ + "ssrc_check,"+ + "as_message_channel,"+ + "geo_coord_sys,"+ + "on_line"+ ") VALUES (" + "#{deviceId}," + "#{name}," + @@ -37,32 +84,210 @@ public interface DeviceMapper { "#{transport}," + "#{streamMode}," + "#{ip}," + + "#{sdpIp}," + + "#{localIp}," + "#{port}," + "#{hostAddress}," + - "#{online}" + + "#{expires}," + + "#{registerTime}," + + "#{keepaliveTime}," + + "#{keepaliveIntervalTime}," + + "#{createTime}," + + "#{updateTime}," + + "#{charset}," + + "#{subscribeCycleForCatalog}," + + "#{subscribeCycleForMobilePosition}," + + "#{mobilePositionSubmissionInterval}," + + "#{subscribeCycleForAlarm}," + + "#{ssrcCheck}," + + "#{asMessageChannel}," + + "#{geoCoordSys}," + + "#{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}") + @Delete("DELETE FROM wvp_device WHERE device_id=#{deviceId}") int del(String deviceId); + + @Select("SELECT " + + "device_id, " + + "coalesce(custom_name, name) as name, " + + "password, " + + "manufacturer, " + + "model, " + + "firmware, " + + "transport," + + "stream_mode," + + "ip," + + "sdp_ip,"+ + "local_ip,"+ + "port,"+ + "host_address,"+ + "expires,"+ + "register_time,"+ + "keepalive_time,"+ + "create_time,"+ + "update_time,"+ + "charset,"+ + "subscribe_cycle_for_catalog,"+ + "subscribe_cycle_for_mobile_position,"+ + "mobile_position_submission_interval,"+ + "subscribe_cycle_for_alarm,"+ + "ssrc_check,"+ + "as_message_channel,"+ + "geo_coord_sys,"+ + "on_line"+ + " FROM wvp_device WHERE on_line = true") + List getOnlineDevices(); + @Select("SELECT " + + "device_id,"+ + "coalesce(custom_name,name)as name,"+ + "password,"+ + "manufacturer,"+ + "model,"+ + "firmware,"+ + "transport,"+ + "stream_mode,"+ + "ip,"+ + "sdp_ip,"+ + "local_ip,"+ + "port,"+ + "host_address,"+ + "expires,"+ + "register_time,"+ + "keepalive_time,"+ + "create_time,"+ + "update_time,"+ + "charset,"+ + "subscribe_cycle_for_catalog,"+ + "subscribe_cycle_for_mobile_position,"+ + "mobile_position_submission_interval,"+ + "subscribe_cycle_for_alarm,"+ + "ssrc_check,"+ + "as_message_channel,"+ + "geo_coord_sys,"+ + "on_line"+ + " FROM wvp_device WHERE ip = #{host} AND port=#{port}") + Device getDeviceByHostAndPort(String host, int port); + + @Update(value = {" "}) + void updateCustom(Device device); + + @Insert("INSERT INTO wvp_device (" + + "device_id,"+ + "custom_name,"+ + "password,"+ + "sdp_ip,"+ + "create_time,"+ + "update_time,"+ + "charset,"+ + "ssrc_check,"+ + "as_message_channel,"+ + "geo_coord_sys,"+ + "on_line,"+ + "media_server_id,"+ + "switch_primary_sub_stream"+ + ") VALUES (" + + "#{deviceId}," + + "#{name}," + + "#{password}," + + "#{sdpIp}," + + "#{createTime}," + + "#{updateTime}," + + "#{charset}," + + "#{ssrcCheck}," + + "#{asMessageChannel}," + + "#{geoCoordSys}," + + "#{onLine}," + + "#{mediaServerId}," + + "#{switchPrimarySubStream}" + + ")") + void addCustomDevice(Device device); + + @Select("select * FROM wvp_device") + List getAll(); + + @Select("select * FROM wvp_device where as_message_channel = true") + List queryDeviceWithAsMessageChannel(); } 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 29f3c4d11..dbc9a1677 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 @@ -1,33 +1,35 @@ package com.genersoft.iot.vmp.storager.dao; +import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + 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, deviceName, time, longitude, latitude, altitude, speed, direction, reportSource, geodeticSystem, cnLng, cnLat) " + - "VALUES ('${deviceId}', '${deviceName}', '${time}', ${longitude}, ${latitude}, ${altitude}, ${speed}, ${direction}, '${reportSource}', '${geodeticSystem}', '${cnLng}', '${cnLat}')") + @Insert("INSERT INTO wvp_device_mobile_position (device_id,channel_id, device_name,time,longitude,latitude,altitude,speed,direction,report_source,longitude_gcj02,latitude_gcj02,longitude_wgs84,latitude_wgs84,create_time)"+ + "VALUES (#{deviceId}, #{channelId}, #{deviceName}, #{time}, #{longitude}, #{latitude}, #{altitude}, #{speed}, #{direction}, #{reportSource}, #{longitudeGcj02}, #{latitudeGcj02}, #{longitudeWgs84}, #{latitudeWgs84}, #{createTime})") 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}" + + @Select("SELECT * FROM wvp_device_mobile_position WHERE device_id = #{deviceId}" + " ORDER BY time DESC LIMIT 1") MobilePosition queryLatestPositionByDevice(String deviceId); - @Delete("DELETE FROM device_mobile_position WHERE deviceId = #{deviceId}") + @Delete("DELETE FROM wvp_device_mobile_position WHERE device_id = #{deviceId}") int clearMobilePositionsByDeviceId(String deviceId); } 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 new file mode 100644 index 000000000..e313acea5 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java @@ -0,0 +1,172 @@ +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; + +import java.util.List; + +@Mapper +@Repository +public interface GbStreamMapper { + + @Insert("REPLACE INTO wvp_gb_stream (app, stream, gb_id, name, " + + "longitude, latitude, stream_type,media_server_id,create_time) VALUES" + + "(#{app}, #{stream}, #{gbId}, #{name}, " + + "#{longitude}, #{latitude}, #{streamType}, " + + "#{mediaServerId}, #{createTime})") + @Options(useGeneratedKeys = true, keyProperty = "gbStreamId", keyColumn = "gbStreamId") + int add(GbStream gbStream); + + @Update("UPDATE wvp_gb_stream " + + "SET app=#{app}," + + "stream=#{stream}," + + "gb_id=#{gbId}," + + "name=#{name}," + + "stream_type=#{streamType}," + + "longitude=#{longitude}, " + + "latitude=#{latitude}," + + "media_server_id=#{mediaServerId}" + + "WHERE app=#{app} AND stream=#{stream}") + int updateByAppAndStream(GbStream gbStream); + + @Update("UPDATE wvp_gb_stream " + + "SET app=#{app}," + + "stream=#{stream}," + + "gb_id=#{gbId}," + + "name=#{name}," + + "stream_type=#{streamType}," + + "longitude=#{longitude}, " + + "latitude=#{latitude}," + + "media_server_id=#{mediaServerId}" + + "WHERE gb_stream_id=#{gbStreamId}") + int update(GbStream gbStream); + + @Delete("DELETE FROM wvp_gb_stream WHERE app=#{app} AND stream=#{stream}") + int del(String app, String stream); + + @Select("") + List selectAll(String platformId, String catalogId, String query, String mediaServerId); + + @Select("SELECT * FROM wvp_gb_stream WHERE app=#{app} AND stream=#{stream}") + GbStream selectOne(String app, String stream); + + @Select("SELECT * FROM wvp_gb_stream WHERE gb_id=#{gbId}") + List selectByGBId(String gbId); + + @Select("SELECT gs.*, pgs.platform_id as platform_id, pgs.catalog_id as catalog_id FROM wvp_gb_stream gs " + + "LEFT JOIN wvp_platform_gb_stream pgs ON gs.gb_stream_id = pgs.gb_stream_id " + + "WHERE gs.gb_id = #{gbId} AND pgs.platform_id = #{platformId}") + GbStream queryStreamInPlatform(String platformId, String gbId); + + @Select("") + List queryGbStreamListInPlatform(String platformId, boolean usPushingAsStatus); + + + @Select("SELECT gs.* FROM wvp_gb_stream gs left join wvp_platform_gb_stream pgs " + + "ON gs.gb_stream_id = pgs.gb_stream_id WHERE pgs.gb_stream_id is NULL") + List queryStreamNotInPlatform(); + + @Delete("DELETE FROM wvp_gb_stream WHERE stream_type=#{type} AND gb_id=NULL AND media_server_id=#{mediaServerId}") + void deleteWithoutGBId(String type, String mediaServerId); + + @Delete("") + void batchDel(List streamProxyItemList); + + @Delete("") + void batchDelForGbStream(List gbStreams); + + @Insert("") + @Options(useGeneratedKeys = true, keyProperty = "gbStreamId", keyColumn = "gb_stream_id") + void batchAdd(List subList); + + @Update({""}) + int updateStreamGPS(List gpsMsgInfos); + + @Select("") + List selectAllForAppAndStream(List streamPushItems); + + @Update("UPDATE wvp_gb_stream " + + "SET media_server_id=#{mediaServerId}" + + "WHERE app=#{app} AND stream=#{stream}") + void updateMediaServer(String app, String stream, String mediaServerId); + + @Update("") + int updateGbIdOrName(List streamPushItemForUpdate); + + @Select("SELECT status FROM wvp_stream_proxy WHERE app=#{app} AND stream=#{stream}") + Boolean selectStatusForProxy(String app, String stream); + + @Select("SELECT status FROM wvp_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 new file mode 100644 index 000000000..ae8471b2a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/LogMapper.java @@ -0,0 +1,36 @@ +package com.genersoft.iot.vmp.storager.dao; + +import com.genersoft.iot.vmp.storager.dao.dto.LogDto; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * 用于存储设服务的日志 + */ +@Mapper +@Repository +public interface LogMapper { + + @Insert("insert into wvp_log ( name,type,uri,address,result,timing,username,create_time) " + + "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 wvp_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 new file mode 100644 index 000000000..cdc303dde --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java @@ -0,0 +1,132 @@ +package com.genersoft.iot.vmp.storager.dao; + +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + + +@Mapper +@Repository +public interface MediaServerMapper { + + @Insert("INSERT INTO wvp_media_server (" + + "id,"+ + "ip,"+ + "hook_ip,"+ + "sdp_ip,"+ + "stream_ip,"+ + "http_port,"+ + "http_ssl_port,"+ + "rtmp_port,"+ + "rtmp_ssl_port,"+ + "rtp_proxy_port,"+ + "rtsp_port,"+ + "rtsp_ssl_port,"+ + "auto_config,"+ + "secret,"+ + "rtp_enable,"+ + "rtp_port_range,"+ + "send_rtp_port_range,"+ + "record_assist_port,"+ + "default_server,"+ + "create_time,"+ + "update_time,"+ + "hook_alive_interval"+ + ") VALUES " + + "(" + + "#{id}, " + + "#{ip}, " + + "#{hookIp}, " + + "#{sdpIp}, " + + "#{streamIp}, " + + "#{httpPort}, " + + "#{httpSSlPort}, " + + "#{rtmpPort}, " + + "#{rtmpSSlPort}, " + + "#{rtpProxyPort}, " + + "#{rtspPort}, " + + "#{rtspSSLPort}, " + + "#{autoConfig}, " + + "#{secret}, " + + "#{rtpEnable}, " + + "#{rtpPortRange}, " + + "#{sendRtpPortRange}, " + + "#{recordAssistPort}, " + + "#{defaultServer}, " + + "#{createTime}, " + + "#{updateTime}, " + + "#{hookAliveInterval})") + int add(MediaServerItem mediaServerItem); + + @Update(value = {" "}) + int update(MediaServerItem mediaServerItem); + + @Update(value = {" "}) + int updateByHostAndPort(MediaServerItem mediaServerItem); + + @Select("SELECT * FROM wvp_media_server WHERE id=#{id}") + MediaServerItem queryOne(String id); + + @Select("SELECT * FROM wvp_media_server") + List queryAll(); + + @Delete("DELETE FROM wvp_media_server WHERE id=#{id}") + void delOne(String id); + + @Select("DELETE FROM wvp_media_server WHERE ip=#{host} and http_port=#{port}") + void delOneByIPAndPort(String host, int port); + + @Delete("DELETE FROM wvp_media_server WHERE default_server=true") + int delDefault(); + + @Select("SELECT * FROM wvp_media_server WHERE ip=#{host} and http_port=#{port}") + MediaServerItem queryOneByHostAndPort(String host, int port); + + @Select("SELECT * FROM wvp_media_server WHERE default_server=true") + MediaServerItem queryDefault(); +} 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 new file mode 100644 index 000000000..386d7dc0e --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java @@ -0,0 +1,100 @@ +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; + +import java.util.List; + +/** + * 用于存储上级平台 + */ +@Mapper +@Repository +public interface ParentPlatformMapper { + + @Insert("INSERT INTO wvp_platform (enable, name, server_gb_id, server_gb_domain, server_ip, server_port,device_gb_id,device_ip,"+ + "device_port,username,password,expires,keep_timeout,transport,character_set,ptz,rtcp,as_message_channel,"+ + "status,start_offline_push,catalog_id,administrative_division,catalog_group,create_time,update_time) " + + " VALUES (#{enable}, #{name}, #{serverGBId}, #{serverGBDomain}, #{serverIP}, #{serverPort}, #{deviceGBId}, #{deviceIp}, " + + " #{devicePort}, #{username}, #{password}, #{expires}, #{keepTimeout}, #{transport}, #{characterSet}, #{ptz}, #{rtcp}, #{asMessageChannel}, " + + " #{status}, #{startOfflinePush}, #{catalogId}, #{administrativeDivision}, #{catalogGroup}, #{createTime}, #{updateTime})") + int addParentPlatform(ParentPlatform parentPlatform); + + @Update("UPDATE wvp_platform " + + "SET enable=#{enable}, " + + "name=#{name}," + + "device_gb_id=#{deviceGBId}," + + "server_gb_id=#{serverGBId}, " + + "server_gb_domain=#{serverGBDomain}, " + + "server_ip=#{serverIP}," + + "server_port=#{serverPort}, " + + "device_ip=#{deviceIp}, " + + "device_port=#{devicePort}, " + + "username=#{username}, " + + "password=#{password}, " + + "expires=#{expires}, " + + "keep_timeout=#{keepTimeout}, " + + "transport=#{transport}, " + + "character_set=#{characterSet}, " + + "ptz=#{ptz}, " + + "rtcp=#{rtcp}, " + + "as_message_channel=#{asMessageChannel}, " + + "status=#{status}, " + + "start_offline_push=#{startOfflinePush}, " + + "catalog_group=#{catalogGroup}, " + + "administrative_division=#{administrativeDivision}, " + + "create_time=#{createTime}, " + + "update_time=#{updateTime}, " + + "catalog_id=#{catalogId} " + + "WHERE id=#{id}") + int updateParentPlatform(ParentPlatform parentPlatform); + + @Delete("DELETE FROM wvp_platform WHERE server_gb_id=#{serverGBId}") + int delParentPlatform(ParentPlatform parentPlatform); + + @Select("SELECT *, ((SELECT count(0)\n" + + " FROM wvp_platform_gb_channel pc\n" + + " WHERE pc.platform_id = pp.server_gb_id)\n" + + " +\n" + + " (SELECT count(0)\n" + + " FROM wvp_platform_gb_stream pgs\n" + + " WHERE pgs.platform_id = pp.server_gb_id)\n" + + " +\n" + + " (SELECT count(0)\n" + + " FROM wvp_platform_catalog pgc\n" + + " WHERE pgc.platform_id = pp.server_gb_id)) as channel_count\n" + + "FROM wvp_platform pp ") + List getParentPlatformList(); + + @Select("SELECT * FROM wvp_platform WHERE enable=#{enable} ") + List getEnableParentPlatformList(boolean enable); + + @Select("SELECT * FROM wvp_platform WHERE enable=true and as_message_channel=true") + List queryEnablePlatformListWithAsMessageChannel(); + + @Select("SELECT * FROM wvp_platform WHERE server_gb_id=#{platformGbId}") + ParentPlatform getParentPlatByServerGBId(String platformGbId); + + @Select("SELECT * FROM wvp_platform WHERE id=#{id}") + ParentPlatform getParentPlatById(int id); + + @Update("UPDATE wvp_platform SET status=false" ) + int outlineForAllParentPlatform(); + + @Update("UPDATE wvp_platform SET status=#{online} WHERE server_gb_id=#{platformGbID}" ) + int updateParentPlatformStatus(String platformGbID, boolean online); + + @Update(value = {" "}) + int setDefaultCatalog(String platformId, String catalogId, String updateTime); + + @Select("select 'channel' as name, count(pgc.platform_id) count from wvp_platform_gb_channel pgc left join wvp_device_channel dc on dc.id = pgc.device_channel_id where pgc.platform_id=#{platform_id} and dc.channel_id =#{gbId} " + + "union " + + "select 'stream' as name, count(pgs.platform_id) count from wvp_platform_gb_stream pgs left join wvp_gb_stream gs on pgs.gb_stream_id = gs.gb_stream_id where pgs.platform_id=#{platform_id} and gs.gb_id =#{gbId}") + List getChannelSource(String platform_id, 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 000000000..15fed7b03 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformCatalogMapper.java @@ -0,0 +1,54 @@ +package com.genersoft.iot.vmp.storager.dao; + +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + + +@Mapper +@Repository +public interface PlatformCatalogMapper { + + @Insert("INSERT INTO wvp_platform_catalog (id, name, platform_id, parent_id, civil_code, business_group_id) VALUES" + + "(#{id}, #{name}, #{platformId}, #{parentId}, #{civilCode}, #{businessGroupId})") + int add(PlatformCatalog platformCatalog); + + @Delete("DELETE from wvp_platform_catalog WHERE id=#{id}") + int del(String id); + + @Delete("DELETE from wvp_platform_catalog WHERE platform_id=#{platformId}") + int delByPlatformId(String platformId); + + @Select("SELECT pc.*, count(pc2.id) as children_count from wvp_platform_catalog pc " + + "left join wvp_platform_catalog pc2 on pc.id = pc2.parent_id " + + "WHERE pc.parent_id=#{parentId} AND pc.platform_id=#{platformId} " + + "group by pc.id, pc.name, pc.platform_id, pc.business_group_id, pc.civil_code, pc.parent_id") + List selectByParentId(String platformId, String parentId); + + @Select("SELECT *, (SELECT COUNT(1) from wvp_platform_catalog where parent_id = pc.id) as children_count from wvp_platform_catalog pc WHERE pc.id=#{id}") + PlatformCatalog select(String id); + + @Update(value = {" "}) + int update(PlatformCatalog platformCatalog); + + @Select("SELECT *, (SELECT COUNT(1) from wvp_platform_catalog where parent_id = pc.id) as children_count from wvp_platform_catalog pc WHERE pc.platform_id=#{platformId}") + List selectByPlatForm(String platformId); + + @Select("SELECT pc.* FROM wvp_platform_catalog pc WHERE pc.id = (SELECT pp.catalog_id from wvp_platform pp WHERE pp.server_gb_id=#{platformId})") + PlatformCatalog selectDefaultByPlatFormId(String platformId); + + + @Select("SELECT pc.* FROM wvp_platform_catalog pc WHERE pc.id = #{id}") + PlatformCatalog selectParentCatalog(String id); + + @Select("SELECT pc.id as channel_id, pc.name, pc.civil_code, pc.business_group_id,'1' as parental, pc.parent_id " + + " from wvp_platform_catalog pc WHERE pc.platform_id=#{platformId}") + List queryCatalogInPlatform(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 new file mode 100644 index 000000000..3aeb0e0cf --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java @@ -0,0 +1,120 @@ +package com.genersoft.iot.vmp.storager.dao; + +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog; +import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Mapper +@Repository +public interface PlatformChannelMapper { + + /** + * 查询列表里已经关联的 + */ + @Select("") + 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 wvp_platform_gb_channel pgc left join wvp_device_channel dc on dc.id = pgc.device_channel_id WHERE dc.channel_id=#{channelId} and pgc.platform_id=#{platformId}") + List queryChannelInParentPlatform(String platformId, String channelId); + + @Select("SELECT dc.* from wvp_platform_gb_channel pgc left join wvp_device_channel dc on dc.id = pgc.device_channel_id WHERE pgc.platform_id=#{platformId} and pgc.catalog_id=#{catalogId}") + List queryAllChannelInCatalog(String platformId, String catalogId); + + @Select(" select dc.channel_id as id, dc.name as name, pgc.platform_id as platform_id, pgc.catalog_id as parent_id, 0 as children_count, 1 as type " + + " from wvp_device_channel dc left join wvp_platform_gb_channel pgc on dc.id = pgc.device_channel_id " + + " where pgc.platform_id=#{platformId} and pgc.catalog_id=#{catalogId}") + List queryChannelInParentPlatformAndCatalog(String platformId, String catalogId); + + @Select("select d.*\n" + + "from wvp_platform_gb_channel pgc\n" + + " left join wvp_device_channel dc on dc.id = pgc.device_channel_id\n" + + " left join wvp_device d on dc.device_id = d.device_id\n" + + "where dc.channel_id = #{channelId} and pgc.platform_id=#{platformId}") + List queryVideoDeviceByPlatformIdAndChannelId(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.channel_id dc.device_id,dc.name,d.manufacturer,d.model,d.firmware\n" + + "from wvp_platform_gb_channel pgc\n" + + " left join wvp_device_channel dc on dc.id = pgc.device_channel_id\n" + + " left join wvp_device d on dc.device_id = d.device_id\n" + + "where dc.channel_id = #{channelId} and pgc.platform_id=#{platformId}") + List queryDeviceInfoByPlatformIdAndChannelId(String platformId, String channelId); + + @Select("SELECT pgc.platform_id from wvp_platform_gb_channel pgc left join wvp_device_channel dc on dc.id = pgc.device_channel_id WHERE dc.channel_id='${channelId}'") + List queryParentPlatformByChannelId(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 new file mode 100644 index 000000000..0b1985540 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformGbStreamMapper.java @@ -0,0 +1,111 @@ +package com.genersoft.iot.vmp.storager.dao; + +import com.genersoft.iot.vmp.gb28181.bean.GbStream; +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog; +import com.genersoft.iot.vmp.gb28181.bean.PlatformGbStream; +import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; +import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + + +@Mapper +@Repository +public interface PlatformGbStreamMapper { + + @Insert("REPLACE INTO wvp_platform_gb_stream (gb_stream_id, platform_id, catalog_id) VALUES" + + "( #{gbStreamId}, #{platformId}, #{catalogId})") + int add(PlatformGbStream platformGbStream); + + + @Insert("") + int batchAdd(List streamPushItems); + + @Delete("DELETE from wvp_platform_gb_stream WHERE gb_stream_id = (select gb_stream_id from wvp_gb_stream where app=#{app} AND stream=#{stream})") + int delByAppAndStream(String app, String stream); + + @Delete("DELETE from wvp_platform_gb_stream WHERE platform_id=#{platformId}") + int delByPlatformId(String platformId); + + @Select("SELECT " + + "pp.* " + + "FROM " + + "wvp_platform_gb_stream pgs " + + "LEFT JOIN wvp_platform pp ON pp.server_gb_id = pgs.platform_id " + + "LEFT join wvp_gb_stream gs ON gs.gb_stream_id = pgs.gb_stream_id " + + "WHERE " + + "gs.app =#{app} " + + "AND gs.stream =#{stream} ") + List selectByAppAndStream(String app, String stream); + + @Select("SELECT pgs.*, gs.gb_id from wvp_platform_gb_stream pgs " + + "LEFT join wvp_gb_stream gs ON pgs.gb_stream_id = gs.gb_stream_id " + + "WHERE gs.app=#{app} AND gs.stream=#{stream} AND pgs.platform_id=#{serverGBId}") + StreamProxyItem selectOne(String app, String stream, String serverGBId); + + @Select("select gs.* \n" + + "from wvp_gb_stream gs\n" + + " left join wvp_platform_gb_stream pgs\n" + + " on gs.gb_stream_id = pgs.gb_stream_id\n" + + "where pgs.platform_id=#{platformId} and pgs.catalog_id=#{catalogId}") + List queryChannelInParentPlatformAndCatalog(String platformId, String catalogId); + + @Select("select gs.gb_id as id, gs.name as name, pgs.platform_id as platform_id, pgs.catalog_id as catalog_id , 0 as children_count, 2 as type\n" + + "from wvp_gb_stream gs\n" + + " left join wvp_platform_gb_stream pgs\n" + + " on gs.gb_stream_id = pgs.gb_stream_id\n" + + "where pgs.platform_id=#{platformId} and pgs.catalog_id=#{catalogId}") + List queryChannelInParentPlatformAndCatalogForCatalog(String platformId, String catalogId); + + @Delete("DELETE from wvp_platform_gb_stream WHERE catalog_id=#{id}") + int delByCatalogId(String id); + + @Select(" ") + List queryPlatFormListForGBWithGBId(String app, String stream, List platforms); + + @Delete("DELETE from wvp_platform_gb_stream WHERE gb_stream_id = (select id from wvp_gb_stream where app=#{app} AND stream=#{stream}) AND platform_id=#{platformId}") + int delByAppAndStreamAndPlatform(String app, String stream, String platformId); + + @Delete("") + void delByGbStreams(List gbStreams); + + @Delete("") + void delByAppAndStreamsByPlatformId(List gbStreams, String platformId); + + @Delete("DELETE from wvp_platform_gb_stream WHERE platform_id=#{platformId} and catalog_id=#{catalogId}") + int delByPlatformAndCatalogId(String platformId, String catalogId); +} 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 new file mode 100644 index 000000000..3df746960 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/RoleMapper.java @@ -0,0 +1,34 @@ +package com.genersoft.iot.vmp.storager.dao; + +import com.genersoft.iot.vmp.storager.dao.dto.Role; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Mapper +@Repository +public interface RoleMapper { + + @Insert("INSERT INTO wvp_user_role (name, authority, create_time, update_time) VALUES" + + "(#{name}, #{authority}, #{createTime}, #{updateTime})") + int add(Role role); + + @Update(value = {" "}) + int update(Role role); + + @Delete("DELETE from wvp_user_role WHERE id != 1 and id=#{id}") + int delete(int id); + + @Select("select * from wvp_user_role WHERE id=#{id}") + Role selectById(int id); + + @Select("select * from wvp_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 new file mode 100644 index 000000000..a5a80c475 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java @@ -0,0 +1,89 @@ +package com.genersoft.iot.vmp.storager.dao; + +import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Mapper +@Repository +public interface StreamProxyMapper { + + @Insert("INSERT INTO wvp_stream_proxy (type, name, app, stream,media_server_id, url, src_url, dst_url, " + + "timeout_ms, ffmpeg_cmd_key, rtp_type, enable_audio, enable_mp4, enable, status, enable_remove_none_reader, enable_disable_none_reader, create_time) VALUES" + + "(#{type}, #{name}, #{app}, #{stream}, #{mediaServerId}, #{url}, #{srcUrl}, #{dstUrl}, " + + "#{timeoutMs}, #{ffmpegCmdKey}, #{rtpType}, #{enableAudio}, #{enableMp4}, #{enable}, #{status}, " + + "#{enableRemoveNoneReader}, #{enableDisableNoneReader}, #{createTime} )") + int add(StreamProxyItem streamProxyDto); + + @Update("UPDATE wvp_stream_proxy " + + "SET type=#{type}, " + + "name=#{name}," + + "app=#{app}," + + "stream=#{stream}," + + "url=#{url}, " + + "media_server_id=#{mediaServerId}, " + + "src_url=#{srcUrl}," + + "dst_url=#{dstUrl}, " + + "timeout_ms=#{timeoutMs}, " + + "ffmpeg_cmd_key=#{ffmpegCmdKey}, " + + "rtp_type=#{rtpType}, " + + "enable_audio=#{enableAudio}, " + + "enable=#{enable}, " + + "status=#{status}, " + + "enable_remove_none_reader=#{enableRemoveNoneReader}, " + + "enable_disable_none_reader=#{enableDisableNoneReader}, " + + "enable_mp4=#{enableMp4} " + + "WHERE app=#{app} AND stream=#{stream}") + int update(StreamProxyItem streamProxyDto); + + @Delete("DELETE FROM wvp_stream_proxy WHERE app=#{app} AND stream=#{stream}") + int del(String app, String stream); + + @Select("SELECT st.*, pgs.gb_id, pgs.name, pgs.longitude, pgs.latitude FROM wvp_stream_proxy st LEFT join wvp_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream order by st.create_time desc") + List selectAll(); + + @Select("SELECT st.*, pgs.gb_id, pgs.name, pgs.longitude, pgs.latitude FROM wvp_stream_proxy st LEFT join wvp_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.enable=#{enable} order by st.create_time desc") + List selectForEnable(boolean enable); + + @Select("SELECT st.*, pgs.gb_id, pgs.name, pgs.longitude, pgs.latitude FROM wvp_stream_proxy st LEFT join wvp_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.app=#{app} AND st.stream=#{stream} order by st.create_time desc") + StreamProxyItem selectOne(String app, String stream); + + @Select("SELECT st.*, pgs.gb_id, pgs.name, pgs.longitude, pgs.latitude FROM wvp_stream_proxy st " + + "LEFT join wvp_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream " + + "WHERE st.enable=#{enable} and st.media_server_id= #{id} order by st.create_time desc") + List selectForEnableInMediaServer(String id, boolean enable); + + @Select("SELECT st.*, pgs.gb_id, pgs.name, pgs.longitude, pgs.latitude FROM wvp_stream_proxy st " + + "LEFT join wvp_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream " + + "WHERE st.media_server_id= #{id} order by st.create_time desc") + List selectInMediaServer(String id); + + @Update("UPDATE wvp_stream_proxy " + + "SET status=#{status} " + + "WHERE media_server_id=#{mediaServerId}") + void updateStatusByMediaServerId(String mediaServerId, boolean status); + + @Update("UPDATE wvp_stream_proxy " + + "SET status=#{status} " + + "WHERE app=#{app} AND stream=#{stream}") + int updateStatus(String app, String stream, boolean status); + + @Delete("DELETE FROM wvp_stream_proxy WHERE enable_remove_none_reader=true AND media_server_id=#{mediaServerId}") + void deleteAutoRemoveItemByMediaServerId(String mediaServerId); + + @Select("SELECT st.*, pgs.gb_id, pgs.name, pgs.longitude, pgs.latitude FROM wvp_stream_proxy st LEFT join wvp_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.enable_remove_none_reader=true AND st.media_server_id=#{mediaServerId} order by st.create_time desc") + List selectAutoRemoveItemByMediaServerId(String mediaServerId); + + @Select("select count(1) as total, sum(status) as online from wvp_stream_proxy") + ResourceBaseInfo getOverview(); + + @Select("select count(1) from wvp_stream_proxy") + + int getAllCount(); + + @Select("select count(1) from wvp_stream_proxy where status = true") + int getOnline(); +} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java new file mode 100644 index 000000000..4efc058d0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java @@ -0,0 +1,197 @@ +package com.genersoft.iot.vmp.storager.dao; + +import com.genersoft.iot.vmp.gb28181.bean.GbStream; +import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; +import com.genersoft.iot.vmp.service.bean.StreamPushItemFromRedis; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Mapper +@Repository +public interface StreamPushMapper { + + @Insert("INSERT INTO wvp_stream_push (app, stream, total_reader_count, origin_type, origin_type_str, " + + "push_time, alive_second, media_server_id, update_time, create_time, push_ing, self) VALUES" + + "(#{app}, #{stream}, #{totalReaderCount}, #{originType}, #{originTypeStr}, " + + "#{pushTime}, #{aliveSecond}, #{mediaServerId} , #{updateTime} , #{createTime}, " + + "#{pushIng}, #{self} )") + int add(StreamPushItem streamPushItem); + + + @Update(value = {" "}) + int update(StreamPushItem streamPushItem); + + @Delete("DELETE FROM wvp_stream_push WHERE app=#{app} AND stream=#{stream}") + int del(String app, String stream); + + @Delete("") + int delAllWithoutGBId(List streamPushItems); + + @Delete("") + int delAll(List streamPushItems); + + @Delete("") + int delAllForGbStream(List gbStreams); + + + @Select(value = {" "}) + List selectAllForList(String query, Boolean pushing, String mediaServerId); + + @Select("SELECT st.*, gs.gb_id, gs.name, gs.longitude, gs.latitude FROM wvp_stream_push st LEFT join wvp_gb_stream gs on st.app = gs.app AND st.stream = gs.stream order by st.create_time desc") + List selectAll(); + + @Select("SELECT st.*, gs.gb_id, gs.name, gs.longitude, gs.latitude FROM wvp_stream_push st LEFT join wvp_gb_stream gs on st.app = gs.app AND st.stream = gs.stream WHERE st.app=#{app} AND st.stream=#{stream}") + StreamPushItem selectOne(String app, String stream); + + @Insert("") + @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") + int addAll(List streamPushItems); + + @Delete("DELETE FROM wvp_stream_push") + void clear(); + + @Delete("delete" + + " from wvp_stream_push " + + " where id in " + + " (select temp.id from " + + " (select wgs.gb_stream_id as id " + + " from wvp_gb_stream wgs" + + " left join wvp_stream_push sp on sp.id = wgs.gb_stream_id" + + " where wgs.gb_id is null and wgs.media_server_id = #{mediaServerId}) temp)" + ) + void deleteWithoutGBId(String mediaServerId); + + @Select("SELECT * FROM wvp_stream_push WHERE media_server_id=#{mediaServerId}") + List selectAllByMediaServerId(String mediaServerId); + + @Select("SELECT sp.* FROM wvp_stream_push sp left join wvp_gb_stream gs on gs.app = sp.app and gs.stream= sp.stream WHERE sp.media_server_id=#{mediaServerId} and gs.gb_id is null") + List selectAllByMediaServerIdWithOutGbID(String mediaServerId); + + @Update("UPDATE wvp_stream_push " + + "SET status=#{status} " + + "WHERE app=#{app} AND stream=#{stream}") + int updateStatus(String app, String stream, boolean status); + + @Update("UPDATE wvp_stream_push " + + "SET push_ing=#{pushIng} " + + "WHERE app=#{app} AND stream=#{stream}") + int updatePushStatus(String app, String stream, boolean pushIng); + + @Update("UPDATE wvp_stream_push " + + "SET status=#{status} " + + "WHERE media_server_id=#{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 wvp_stream_push sp left join wvp_gb_stream gs on sp.app = gs.app AND sp.stream = gs.stream where sp.status = true") + List getOnlinePusherForGb(); + + @Update("UPDATE wvp_stream_push SET status=0") + void setAllStreamOffline(); + + @Select("SELECT CONCAT(app,stream) from wvp_gb_stream") + List getAllAppAndStream(); + + @Select("select count(1) from wvp_stream_push ") + int getAllCount(); + + @Select(value = {" "}) + int getAllOnline(Boolean usePushingAsStatus); + + @Select("") + List getListIn(List streamPushItems); +} 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 new file mode 100644 index 000000000..c96aeaf35 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/UserMapper.java @@ -0,0 +1,59 @@ +package com.genersoft.iot.vmp.storager.dao; + +import com.genersoft.iot.vmp.storager.dao.dto.User; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Mapper +@Repository +public interface UserMapper { + + @Insert("INSERT INTO wvp_user (username, password, role_id, push_key, create_time, update_time) VALUES" + + "(#{username}, #{password}, #{role.id}, #{pushKey}, #{createTime}, #{updateTime})") + int add(User user); + + @Update(value = {" "}) + int update(User user); + + @Delete("DELETE from wvp_user WHERE id != 1 and id=#{id}") + int delete(int id); + + @Select("select u.*, r.id as role_id, r.name as roleName, r.authority as roleAuthority , r.create_time as role_create_time , r.update_time as role_update_time from wvp_user u, wvp_user_role r WHERE u.role_id=r.id and u.username=#{username} AND u.password=#{password}") + @Results(id = "roleMap", value = { + @Result(column = "role_id", property = "role.id"), + @Result(column = "role_name", property = "role.name"), + @Result(column = "role_authority", property = "role.authority"), + @Result(column = "role_create_time", property = "role.createTime"), + @Result(column = "role_update_time", property = "role.updateTime") + }) + User select(String username, String password); + + @Select("select u.*, r.id as role_id, r.name as role_name, r.authority as role_authority , r.create_time as role_create_time , r.update_time as role_update_time from wvp_user u, wvp_user_role r WHERE u.role_id=r.id and u.id=#{id}") + @ResultMap(value="roleMap") + User selectById(int id); + + @Select("select u.*, r.id as role_id, r.name as role_name, r.authority as role_authority , r.create_time as role_create_time , r.update_time as role_update_time from wvp_user u, wvp_user_role r WHERE u.role_id=r.id and u.username=#{username}") + @ResultMap(value="roleMap") + User getUserByUsername(String username); + + @Select("select u.*, r.id as role_id, r.name as role_name, r.authority as role_authority , r.create_time as role_create_time , r.update_time as role_update_time from wvp_user u, wvp_user_role r WHERE u.role_id=r.id") + @ResultMap(value="roleMap") + List selectAll(); + + @Select("select u.id, u.username,u.push_key,u.role_id, r.id as role_id, r.name as role_name, r.authority as role_authority , r.create_time as role_create_time , r.update_time as role_update_time from wvp_user u join wvp_user_role r on u.role_id=r.id") + @ResultMap(value="roleMap") + List getUsers(); + + @Update("UPDATE wvp_user set push_key=#{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 000000000..e8b91e7c0 --- /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/LogDto.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/LogDto.java new file mode 100644 index 000000000..cfe29f55f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/LogDto.java @@ -0,0 +1,86 @@ +package com.genersoft.iot.vmp.storager.dao.dto; + +public class LogDto { + + private int id; + private String name; + private String type; + private String uri; + private String address; + private String result; + private long timing; + private String username; + private String createTime; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getResult() { + return result; + } + + public void setResult(String result) { + this.result = result; + } + + public long getTiming() { + return timing; + } + + public void setTiming(long timing) { + this.timing = timing; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } +} 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 000000000..16f663631 --- /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/RecordInfo.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/RecordInfo.java new file mode 100644 index 000000000..278e3e432 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/RecordInfo.java @@ -0,0 +1,133 @@ +package com.genersoft.iot.vmp.storager.dao.dto; + +/** + * 录像记录 + */ +public class RecordInfo { + + /** + * ID + */ + private int id; + + /** + * 应用名 + */ + private String app; + + /** + * 流ID + */ + private String stream; + + /** + * 对应的zlm流媒体的ID + */ + private String mediaServerId; + + /** + * 创建时间 + */ + private String createTime; + + /** + * 类型 对应zlm的 originType + * unknown = 0, + * rtmp_push=1, + * rtsp_push=2, + * rtp_push=3, + * pull=4, + * ffmpeg_pull=5, + * mp4_vod=6, + * device_chn=7, + * rtc_push=8 + */ + private int type; + + /** + * 国标录像时的设备ID + */ + private String deviceId; + + /** + * 国标录像时的通道ID + */ + private String channelId; + + /** + * 拉流代理录像时的名称 + */ + private String name; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/Role.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/Role.java new file mode 100644 index 000000000..44631f87a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/Role.java @@ -0,0 +1,50 @@ +package com.genersoft.iot.vmp.storager.dao.dto; + +public class Role { + + private int id; + private String name; + private String authority; + private String createTime; + private String updateTime; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAuthority() { + return authority; + } + + public void setAuthority(String authority) { + this.authority = authority; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } +} 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 new file mode 100644 index 000000000..c9b4002ed --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/User.java @@ -0,0 +1,68 @@ +package com.genersoft.iot.vmp.storager.dao.dto; + +public class User { + + private int id; + private String username; + private String password; + private String createTime; + private String updateTime; + private String pushKey; + private Role role; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public Role getRole() { + return role; + } + + 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 958b38a10..8b23f2272 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,159 +1,640 @@ package com.genersoft.iot.vmp.storager.impl; -import com.genersoft.iot.vmp.common.StreamInfo; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.common.SystemAllInfo; import com.genersoft.iot.vmp.common.VideoManagerConstants; -import com.genersoft.iot.vmp.conf.MediaServerConfig; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.AlarmChannelMessage; +import com.genersoft.iot.vmp.gb28181.bean.Device; +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.media.zlm.dto.StreamAuthorityInfo; +import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; +import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; +import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; import com.genersoft.iot.vmp.storager.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.JsonUtil; +import com.genersoft.iot.vmp.utils.SystemInfoUtils; import com.genersoft.iot.vmp.utils.redis.RedisUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.time.Duration; +import java.util.*; +@SuppressWarnings("rawtypes") @Component public class RedisCatchStorageImpl implements IRedisCatchStorage { - @Autowired - private RedisUtil redis; + private final Logger logger = LoggerFactory.getLogger(RedisCatchStorageImpl.class); @Autowired private DeviceChannelMapper deviceChannelMapper; + @Autowired + private UserSetting userSetting; - /** - * 开始播放时将流存入redis - * - * @return - */ - @Override - public boolean startPlay(StreamInfo stream) { - return redis.set(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX, stream.getStreamId(),stream.getDeviceID(), stream.getChannelId()), - stream); - } + @Autowired + private RedisTemplate redisTemplate; - /** - * 停止播放时从redis删除 - * - * @return - */ - @Override - public boolean stopPlay(StreamInfo streamInfo) { - if (streamInfo == null) return false; - return redis.del(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX, - streamInfo.getStreamId(), - streamInfo.getDeviceID(), - streamInfo.getChannelId())); - } - - /** - * 查询播放列表 - * @return - */ - @Override - public StreamInfo queryPlay(StreamInfo streamInfo) { - return (StreamInfo)redis.get(String.format("%S_%s_%s_%s", - VideoManagerConstants.PLAYER_PREFIX, - streamInfo.getStreamId(), - streamInfo.getDeviceID(), - streamInfo.getChannelId())); - } - @Override - public StreamInfo queryPlayByStreamId(String steamId) { - List playLeys = redis.scan(String.format("%S_%s_*", VideoManagerConstants.PLAYER_PREFIX, steamId)); - if (playLeys == null || playLeys.size() == 0) return null; - return (StreamInfo)redis.get(playLeys.get(0).toString()); - } + @Autowired + private StringRedisTemplate stringRedisTemplate; @Override - public StreamInfo queryPlaybackByStreamId(String steamId) { - List playLeys = redis.scan(String.format("%S_%s_*", VideoManagerConstants.PLAY_BLACK_PREFIX, steamId)); - if (playLeys == null || playLeys.size() == 0) return null; - return (StreamInfo)redis.get(playLeys.get(0).toString()); - } + public Long getCSEQ() { + String key = VideoManagerConstants.SIP_CSEQ_PREFIX + userSetting.getServerId(); - @Override - public StreamInfo queryPlayByDevice(String deviceId, String code) { -// List playLeys = redis.keys(String.format("%S_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX, - List playLeys = redis.scan(String.format("%S_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX, - deviceId, - code)); - if (playLeys == null || playLeys.size() == 0) return null; - return (StreamInfo)redis.get(playLeys.get(0).toString()); - } - - /** - * 更新流媒体信息 - * @param mediaServerConfig - * @return - */ - @Override - public boolean updateMediaInfo(MediaServerConfig mediaServerConfig) { - return redis.set(VideoManagerConstants.MEDIA_SERVER_PREFIX,mediaServerConfig); - } - - /** - * 获取流媒体信息 - * @return - */ - @Override - public MediaServerConfig getMediaInfo() { - return (MediaServerConfig)redis.get(VideoManagerConstants.MEDIA_SERVER_PREFIX); - } - - @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_*", VideoManagerConstants.PLAYER_PREFIX, 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); - streamInfos.put(streamInfo.getDeviceID() + "_" + streamInfo.getChannelId(), streamInfo); + Long result = redisTemplate.opsForValue().increment(key, 1L); + if (result != null && result > Integer.MAX_VALUE) { + redisTemplate.opsForValue().set(key, 1); + result = 1L; } - return streamInfos; + return result; } - @Override - public boolean startPlayback(StreamInfo stream) { - return redis.set(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, stream.getStreamId(),stream.getDeviceID(), stream.getChannelId()), - stream); - } + public Long getSN(String method) { + String key = VideoManagerConstants.SIP_SN_PREFIX + userSetting.getServerId() + "_" + method; - - @Override - public boolean stopPlayback(StreamInfo streamInfo) { - if (streamInfo == null) return false; - DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(streamInfo.getDeviceID(), streamInfo.getChannelId()); - if (deviceChannel != null) { - deviceChannel.setStreamId(null); - deviceChannel.setDeviceId(streamInfo.getDeviceID()); - deviceChannelMapper.update(deviceChannel); + Long result = redisTemplate.opsForValue().increment(key, 1L); + if (result != null && result > Integer.MAX_VALUE) { + redisTemplate.opsForValue().set(key, 1); + result = 1L; } - return redis.del(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, - streamInfo.getStreamId(), - streamInfo.getDeviceID(), - streamInfo.getChannelId())); + return result; } @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", VideoManagerConstants.PLAY_BLACK_PREFIX, - deviceId, - code)); - if (playLeys == null || playLeys.size() == 0) { - playLeys = redis.scan(String.format("%S_*_*_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, - deviceId)); + public void resetAllCSEQ() { + String scanKey = VideoManagerConstants.SIP_CSEQ_PREFIX + userSetting.getServerId() + "_*"; + List keys = RedisUtil.scan(redisTemplate, scanKey); + for (Object o : keys) { + String key = (String) o; + redisTemplate.opsForValue().set(key, 1); } - if (playLeys == null || playLeys.size() == 0) return null; - return (StreamInfo)redis.get(playLeys.get(0).toString()); + } + + @Override + public void resetAllSN() { + String scanKey = VideoManagerConstants.SIP_SN_PREFIX + userSetting.getServerId() + "_*"; + List keys = RedisUtil.scan(redisTemplate, scanKey); + for (Object o : keys) { + String key = (String) o; + redisTemplate.opsForValue().set(key, 1); + } + } + + @Override + public void updatePlatformCatchInfo(ParentPlatformCatch parentPlatformCatch) { + String key = VideoManagerConstants.PLATFORM_CATCH_PREFIX + userSetting.getServerId() + "_" + parentPlatformCatch.getId(); + redisTemplate.opsForValue().set(key, parentPlatformCatch); + } + + @Override + public ParentPlatformCatch queryPlatformCatchInfo(String platformGbId) { + return (ParentPlatformCatch)redisTemplate.opsForValue().get(VideoManagerConstants.PLATFORM_CATCH_PREFIX + userSetting.getServerId() + "_" + platformGbId); + } + + @Override + public void delPlatformCatchInfo(String platformGbId) { + redisTemplate.delete(VideoManagerConstants.PLATFORM_CATCH_PREFIX + userSetting.getServerId() + "_" + platformGbId); + } + + @Override + public void delPlatformKeepalive(String platformGbId) { + redisTemplate.delete(VideoManagerConstants.PLATFORM_KEEPALIVE_PREFIX + userSetting.getServerId() + "_" + platformGbId); + } + + @Override + public void delPlatformRegister(String platformGbId) { + redisTemplate.delete(VideoManagerConstants.PLATFORM_REGISTER_PREFIX + userSetting.getServerId() + "_" + platformGbId); + } + + + @Override + public void updatePlatformRegisterInfo(String callId, PlatformRegisterInfo platformRegisterInfo) { + String key = VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetting.getServerId() + "_" + callId; + Duration duration = Duration.ofSeconds(30L); + redisTemplate.opsForValue().set(key, platformRegisterInfo, duration); + } + + + @Override + public PlatformRegisterInfo queryPlatformRegisterInfo(String callId) { + return (PlatformRegisterInfo)redisTemplate.opsForValue().get(VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetting.getServerId() + "_" + callId); + } + + @Override + public void delPlatformRegisterInfo(String callId) { + redisTemplate.delete(VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetting.getServerId() + "_" + callId); + } + + @Override + public void updateSendRTPSever(SendRtpItem sendRtpItem) { + + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_" + + sendRtpItem.getMediaServerId() + "_" + + sendRtpItem.getPlatformId() + "_" + + sendRtpItem.getChannelId() + "_" + + sendRtpItem.getStreamId() + "_" + + sendRtpItem.getCallId(); + redisTemplate.opsForValue().set(key, sendRtpItem); + } + + @Override + public SendRtpItem querySendRTPServer(String platformGbId, String channelId, String streamId, String callId) { + if (platformGbId == null) { + platformGbId = "*"; + } + if (channelId == null) { + channelId = "*"; + } + if (streamId == null) { + streamId = "*"; + } + if (callId == null) { + callId = "*"; + } + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_*_" + + platformGbId + "_" + + channelId + "_" + + streamId + "_" + + callId; + List scan = RedisUtil.scan(redisTemplate, key); + if (scan.size() > 0) { + return (SendRtpItem)redisTemplate.opsForValue().get(scan.get(0)); + }else { + return null; + } + } + + @Override + public List 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(redisTemplate, key); + List result = new ArrayList<>(); + for (Object o : scan) { + result.add((SendRtpItem) redisTemplate.opsForValue().get(o)); + } + return result; + } + + @Override + public List querySendRTPServerByStream(String stream) { + if (stream == null) { + return null; + } + String platformGbId = "*"; + String callId = "*"; + String channelId = "*"; + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_*_" + + platformGbId + "_" + + channelId + "_" + + stream + "_" + + callId; + List scan = RedisUtil.scan(redisTemplate, key); + List result = new ArrayList<>(); + for (Object o : scan) { + result.add((SendRtpItem) redisTemplate.opsForValue().get(o)); + } + return result; + } + + @Override + public List querySendRTPServer(String platformGbId) { + if (platformGbId == null) { + platformGbId = "*"; + } + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_*_" + + platformGbId + "_*" + "_*" + "_*"; + List queryResult = RedisUtil.scan(redisTemplate, key); + List result= new ArrayList<>(); + + for (Object o : queryResult) { + String keyItem = (String) o; + result.add((SendRtpItem) redisTemplate.opsForValue().get(keyItem)); + } + + return result; + } + + /** + * 删除RTP推送信息缓存 + */ + @Override + public void deleteSendRTPServer(String platformGbId, String channelId, String callId, String streamId) { + if (streamId == null) { + streamId = "*"; + } + if (callId == null) { + callId = "*"; + } + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_*_" + + platformGbId + "_" + + channelId + "_" + + streamId + "_" + + callId; + List scan = RedisUtil.scan(redisTemplate, key); + if (scan.size() > 0) { + for (Object keyStr : scan) { + redisTemplate.delete(keyStr); + } + } + } + + @Override + public List queryAllSendRTPServer() { + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_*"; + List queryResult = RedisUtil.scan(redisTemplate, key); + List result= new ArrayList<>(); + + for (Object o : queryResult) { + String keyItem = (String) o; + result.add((SendRtpItem) redisTemplate.opsForValue().get(keyItem)); + } + + return result; + } + + /** + * 查询某个通道是否存在上级点播(RTP推送) + */ + @Override + public boolean isChannelSendingRTP(String channelId) { + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_*_*_" + + channelId + "*_" + "*_"; + List RtpStreams = RedisUtil.scan(redisTemplate, key); + return RtpStreams.size() > 0; + } + + @Override + public void updateWVPInfo(JSONObject jsonObject, int time) { + String key = VideoManagerConstants.WVP_SERVER_PREFIX + userSetting.getServerId(); + Duration duration = Duration.ofSeconds(time); + redisTemplate.opsForValue().set(key, jsonObject, duration); + } + + @Override + public void sendStreamChangeMsg(String type, JSONObject jsonObject) { + String key = VideoManagerConstants.WVP_MSG_STREAM_CHANGE_PREFIX + type; + logger.info("[redis 流变化事件] 发送 {}: {}", key, jsonObject.toString()); + redisTemplate.convertAndSend(key, jsonObject); + } + + @Override + 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()); + } + redisTemplate.opsForValue().set(key, onStreamChangedHookParam); + } + + @Override + public void removeStream(String mediaServerId, String type, String app, String streamId) { + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type + "_" + app + "_" + streamId + "_" + mediaServerId; + redisTemplate.delete(key); + } + + @Override + public void removeStream(String mediaServerId, String type) { + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type + "_*_*_" + mediaServerId; + List streams = RedisUtil.scan(redisTemplate, key); + for (Object stream : streams) { + redisTemplate.delete(stream); + } + } + + @Override + 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(redisTemplate, key); + for (Object stream : streams) { + OnStreamChangedHookParam onStreamChangedHookParam = (OnStreamChangedHookParam)redisTemplate.opsForValue().get(stream); + result.add(onStreamChangedHookParam); + } + return result; + } + + @Override + public void updateDevice(Device device) { + String key = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_" + device.getDeviceId(); + redisTemplate.opsForValue().set(key, device); + } + + @Override + public void removeDevice(String deviceId) { + String key = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_" + deviceId; + redisTemplate.delete(key); + } + + @Override + public void removeAllDevice() { + String scanKey = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_*"; + List keys = RedisUtil.scan(redisTemplate, scanKey); + for (Object key : keys) { + redisTemplate.delete(key); + } + } + + @Override + public List getAllDevices() { + String scanKey = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_*"; + List result = new ArrayList<>(); + List keys = RedisUtil.scan(redisTemplate, scanKey); + for (Object o : keys) { + String key = (String) o; + Device device = JsonUtil.redisJsonToObject(redisTemplate, key, Device.class); + if (Objects.nonNull(device)) { // 只取没有存过得 + result.add(JsonUtil.redisJsonToObject(redisTemplate, key, Device.class)); + } + } + + return result; + } + + @Override + public Device getDevice(String deviceId) { + String key = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_" + deviceId; + return JsonUtil.redisJsonToObject(redisTemplate, key, Device.class); + } + + @Override + public void updateGpsMsgInfo(GPSMsgInfo gpsMsgInfo) { + String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_" + gpsMsgInfo.getId(); + Duration duration = Duration.ofSeconds(60L); + redisTemplate.opsForValue().set(key, gpsMsgInfo, duration); // 默认GPS消息保存1分钟 + } + + @Override + public GPSMsgInfo getGpsMsgInfo(String gbId) { + String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_" + gbId; + return JsonUtil.redisJsonToObject(redisTemplate, key, GPSMsgInfo.class); + } + + @Override + public List getAllGpsMsgInfo() { + String scanKey = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_*"; + List result = new ArrayList<>(); + List keys = RedisUtil.scan(redisTemplate, scanKey); + for (Object o : keys) { + String key = (String) o; + GPSMsgInfo gpsMsgInfo = JsonUtil.redisJsonToObject(redisTemplate, key, GPSMsgInfo.class); + if (Objects.nonNull(gpsMsgInfo) && !gpsMsgInfo.isStored()) { // 只取没有存过得 + result.add(JsonUtil.redisJsonToObject(redisTemplate, key, GPSMsgInfo.class)); + } + } + + return result; + } + + @Override + public void updateStreamAuthorityInfo(String app, String stream, StreamAuthorityInfo streamAuthorityInfo) { + String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId() + "_" + app+ "_" + stream; + redisTemplate.opsForValue().set(key, streamAuthorityInfo); + } + + @Override + public void removeStreamAuthorityInfo(String app, String stream) { + String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId() + "_" + app+ "_" + stream ; + redisTemplate.delete(key); + } + + @Override + public StreamAuthorityInfo getStreamAuthorityInfo(String app, String stream) { + String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId() + "_" + app+ "_" + stream ; + return JsonUtil.redisJsonToObject(redisTemplate, key, StreamAuthorityInfo.class); + + } + + @Override + public List getAllStreamAuthorityInfo() { + String scanKey = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId() + "_*_*" ; + List result = new ArrayList<>(); + List keys = RedisUtil.scan(redisTemplate, scanKey); + for (Object o : keys) { + String key = (String) o; + result.add(JsonUtil.redisJsonToObject(redisTemplate, key, StreamAuthorityInfo.class)); + } + 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(redisTemplate, scanKey); + if (keys.size() > 0) { + String key = (String) keys.get(0); + result = JsonUtil.redisJsonToObject(redisTemplate, key, OnStreamChangedHookParam.class); + } + + 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", String.valueOf(cpuInfo)); + redisTemplate.opsForList().rightPush(key, infoMap); + // 每秒一个,最多只存30个 + Long size = redisTemplate.opsForList().size(key); + if (size != null && size >= 30) { + for (int i = 0; i < size - 30; i++) { + redisTemplate.opsForList().leftPop(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", String.valueOf(memInfo)); + redisTemplate.opsForList().rightPush(key, infoMap); + // 每秒一个,最多只存30个 + Long size = redisTemplate.opsForList().size(key); + if (size != null && size >= 30) { + for (int i = 0; i < size - 30; i++) { + redisTemplate.opsForList().leftPop(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)); + } + redisTemplate.opsForList().rightPush(key, infoMap); + // 每秒一个,最多只存30个 + Long size = redisTemplate.opsForList().size(key); + if (size != null && size >= 30) { + for (int i = 0; i < size - 30; i++) { + redisTemplate.opsForList().leftPop(key); + } + } + } + + @Override + public void addDiskInfo(List> diskInfo) { + + String key = VideoManagerConstants.SYSTEM_INFO_DISK_PREFIX + userSetting.getServerId(); + redisTemplate.opsForValue().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(redisTemplate.opsForList().range(cpuKey, 0, -1)); + systemAllInfo.setMem(redisTemplate.opsForList().range(memKey, 0, -1)); + systemAllInfo.setNet(redisTemplate.opsForList().range(netKey, 0, -1)); + + systemAllInfo.setDisk(redisTemplate.opsForValue().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()); + redisTemplate.convertAndSend(key, jsonObject); + } + + @Override + public void sendStreamPushRequestedMsg(MessageForPushChannel msg) { + String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_REQUESTED; + logger.info("[redis发送通知] 发送 推流被请求 {}: {}/{}", key, msg.getApp(), msg.getStream()); + redisTemplate.convertAndSend(key, JSON.toJSON(msg)); + } + + @Override + public void sendAlarmMsg(AlarmChannelMessage msg) { + // 此消息用于对接第三方服务下级来的消息内容 + String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM; + logger.info("[redis发送通知] 发送 报警{}: {}", key, JSON.toJSON(msg)); + redisTemplate.convertAndSend(key, JSON.toJSON(msg)); + } + + @Override + public boolean deviceIsOnline(String deviceId) { + return getDevice(deviceId).isOnLine(); + } + + + @Override + public void sendStreamPushRequestedMsgForStatus() { + String key = VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED; + logger.info("[redis通知] 发送 获取所有推流设备的状态"); + JSONObject jsonObject = new JSONObject(); + jsonObject.put(key, key); + redisTemplate.convertAndSend(key, jsonObject); + } + + @Override + public int getPushStreamCount(String id) { + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_PUSH_*_*_" + id; + return RedisUtil.scan(redisTemplate, key).size(); + } + + @Override + public int getProxyStreamCount(String id) { + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_PULL_*_*_" + id; + return RedisUtil.scan(redisTemplate, key).size(); + } + + @Override + public int getGbSendCount(String id) { + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_*_" + id + "_*"; + return RedisUtil.scan(redisTemplate, key).size(); + } + + @Override + public void sendDeviceOrChannelStatus(String deviceId, String channelId, boolean online) { + String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_DEVICE_STATUS; + logger.info("[redis通知] 发送 推送设备/通道状态, {}/{}-{}", deviceId, channelId, online); + StringBuilder msg = new StringBuilder(); + msg.append(deviceId); + if (channelId != null) { + msg.append(":").append(channelId); + } + msg.append(" ").append(online? "ON":"OFF"); + logger.info("[redis通知] 推送状态-> {} ", msg); + // 使用 RedisTemplate 发送字符串消息会导致发送的消息多带了双引号 + stringRedisTemplate.convertAndSend(key, msg.toString()); + } + + @Override + public void sendChannelAddOrDelete(String deviceId, String channelId, boolean add) { + String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_DEVICE_STATUS; + + + StringBuilder msg = new StringBuilder(); + msg.append(deviceId); + if (channelId != null) { + msg.append(":").append(channelId); + } + msg.append(" ").append(add? "ADD":"DELETE"); + logger.info("[redis通知] 推送通道-> {}", msg); + // 使用 RedisTemplate 发送字符串消息会导致发送的消息多带了双引号 + stringRedisTemplate.convertAndSend(key, msg.toString()); + } + + @Override + public void sendPlatformStartPlayMsg(MessageForPushChannel msg) { + String key = VideoManagerConstants.VM_MSG_STREAM_START_PLAY_NOTIFY; + logger.info("[redis发送通知] 发送 推流被上级平台观看 {}: {}/{}->{}", key, msg.getApp(), msg.getStream(), msg.getPlatFormId()); + redisTemplate.convertAndSend(key, JSON.toJSON(msg)); + } + + @Override + public void sendPlatformStopPlayMsg(MessageForPushChannel msg) { + String key = VideoManagerConstants.VM_MSG_STREAM_STOP_PLAY_NOTIFY; + logger.info("[redis发送通知] 发送 上级平台停止观看 {}: {}/{}->{}", key, msg.getApp(), msg.getStream(), msg.getPlatFormId()); + redisTemplate.convertAndSend(key, JSON.toJSON(msg)); } } 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 000000000..1d4c35535 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java @@ -0,0 +1,1062 @@ +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.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.stereotype.Component; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 视频设备数据存储-jdbc实现 + * swwheihei + * 2020年5月6日 下午2:31:42 + */ +@SuppressWarnings("rawtypes") +@Component +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<>(); + + List updateChannels = new ArrayList<>(); + List addChannels = new ArrayList<>(); + StringBuilder stringBuilder = new StringBuilder(); + Map subContMap = new HashMap<>(); + + // 数据去重 + Set gbIdSet = new HashSet<>(); + for (DeviceChannel deviceChannel : deviceChannelList) { + if (gbIdSet.contains(deviceChannel.getChannelId())) { + stringBuilder.append(deviceChannel.getChannelId()).append(","); + continue; + } + gbIdSet.add(deviceChannel.getChannelId()); + if (allChannelMap.containsKey(deviceChannel.getChannelId())) { + deviceChannel.setStreamId(allChannelMap.get(deviceChannel.getChannelId()).getStreamId()); + deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).isHasAudio()); + if (allChannelMap.get(deviceChannel.getChannelId()).isStatus() !=deviceChannel.isStatus()){ + List strings = platformChannelMapper.queryParentPlatformByChannelId(deviceChannel.getChannelId()); + if (!CollectionUtils.isEmpty(strings)){ + strings.forEach(platformId->{ + eventPublisher.catalogEventPublish(platformId, deviceChannel, deviceChannel.isStatus()?CatalogEvent.ON:CatalogEvent.OFF); + }); + } + + } + deviceChannel.setUpdateTime(DateUtil.getNow()); + updateChannels.add(deviceChannel); + }else { + deviceChannel.setCreateTime(DateUtil.getNow()); + deviceChannel.setUpdateTime(DateUtil.getNow()); + addChannels.add(deviceChannel); + } + 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++); + } + } + } + if (channels.size() > 0) { + for (DeviceChannel channel : channels) { + if (subContMap.get(channel.getChannelId()) != null){ + Integer count = subContMap.get(channel.getChannelId()); + if (count > 0) { + channel.setSubCount(count); + channel.setParental(1); + } + } + } + } + + if (stringBuilder.length() > 0) { + logger.info("[目录查询]收到的数据存在重复: {}" , stringBuilder); + } + if(CollectionUtils.isEmpty(channels)){ + logger.info("通道重设,数据为空={}" , deviceChannelList); + return false; + } + try { + int cleanChannelsResult = deviceChannelMapper.cleanChannelsNotInList(deviceId, channels); + + int limitCount = 300; + boolean result = cleanChannelsResult < 0; + if (!result && 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 (!result && updateChannels.size() > 0) { + if (updateChannels.size() > limitCount) { + for (int i = 0; i < updateChannels.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > updateChannels.size()) { + toIndex = updateChannels.size(); + } + result = result || deviceChannelMapper.batchUpdate(updateChannels.subList(i, toIndex)) < 0; + } + }else { + result = result || deviceChannelMapper.batchUpdate(updateChannels) < 0; + } + } + + if (result) { + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + } + dataSourceTransactionManager.commit(transactionStatus); //手动提交 + return true; + }catch (Exception e) { + logger.error("未处理的异常 ", e); + dataSourceTransactionManager.rollback(transactionStatus); + return false; + } + + } + + + @Override + public 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()); + deviceChannel.setUpdateTime(DateUtil.getNow()); + updateChannels.add(deviceChannel); + }else { + deviceChannel.setCreateTime(DateUtil.getNow()); + 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.batchAddOrUpdate(addChannels.subList(i, toIndex)) < 0; + } + }else { + result = result || deviceChannelMapper.batchAddOrUpdate(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) { + logger.error("未处理的异常 ", e); + dataSourceTransactionManager.rollback(transactionStatus); + return false; + } + } + + @Override + public void deviceChannelOnline(String deviceId, String channelId) { + deviceChannelMapper.online(deviceId, channelId); + } + + @Override + public void deviceChannelOffline(String deviceId, String channelId) { + deviceChannelMapper.offline(deviceId, channelId); + } + + @Override + public void startPlay(String deviceId, String channelId, String streamId) { + deviceChannelMapper.startPlay(deviceId, channelId, streamId); + } + + @Override + public void stopPlay(String deviceId, String channelId) { + deviceChannelMapper.stopPlay(deviceId, channelId); + } + + /** + * 获取设备 + * + * @param deviceId 设备ID + * @return Device 设备对象 + */ + @Override + public Device queryVideoDevice(String deviceId) { + return deviceMapper.getDeviceByDeviceId(deviceId); + } + + @Override + public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, Boolean catalogUnderDevice, int page, int count) { + // 获取到所有正在播放的流 + PageHelper.startPage(page, count); + List all; + if (catalogUnderDevice != null && catalogUnderDevice) { + all = deviceChannelMapper.queryChannels(deviceId, deviceId, query, hasSubChannel, online,null); + // 海康设备的parentId是SIP id + List deviceChannels = deviceChannelMapper.queryChannels(deviceId, sipConfig.getId(), query, hasSubChannel, online,null); + all.addAll(deviceChannels); + }else { + all = deviceChannelMapper.queryChannels(deviceId, null, query, hasSubChannel, online,null); + } + return new PageInfo<>(all); + } + + @Override + public List queryChannelsByDeviceIdWithStartAndLimit(String deviceId, List channelIds, String query, Boolean hasSubChannel, Boolean online, int start, int limit) { + return deviceChannelMapper.queryChannelsByDeviceIdWithStartAndLimit(deviceId, channelIds, null, query, hasSubChannel, online, start, limit); + } + + + @Override + public List queryChannelsByDeviceId(String deviceId,Boolean online,List channelIds) { + return deviceChannelMapper.queryChannels(deviceId, null,null, null, online,channelIds); + } + + @Override + public List queryChannelsByDeviceId(String deviceId, List channelIds, Boolean online) { + return deviceChannelMapper.queryChannelsWithDeviceInfo(deviceId, null,null, null, online,channelIds); + } + + @Override + public PageInfo querySubChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online, int page, int count) { + PageHelper.startPage(page, count); + List all = deviceChannelMapper.queryChannels(deviceId, parentChannelId, query, hasSubChannel, online,null); + return new PageInfo<>(all); + } + + @Override + public DeviceChannel queryChannel(String deviceId, String channelId) { + return deviceChannelMapper.queryChannel(deviceId, channelId); + } + + + @Override + public int delChannel(String deviceId, String channelId) { + return deviceChannelMapper.del(deviceId, channelId); + } + + /** + * 获取多个设备 + * + * @param page 当前页数 + * @param count 每页数量 + * @return PageInfo 分页设备对象数组 + */ + @Override + public PageInfo queryVideoDeviceList(int page, int count,Boolean online) { + PageHelper.startPage(page, count); + List all = deviceMapper.getDevices(online); + return new PageInfo<>(all); + } + + /** + * 获取多个设备 + * + * @return List 设备对象数组 + */ + @Override + public List queryVideoDeviceList(Boolean online) { + + List deviceList = deviceMapper.getDevices(online); + return deviceList; + } + + /** + * 清空通道 + * @param deviceId + */ + @Override + public void cleanChannelsForDevice(String deviceId) { + deviceChannelMapper.cleanChannelsByDeviceId(deviceId); + } + + /** + * 添加Mobile Position设备移动位置 + * @param mobilePosition + */ + @Override + public synchronized boolean insertMobilePosition(MobilePosition mobilePosition) { + return deviceMobilePositionMapper.insertNewPosition(mobilePosition) > 0; + } + + /** + * 查询移动位置轨迹 + * @param deviceId + * @param startTime + * @param endTime + */ + @Override + public synchronized List queryMobilePositions(String deviceId, String channelId, String startTime, String endTime) { + return deviceMobilePositionMapper.queryPositionByDeviceIdAndTime(deviceId, channelId, startTime, endTime); + } + + @Override + public boolean addParentPlatform(ParentPlatform parentPlatform) { + if (parentPlatform.getCatalogId() == null) { + parentPlatform.setCatalogId(parentPlatform.getServerGBId()); + } + int result = platformMapper.addParentPlatform(parentPlatform); + return result > 0; + } + + @Override + public boolean updateParentPlatform(ParentPlatform parentPlatform) { + int result = 0; + if (parentPlatform.getCatalogGroup() == 0) { + parentPlatform.setCatalogGroup(1); + } + if (parentPlatform.getAdministrativeDivision() == null) { + parentPlatform.setAdministrativeDivision(parentPlatform.getAdministrativeDivision()); + } + ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); // .getDeviceGBId()); + if (parentPlatform.getId() == null ) { + if (parentPlatform.getCatalogId() == null) { + parentPlatform.setCatalogId(parentPlatform.getServerGBId()); + } + result = platformMapper.addParentPlatform(parentPlatform); + if (parentPlatformCatch == null) { + parentPlatformCatch = new ParentPlatformCatch(); + parentPlatformCatch.setParentPlatform(parentPlatform); + parentPlatformCatch.setId(parentPlatform.getServerGBId()); + } + }else { + if (parentPlatformCatch == null) { // serverGBId 已变化 + ParentPlatform parentPlatById = platformMapper.getParentPlatById(parentPlatform.getId()); + // 使用旧的查出缓存ID + parentPlatformCatch = new ParentPlatformCatch(); + parentPlatformCatch.setId(parentPlatform.getServerGBId()); + redisCatchStorage.delPlatformCatchInfo(parentPlatById.getServerGBId()); + } + + result = platformMapper.updateParentPlatform(parentPlatform); + } + // 更新缓存 + parentPlatformCatch.setParentPlatform(parentPlatform); + redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); + + return result > 0; + } + + @Transactional + @Override + public boolean deleteParentPlatform(ParentPlatform parentPlatform) { + int result = platformMapper.delParentPlatform(parentPlatform); + // 删除关联的通道 + platformChannelMapper.cleanChannelForGB(parentPlatform.getServerGBId()); + return result > 0; + } + + @Override + public ParentPlatform queryParentPlatByServerGBId(String platformGbId) { + return platformMapper.getParentPlatByServerGBId(platformGbId); + } + + @Override + public List queryEnableParentPlatformList(boolean enable) { + return platformMapper.getEnableParentPlatformList(enable); + } + + @Override + public List queryEnablePlatformListWithAsMessageChannel() { + return platformMapper.queryEnablePlatformListWithAsMessageChannel(); + } + + @Override + public List queryDeviceWithAsMessageChannel() { + return deviceMapper.queryDeviceWithAsMessageChannel(); + } + + @Override + public void outlineForAllParentPlatform() { + platformMapper.outlineForAllParentPlatform(); + } + + + @Override + public PageInfo queryAllChannelList(int page, int count, String query, Boolean online, + Boolean channelType, String platformId, String catalogId) { + PageHelper.startPage(page, count); + List all = deviceChannelMapper.queryChannelListInAll(query, online, channelType, platformId, catalogId); + return new PageInfo<>(all); + } + + @Override + public List queryChannelListInParentPlatform(String platformId) { + + return deviceChannelMapper.queryChannelByPlatformId(platformId); + } + + + @Override + public int delChannelForGB(String platformId, List channelReduces) { + + int result = platformChannelMapper.delChannelForGB(platformId, channelReduces); + List deviceChannelList = new ArrayList<>(); + for (ChannelReduce channelReduce : channelReduces) { + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setChannelId(channelReduce.getChannelId()); + deviceChannelList.add(deviceChannel); + } + eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.DEL); + return result; + } + + @Override + public DeviceChannel queryChannelInParentPlatform(String platformId, String channelId) { + List channels = platformChannelMapper.queryChannelInParentPlatform(platformId, channelId); + if (channels.size() > 1) { + // 出现长度大于0的时候肯定是国标通道的ID重复了 + logger.warn("国标ID存在重复:{}", channelId); + } + if (channels.size() == 0) { + return null; + }else { + return channels.get(0); + } + } + + @Override + public List queryChannelInParentPlatformAndCatalog(String platformId, String catalogId) { + List catalogs = platformChannelMapper.queryChannelInParentPlatformAndCatalog(platformId, catalogId); + return catalogs; + } + + @Override + public List queryStreamInParentPlatformAndCatalog(String platformId, String catalogId) { + List catalogs = platformGbStreamMapper.queryChannelInParentPlatformAndCatalogForCatalog(platformId, catalogId); + return catalogs; + } + + @Override + public Device queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId) { + List devices = platformChannelMapper.queryVideoDeviceByPlatformIdAndChannelId(platformId, channelId); + if (devices.size() > 1) { + // 出现长度大于0的时候肯定是国标通道的ID重复了 + logger.warn("国标ID存在重复:{}", channelId); + } + if (devices.size() == 0) { + return null; + }else { + return devices.get(0); + } + + + } + + @Override + public Device queryDeviceInfoByPlatformIdAndChannelId(String platformId, String channelId) { + List devices = platformChannelMapper.queryDeviceInfoByPlatformIdAndChannelId(platformId, channelId); + if (devices.size() > 1) { + // 出现长度大于0的时候肯定是国标通道的ID重复了 + logger.warn("国标ID存在重复:{}", channelId); + } + if (devices.size() == 0) { + return null; + }else { + return devices.get(0); + } + } + + /** + * 查询最新移动位置 + * @param deviceId + */ + @Override + public MobilePosition queryLatestPosition(String deviceId) { + return deviceMobilePositionMapper.queryLatestPositionByDevice(deviceId); + } + + /** + * 删除指定设备的所有移动位置 + * @param deviceId + */ + @Override + public int clearMobilePositionsByDeviceId(String deviceId) { + return deviceMobilePositionMapper.clearMobilePositionsByDeviceId(deviceId); + } + + + /** + * 移除代理流 + * @param app + * @param stream + * @return + */ + @Override + public int deleteStreamProxy(String app, String stream) { + return streamProxyMapper.del(app, stream); + } + + /** + * 根据是否启用获取代理流列表 + * @param enable + * @return + */ + @Override + public List getStreamProxyListForEnable(boolean enable) { + return streamProxyMapper.selectForEnable(enable); + } + + /** + * 分页查询代理流列表 + * @param page + * @param count + * @return + */ + @Override + public PageInfo queryStreamProxyList(Integer page, Integer count) { + PageHelper.startPage(page, count); + List all = streamProxyMapper.selectAll(); + return new PageInfo<>(all); + } + + /** + * 根据国标ID获取平台关联的直播流 + * @param platformId + * @param gbId + * @return + */ + @Override + public GbStream queryStreamInParentPlatform(String platformId, String gbId) { + return gbStreamMapper.queryStreamInPlatform(platformId, gbId); + } + + /** + * 获取平台关联的直播流 + * @param platformId + * @return + */ + @Override + public List queryGbStreamListInPlatform(String platformId) { + return gbStreamMapper.queryGbStreamListInPlatform(platformId, userSetting.isUsePushingAsStatus()); + } + + /** + * 按照是app和stream获取代理流 + * @param app + * @param stream + * @return + */ + @Override + public StreamProxyItem queryStreamProxy(String app, String stream){ + return streamProxyMapper.selectOne(app, stream); + } + + @Override + public int removeMedia(String app, String stream) { + return streamPushMapper.del(app, stream); + } + + @Override + public int mediaOffline(String app, String stream) { + GbStream gbStream = gbStreamMapper.selectOne(app, stream); + int result; + if ("proxy".equals(gbStream.getStreamType())) { + result = streamProxyMapper.updateStatus(app, stream, false); + }else { + result = streamPushMapper.updatePushStatus(app, stream, false); + } + return result; + } + + @Override + public int mediaOnline(String app, String stream) { + GbStream gbStream = gbStreamMapper.selectOne(app, stream); + int result; + if ("proxy".equals(gbStream.getStreamType())) { + result = streamProxyMapper.updateStatus(app, stream, true); + }else { + result = streamPushMapper.updatePushStatus(app, stream, true); + } + return result; + } + + @Override + public void updateParentPlatformStatus(String platformGbID, boolean online) { + platformMapper.updateParentPlatformStatus(platformGbID, online); + } + + @Override + public List getStreamProxyListForEnableInMediaServer(String id, boolean enable) { + return streamProxyMapper.selectForEnableInMediaServer(id, enable); + } + + + @Override + public Device queryVideoDeviceByChannelId( String channelId) { + Device result = null; + List channelList = deviceChannelMapper.queryChannelByChannelId(channelId); + if (channelList.size() == 1) { + result = deviceMapper.getDeviceByDeviceId(channelList.get(0).getDeviceId()); + } + return result; + } + + @Override + public StreamProxyItem getStreamProxyByAppAndStream(String app, String streamId) { + return streamProxyMapper.selectOne(app, streamId); + } + + @Override + public List getChildrenCatalogByPlatform(String platformId, String parentId) { + return catalogMapper.selectByParentId(platformId, parentId); + } + + @Override + public int addCatalog(PlatformCatalog platformCatalog) { + ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformCatalog.getPlatformId()); + if (platform == null) { + return 0; + } + if (platformCatalog.getId().length() <= 8) { + platformCatalog.setCivilCode(platformCatalog.getParentId()); + }else { + if (platformCatalog.getId().length() != 20) { + return 0; + } + if (platformCatalog.getParentId() != null) { + switch (Integer.parseInt(platformCatalog.getId().substring(10, 13))){ + case 200: + case 215: + if (platformCatalog.getParentId().length() <= 8) { + platformCatalog.setCivilCode(platformCatalog.getParentId()); + }else { + PlatformCatalog catalog = catalogMapper.select(platformCatalog.getParentId()); + if (catalog != null) { + platformCatalog.setCivilCode(catalog.getCivilCode()); + } + } + break; + case 216: + if (platformCatalog.getParentId().length() <= 8) { + platformCatalog.setCivilCode(platformCatalog.getParentId()); + }else { + PlatformCatalog catalog = catalogMapper.select(platformCatalog.getParentId()); + if (catalog == null) { + logger.warn("[添加目录] 无法获取目录{}的CivilCode和BusinessGroupId", platformCatalog.getPlatformId()); + break; + } + platformCatalog.setCivilCode(catalog.getCivilCode()); + if (Integer.parseInt(platformCatalog.getParentId().substring(10, 13)) == 215) { + platformCatalog.setBusinessGroupId(platformCatalog.getParentId()); + }else { + if (Integer.parseInt(platformCatalog.getParentId().substring(10, 13)) == 216) { + platformCatalog.setBusinessGroupId(catalog.getBusinessGroupId()); + } + } + } + break; + default: + break; + } + } + } + int result = catalogMapper.add(platformCatalog); + if (result > 0) { + DeviceChannel deviceChannel = getDeviceChannelByCatalog(platformCatalog); + eventPublisher.catalogEventPublish(platformCatalog.getPlatformId(), deviceChannel, CatalogEvent.ADD); + } + return result; + } + + private PlatformCatalog getTopCatalog(String id, String platformId) { + PlatformCatalog catalog = catalogMapper.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.setDeviceId(platform.getDeviceGBId()); + deviceChannel.setManufacture("wvp-pro"); + deviceChannel.setStatus(true); + deviceChannel.setParental(1); + + deviceChannel.setRegisterWay(1); + deviceChannel.setParentId(catalog.getParentId()); + deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId()); + + deviceChannel.setModel("live"); + deviceChannel.setOwner("wvp-pro"); + deviceChannel.setSecrecy("0"); + return deviceChannel; + } + + @Override + public List queryOnlineChannelsByDeviceId(String deviceId) { + return deviceChannelMapper.queryOnlineChannelsByDeviceId(deviceId); + } + + @Override + public List queryPlatFormListForGBWithGBId(String channelId, List platforms) { + return platformChannelMapper.queryPlatFormListForGBWithGBId(channelId, platforms); + } + + @Override + public List queryPlatFormListForStreamWithGBId(String app, String stream, List platforms) { + if (platforms == null || platforms.size() == 0) { + return new ArrayList<>(); + } + return platformGbStreamMapper.queryPlatFormListForGBWithGBId(app, stream, platforms); + } + + @Override + public GbStream getGbStream(String app, String streamId) { + return gbStreamMapper.selectOne(app, streamId); + } + + @Override + public void delCatalogByPlatformId(String serverGBId) { + catalogMapper.delByPlatformId(serverGBId); + } + + @Override + public void delRelationByPlatformId(String serverGBId) { + platformGbStreamMapper.delByPlatformId(serverGBId); + platformChannelMapper.delByPlatformId(serverGBId); + } + + @Override + public PlatformCatalog queryDefaultCatalogInPlatform(String platformId) { + return catalogMapper.selectDefaultByPlatFormId(platformId); + } + + @Override + public List getChannelSource(String platformId, String gbId) { + return platformMapper.getChannelSource(platformId, gbId); + } + + @Override + public void updateChannelPosition(DeviceChannel deviceChannel) { + if (deviceChannel.getChannelId().equals(deviceChannel.getDeviceId())) { + deviceChannel.setChannelId(null); + } + if (deviceChannel.getGpsTime() == null) { + deviceChannel.setGpsTime(DateUtil.getNow()); + } + + deviceChannelMapper.updatePosition(deviceChannel); + } + + @Override + public void cleanContentForPlatform(String serverGBId) { +// List catalogList = catalogMapper.selectByPlatForm(serverGBId); +// if (catalogList.size() > 0) { +// int result = catalogMapper.delByPlatformId(serverGBId); +// if (result > 0) { +// List deviceChannels = new ArrayList<>(); +// for (PlatformCatalog catalog : catalogList) { +// deviceChannels.add(getDeviceChannelByCatalog(catalog)); +// } +// eventPublisher.catalogEventPublish(serverGBId, deviceChannels, CatalogEvent.DEL); +// } +// } + catalogMapper.delByPlatformId(serverGBId); + platformChannelMapper.delByPlatformId(serverGBId); + platformGbStreamMapper.delByPlatformId(serverGBId); + } + + @Override + public List queryChannelWithCatalog(String serverGBId) { + return deviceChannelMapper.queryChannelWithCatalog(serverGBId); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java deleted file mode 100644 index 22292ef97..000000000 --- a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java +++ /dev/null @@ -1,252 +0,0 @@ -package com.genersoft.iot.vmp.storager.impl; - -import java.util.*; - -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; -import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; -import com.genersoft.iot.vmp.storager.dao.DeviceMapper; -import com.genersoft.iot.vmp.storager.dao.DeviceMobilePositionMapper; -import com.github.pagehelper.PageHelper; -import com.github.pagehelper.PageInfo; -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.storager.IVideoManagerStorager; - -/** - * @Description:视频设备数据存储-jdbc实现 - * @author: swwheihei - * @date: 2020年5月6日 下午2:31:42 - */ -@SuppressWarnings("rawtypes") -@Component -public class VideoManagerStoragerImpl implements IVideoManagerStorager { - - @Autowired - private DeviceMapper deviceMapper; - - @Autowired - private DeviceChannelMapper deviceChannelMapper; - - @Autowired - private DeviceMobilePositionMapper deviceMobilePositionMapper; - - - /** - * 根据设备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) { - return deviceMapper.add(device) > 0; - } - - - - /** - * 视频设备更新 - * - * @param device 设备对象 - * @return true:更新成功 false:更新失败 - */ - @Override - public synchronized boolean updateDevice(Device device) { - Device deviceByDeviceId = deviceMapper.getDeviceByDeviceId(device.getDeviceId()); - if (deviceByDeviceId == null) { - return deviceMapper.add(device) > 0; - }else { - return deviceMapper.update(device) > 0; - } - - } - - @Override - public synchronized void updateChannel(String deviceId, DeviceChannel channel) { - String channelId = channel.getChannelId(); - channel.setDeviceId(deviceId); - DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(deviceId, channelId); - if (deviceChannel == null) { - deviceChannelMapper.add(channel); - }else { - deviceChannelMapper.update(channel); - } - } - - @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.queryChannelsByDeviceId(deviceId, null, query, hasSubChannel, online); - return new PageInfo<>(all); - } - - @Override - public List queryChannelsByDeviceId(String deviceId) { - return deviceChannelMapper.queryChannelsByDeviceId(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.queryChannelsByDeviceId(deviceId, parentChannelId, null, null, null); - return new PageInfo<>(all); - } - - @Override - public DeviceChannel queryChannel(String deviceId, String channelId) { - return deviceChannelMapper.queryChannel(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) { - int result = deviceMapper.del(deviceId); - - return result > 0; - } - - /** - * 更新设备在线 - * - * @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); - System.out.println("更新设备在线"); - return deviceMapper.update(device) > 0; - } - - /** - * 更新设备离线 - * - * @param deviceId 设备ID - * @return true:更新成功 false:更新失败 - */ - @Override - public synchronized boolean outline(String deviceId) { - Device device = deviceMapper.getDeviceByDeviceId(deviceId); - device.setOnline(0); - System.out.println("更新设备离线"); - return deviceMapper.update(device) > 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); - } - - /** - * 查询最新移动位置 - * @param deviceId - */ - @Override - public MobilePosition queryLatestPosition(String deviceId) { - return deviceMobilePositionMapper.queryLatestPositionByDevice(deviceId); - } - - /** - * 删除指定设备的所有移动位置 - * @param deviceId - */ - public int clearMobilePositionsByDeviceId(String deviceId) { - return deviceMobilePositionMapper.clearMobilePositionsByDeviceId(deviceId); - } -} 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 000000000..c10357c54 --- /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 000000000..7a65a6104 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/utils/DateUtil.java @@ -0,0 +1,104 @@ +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"; + + /** + * wvp内部统一时间格式 + */ + public static final String URL_PATTERN = "yyyyMMddHHmmss"; + + public static final String zoneStr = "Asia/Shanghai"; + + public static final DateTimeFormatter formatterCompatibleISO8601 = DateTimeFormatter.ofPattern(ISO8601_COMPATIBLE_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr)); + public static final DateTimeFormatter formatterISO8601 = DateTimeFormatter.ofPattern(ISO8601_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr)); + public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr)); + public static final DateTimeFormatter urlFormatter = DateTimeFormatter.ofPattern(URL_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); + } + + /** + * 获取当前时间 + * @return + */ + public static String getNowForUrl() { + LocalDateTime nowDateTime = LocalDateTime.now(); + return urlFormatter.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 new file mode 100644 index 000000000..ca637dda7 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/utils/GitUtil.java @@ -0,0 +1,59 @@ +package com.genersoft.iot.vmp.utils; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Component; + +/** + * 一个优秀的颓废程序猿(CSDN) + */ +@Component +@PropertySource(value = {"classpath:git.properties" }, ignoreResourceNotFound = true) +public class GitUtil { + + @Value("${git.branch:}") + private String branch; + @Value("${git.commit.id:}") + private String gitCommitId; + @Value("${git.remote.origin.url:}") + private String gitUrl; + @Value("${git.build.time:}") + private String buildDate; + + @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; + } + + public String getBranch() { + return branch; + } + + public String getGitUrl() { + return gitUrl; + } + + public String getBuildDate() { + return buildDate; + } + + 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 16724410e..2d9bd39b1 100644 --- a/src/main/java/com/genersoft/iot/vmp/utils/GpsUtil.java +++ b/src/main/java/com/genersoft/iot/vmp/utils/GpsUtil.java @@ -7,51 +7,24 @@ import java.net.Socket; import java.util.Base64; import com.genersoft.iot.vmp.gb28181.bean.BaiduPoint; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; 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) { - // System.out.println(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 { - System.out.println("gps坐标无效!!"); - } - out.close(); - br.close(); - } catch (Exception e) { - e.printStackTrace(); - } - return null; + private static Logger logger = LoggerFactory.getLogger(GpsUtil.class); + + public static BaiduPoint Wgs84ToBd09(String xx, String yy) { + + + 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 d754554b0..000000000 --- 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/JsonUtil.java b/src/main/java/com/genersoft/iot/vmp/utils/JsonUtil.java new file mode 100644 index 000000000..de8675f12 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/utils/JsonUtil.java @@ -0,0 +1,34 @@ +package com.genersoft.iot.vmp.utils; + +import org.springframework.data.redis.core.RedisTemplate; + +import java.util.Objects; + +/** + * JsonUtil + * + * @author KunLong-Luo + * @version 1.0.0 + * @since 2023/2/2 15:24 + */ +public final class JsonUtil { + + private JsonUtil() { + } + + /** + * safe json type conversion + * + * @param key redis key + * @param clazz cast type + * @param + * @return result type + */ + public static T redisJsonToObject(RedisTemplate redisTemplate, String key, Class clazz) { + Object jsonObject = redisTemplate.opsForValue().get(key); + if (Objects.isNull(jsonObject)) { + return null; + } + return clazz.cast(jsonObject); + } +} \ No newline at end of file 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 ccbe94d69..532387545 100644 --- a/src/main/java/com/genersoft/iot/vmp/utils/SpringBeanFactory.java +++ b/src/main/java/com/genersoft/iot/vmp/utils/SpringBeanFactory.java @@ -6,7 +6,7 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** - * @Description:spring bean获取工厂,获取spring中的已初始化的bean + * @description:spring bean获取工厂,获取spring中的已初始化的bean * @author: swwheihei * @date: 2019年6月25日 下午4:51:52 * @@ -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 000000000..ec2f3b222 --- /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/UJson.java b/src/main/java/com/genersoft/iot/vmp/utils/UJson.java new file mode 100644 index 000000000..a2d403392 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/utils/UJson.java @@ -0,0 +1,150 @@ +package com.genersoft.iot.vmp.utils; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; + +/** + * @author gaofuwang + * @version 1.0 + * @date 2022/3/11 10:17 + */ +public class UJson { + + private static Logger logger = LoggerFactory.getLogger(UJson.class); + public static final ObjectMapper JSON_MAPPER = new ObjectMapper(); + + static { + JSON_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false); + } + + private ObjectNode node; + + public UJson(){ + this.node = JSON_MAPPER.createObjectNode(); + } + + public UJson(String json){ + if(StringUtils.isBlank(json)){ + this.node = JSON_MAPPER.createObjectNode(); + }else{ + try { + this.node = JSON_MAPPER.readValue(json, ObjectNode.class); + }catch (Exception e){ + logger.error(e.getMessage(), e); + this.node = JSON_MAPPER.createObjectNode(); + } + } + } + + public UJson(ObjectNode node){ + this.node = node; + } + + public String asText(String key){ + JsonNode jsonNode = node.get(key); + if(Objects.isNull(jsonNode)){ + return ""; + } + return jsonNode.asText(); + } + + public String asText(String key, String defaultVal){ + JsonNode jsonNode = node.get(key); + if(Objects.isNull(jsonNode)){ + return ""; + } + return jsonNode.asText(defaultVal); + } + + public UJson put(String key, String value){ + this.node.put(key, value); + return this; + } + + public UJson put(String key, Integer value){ + this.node.put(key, value); + return this; + } + + public static UJson json(){ + return new UJson(); + } + + public static UJson json(String json){ + return new UJson(json); + } + + public static T readJson(String json, Class clazz){ + if(StringUtils.isBlank(json)){ + return null; + } + try { + return JSON_MAPPER.readValue(json, clazz); + }catch (Exception e){ + logger.error(e.getMessage(), e); + return null; + } + } + + public static String writeJson(Object object) { + try{ + return JSON_MAPPER.writeValueAsString(object); + }catch (Exception e){ + logger.error(e.getMessage(), e); + return ""; + } + } + + @Override + public String toString() { + return node.toString(); + } + + public int asInt(String key, int defValue) { + JsonNode jsonNode = this.node.get(key); + if(Objects.isNull(jsonNode)){ + return defValue; + } + return jsonNode.asInt(defValue); + } + + public UJson getSon(String key) { + JsonNode sonNode = this.node.get(key); + if(Objects.isNull(sonNode)){ + return new UJson(); + } + return new UJson((ObjectNode) sonNode); + } + + public UJson set(String key, ObjectNode sonNode) { + this.node.set(key, sonNode); + return this; + } + + public UJson set(String key, UJson sonNode) { + this.node.set(key, sonNode.node); + return this; + } + + public Iterator> fields() { + return this.node.fields(); + } + + public ObjectNode getNode() { + return this.node; + } + + public UJson setAll(UJson json) { + this.node.setAll(json.node); + return this; + } +} 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 411f96228..466a50355 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,15 +1,15 @@ 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的序列化 + * @description:使用fastjson实现redis的序列化 * @author: swwheihei * @date: 2020年5月6日 下午8:40:11 */ @@ -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/RedisUtil.java b/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java index f83d5f3e9..31e89f971 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,732 +1,46 @@ package com.genersoft.iot.vmp.utils.redis; -import java.util.*; -import java.util.concurrent.TimeUnit; +import org.springframework.data.redis.core.Cursor; +import org.springframework.data.redis.core.RedisCallback; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ScanOptions; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.*; -import org.springframework.stereotype.Component; -import org.springframework.util.CollectionUtils; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; -/** - * @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; - - /** - * 指定缓存失效时间 - * @param key 键 - * @param time 时间(秒) - * @return true / false - */ - public boolean expire(String key, long time) { - try { - if (time > 0) { - redisTemplate.expire(key, time, TimeUnit.SECONDS); - } - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 根据 key 获取过期时间 - * @param key 键 - * @return - */ - public long getExpire(String key) { - return redisTemplate.getExpire(key, TimeUnit.SECONDS); - } - - /** - * 判断 key 是否存在 - * @param key 键 - * @return true / false - */ - public boolean hasKey(String key) { - try { - return redisTemplate.hasKey(key); - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 删除缓存 - * @SuppressWarnings("unchecked") 忽略类型转换警告 - * @param key 键(一个或者多个) - */ - public boolean del(String... key) { - try { - if (key != null && key.length > 0) { - if (key.length == 1) { - redisTemplate.delete(key[0]); - } else { -// 传入一个 Collection 集合 - redisTemplate.delete(CollectionUtils.arrayToList(key)); - } - } - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - -// ============================== String ============================== - - /** - * 普通缓存获取 - * @param key 键 - * @return 值 - */ - public Object get(String key) { - return key == null ? null : redisTemplate.opsForValue().get(key); - } - - /** - * 普通缓存放入 - * @param key 键 - * @param value 值 - * @return true / false - */ - public boolean set(String key, Object value) { - try { - redisTemplate.opsForValue().set(key, value); - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 普通缓存放入并设置时间 - * @param key 键 - * @param value 值 - * @param time 时间(秒),如果 time < 0 则设置无限时间 - * @return true / false - */ - public boolean set(String key, Object value, long time) { - try { - if (time > 0) { - redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); - } else { - set(key, value); - } - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 递增 - * @param key 键 - * @param delta 递增大小 - * @return - */ - public long incr(String key, long delta) { - if (delta < 0) { - throw new RuntimeException("递增因子必须大于 0"); - } - return redisTemplate.opsForValue().increment(key, delta); - } - - /** - * 递减 - * @param key 键 - * @param delta 递减大小 - * @return - */ - public long decr(String key, long delta) { - if (delta < 0) { - throw new RuntimeException("递减因子必须大于 0"); - } - return redisTemplate.opsForValue().increment(key, delta); - } - -// ============================== Map ============================== - - /** - * HashGet - * @param key 键(no null) - * @param item 项(no null) - * @return 值 - */ - public Object hget(String key, String item) { - return redisTemplate.opsForHash().get(key, item); - } - - /** - * 获取 key 对应的 map - * @param key 键(no null) - * @return 对应的多个键值 - */ - public Map hmget(String key) { - return redisTemplate.opsForHash().entries(key); - } - - /** - * HashSet - * @param key 键 - * @param map 值 - * @return true / false - */ - public boolean hmset(String key, Map map) { - try { - redisTemplate.opsForHash().putAll(key, map); - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * HashSet 并设置时间 - * @param key 键 - * @param map 值 - * @param time 时间 - * @return true / false - */ - public boolean hmset(String key, Map map, long time) { - try { - redisTemplate.opsForHash().putAll(key, map); - if (time > 0) { - expire(key, time); - } - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 向一张 Hash表 中放入数据,如不存在则创建 - * @param key 键 - * @param item 项 - * @param value 值 - * @return true / false - */ - public boolean hset(String key, String item, Object value) { - try { - redisTemplate.opsForHash().put(key, item, value); - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 向一张 Hash表 中放入数据,并设置时间,如不存在则创建 - * @param key 键 - * @param item 项 - * @param value 值 - * @param time 时间(如果原来的 Hash表 设置了时间,这里会覆盖) - * @return true / false - */ - public boolean hset(String key, String item, Object value, long time) { - try { - redisTemplate.opsForHash().put(key, item, value); - if (time > 0) { - expire(key, time); - } - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 删除 Hash表 中的值 - * @param key 键 - * @param item 项(可以多个,no null) - */ - public void hdel(String key, Object... item) { - redisTemplate.opsForHash().delete(key, item); - } - - /** - * 判断 Hash表 中是否有该键的值 - * @param key 键(no null) - * @param item 值(no null) - * @return true / false - */ - public boolean hHasKey(String key, String item) { - return redisTemplate.opsForHash().hasKey(key, item); - } - - /** - * Hash递增,如果不存在则创建一个,并把新增的值返回 - * @param key 键 - * @param item 项 - * @param by 递增大小 > 0 - * @return - */ - public Double hincr(String key, String item, Double by) { - return redisTemplate.opsForHash().increment(key, item, by); - } - - /** - * Hash递减 - * @param key 键 - * @param item 项 - * @param by 递减大小 - * @return - */ - public Double hdecr(String key, String item, Double by) { - return redisTemplate.opsForHash().increment(key, item, -by); - } - -// ============================== Set ============================== - - /** - * 根据 key 获取 set 中的所有值 - * @param key 键 - * @return 值 - */ - public Set sGet(String key) { - try { - return redisTemplate.opsForSet().members(key); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - /** - * 从键为 key 的 set 中,根据 value 查询是否存在 - * @param key 键 - * @param value 值 - * @return true / false - */ - public boolean sHasKey(String key, Object value) { - try { - return redisTemplate.opsForSet().isMember(key, value); - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 将数据放入 set缓存 - * @param key 键值 - * @param values 值(可以多个) - * @return 成功个数 - */ - public long sSet(String key, Object... values) { - try { - return redisTemplate.opsForSet().add(key, values); - } catch (Exception e) { - e.printStackTrace(); - return 0; - } - } - - /** - * 将数据放入 set缓存,并设置时间 - * @param key 键 - * @param time 时间 - * @param values 值(可以多个) - * @return 成功放入个数 - */ - public long sSet(String key, long time, Object... values) { - try { - long count = redisTemplate.opsForSet().add(key, values); - if (time > 0) { - expire(key, time); - } - return count; - } catch (Exception e) { - e.printStackTrace(); - return 0; - } - } - - /** - * 获取 set缓存的长度 - * @param key 键 - * @return 长度 - */ - public long sGetSetSize(String key) { - try { - return redisTemplate.opsForSet().size(key); - } catch (Exception e) { - e.printStackTrace(); - return 0; - } - } - - /** - * 移除 set缓存中,值为 value 的 - * @param key 键 - * @param values 值 - * @return 成功移除个数 - */ - public long setRemove(String key, Object... values) { - try { - return redisTemplate.opsForSet().remove(key, values); - } catch (Exception e) { - e.printStackTrace(); - return 0; - } - } -// ============================== ZSet ============================== - - /** - * 添加一个元素, zset与set最大的区别就是每个元素都有一个score,因此有个排序的辅助功能; zadd - * - * @param key - * @param value - * @param score - */ - public void zAdd(String key, String value, double score) { - redisTemplate.opsForZSet().add(key, value, score); - } - - /** - * 删除元素 zrem - * - * @param key - * @param value - */ - public void zRemove(String key, String value) { - redisTemplate.opsForZSet().remove(key, value); - } - - /** - * score的增加or减少 zincrby - * - * @param key - * @param value - * @param score - */ - public Double zIncrScore(String key, String value, double score) { - return redisTemplate.opsForZSet().incrementScore(key, value, score); - } - - /** - * 查询value对应的score zscore - * - * @param key - * @param value - * @return - */ - public Double zScore(String key, String value) { - return redisTemplate.opsForZSet().score(key, value); - } - - /** - * 判断value在zset中的排名 zrank - * - * @param key - * @param value - * @return - */ - public Long zRank(String key, String value) { - return redisTemplate.opsForZSet().rank(key, value); - } - - /** - * 返回集合的长度 - * - * @param key - * @return - */ - public Long zSize(String key) { - return redisTemplate.opsForZSet().zCard(key); - } - - /** - * 查询集合中指定顺序的值, 0 -1 表示获取全部的集合内容 zrange - * - * 返回有序的集合,score小的在前面 - * - * @param key - * @param start - * @param end - * @return - */ - public Set ZRange(String key, int start, int end) { - return redisTemplate.opsForZSet().range(key, start, end); - } - /** - * 查询集合中指定顺序的值和score,0, -1 表示获取全部的集合内容 - * - * @param key - * @param start - * @param end - * @return - */ - public Set> zRangeWithScore(String key, int start, int end) { - return redisTemplate.opsForZSet().rangeWithScores(key, start, end); - } - /** - * 查询集合中指定顺序的值 zrevrange - * - * 返回有序的集合中,score大的在前面 - * - * @param key - * @param start - * @param end - * @return - */ - public Set zRevRange(String key, int start, int end) { - return redisTemplate.opsForZSet().reverseRange(key, start, end); - } - /** - * 根据score的值,来获取满足条件的集合 zrangebyscore - * - * @param key - * @param min - * @param max - * @return - */ - public Set zSortRange(String key, int min, int max) { - return redisTemplate.opsForZSet().rangeByScore(key, min, max); - } - - -// ============================== List ============================== - - /** - * 获取 list缓存的内容 - * @param key 键 - * @param start 开始 - * @param end 结束(0 到 -1 代表所有值) - * @return - */ - public List lGet(String key, long start, long end) { - try { - return redisTemplate.opsForList().range(key, start, end); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - /** - * 获取 list缓存的长度 - * @param key 键 - * @return 长度 - */ - public long lGetListSize(String key) { - try { - return redisTemplate.opsForList().size(key); - } catch (Exception e) { - e.printStackTrace(); - return 0; - } - } - - /** - * 根据索引 index 获取键为 key 的 list 中的元素 - * @param key 键 - * @param index 索引 - * 当 index >= 0 时 {0:表头, 1:第二个元素} - * 当 index < 0 时 {-1:表尾, -2:倒数第二个元素} - * @return 值 - */ - public Object lGetIndex(String key, long index) { - try { - return redisTemplate.opsForList().index(key, index); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - /** - * 将值 value 插入键为 key 的 list 中,如果 list 不存在则创建空 list - * @param key 键 - * @param value 值 - * @return true / false - */ - public boolean lSet(String key, Object value) { - try { - redisTemplate.opsForList().rightPush(key, value); - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 将值 value 插入键为 key 的 list 中,并设置时间 - * @param key 键 - * @param value 值 - * @param time 时间 - * @return true / false - */ - public boolean lSet(String key, Object value, long time) { - try { - redisTemplate.opsForList().rightPush(key, value); - if (time > 0) { - expire(key, time); - } - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 将 values 插入键为 key 的 list 中 - * @param key 键 - * @param values 值 - * @return true / false - */ - public boolean lSetList(String key, List values) { - try { - redisTemplate.opsForList().rightPushAll(key, values); - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 将 values 插入键为 key 的 list 中,并设置时间 - * @param key 键 - * @param values 值 - * @param time 时间 - * @return true / false - */ - public boolean lSetList(String key, List values, long time) { - try { - redisTemplate.opsForList().rightPushAll(key, values); - if (time > 0) { - expire(key, time); - } - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 根据索引 index 修改键为 key 的值 - * @param key 键 - * @param index 索引 - * @param value 值 - * @return true / false - */ - public boolean lUpdateIndex(String key, long index, Object value) { - try { - redisTemplate.opsForList().set(key, index, value); - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 在键为 key 的 list 中删除值为 value 的元素 - * @param key 键 - * @param count 如果 count == 0 则删除 list 中所有值为 value 的元素 - * 如果 count > 0 则删除 list 中最左边那个值为 value 的元素 - * 如果 count < 0 则删除 list 中最右边那个值为 value 的元素 - * @param value - * @return - */ - public long lRemove(String key, long count, Object value) { - try { - return redisTemplate.opsForList().remove(key, count, value); - } catch (Exception e) { - e.printStackTrace(); - return 0; - } - } - - /** - * 模糊查询 - * @param key 键 - * @return true / false - */ - public List keys(String key) { - try { - Set set = redisTemplate.keys(key); - return new ArrayList<>(set); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - /** * 模糊查询 + * * @param query 查询参数 * @return */ -// public List scan(String query) { -// List result = new ArrayList<>(); -// try { -// Cursor> cursor = redisTemplate.opsForHash().scan("field", -// ScanOptions.scanOptions().match(query).count(1000).build()); -// while (cursor.hasNext()) { -// Map.Entry entry = cursor.next(); -// result.add(entry.getKey()); -// Object key = entry.getKey(); -// Object valueSet = entry.getValue(); -// } -// //关闭cursor -// cursor.close(); -// } catch (Exception e) { -// e.printStackTrace(); -// } -// return result; -// } + public static List scan(RedisTemplate redisTemplate, String query) { - /** - * 模糊查询 - * @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())); + 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); } - } + + + diff --git a/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil2.java b/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil2.java new file mode 100644 index 000000000..18c1fd504 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil2.java @@ -0,0 +1,899 @@ +//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; +// +///** +// * Redis工具类 +// * @author swwheihei +// * @date 2020年5月6日 下午8:27:29 +// */ +//@SuppressWarnings(value = {"rawtypes", "unchecked"}) +//public class RedisUtil2 { +// +// private static RedisTemplate redisTemplate; +// +// static { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// +// /** +// * 指定缓存失效时间 +// * @param key 键 +// * @param time 时间(秒) +// * @return true / false +// */ +// 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); +// } +// return true; +// } catch (Exception e) { +// e.printStackTrace(); +// return false; +// } +// } +// +// /** +// * 根据 key 获取过期时间 +// * @param key 键 +// */ +// public static long getExpire(String key) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// return redisTemplate.getExpire(key, TimeUnit.SECONDS); +// } +// +// /** +// * 判断 key 是否存在 +// * @param key 键 +// * @return true / false +// */ +// public static boolean hasKey(String key) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// try { +// return redisTemplate.hasKey(key); +// } catch (Exception e) { +// e.printStackTrace(); +// return false; +// } +// } +// +// /** +// * 删除缓存 +// * @SuppressWarnings("unchecked") 忽略类型转换警告 +// * @param 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) { +// redisTemplate.delete(key[0]); +// } else { +//// 传入一个 Collection 集合 +// redisTemplate.delete(CollectionUtils.arrayToList(key)); +// } +// } +// return true; +// } catch (Exception e) { +// e.printStackTrace(); +// return false; +// } +// } +// +//// ============================== String ============================== +// +// /** +// * 普通缓存获取 +// * @param key 键 +// * @return 值 +// */ +// public static Object get(String key) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// return key == null ? null : redisTemplate.opsForValue().get(key); +// } +// +// /** +// * 普通缓存放入 +// * @param key 键 +// * @param value 值 +// * @return true / false +// */ +// public static boolean set(String key, Object value) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// try { +// redisTemplate.opsForValue().set(key, value); +// return true; +// } catch (Exception e) { +// e.printStackTrace(); +// return false; +// } +// } +// +// /** +// * 普通缓存放入并设置时间 +// * @param key 键 +// * @param value 值 +// * @param time 时间(秒),如果 time < 0 则设置无限时间 +// * @return true / false +// */ +// 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); +// } else { +// set(key, value); +// } +// return true; +// } catch (Exception e) { +// e.printStackTrace(); +// return false; +// } +// } +// +// /** +// * 递增 +// * @param key 键 +// * @param delta 递增大小 +// * @return +// */ +// public static long incr(String key, long delta) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// if (delta < 0) { +// throw new RuntimeException("递增因子必须大于 0"); +// } +// return redisTemplate.opsForValue().increment(key, delta); +// } +// +// /** +// * 递减 +// * @param key 键 +// * @param delta 递减大小 +// * @return +// */ +// public static long decr(String key, long delta) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// if (delta < 0) { +// throw new RuntimeException("递减因子必须大于 0"); +// } +// return redisTemplate.opsForValue().increment(key, delta); +// } +// +//// ============================== Map ============================== +// +// /** +// * HashGet +// * @param key 键(no null) +// * @param item 项(no null) +// * @return 值 +// */ +// public static Object hget(String key, String item) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// return redisTemplate.opsForHash().get(key, item); +// } +// +// /** +// * 获取 key 对应的 map +// * @param key 键(no null) +// * @return 对应的多个键值 +// */ +// public static Map hmget(String key) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// return redisTemplate.opsForHash().entries(key); +// } +// +// /** +// * HashSet +// * @param key 键 +// * @param map 值 +// * @return true / false +// */ +// public static boolean hmset(String key, Map map) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// try { +// redisTemplate.opsForHash().putAll(key, map); +// return true; +// } catch (Exception e) { +// e.printStackTrace(); +// return false; +// } +// } +// +// /** +// * HashSet 并设置时间 +// * @param key 键 +// * @param map 值 +// * @param time 时间 +// * @return true / false +// */ +// 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) { +// expire(key, time); +// } +// return true; +// } catch (Exception e) { +// e.printStackTrace(); +// return false; +// } +// } +// +// /** +// * 向一张 Hash表 中放入数据,如不存在则创建 +// * @param key 键 +// * @param item 项 +// * @param value 值 +// * @return true / false +// */ +// 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; +// } catch (Exception e) { +// e.printStackTrace(); +// return false; +// } +// } +// +// /** +// * 向一张 Hash表 中放入数据,并设置时间,如不存在则创建 +// * @param key 键 +// * @param item 项 +// * @param value 值 +// * @param time 时间(如果原来的 Hash表 设置了时间,这里会覆盖) +// * @return true / false +// */ +// 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) { +// expire(key, time); +// } +// return true; +// } catch (Exception e) { +// e.printStackTrace(); +// return false; +// } +// } +// +// /** +// * 删除 Hash表 中的值 +// * @param key 键 +// * @param item 项(可以多个,no null) +// */ +// public static void hdel(String key, Object... item) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// redisTemplate.opsForHash().delete(key, item); +// } +// +// /** +// * 判断 Hash表 中是否有该键的值 +// * @param key 键(no null) +// * @param item 值(no null) +// * @return true / false +// */ +// public static boolean hHasKey(String key, String item) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// return redisTemplate.opsForHash().hasKey(key, item); +// } +// +// /** +// * Hash递增,如果不存在则创建一个,并把新增的值返回 +// * @param key 键 +// * @param item 项 +// * @param by 递增大小 > 0 +// * @return +// */ +// public static Double hincr(String key, String item, Double by) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// return redisTemplate.opsForHash().increment(key, item, by); +// } +// +// /** +// * Hash递减 +// * @param key 键 +// * @param item 项 +// * @param by 递减大小 +// * @return +// */ +// public static Double hdecr(String key, String item, Double by) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// return redisTemplate.opsForHash().increment(key, item, -by); +// } +// +//// ============================== Set ============================== +// +// /** +// * 根据 key 获取 set 中的所有值 +// * @param key 键 +// * @return 值 +// */ +// public static Set sGet(String key) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// try { +// return redisTemplate.opsForSet().members(key); +// } catch (Exception e) { +// e.printStackTrace(); +// return null; +// } +// } +// +// /** +// * 从键为 key 的 set 中,根据 value 查询是否存在 +// * @param key 键 +// * @param value 值 +// * @return true / false +// */ +// 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) { +// e.printStackTrace(); +// return false; +// } +// } +// +// /** +// * 将数据放入 set缓存 +// * @param key 键值 +// * @param values 值(可以多个) +// * @return 成功个数 +// */ +// 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) { +// e.printStackTrace(); +// return 0; +// } +// } +// +// /** +// * 将数据放入 set缓存,并设置时间 +// * @param key 键 +// * @param time 时间 +// * @param values 值(可以多个) +// * @return 成功放入个数 +// */ +// 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) { +// expire(key, time); +// } +// return count; +// } catch (Exception e) { +// e.printStackTrace(); +// return 0; +// } +// } +// +// /** +// * 获取 set缓存的长度 +// * @param key 键 +// * @return 长度 +// */ +// public static long sGetSetSize(String key) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// try { +// return redisTemplate.opsForSet().size(key); +// } catch (Exception e) { +// e.printStackTrace(); +// return 0; +// } +// } +// +// /** +// * 移除 set缓存中,值为 value 的 +// * @param key 键 +// * @param values 值 +// * @return 成功移除个数 +// */ +// 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) { +// e.printStackTrace(); +// return 0; +// } +// } +//// ============================== ZSet ============================== +// +// /** +// * 添加一个元素, zset与set最大的区别就是每个元素都有一个score,因此有个排序的辅助功能; zadd +// * +// * @param key +// * @param value +// * @param score +// */ +// public static void zAdd(Object key, Object value, double score) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// redisTemplate.opsForZSet().add(key, value, score); +// } +// +// /** +// * 删除元素 zrem +// * +// * @param key +// * @param value +// */ +// public static void zRemove(Object key, Object value) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// redisTemplate.opsForZSet().remove(key, value); +// } +// +// /** +// * score的增加or减少 zincrby +// * +// * @param key +// * @param value +// * @param delta -1 表示减 1 表示加1 +// */ +// public static Double zIncrScore(Object key, Object value, double delta) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// return redisTemplate.opsForZSet().incrementScore(key, value, delta); +// } +// +// /** +// * 查询value对应的score zscore +// * +// * @param key +// * @param value +// * @return +// */ +// public static Double zScore(Object key, Object value) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// return redisTemplate.opsForZSet().score(key, value); +// } +// +// /** +// * 判断value在zset中的排名 zrank +// * +// * @param key +// * @param value +// * @return +// */ +// public static Long zRank(Object key, Object value) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// return redisTemplate.opsForZSet().rank(key, value); +// } +// +// /** +// * 返回集合的长度 +// * +// * @param key +// * @return +// */ +// public static Long zSize(Object key) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// return redisTemplate.opsForZSet().zCard(key); +// } +// +// /** +// * 查询集合中指定顺序的值, 0 -1 表示获取全部的集合内容 zrange +// * +// * 返回有序的集合,score小的在前面 +// * +// * @param key +// * @param start +// * @param end +// * @return +// */ +// public static Set zRange(Object key, int start, int end) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// return redisTemplate.opsForZSet().range(key, start, end); +// } +// /** +// * 查询集合中指定顺序的值和score,0, -1 表示获取全部的集合内容 +// * +// * @param key +// * @param start +// * @param end +// * @return +// */ +// public static Set> zRangeWithScore(Object key, int start, int end) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// return redisTemplate.opsForZSet().rangeWithScores(key, start, end); +// } +// /** +// * 查询集合中指定顺序的值 zrevrange +// * +// * 返回有序的集合中,score大的在前面 +// * +// * @param key +// * @param start +// * @param end +// * @return +// */ +// public static Set zRevRange(Object key, int start, int end) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// return redisTemplate.opsForZSet().reverseRange(key, start, end); +// } +// /** +// * 根据score的值,来获取满足条件的集合 zrangebyscore +// * +// * @param key +// * @param min +// * @param max +// * @return +// */ +// public static Set zSortRange(Object key, int min, int max) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// return redisTemplate.opsForZSet().rangeByScore(key, min, max); +// } +// +// +//// ============================== List ============================== +// +// /** +// * 获取 list缓存的内容 +// * @param key 键 +// * @param start 开始 +// * @param end 结束(0 到 -1 代表所有值) +// * @return +// */ +// 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) { +// e.printStackTrace(); +// return null; +// } +// } +// +// /** +// * 获取 list缓存的长度 +// * @param key 键 +// * @return 长度 +// */ +// public static long lGetListSize(String key) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// try { +// return redisTemplate.opsForList().size(key); +// } catch (Exception e) { +// e.printStackTrace(); +// return 0; +// } +// } +// +// /** +// * 根据索引 index 获取键为 key 的 list 中的元素 +// * @param key 键 +// * @param index 索引 +// * 当 index >= 0 时 {0:表头, 1:第二个元素} +// * 当 index < 0 时 {-1:表尾, -2:倒数第二个元素} +// * @return 值 +// */ +// 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) { +// e.printStackTrace(); +// return null; +// } +// } +// +// /** +// * 将值 value 插入键为 key 的 list 中,如果 list 不存在则创建空 list +// * @param key 键 +// * @param value 值 +// * @return true / false +// */ +// public static boolean lSet(String key, Object value) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// try { +// redisTemplate.opsForList().rightPush(key, value); +// return true; +// } catch (Exception e) { +// e.printStackTrace(); +// return false; +// } +// } +// +// /** +// * 将值 value 插入键为 key 的 list 中,并设置时间 +// * @param key 键 +// * @param value 值 +// * @param time 时间 +// * @return true / false +// */ +// 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) { +// expire(key, time); +// } +// return true; +// } catch (Exception e) { +// e.printStackTrace(); +// return false; +// } +// } +// +// /** +// * 将 values 插入键为 key 的 list 中 +// * @param key 键 +// * @param values 值 +// * @return true / false +// */ +// public static boolean lSetList(String key, List values) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// try { +// redisTemplate.opsForList().rightPushAll(key, values); +// return true; +// } catch (Exception e) { +// e.printStackTrace(); +// return false; +// } +// } +// +// /** +// * 将 values 插入键为 key 的 list 中,并设置时间 +// * @param key 键 +// * @param values 值 +// * @param time 时间 +// * @return true / false +// */ +// 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) { +// expire(key, time); +// } +// return true; +// } catch (Exception e) { +// e.printStackTrace(); +// return false; +// } +// } +// +// /** +// * 根据索引 index 修改键为 key 的值 +// * @param key 键 +// * @param index 索引 +// * @param value 值 +// * @return true / false +// */ +// 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; +// } catch (Exception e) { +// e.printStackTrace(); +// return false; +// } +// } +// +// /** +// * 在键为 key 的 list 中删除值为 value 的元素 +// * @param key 键 +// * @param count 如果 count == 0 则删除 list 中所有值为 value 的元素 +// * 如果 count > 0 则删除 list 中最左边那个值为 value 的元素 +// * 如果 count < 0 则删除 list 中最右边那个值为 value 的元素 +// * @param value +// * @return +// */ +// 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) { +// e.printStackTrace(); +// return 0; +// } +// } +// +// /** +// * 在键为 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 static List keys(String key) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// try { +// Set set = redisTemplate.keys(key); +// return new ArrayList<>(set); +// } catch (Exception e) { +// e.printStackTrace(); +// return null; +// } +// } +// +// +// /** +// * 模糊查询 +// * @param query 查询参数 +// * @return +// */ +//// public static List scan(String query) { +//// List result = new ArrayList<>(); +//// try { +//// Cursor> cursor = redisTemplate.opsForHash().scan("field", +//// ScanOptions.scanOptions().match(query).count(1000).build()); +//// while (cursor.hasNext()) { +//// Map.Entry entry = cursor.next(); +//// result.add(entry.getKey()); +//// Object key = entry.getKey(); +//// Object valueSet = entry.getValue(); +//// } +//// //关闭cursor +//// cursor.close(); +//// } catch (Exception e) { +//// e.printStackTrace(); +//// } +//// return result; +//// } +// +// /** +// * 模糊查询 +// * @param query 查询参数 +// * @return +// */ +// 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 keys; +// }); +// +// return new ArrayList<>(resultKeys); +// } +// public static List scan2(String query) { +// if (redisTemplate == null) { +// redisTemplate = SpringBeanFactory.getBean("redisTemplate"); +// } +// Set keys = redisTemplate.keys(query); +// return new ArrayList<>(keys); +// } +// // ============================== 消息发送与订阅 ============================== +// 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/MobilePosition/MobilePositionController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/MobilePosition/MobilePositionController.java deleted file mode 100644 index 92806b17a..000000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/MobilePosition/MobilePositionController.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.genersoft.iot.vmp.vmanager.MobilePosition; - -import java.util.List; - -import javax.sip.message.Response; - -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.github.pagehelper.util.StringUtil; - -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.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.context.request.async.DeferredResult; - -@CrossOrigin -@RestController -@RequestMapping("/api") -public class MobilePositionController { - - private final static Logger logger = LoggerFactory.getLogger(MobilePositionController.class); - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private SIPCommander cmder; - - @Autowired - private DeferredResultHolder resultHolder; - - @GetMapping("/positions/{deviceId}/history") - public ResponseEntity> positions(@PathVariable String deviceId, - @RequestParam(required = false) String start, - @RequestParam(required = false) String end) { - if (logger.isDebugEnabled()) { - logger.debug("查询设备" + deviceId + "的历史轨迹"); - } - - if (StringUtil.isEmpty(start)) { - start = null; - } - if (StringUtil.isEmpty(end)) { - end = null; - } - - List result = storager.queryMobilePositions(deviceId, start, end); - return new ResponseEntity<>(result, HttpStatus.OK); - } - - @GetMapping("/positions/{deviceId}/latest") - public ResponseEntity latestPosition(@PathVariable String deviceId) { - if (logger.isDebugEnabled()) { - logger.debug("查询设备" + deviceId + "的最新位置"); - } - MobilePosition result = storager.queryLatestPosition(deviceId); - return new ResponseEntity<>(result, HttpStatus.OK); - } - - @GetMapping("/positions/{deviceId}/realtime") - public DeferredResult> realTimePosition(@PathVariable String deviceId) { - Device device = storager.queryVideoDevice(deviceId); - cmder.mobilePostitionQuery(device, event -> { - Response response = event.getResponse(); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_MOBILEPOSITION + deviceId); - msg.setData(String.format("获取移动位置信息失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); - resultHolder.invokeResult(msg); - }); - DeferredResult> result = new DeferredResult>(5*1000L); - result.onTimeout(()->{ - logger.warn(String.format("获取移动位置信息超时")); - // 释放rtpserver - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId); - msg.setData("Timeout"); - resultHolder.invokeResult(msg); - }); - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId, result); - return result; - } - - @GetMapping("/positions/{deviceId}/subscribe") - public ResponseEntity 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 += ",失败"; - } - - return new ResponseEntity<>(result, HttpStatus.OK); - } -} 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 000000000..23690e545 --- /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 000000000..009048898 --- /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 000000000..0b9d3d9a2 --- /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 000000000..18c22407b --- /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 000000000..e2e3879bd --- /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/bean/OtherRtpSendInfo.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/OtherRtpSendInfo.java new file mode 100644 index 000000000..225e40c4f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/OtherRtpSendInfo.java @@ -0,0 +1,135 @@ +package com.genersoft.iot.vmp.vmanager.bean; + +public class OtherRtpSendInfo { + + /** + * 发流IP + */ + private String ip; + + /** + * 发流端口 + */ + private int port; + + /** + * 收流IP + */ + private String receiveIp; + + /** + * 收流端口 + */ + private int receivePort; + + /** + * 会话ID + */ + private String callId; + + /** + * 流ID + */ + private String stream; + + /** + * 推流应用名 + */ + private String pushApp; + + /** + * 推流流ID + */ + private String pushStream; + + /** + * 推流SSRC + */ + private String pushSSRC; + + + + 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 getReceiveIp() { + return receiveIp; + } + + public void setReceiveIp(String receiveIp) { + this.receiveIp = receiveIp; + } + + public int getReceivePort() { + return receivePort; + } + + public void setReceivePort(int receivePort) { + this.receivePort = receivePort; + } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public String getPushApp() { + return pushApp; + } + + public void setPushApp(String pushApp) { + this.pushApp = pushApp; + } + + public String getPushStream() { + return pushStream; + } + + public void setPushStream(String pushStream) { + this.pushStream = pushStream; + } + + public String getPushSSRC() { + return pushSSRC; + } + + public void setPushSSRC(String pushSSRC) { + this.pushSSRC = pushSSRC; + } + + @Override + public String toString() { + return "OtherRtpSendInfo{" + + "ip='" + ip + '\'' + + ", port=" + port + + ", receiveIp='" + receiveIp + '\'' + + ", receivePort=" + receivePort + + ", callId='" + callId + '\'' + + ", stream='" + stream + '\'' + + '}'; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/PlayTypeEnum.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/PlayTypeEnum.java new file mode 100644 index 000000000..d2ee56bfb --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/PlayTypeEnum.java @@ -0,0 +1,23 @@ +package com.genersoft.iot.vmp.vmanager.bean; + +public enum PlayTypeEnum { + + PLAY("0", "直播"), + PLAY_BACK("1", "回放"); + + private String value; + private String name; + + PlayTypeEnum(String value, String name) { + this.value = value; + this.name = name; + } + + public String getValue() { + return value; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/ResourceBaseInfo.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/ResourceBaseInfo.java new file mode 100644 index 000000000..dab9b0ae0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/ResourceBaseInfo.java @@ -0,0 +1,30 @@ +package com.genersoft.iot.vmp.vmanager.bean; + +public class ResourceBaseInfo { + private int total; + private int online; + + public ResourceBaseInfo() { + } + + public ResourceBaseInfo(int total, int online) { + this.total = total; + this.online = 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 000000000..3b0ee0d38 --- /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 ResourceBaseInfo device; + private ResourceBaseInfo channel; + private ResourceBaseInfo push; + private ResourceBaseInfo proxy; + + public ResourceBaseInfo getDevice() { + return device; + } + + public void setDevice(ResourceBaseInfo device) { + this.device = device; + } + + public ResourceBaseInfo getChannel() { + return channel; + } + + public void setChannel(ResourceBaseInfo channel) { + this.channel = channel; + } + + public ResourceBaseInfo getPush() { + return push; + } + + public void setPush(ResourceBaseInfo push) { + this.push = push; + } + + public ResourceBaseInfo getProxy() { + return proxy; + } + + public void setProxy(ResourceBaseInfo proxy) { + this.proxy = proxy; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/SnapPath.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/SnapPath.java new file mode 100644 index 000000000..ce34d72c4 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/SnapPath.java @@ -0,0 +1,50 @@ +package com.genersoft.iot.vmp.vmanager.bean; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "截图地址信息") +public class SnapPath { + + @Schema(description = "相对地址") + private String path; + + @Schema(description = "绝对地址") + private String absoluteFilePath; + + @Schema(description = "请求地址") + private String url; + + + public static SnapPath getInstance(String path, String absoluteFilePath, String url) { + SnapPath snapPath = new SnapPath(); + snapPath.setPath(path); + snapPath.setAbsoluteFilePath(absoluteFilePath); + snapPath.setUrl(url); + return snapPath; + } + + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getAbsoluteFilePath() { + return absoluteFilePath; + } + + public void setAbsoluteFilePath(String absoluteFilePath) { + this.absoluteFilePath = absoluteFilePath; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } +} 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 000000000..abf6df442 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamContent.java @@ -0,0 +1,414 @@ +package com.genersoft.iot.vmp.vmanager.bean; + +import com.genersoft.iot.vmp.common.StreamInfo; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "流信息") +public class StreamContent { + + @Schema(description = "应用名") + private String app; + + @Schema(description = "流ID") + private String stream; + + @Schema(description = "IP") + private String ip; + + @Schema(description = "HTTP-FLV流地址") + private String flv; + + @Schema(description = "HTTPS-FLV流地址") + private String https_flv; + + @Schema(description = "Websocket-FLV流地址") + private String ws_flv; + + @Schema(description = "Websockets-FLV流地址") + private String wss_flv; + + @Schema(description = "HTTP-FMP4流地址") + private String fmp4; + + @Schema(description = "HTTPS-FMP4流地址") + private String https_fmp4; + + @Schema(description = "Websocket-FMP4流地址") + private String ws_fmp4; + + @Schema(description = "Websockets-FMP4流地址") + private String wss_fmp4; + + @Schema(description = "HLS流地址") + private String hls; + + @Schema(description = "HTTPS-HLS流地址") + private String https_hls; + + @Schema(description = "Websocket-HLS流地址") + private String ws_hls; + + @Schema(description = "Websockets-HLS流地址") + private String wss_hls; + + @Schema(description = "HTTP-TS流地址") + private String ts; + + @Schema(description = "HTTPS-TS流地址") + private String https_ts; + + @Schema(description = "Websocket-TS流地址") + private String ws_ts; + + @Schema(description = "Websockets-TS流地址") + private String wss_ts; + + @Schema(description = "RTMP流地址") + private String rtmp; + + @Schema(description = "RTMPS流地址") + private String rtmps; + + @Schema(description = "RTSP流地址") + private String rtsp; + + @Schema(description = "RTSPS流地址") + private String rtsps; + + @Schema(description = "RTC流地址") + private String rtc; + + @Schema(description = "RTCS流地址") + private String rtcs; + + @Schema(description = "流媒体ID") + private String mediaServerId; + + @Schema(description = "流编码信息") + private Object tracks; + + @Schema(description = "开始时间") + private String startTime; + + @Schema(description = "结束时间") + 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.getHttps_fmp4() != null) { + this.https_fmp4 = streamInfo.getHttps_fmp4().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 000000000..dcec60768 --- /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 000000000..f5a52b973 --- /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 new file mode 100644 index 000000000..d293b8d4f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java @@ -0,0 +1,75 @@ +package com.genersoft.iot.vmp.vmanager.bean; + + +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() { + return new WVPResult<>(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), null); + } + + 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; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public T getData() { + return data; + } + + 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/device/DeviceConfig.java b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceConfig.java deleted file mode 100644 index d046c0dfc..000000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceConfig.java +++ /dev/null @@ -1,120 +0,0 @@ -/** - * 设备设置命令API接口 - * - * @author lawrencehj - * @date 2021年2月2日 - */ - -package com.genersoft.iot.vmp.vmanager.device; - -import javax.sip.message.Response; - -import com.alibaba.fastjson.JSONObject; -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.gb28181.utils.XmlUtil; -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.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.context.request.async.DeferredResult; - -@CrossOrigin -@RestController -@RequestMapping("/api") -public class DeviceConfig { - - private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class); - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private SIPCommander cmder; - - @Autowired - private DeferredResultHolder resultHolder; - - /** - * 看守位控制命令API接口 - * - * @param deviceId - * @param enabled 看守位使能1:开启,0:关闭 - * @param resetTime 自动归位时间间隔(可选) - * @param presetIndex 调用预置位编号(可选) - * @param channelId 通道编码(可选) - */ - @GetMapping("/config/{deviceId}/basicParam") - public DeferredResult> homePositionApi(@PathVariable String deviceId, - @RequestParam(required = false) String channelId, - @RequestParam(required = false) String name, - @RequestParam(required = false) String expiration, - @RequestParam(required = false) String heartBeatInterval, - @RequestParam(required = false) String heartBeatCount) { - if (logger.isDebugEnabled()) { - logger.debug("报警复位API调用"); - } - Device device = storager.queryVideoDevice(deviceId); - cmder.deviceBasicConfigCmd(device, channelId, name, expiration, heartBeatInterval, heartBeatCount, event -> { - Response response = event.getResponse(); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); - msg.setData(String.format("设备配置操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); - resultHolder.invokeResult(msg); - }); - DeferredResult> result = new DeferredResult>(3 * 1000L); - result.onTimeout(() -> { - logger.warn(String.format("设备配置操作超时, 设备未返回应答指令")); - // 释放rtpserver - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); - JSONObject json = new JSONObject(); - json.put("DeviceID", deviceId); - json.put("Status", "Timeout"); - json.put("Description", "设备配置操作超时, 设备未返回应答指令"); - msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令"); - resultHolder.invokeResult(msg); - }); - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result); - return result; - } - - /** - * 设备配置查询请求API接口 - * - * @param deviceId - */ - @GetMapping("/config/{deviceId}/query/{configType}") - public DeferredResult> configDownloadApi(@PathVariable String deviceId, - @PathVariable String configType, - @RequestParam(required = false) String channelId) { - if (logger.isDebugEnabled()) { - logger.debug("设备状态查询API调用"); - } - Device device = storager.queryVideoDevice(deviceId); - cmder.deviceConfigQuery(device, channelId, configType, event -> { - Response response = event.getResponse(); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); - msg.setData(String.format("获取设备配置失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); - resultHolder.invokeResult(msg); - }); - DeferredResult> result = new DeferredResult> (3 * 1000L); - result.onTimeout(()->{ - logger.warn(String.format("获取设备配置超时")); - // 释放rtpserver - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); - msg.setData("Timeout. Device did not response to this command."); - resultHolder.invokeResult(msg); - }); - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result); - return result; - } - -} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceControl.java b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceControl.java deleted file mode 100644 index 722135947..000000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceControl.java +++ /dev/null @@ -1,238 +0,0 @@ -/** - * 设备控制命令API接口 - * - * @author lawrencehj - * @date 2021年2月1日 - */ - -package com.genersoft.iot.vmp.vmanager.device; - -import javax.sip.message.Response; - -import com.alibaba.fastjson.JSONObject; -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.gb28181.utils.XmlUtil; -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.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.context.request.async.DeferredResult; - -@CrossOrigin -@RestController -@RequestMapping("/api") -public class DeviceControl { - - private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class); - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private SIPCommander cmder; - - @Autowired - private DeferredResultHolder resultHolder; - - /** - * 远程启动控制命令API接口 - * - * @param deviceId - */ - @GetMapping("/control/{deviceId}/teleboot") - @PostMapping("/control/{deviceId}/teleboot") - public ResponseEntity 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); - } - } - - /** - * 录像控制命令API接口 - * - * @param deviceId - * @param recordCmdStr Record:手动录像,StopRecord:停止手动录像 - * @param channelId 通道编码(可选) - */ - @GetMapping("/control/{deviceId}/record/{recordCmdStr}") - public DeferredResult> recordApi(@PathVariable String deviceId, - @PathVariable String recordCmdStr, @RequestParam(required = false) String channelId) { - if (logger.isDebugEnabled()) { - logger.debug("开始/停止录像API调用"); - } - Device device = storager.queryVideoDevice(deviceId); - cmder.recordCmd(device, channelId, recordCmdStr, event -> { - Response response = event.getResponse(); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); - msg.setData(String.format("开始/停止录像操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); - resultHolder.invokeResult(msg); - }); - DeferredResult> result = new DeferredResult>(3 * 1000L); - result.onTimeout(() -> { - logger.warn(String.format("开始/停止录像操作超时, 设备未返回应答指令")); - // 释放rtpserver - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); - msg.setData("Timeout. Device did not response to this command."); - resultHolder.invokeResult(msg); - }); - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result); - return result; - } - - /** - * 报警布防/撤防命令API接口 - * - * @param deviceId - * @param guardCmdStr SetGuard:布防,ResetGuard:撤防 - */ - @GetMapping("/control/{deviceId}/guard/{guardCmdStr}") - public DeferredResult> guardApi(@PathVariable String deviceId, @PathVariable String guardCmdStr) { - if (logger.isDebugEnabled()) { - logger.debug("布防/撤防API调用"); - } - Device device = storager.queryVideoDevice(deviceId); - cmder.guardCmd(device, guardCmdStr, event -> { - Response response = event.getResponse(); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId); - msg.setData(String.format("布防/撤防操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); - resultHolder.invokeResult(msg); - }); - DeferredResult> result = new DeferredResult>(3 * 1000L); - result.onTimeout(() -> { - logger.warn(String.format("布防/撤防操作超时, 设备未返回应答指令")); - // 释放rtpserver - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId); - msg.setData("Timeout. Device did not response to this command."); - resultHolder.invokeResult(msg); - }); - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId, result); - return result; - } - - /** - * 报警复位API接口 - * - * @param deviceId - * @param alarmMethod 报警方式(可选) - * @param alarmType 报警类型(可选) - */ - @GetMapping("/control/{deviceId}/resetAlarm") - public DeferredResult> resetAlarmApi(@PathVariable String deviceId, - @RequestParam(required = false) String alarmMethod, - @RequestParam(required = false) String alarmType) { - if (logger.isDebugEnabled()) { - logger.debug("报警复位API调用"); - } - Device device = storager.queryVideoDevice(deviceId); - cmder.alarmCmd(device, alarmMethod, alarmType, event -> { - Response response = event.getResponse(); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId); - msg.setData(String.format("报警复位操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); - resultHolder.invokeResult(msg); - }); - DeferredResult> result = new DeferredResult>(3 * 1000L); - result.onTimeout(() -> { - logger.warn(String.format("报警复位操作超时, 设备未返回应答指令")); - // 释放rtpserver - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId); - msg.setData("Timeout. Device did not response to this command."); - resultHolder.invokeResult(msg); - }); - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId, result); - return result; - } - - /** - * 强制关键帧API接口 - * - * @param deviceId - * @param channelId - */ - @GetMapping("/control/{deviceId}/iFrame") - @PostMapping("/control/{deviceId}/iFrame") - public ResponseEntity 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); - } - } - - /** - * 看守位控制命令API接口 - * - * @param deviceId - * @param enabled 看守位使能1:开启,0:关闭 - * @param resetTime 自动归位时间间隔(可选) - * @param presetIndex 调用预置位编号(可选) - * @param channelId 通道编码(可选) - */ - @GetMapping("/control/{deviceId}/homePosition/{enabled}") - public DeferredResult> homePositionApi(@PathVariable String deviceId, - @PathVariable String enabled, - @RequestParam(required = false) String resetTime, - @RequestParam(required = false) String presetIndex, - @RequestParam(required = false) String channelId) { - if (logger.isDebugEnabled()) { - logger.debug("报警复位API调用"); - } - Device device = storager.queryVideoDevice(deviceId); - cmder.homePositionCmd(device, channelId, enabled, resetTime, presetIndex, event -> { - Response response = event.getResponse(); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); - msg.setData(String.format("看守位控制操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); - resultHolder.invokeResult(msg); - }); - DeferredResult> result = new DeferredResult>(3 * 1000L); - result.onTimeout(() -> { - logger.warn(String.format("看守位控制操作超时, 设备未返回应答指令")); - // 释放rtpserver - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); - JSONObject json = new JSONObject(); - json.put("DeviceID", deviceId); - json.put("Status", "Timeout"); - json.put("Description", "看守位控制操作超时, 设备未返回应答指令"); - msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令"); - resultHolder.invokeResult(msg); - }); - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result); - return result; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceQuery.java b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceQuery.java deleted file mode 100644 index 997a43b26..000000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceQuery.java +++ /dev/null @@ -1,255 +0,0 @@ -package com.genersoft.iot.vmp.vmanager.device; - -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; -import com.github.pagehelper.PageInfo; -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.web.bind.annotation.*; -import org.springframework.web.context.request.async.DeferredResult; - -import com.alibaba.fastjson.JSONObject; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector; -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 javax.sip.message.Response; - -@SuppressWarnings("rawtypes") -@CrossOrigin -@RestController -@RequestMapping("/api") -public class DeviceQuery { - - private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class); - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private SIPCommander cmder; - - @Autowired - private DeferredResultHolder resultHolder; - - @Autowired - private DeviceOffLineDetector offLineDetector; - - @GetMapping("/devices/{deviceId}") - public ResponseEntity devices(@PathVariable String deviceId){ - - if (logger.isDebugEnabled()) { - logger.debug("查询视频设备API调用,deviceId:" + deviceId); - } - - Device device = storager.queryVideoDevice(deviceId); - return new ResponseEntity<>(device,HttpStatus.OK); - } - - @GetMapping("/devices") - public PageInfo devices(int page, int count){ - - if (logger.isDebugEnabled()) { - logger.debug("查询所有视频设备API调用"); - } - - return storager.queryVideoDeviceList(page, count); - } - - /** - * 分页查询通道数 - * - * @param deviceId 设备id - * @param page 当前页 - * @param count 每页条数 - * @param query 查询内容 - * @param online 是否在线 在线 true / 离线 false - * @param channelType 设备 false/子目录 true - * @return 通道列表 - */ - @GetMapping("/devices/{deviceId}/channels") - public ResponseEntity 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)) { - query = null; - } - - PageInfo pageResult = storager.queryChannelsByDeviceId(deviceId, query, channelType, online, page, count); - return new ResponseEntity<>(pageResult,HttpStatus.OK); - } - - @PostMapping("/devices/{deviceId}/sync") - public DeferredResult> devicesSync(@PathVariable String deviceId){ - - if (logger.isDebugEnabled()) { - } - logger.debug("设备通道信息同步API调用,deviceId:" + deviceId); - - Device device = storager.queryVideoDevice(deviceId); - cmder.catalogQuery(device, event -> { - Response response = event.getResponse(); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId); - msg.setData(String.format("同步通道失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); - resultHolder.invokeResult(msg); - }); - DeferredResult> result = new DeferredResult>(2*1000L); - result.onTimeout(()->{ - logger.warn(String.format("设备通道信息同步超时")); - // 释放rtpserver - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId); - msg.setData("Timeout"); - resultHolder.invokeResult(msg); - }); - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId, result); - return result; - } - - @PostMapping("/devices/{deviceId}/delete") - public ResponseEntity delete(@PathVariable String deviceId){ - - if (logger.isDebugEnabled()) { - logger.debug("设备信息删除API调用,deviceId:" + deviceId); - } - - if (offLineDetector.isOnline(deviceId)) { - return new ResponseEntity("不允许删除在线设备!", HttpStatus.NOT_ACCEPTABLE); - } - boolean isSuccess = storager.delete(deviceId); - if (isSuccess) { - JSONObject json = new JSONObject(); - json.put("deviceId", deviceId); - return new ResponseEntity<>(json.toString(),HttpStatus.OK); - } else { - logger.warn("设备信息删除API调用失败!"); - return new ResponseEntity("设备信息删除API调用失败!", HttpStatus.INTERNAL_SERVER_ERROR); - } - } - - /** - * 分页查询通道数 - * @param channelId 通道id - * @param page 当前页 - * @param count 每页条数 - * @return 子通道列表 - */ - @GetMapping("/subChannels/{deviceId}/{channelId}/channels") - public ResponseEntity 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 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); - } - - PageInfo pageResult = storager.querySubChannels(deviceId, channelId, query, channelType, online, page, count); - return new ResponseEntity<>(pageResult,HttpStatus.OK); - } - - @PostMapping("/channel/update/{deviceId}") - public ResponseEntity updateChannel(@PathVariable String deviceId,DeviceChannel channel){ - storager.updateChannel(deviceId, channel); - return new ResponseEntity<>(null,HttpStatus.OK); - } - - @GetMapping("/devices/{deviceId}/transport/{streamMode}") - @PostMapping("/devices/{deviceId}/transport/{streamMode}") - public ResponseEntity updateTransport(@PathVariable String deviceId, @PathVariable String streamMode){ - Device device = storager.queryVideoDevice(deviceId); - device.setStreamMode(streamMode); - storager.updateDevice(device); - return new ResponseEntity<>(null,HttpStatus.OK); - } - - /** - * 设备状态查询请求API接口 - * - * @param deviceId - */ - @GetMapping("/devices/{deviceId}/status") - public DeferredResult> deviceStatusApi(@PathVariable String deviceId) { - if (logger.isDebugEnabled()) { - logger.debug("设备状态查询API调用"); - } - Device device = storager.queryVideoDevice(deviceId); - cmder.deviceStatusQuery(device, event -> { - Response response = event.getResponse(); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId); - msg.setData(String.format("获取设备状态失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); - resultHolder.invokeResult(msg); - }); - DeferredResult> result = new DeferredResult>(2*1000L); - result.onTimeout(()->{ - logger.warn(String.format("获取设备状态超时")); - // 释放rtpserver - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId); - msg.setData("Timeout. Device did not response to this command."); - resultHolder.invokeResult(msg); - }); - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId, result); - return result; - } - - /** - * 设备报警查询请求API接口 - * - * @param deviceId - */ - @GetMapping("/alarm/{deviceId}") - public DeferredResult> alarmApi(@PathVariable String deviceId, - @RequestParam(required = false) String startPriority, - @RequestParam(required = false) String endPriority, - @RequestParam(required = false) String alarmMethod, - @RequestParam(required = false) String alarmType, - @RequestParam(required = false) String startTime, - @RequestParam(required = false) String endTime) { - if (logger.isDebugEnabled()) { - logger.debug("设备报警查询API调用"); - } - Device device = storager.queryVideoDevice(deviceId); - cmder.alarmInfoQuery(device, startPriority, endPriority, alarmMethod, alarmType, startTime, endTime, event -> { - Response response = event.getResponse(); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId); - msg.setData(String.format("设备报警查询失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); - resultHolder.invokeResult(msg); - }); - DeferredResult> result = new DeferredResult> (3 * 1000L); - result.onTimeout(()->{ - logger.warn(String.format("设备报警查询超时")); - // 释放rtpserver - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId); - msg.setData("设备报警查询超时"); - resultHolder.invokeResult(msg); - }); - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId, result); - return result; - } - - -} 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 new file mode 100644 index 000000000..684708cda --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/MobilePosition/MobilePositionController.java @@ -0,0 +1,179 @@ +package com.genersoft.iot.vmp.vmanager.gb28181.MobilePosition; + +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.service.IDeviceChannelService; +import com.genersoft.iot.vmp.service.IDeviceService; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.github.pagehelper.util.StringUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +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; +import java.util.List; +import java.util.UUID; + +/** + * 位置信息管理 + */ +@Tag(name = "位置信息管理") + +@RestController +@RequestMapping("/api/position") +public class MobilePositionController { + + private final static Logger logger = LoggerFactory.getLogger(MobilePositionController.class); + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommander cmder; + + @Autowired + private DeferredResultHolder resultHolder; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private IDeviceChannelService deviceChannelService; + + /** + * 查询历史轨迹 + * @param deviceId 设备ID + * @param start 开始时间 + * @param end 结束时间 + * @return + */ + @Operation(summary = "查询历史轨迹") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号") + @Parameter(name = "start", description = "开始时间") + @Parameter(name = "end", description = "结束时间") + @GetMapping("/history/{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; + } + if (StringUtil.isEmpty(end)) { + end = null; + } + return storager.queryMobilePositions(deviceId, channelId, start, end); + } + + /** + * 查询设备最新位置 + * @param deviceId 设备ID + * @return + */ + @Operation(summary = "查询设备最新位置") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @GetMapping("/latest/{deviceId}") + public MobilePosition latestPosition(@PathVariable String deviceId) { + return storager.queryLatestPosition(deviceId); + } + + /** + * 获取移动位置信息 + * @param deviceId 设备ID + * @return + */ + @Operation(summary = "获取移动位置信息") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @GetMapping("/realtime/{deviceId}") + public DeferredResult realTimePosition(@PathVariable String deviceId) { + Device device = storager.queryVideoDevice(deviceId); + String uuid = UUID.randomUUID().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_MOBILE_POSITION + deviceId; + 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 + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData("Timeout"); + resultHolder.invokeResult(msg); + }); + resultHolder.put(key, uuid, result); + return result; + } + + /** + * 订阅位置信息 + * @param deviceId 设备ID + * @param expires 订阅超时时间 + * @param interval 上报时间间隔 + * @return true = 命令发送成功 + */ + @Operation(summary = "订阅位置信息") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "expires", description = "订阅超时时间", required = true) + @Parameter(name = "interval", description = "上报时间间隔", required = true) + @GetMapping("/subscribe/{deviceId}") + public void positionSubscribe(@PathVariable String deviceId, + @RequestParam String expires, + @RequestParam String interval) { + + if (StringUtil.isEmpty(interval)) { + interval = "5"; + } + Device device = storager.queryVideoDevice(deviceId); + device.setSubscribeCycleForMobilePosition(Integer.parseInt(expires)); + device.setMobilePositionSubmissionInterval(Integer.parseInt(interval)); + deviceService.updateDevice(device); + if (!deviceService.removeMobilePositionSubscribe(device)) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + /** + * 数据位置信息格式处理 + * @param deviceId 设备ID + * @return true = 命令发送成功 + */ + @Operation(summary = "数据位置信息格式处理") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @GetMapping("/transform/{deviceId}") + public void positionTransform(@PathVariable String deviceId) { + + Device device = deviceService.getDevice(deviceId); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "未找到设备: " + deviceId); + } + boolean result = deviceChannelService.updateAllGps(device); + if (!result) { + throw new ControllerException(ErrorCode.ERROR100); + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/SseController/SseController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/SseController/SseController.java similarity index 80% rename from src/main/java/com/genersoft/iot/vmp/vmanager/SseController/SseController.java rename to src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/SseController/SseController.java index 1e55c3269..b1ad3b9f5 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/SseController/SseController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/SseController/SseController.java @@ -1,9 +1,12 @@ -package com.genersoft.iot.vmp.vmanager.SseController; +package com.genersoft.iot.vmp.vmanager.gb28181.SseController; import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEventListener; + +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; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; @@ -13,16 +16,15 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; * @author: lawrencehj * @data: 2021-01-20 */ +@Tag(name = "SSE推送") -@CrossOrigin @Controller @RequestMapping("/api") public class SseController { - @Autowired + @Autowired AlarmEventListener alarmEventListener; - - //设置响应 - @RequestMapping("/emit") + + @GetMapping("/emit") public SseEmitter emit(@RequestParam String browserId) { final SseEmitter sseEmitter = new SseEmitter(0L); try { 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 new file mode 100644 index 000000000..6f2a58527 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java @@ -0,0 +1,189 @@ +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.service.IDeviceAlarmService; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.github.pagehelper.PageInfo; +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.*; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.Arrays; +import java.util.List; + +@Tag(name = "报警信息管理") + +@RestController +@RequestMapping("/api/alarm") +public class AlarmController { + + private final static Logger logger = LoggerFactory.getLogger(AlarmController.class); + + @Autowired + private IDeviceAlarmService deviceAlarmService; + + @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; + }else if (!DateUtil.verification(time, DateUtil.formatter) ){ + throw new ControllerException(ErrorCode.ERROR400.getCode(), "time格式为" + DateUtil.PATTERN); + } + 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.getNow()); + 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 + "是平台还是设备"); + } + + } + + /** + * 分页查询报警 + * + * @param deviceId 设备id + * @param page 当前页 + * @param count 每页查询数量 + * @param alarmPriority 报警级别 + * @param alarmMethod 报警方式 + * @param alarmType 报警类型 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return + */ + @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") + 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 (ObjectUtils.isEmpty(alarmPriority)) { + alarmPriority = null; + } + if (ObjectUtils.isEmpty(alarmMethod)) { + alarmMethod = null; + } + if (ObjectUtils.isEmpty(alarmType)) { + alarmType = null; + } + + if (ObjectUtils.isEmpty(startTime)) { + startTime = null; + }else if (!DateUtil.verification(startTime, DateUtil.formatter) ){ + throw new ControllerException(ErrorCode.ERROR400.getCode(), "startTime格式为" + DateUtil.PATTERN); + } + + if (ObjectUtils.isEmpty(endTime)) { + endTime = null; + }else if (!DateUtil.verification(endTime, DateUtil.formatter) ){ + throw new ControllerException(ErrorCode.ERROR400.getCode(), "endTime格式为" + DateUtil.PATTERN); + } + + 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 new file mode 100644 index 000000000..544e345bb --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceConfig.java @@ -0,0 +1,156 @@ +/** + * 设备设置命令API接口 + * + * @author lawrencehj + * @date 2021年2月2日 + */ + +package com.genersoft.iot.vmp.vmanager.gb28181.device; + +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.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.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; + +@Tag(name = "国标设备配置") +@RestController +@RequestMapping("/api/device/config") +public class DeviceConfig { + + private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class); + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommander cmder; + + @Autowired + private DeferredResultHolder resultHolder; + + /** + * 看守位控制命令API接口 + * @param deviceId 设备ID + * @param channelId 通道ID + * @param name 名称 + * @param expiration 到期时间 + * @param heartBeatInterval 心跳间隔 + * @param heartBeatCount 心跳计数 + * @return + */ + @GetMapping("/basicParam/{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, + @RequestParam(required = false) String heartBeatInterval, + @RequestParam(required = false) String heartBeatCount) { + if (logger.isDebugEnabled()) { + logger.debug("报警复位API调用"); + } + Device device = storager.queryVideoDevice(deviceId); + String uuid = UUID.randomUUID().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + deviceId + channelId; + 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 + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + JSONObject json = new JSONObject(); + json.put("DeviceID", deviceId); + json.put("Status", "Timeout"); + json.put("Description", "设备配置操作超时, 设备未返回应答指令"); + msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令"); + resultHolder.invokeResult(msg); + }); + resultHolder.put(key, uuid, result); + return result; + } + + /** + * 设备配置查询请求API接口 + * @param deviceId 设备ID + * @param configType 配置类型 + * @param channelId 通道ID + * @return + */ + @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, + @PathVariable String configType, + @RequestParam(required = false) String channelId) { + if (logger.isDebugEnabled()) { + logger.debug("设备状态查询API调用"); + } + String key = DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (ObjectUtils.isEmpty(channelId) ? deviceId : channelId); + String uuid = UUID.randomUUID().toString(); + Device device = storager.queryVideoDevice(deviceId); + 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 + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData("Timeout. Device did not response to this command."); + resultHolder.invokeResult(msg); + }); + resultHolder.put(key, uuid, result); + return result; + } + +} 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 new file mode 100644 index 000000000..bf0acb5ab --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java @@ -0,0 +1,401 @@ +/** + * 设备控制命令API接口 + * + * @author lawrencehj + * @date 2021年2月1日 + */ + +package com.genersoft.iot.vmp.vmanager.gb28181.device; + +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.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.ResponseEntity; +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; + +@Tag(name = "国标设备控制") + +@RestController +@RequestMapping("/api/device/control") +public class DeviceControl { + + private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class); + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommander cmder; + + @Autowired + private DeferredResultHolder resultHolder; + + /** + * 远程启动控制命令API接口 + * + * @param deviceId 设备ID + */ + @Operation(summary = "远程启动控制命令") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @GetMapping("/teleboot/{deviceId}") + public void teleBootApi(@PathVariable String deviceId) { + if (logger.isDebugEnabled()) { + logger.debug("设备远程启动API调用"); + } + Device device = storager.queryVideoDevice(deviceId); + try { + cmder.teleBootCmd(device); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 远程启动: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + } + + /** + * 录像控制命令API接口 + * + * @param deviceId 设备ID + * @param recordCmdStr Record:手动录像,StopRecord:停止手动录像 + * @param channelId 通道编码(可选) + */ + @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) { + if (logger.isDebugEnabled()) { + logger.debug("开始/停止录像API调用"); + } + Device device = storager.queryVideoDevice(deviceId); + String uuid = UUID.randomUUID().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId + channelId; + DeferredResult> result = new DeferredResult>(3 * 1000L); + result.onTimeout(() -> { + logger.warn(String.format("开始/停止录像操作超时, 设备未返回应答指令")); + // 释放rtpserver + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setId(uuid); + msg.setData("Timeout. Device did not response to this command."); + resultHolder.invokeAllResult(msg); + }); + if (resultHolder.exist(key, null)){ + return result; + } + resultHolder.put(key, uuid, result); + 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); + },null); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 开始/停止录像: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + + return result; + } + + /** + * 报警布防/撤防命令API接口 + * + * @param deviceId 设备ID + * @param guardCmdStr SetGuard:布防,ResetGuard:撤防 + */ + @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, @PathVariable String guardCmdStr) { + if (logger.isDebugEnabled()) { + logger.debug("布防/撤防API调用"); + } + Device device = storager.queryVideoDevice(deviceId); + String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId + deviceId; + String uuid =UUID.randomUUID().toString(); + 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); + },null); + } 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("布防/撤防操作超时, 设备未返回应答指令")); + // 释放rtpserver + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setId(uuid); + msg.setData("Timeout. Device did not response to this command."); + resultHolder.invokeResult(msg); + }); + + return result; + } + + /** + * 报警复位API接口 + * + * @param deviceId 设备ID + * @param alarmMethod 报警方式(可选) + * @param alarmType 报警类型(可选) + */ + @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, + @RequestParam(required = false) String alarmType) { + if (logger.isDebugEnabled()) { + logger.debug("报警复位API调用"); + } + Device device = storager.queryVideoDevice(deviceId); + String uuid = UUID.randomUUID().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId + channelId; + 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); + },null); + } 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 + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData("Timeout. Device did not response to this command."); + resultHolder.invokeResult(msg); + }); + resultHolder.put(key, uuid, result); + return result; + } + + /** + * 强制关键帧API接口 + * + * @param deviceId 设备ID + * @param channelId 通道ID + */ + @Operation(summary = "强制关键帧") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号") + @GetMapping("/i_frame/{deviceId}") + public JSONObject iFrame(@PathVariable String deviceId, + @RequestParam(required = false) String channelId) { + if (logger.isDebugEnabled()) { + logger.debug("强制关键帧API调用"); + } + Device device = storager.queryVideoDevice(deviceId); + 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; + } + + /** + * 看守位控制命令API接口 + * + * @param deviceId 设备ID + * @param enabled 看守位使能1:开启,0:关闭 + * @param resetTime 自动归位时间间隔(可选) + * @param presetIndex 调用预置位编号(可选) + * @param channelId 通道编码(可选) + */ + @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, + @PathVariable String enabled, + @RequestParam(required = false) String resetTime, + @RequestParam(required = false) String presetIndex, + String channelId) { + if (logger.isDebugEnabled()) { + logger.debug("报警复位API调用"); + } + String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (ObjectUtils.isEmpty(channelId) ? deviceId : channelId); + String uuid = UUID.randomUUID().toString(); + Device device = storager.queryVideoDevice(deviceId); + 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); + },null); + } 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 + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + JSONObject json = new JSONObject(); + json.put("DeviceID", deviceId); + json.put("Status", "Timeout"); + json.put("Description", "看守位控制操作超时, 设备未返回应答指令"); + msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令"); + resultHolder.invokeResult(msg); + }); + 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 new file mode 100644 index 000000000..b182b2653 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java @@ -0,0 +1,572 @@ +package com.genersoft.iot.vmp.vmanager.gb28181.device; + +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.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.service.IInviteStreamService; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.vmanager.bean.BaseTree; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.bean.WVPResult; +import com.github.pagehelper.PageInfo; +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.apache.ibatis.annotations.Options; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.async.DeferredResult; + +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.*; + +@Tag(name = "国标设备查询", description = "国标设备查询") +@SuppressWarnings("rawtypes") + +@RestController +@RequestMapping("/api/device/query") +public class DeviceQuery { + + private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class); + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IDeviceChannelService deviceChannelService; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IInviteStreamService inviteStreamService; + + @Autowired + private SIPCommander cmder; + + @Autowired + private DeferredResultHolder resultHolder; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private DynamicTask dynamicTask; + + /** + * 使用ID查询国标设备 + * @param deviceId 国标ID + * @return 国标设备 + */ + @Operation(summary = "查询国标设备") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @GetMapping("/devices/{deviceId}") + public Device devices(@PathVariable String deviceId){ + + return storager.queryVideoDevice(deviceId); + } + + /** + * 分页查询国标设备 + * @param page 当前页 + * @param count 每页查询数量 + * @return 分页国标列表 + */ + @Operation(summary = "分页查询国标设备") + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页查询数量", required = true) + @GetMapping("/devices") + @Options() + public PageInfo devices(int page, int count){ +// if (page == null) page = 0; +// if (count == null) count = 20; + return storager.queryVideoDeviceList(page, count,null); + } + + /** + * 分页查询通道数 + * + * @param deviceId 设备id + * @param page 当前页 + * @param count 每页条数 + * @param query 查询内容 + * @param online 是否在线 在线 true / 离线 false + * @param channelType 设备 false/子目录 true + * @param catalogUnderDevice 是否直属与设备的目录 + * @return 通道列表 + */ + @GetMapping("/devices/{deviceId}/channels") + @Operation(summary = "分页查询通道") + @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, + @RequestParam(required = false) Boolean catalogUnderDevice) { + if (ObjectUtils.isEmpty(query)) { + query = null; + } + + return storager.queryChannelsByDeviceId(deviceId, query, channelType, online, catalogUnderDevice, page, count); + } + + /** + * 同步设备通道 + * @param deviceId 设备id + * @return + */ + @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); + boolean status = deviceService.isSyncRunning(deviceId); + // 已存在则返回进度 + if (status) { + SyncStatus channelSyncStatus = deviceService.getChannelSyncStatus(deviceId); + return WVPResult.success(channelSyncStatus); + } + deviceService.sync(device); + + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(0); + wvpResult.setMsg("开始同步"); + return wvpResult; + } + + /** + * 移除设备 + * @param deviceId 设备id + * @return + */ + @Operation(summary = "移除设备") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @DeleteMapping("/devices/{deviceId}/delete") + public String delete(@PathVariable String deviceId){ + + if (logger.isDebugEnabled()) { + logger.debug("设备信息删除API调用,deviceId:" + deviceId); + } + + // 清除redis记录 + boolean isSuccess = deviceService.delete(deviceId); + if (isSuccess) { + inviteStreamService.clearInviteInfo(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 json.toString(); + } else { + logger.warn("设备信息删除API调用失败!"); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备信息删除API调用失败!"); + } + } + + /** + * 分页查询子目录通道 + * @param deviceId 通道id + * @param channelId 通道id + * @param page 当前页 + * @param count 每页条数 + * @param query 查询内容 + * @param online 是否在线 + * @param channelType 通道类型 + * @return 子通道列表 + */ + @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 PageInfo subChannels(@PathVariable String deviceId, + @PathVariable String channelId, + int page, + int count, + @RequestParam(required = false) String query, + @RequestParam(required = false) Boolean online, + @RequestParam(required = false) Boolean channelType){ + + DeviceChannel deviceChannel = storager.queryChannel(deviceId,channelId); + if (deviceChannel == null) { + PageInfo deviceChannelPageResult = new PageInfo<>(); + return deviceChannelPageResult; + } + + return storager.querySubChannels(deviceId, channelId, query, channelType, online, page, count); + } + + /** + * 更新通道信息 + * @param deviceId 设备id + * @param channel 通道 + * @return + */ + @Operation(summary = "更新通道信息") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channel", description = "通道信息", required = true) + @PostMapping("/channel/update/{deviceId}") + public void updateChannel(@PathVariable String deviceId,DeviceChannel channel){ + deviceChannelService.updateChannel(deviceId, channel); + } + + /** + * 修改数据流传输模式 + * @param deviceId 设备id + * @param streamMode 数据流传输模式 + * @return + */ + @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 void updateTransport(@PathVariable String deviceId, @PathVariable String streamMode){ + Device device = deviceService.getDevice(deviceId); + device.setStreamMode(streamMode); + 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); + } + + /** + * 更新设备信息 + * @param device 设备信息 + * @return + */ + @Operation(summary = "更新设备信息") + @Parameter(name = "device", description = "设备", required = true) + @PostMapping("/device/update/") + public void updateDevice(Device device){ + + if (device != null && device.getDeviceId() != null) { + deviceService.updateCustomDevice(device); + } + } + + /** + * 设备状态查询请求API接口 + * + * @param deviceId 设备id + */ + @Operation(summary = "设备状态查询") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @GetMapping("/devices/{deviceId}/status") + public DeferredResult> deviceStatusApi(@PathVariable String deviceId) { + if (logger.isDebugEnabled()) { + logger.debug("设备状态查询API调用"); + } + Device device = storager.queryVideoDevice(deviceId); + String uuid = UUID.randomUUID().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId; + 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 + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData("Timeout. Device did not response to this command."); + resultHolder.invokeResult(msg); + }); + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId, uuid, result); + return result; + } + + /** + * 设备报警查询请求API接口 + * @param deviceId 设备id + * @param startPriority 报警起始级别(可选) + * @param endPriority 报警终止级别(可选) + * @param alarmMethod 报警方式条件(可选) + * @param alarmType 报警类型 + * @param startTime 报警发生起始时间(可选) + * @param endTime 报警发生终止时间(可选) + * @return true = 命令发送成功 + */ + @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, + @RequestParam(required = false) String endPriority, + @RequestParam(required = false) String alarmMethod, + @RequestParam(required = false) String alarmType, + @RequestParam(required = false) String startTime, + @RequestParam(required = false) String endTime) { + if (logger.isDebugEnabled()) { + logger.debug("设备报警查询API调用"); + } + Device device = storager.queryVideoDevice(deviceId); + String key = DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId; + String uuid = UUID.randomUUID().toString(); + 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 + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData("设备报警查询超时"); + resultHolder.invokeResult(msg); + }); + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId, uuid, result); + return result; + } + + + @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) + @Parameter(name = "mark", description = "标识", required = false) + public void getSnap(HttpServletResponse resp, @PathVariable String deviceId, @PathVariable String channelId, @RequestParam(required = false) String mark) { + + try { + final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + (mark == null? ".jpg": ("_" + mark + ".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 new file mode 100644 index 000000000..5e673729b --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/GbStreamController.java @@ -0,0 +1,110 @@ +package com.genersoft.iot.vmp.vmanager.gb28181.gbStream; + +import com.genersoft.iot.vmp.gb28181.bean.GbStream; +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.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.*; + +import java.util.List; + +@Tag(name = "视频流关联到级联平台") + +@RestController +@RequestMapping("/api/gbStream") +public class GbStreamController { + + private final static Logger logger = LoggerFactory.getLogger(GbStreamController.class); + + @Autowired + private IGbStreamService gbStreamService; + + @Autowired + private IVideoManagerStorage storager; + + + /** + * 查询国标通道 + * @param page 当前页 + * @param count 每页条数 + * @param platformId 平台ID + * @return + */ + @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 = true)Integer page, + @RequestParam(required = true)Integer count, + @RequestParam(required = true)String platformId, + @RequestParam(required = false)String catalogId, + @RequestParam(required = false)String query, + @RequestParam(required = false)String mediaServerId){ + if (ObjectUtils.isEmpty(catalogId)) { + catalogId = null; + } + if (ObjectUtils.isEmpty(query)) { + query = null; + } + if (ObjectUtils.isEmpty(mediaServerId)) { + mediaServerId = null; + } + + // catalogId 为null 查询未在平台下分配的数据 + // catalogId 不为null 查询平台下这个,目录下的通道 + return gbStreamService.getAll(page, count, platformId, catalogId, query, mediaServerId); + } + + + /** + * 移除国标关联 + * @param gbStreamParam + * @return + */ + @Operation(summary = "移除国标关联") + @DeleteMapping(value = "/del") + @ResponseBody + public void del(@RequestBody GbStreamParam gbStreamParam){ + + if (gbStreamParam.getGbStreams() == null || gbStreamParam.getGbStreams().size() == 0) { + if (gbStreamParam.isAll()) { + gbStreamService.delAllPlatformInfo(gbStreamParam.getPlatformId(), gbStreamParam.getCatalogId()); + } + }else { + gbStreamService.delPlatformInfo(gbStreamParam.getPlatformId(), gbStreamParam.getGbStreams()); + } + + } + + /** + * 保存国标关联 + * @param gbStreamParam + * @return + */ + @Operation(summary = "保存国标关联") + @PostMapping(value = "/add") + @ResponseBody + 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 { + 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 new file mode 100644 index 000000000..3705c7090 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/bean/GbStreamParam.java @@ -0,0 +1,54 @@ +package com.genersoft.iot.vmp.vmanager.gb28181.gbStream.bean; + +import com.genersoft.iot.vmp.gb28181.bean.GbStream; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.List; + +@Schema(description = "国标关联参数") +public class GbStreamParam { + + @Schema(description = "平台ID") + private String platformId; + + @Schema(description = "目录ID") + private String catalogId; + + @Schema(description = "关联所有通道") + private boolean all; + + @Schema(description = "流国标信息列表") + private List gbStreams; + + public String getPlatformId() { + return platformId; + } + + public String getCatalogId() { + return catalogId; + } + + public void setCatalogId(String catalogId) { + this.catalogId = catalogId; + } + + public void setPlatformId(String platformId) { + this.platformId = platformId; + } + + public List getGbStreams() { + return gbStreams; + } + + public void setGbStreams(List gbStreams) { + this.gbStreams = gbStreams; + } + + public boolean isAll() { + return all; + } + + public void setAll(boolean all) { + this.all = all; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java new file mode 100644 index 000000000..8e0e2012f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java @@ -0,0 +1,117 @@ +package com.genersoft.iot.vmp.vmanager.gb28181.media; + +import com.genersoft.iot.vmp.common.StreamInfo; +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.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; + + +@Tag(name = "媒体流相关") +@Controller + +@RequestMapping(value = "/api/media") +public class MediaController { + + private final static Logger logger = LoggerFactory.getLogger(MediaController.class); + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IMediaService mediaService; + @Autowired + private IStreamProxyService streamProxyService; + + + /** + * 根据应用名和流id获取播放地址 + * @param app 应用名 + * @param stream 流id + * @return + */ + @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 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 { + // 是否登陆用户, 登陆用户返回完整信息 + 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); + } + } + } +} 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 new file mode 100644 index 000000000..7cf78e9cc --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java @@ -0,0 +1,550 @@ +package com.genersoft.iot.vmp.vmanager.gb28181.platform; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.common.VideoManagerConstants; +import com.genersoft.iot.vmp.conf.DynamicTask; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch; +import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog; +import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; +import com.genersoft.iot.vmp.service.*; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; +import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.UpdateChannelParam; +import com.github.pagehelper.PageInfo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; +import com.genersoft.iot.vmp.conf.SipConfig; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.List; + +/** + * 级联平台管理 + */ +@Tag(name = "级联平台管理") + +@RestController +@RequestMapping("/api/platform") +public class PlatformController { + + private final static Logger logger = LoggerFactory.getLogger(PlatformController.class); + + @Autowired + private UserSetting userSetting; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IPlatformChannelService platformChannelService; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private SubscribeHolder subscribeHolder; + + @Autowired + private ISIPCommanderForPlatform commanderForPlatform; + + @Autowired + private SipConfig sipConfig; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private IPlatformService platformService; + + @Autowired + private IDeviceChannelService deviceChannelService; + + @Autowired + private IGbStreamService gbStreamService; + + /** + * 获取国标服务的配置 + * + * @return + */ + @Operation(summary = "获取国标服务的配置") + @GetMapping("/server_config") + 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 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 count 每页条数 + * @return + */ + @GetMapping("/query/{count}/{page}") + @Operation(summary = "分页查询级联平台") + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页条数", required = true) + public PageInfo platforms(@PathVariable int page, @PathVariable int count) { + + PageInfo parentPlatformPageInfo = platformService.queryParentPlatformList(page, count); + if (parentPlatformPageInfo.getList().size() > 0) { + for (ParentPlatform platform : parentPlatformPageInfo.getList()) { + platform.setMobilePositionSubscribe(subscribeHolder.getMobilePositionSubscribe(platform.getServerGBId()) != null); + platform.setCatalogSubscribe(subscribeHolder.getCatalogSubscribe(platform.getServerGBId()) != null); + } + } + return parentPlatformPageInfo; + } + + /** + * 添加上级平台信息 + * + * @param parentPlatform + * @return + */ + @Operation(summary = "添加上级平台信息") + @PostMapping("/add") + @ResponseBody + public void addPlatform(@RequestBody ParentPlatform parentPlatform) { + + if (logger.isDebugEnabled()) { + logger.debug("保存上级平台信息API调用"); + } + if (ObjectUtils.isEmpty(parentPlatform.getName()) + || ObjectUtils.isEmpty(parentPlatform.getServerGBId()) + || ObjectUtils.isEmpty(parentPlatform.getServerGBDomain()) + || ObjectUtils.isEmpty(parentPlatform.getServerIP()) + || ObjectUtils.isEmpty(parentPlatform.getServerPort()) + || ObjectUtils.isEmpty(parentPlatform.getDeviceGBId()) + || ObjectUtils.isEmpty(parentPlatform.getExpires()) + || ObjectUtils.isEmpty(parentPlatform.getKeepTimeout()) + || ObjectUtils.isEmpty(parentPlatform.getTransport()) + || ObjectUtils.isEmpty(parentPlatform.getCharacterSet()) + ) { + throw new ControllerException(ErrorCode.ERROR400); + } + if (parentPlatform.getServerPort() < 0 || parentPlatform.getServerPort() > 65535) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "error severPort"); + } + + + ParentPlatform parentPlatformOld = storager.queryParentPlatByServerGBId(parentPlatform.getServerGBId()); + if (parentPlatformOld != null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台 " + parentPlatform.getServerGBId() + " 已存在"); + } + parentPlatform.setCreateTime(DateUtil.getNow()); + parentPlatform.setUpdateTime(DateUtil.getNow()); + boolean updateResult = platformService.add(parentPlatform); + + if (!updateResult) { + throw new ControllerException(ErrorCode.ERROR100.getCode(),"写入数据库失败"); + } + } + + /** + * 保存上级平台信息 + * + * @param parentPlatform + * @return + */ + @Operation(summary = "保存上级平台信息") + @PostMapping("/save") + @ResponseBody + public void savePlatform(@RequestBody ParentPlatform parentPlatform) { + + if (logger.isDebugEnabled()) { + logger.debug("保存上级平台信息API调用"); + } + if (ObjectUtils.isEmpty(parentPlatform.getName()) + || ObjectUtils.isEmpty(parentPlatform.getServerGBId()) + || ObjectUtils.isEmpty(parentPlatform.getServerGBDomain()) + || ObjectUtils.isEmpty(parentPlatform.getServerIP()) + || ObjectUtils.isEmpty(parentPlatform.getServerPort()) + || ObjectUtils.isEmpty(parentPlatform.getDeviceGBId()) + || ObjectUtils.isEmpty(parentPlatform.getExpires()) + || ObjectUtils.isEmpty(parentPlatform.getKeepTimeout()) + || ObjectUtils.isEmpty(parentPlatform.getTransport()) + || ObjectUtils.isEmpty(parentPlatform.getCharacterSet()) + ) { + throw new ControllerException(ErrorCode.ERROR400); + } + + platformService.update(parentPlatform); + } + + /** + * 删除上级平台 + * + * @param serverGBId 上级平台国标ID + * @return + */ + @Operation(summary = "删除上级平台") + @Parameter(name = "serverGBId", description = "上级平台的国标编号") + @DeleteMapping("/delete/{serverGBId}") + @ResponseBody + public void deletePlatform(@PathVariable String serverGBId) { + + if (logger.isDebugEnabled()) { + logger.debug("删除上级平台API调用"); + } + if (ObjectUtils.isEmpty(serverGBId) + ) { + throw new ControllerException(ErrorCode.ERROR400); + } + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(serverGBId); + ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(serverGBId); + if (parentPlatform == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台不存在"); + } + if (parentPlatformCatch == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台不存在"); + } + // 发送离线消息,无论是否成功都删除缓存 + try { + commanderForPlatform.unregister(parentPlatform, parentPlatformCatch.getSipTransactionInfo(), (event -> { + // 清空redis缓存 + redisCatchStorage.delPlatformCatchInfo(parentPlatform.getServerGBId()); + redisCatchStorage.delPlatformKeepalive(parentPlatform.getServerGBId()); + redisCatchStorage.delPlatformRegister(parentPlatform.getServerGBId()); + }), (event -> { + // 清空redis缓存 + redisCatchStorage.delPlatformCatchInfo(parentPlatform.getServerGBId()); + redisCatchStorage.delPlatformKeepalive(parentPlatform.getServerGBId()); + redisCatchStorage.delPlatformRegister(parentPlatform.getServerGBId()); + })); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage()); + } + + boolean deleteResult = storager.deleteParentPlatform(parentPlatform); + storager.delCatalogByPlatformId(parentPlatform.getServerGBId()); + storager.delRelationByPlatformId(parentPlatform.getServerGBId()); + // 停止发送位置订阅定时任务 + String key = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + userSetting.getServerId() + "_MobilePosition_" + parentPlatform.getServerGBId(); + dynamicTask.stop(key); + // 删除缓存的订阅信息 + subscribeHolder.removeAllSubscribe(parentPlatform.getServerGBId()); + if (!deleteResult) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + /** + * 查询上级平台是否存在 + * + * @param serverGBId 上级平台国标ID + * @return + */ + @Operation(summary = "查询上级平台是否存在") + @Parameter(name = "serverGBId", description = "上级平台的国标编号") + @GetMapping("/exit/{serverGBId}") + @ResponseBody + public Boolean exitPlatform(@PathVariable String serverGBId) { + + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(serverGBId); + return parentPlatform != null; + } + + /** + * 分页查询级联平台的所有所有通道 + * + * @param page 当前页 + * @param count 每页条数 + * @param platformId 上级平台ID + * @param query 查询内容 + * @param online 是否在线 + * @param channelType 通道类型 + * @return + */ + @Operation(summary = "查询上级平台是否存在") + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页条数", required = true) + @Parameter(name = "platformId", description = "上级平台的国标编号") + @Parameter(name = "catalogId", description = "目录ID") + @Parameter(name = "query", description = "查询内容") + @Parameter(name = "online", description = "是否在线") + @Parameter(name = "channelType", description = "通道类型") + @GetMapping("/channel_list") + @ResponseBody + public PageInfo channelList(int page, int count, + @RequestParam(required = false) String platformId, + @RequestParam(required = false) String catalogId, + @RequestParam(required = false) String query, + @RequestParam(required = false) Boolean online, + @RequestParam(required = false) Boolean channelType) { + + if (ObjectUtils.isEmpty(platformId)) { + platformId = null; + } + if (ObjectUtils.isEmpty(query)) { + query = null; + } + if (ObjectUtils.isEmpty(platformId) || ObjectUtils.isEmpty(catalogId)) { + catalogId = null; + } + PageInfo channelReduces = storager.queryAllChannelList(page, count, query, online, channelType, platformId, catalogId); + + return channelReduces; + } + + /** + * 向上级平台添加国标通道 + * + * @param param 通道关联参数 + * @return + */ + @Operation(summary = "向上级平台添加国标通道") + @PostMapping("/update_channel_for_gb") + @ResponseBody + public void updateChannelForGB(@RequestBody UpdateChannelParam param) { + + if (logger.isDebugEnabled()) { + logger.debug("给上级平台添加国标通道API调用"); + } + int result = 0; + if (param.getChannelReduces() == null || param.getChannelReduces().size() == 0) { + if (param.isAll()) { + logger.info("[国标级联]添加所有通道到上级平台, {}", param.getPlatformId()); + List allChannelForDevice = deviceChannelService.queryAllChannelList(param.getPlatformId()); + result = platformChannelService.updateChannelForGB(param.getPlatformId(), allChannelForDevice, param.getCatalogId()); + } + }else { + result = platformChannelService.updateChannelForGB(param.getPlatformId(), param.getChannelReduces(), param.getCatalogId()); + } + if (result <= 0) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + /** + * 从上级平台移除国标通道 + * + * @param param 通道关联参数 + * @return + */ + @Operation(summary = "从上级平台移除国标通道") + @DeleteMapping("/del_channel_for_gb") + @ResponseBody + public void delChannelForGB(@RequestBody UpdateChannelParam param) { + + if (logger.isDebugEnabled()) { + logger.debug("给上级平台删除国标通道API调用"); + } + int result = 0; + if (param.getChannelReduces() == null || param.getChannelReduces().size() == 0) { + if (param.isAll()) { + logger.info("[国标级联]移除所有通道,上级平台, {}", param.getPlatformId()); + result = platformChannelService.delAllChannelForGB(param.getPlatformId(), param.getCatalogId()); + } + }else { + result = storager.delChannelForGB(param.getPlatformId(), param.getChannelReduces()); + } + if (result <= 0) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + /** + * 获取目录 + * + * @param platformId 平台ID + * @param parentId 目录父ID + * @return + */ + @Operation(summary = "获取目录") + @Parameter(name = "platformId", description = "上级平台的国标编号", required = true) + @Parameter(name = "parentId", description = "父级目录的国标编号", required = true) + @GetMapping("/catalog") + @ResponseBody + public List getCatalogByPlatform(String platformId, String parentId) { + + if (logger.isDebugEnabled()) { + logger.debug("查询目录,platformId: {}, parentId: {}", platformId, parentId); + } + ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId); + if (platform == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台未找到"); + } +// if (platformId.equals(parentId)) { +// parentId = platform.getDeviceGBId(); +// } + + if (platformId.equals(platform.getDeviceGBId())) { + parentId = null; + } + + return storager.getChildrenCatalogByPlatform(platformId, parentId); + } + + /** + * 添加目录 + * + * @param platformCatalog 目录 + * @return + */ + @Operation(summary = "添加目录") + @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 new file mode 100644 index 000000000..2b01d45bb --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/bean/ChannelReduce.java @@ -0,0 +1,137 @@ +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; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getManufacturer() { + return manufacturer; + } + + public void setManufacturer(String manufacturer) { + this.manufacturer = manufacturer; + } + + public String getHostAddress() { + return hostAddress; + } + + public void setHostAddress(String hostAddress) { + this.hostAddress = hostAddress; + } + + public int getSubCount() { + return subCount; + } + + public void setSubCount(int subCount) { + this.subCount = subCount; + } + + 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/vmanager/gb28181/platform/bean/UpdateChannelParam.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/bean/UpdateChannelParam.java new file mode 100644 index 000000000..2d598e44d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/bean/UpdateChannelParam.java @@ -0,0 +1,57 @@ +package com.genersoft.iot.vmp.vmanager.gb28181.platform.bean; + +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.List; + +/** + * 通道关联参数 + * @author lin + */ +@Schema(description = "通道关联参数") +public class UpdateChannelParam { + + @Schema(description = "上级平台的国标编号") + private String platformId; + + @Schema(description = "目录的国标编号") + private String catalogId; + + @Schema(description = "处理所有通道") + private boolean all; + + @Schema(description = "") + private List channelReduces; + + public String getPlatformId() { + return platformId; + } + + public void setPlatformId(String platformId) { + this.platformId = platformId; + } + + public List getChannelReduces() { + return channelReduces; + } + + public void setChannelReduces(List channelReduces) { + this.channelReduces = channelReduces; + } + + public String getCatalogId() { + return catalogId; + } + + public void setCatalogId(String catalogId) { + this.catalogId = catalogId; + } + + public boolean isAll() { + return all; + } + + public void setAll(boolean all) { + this.all = all; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java new file mode 100644 index 000000000..9e41f22da --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java @@ -0,0 +1,375 @@ +package com.genersoft.iot.vmp.vmanager.gb28181.play; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.common.InviteInfo; +import com.genersoft.iot.vmp.common.InviteSessionStatus; +import com.genersoft.iot.vmp.common.InviteSessionType; +import com.genersoft.iot.vmp.common.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.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.IInviteStreamService; +import com.genersoft.iot.vmp.service.IMediaServerService; +import com.genersoft.iot.vmp.service.IMediaService; +import com.genersoft.iot.vmp.service.IPlayService; +import com.genersoft.iot.vmp.service.bean.InviteErrorCode; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.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.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; + +@Tag(name = "国标设备点播") + +@RestController +@RequestMapping("/api/play") +public class PlayController { + + private final static Logger logger = LoggerFactory.getLogger(PlayController.class); + + @Autowired + private SIPCommander cmder; + + @Autowired + private VideoStreamSessionManager streamSession; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IInviteStreamService inviteStreamService; + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private DeferredResultHolder resultHolder; + + @Autowired + private IPlayService playService; + + @Autowired + private IMediaService mediaService; + + @Autowired + private IMediaServerService mediaServerService; + + @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(HttpServletRequest request, @PathVariable String deviceId, + @PathVariable String channelId) { + + // 获取可用的zlm + Device device = storager.queryVideoDevice(deviceId); + MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device); + + RequestMessage requestMessage = new RequestMessage(); + String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId; + requestMessage.setKey(key); + String uuid = UUID.randomUUID().toString(); + requestMessage.setId(uuid); + DeferredResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue()); + + result.onTimeout(()->{ + logger.info("点播接口等待超时"); + // 释放rtpserver + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + wvpResult.setMsg("点播超时"); + requestMessage.setData(wvpResult); + resultHolder.invokeResult(requestMessage); + }); + + // 录像查询以channelId作为deviceId查询 + resultHolder.put(key, uuid, result); + + playService.play(newMediaServerItem, deviceId, channelId, (code, msg, data) -> { + WVPResult wvpResult = new WVPResult<>(); + if (code == InviteErrorCode.SUCCESS.getCode()) { + wvpResult.setCode(ErrorCode.SUCCESS.getCode()); + wvpResult.setMsg(ErrorCode.SUCCESS.getMsg()); + + if (data != null) { + StreamInfo streamInfo = (StreamInfo)data; + if (userSetting.getUseSourceIpAsStreamIp()) { + streamInfo.channgeStreamIp(request.getLocalAddr()); + } + wvpResult.setData(new StreamContent(streamInfo)); + } + }else { + wvpResult.setCode(code); + wvpResult.setMsg(msg); + } + requestMessage.setData(wvpResult); + resultHolder.invokeResult(requestMessage); + }); + return result; + } + + @Operation(summary = "停止点播") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "isSubStream", description = "是否子码流(true-子码流,false-主码流),默认为false", required = true) + @GetMapping("/stop/{deviceId}/{channelId}") + public JSONObject playStop(@PathVariable String deviceId, @PathVariable String channelId,boolean isSubStream) { + + logger.debug(String.format("设备预览/回放停止API调用,streamId:%s_%s", deviceId, channelId )); + + if (deviceId == null || channelId == null) { + throw new ControllerException(ErrorCode.ERROR400); + } + + Device device = storager.queryVideoDevice(deviceId); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备[" + deviceId + "]不存在"); + } + + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); + if (inviteInfo == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "点播未找到"); + } + if (InviteSessionStatus.ok == inviteInfo.getStatus()) { + try { + logger.warn("[停止点播] {}/{}", device.getDeviceId(), channelId); + cmder.streamByeCmd(device, channelId, inviteInfo.getStream(), null, null); + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { + logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + } + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); + storager.stopPlay(deviceId, channelId); + + JSONObject json = new JSONObject(); + json.put("deviceId", deviceId); + json.put("channelId", channelId); + json.put("isSubStream", isSubStream); + return json; + } + + /** + * 将不是h264的视频通过ffmpeg 转码为h264 + aac + * @param streamId 流ID + */ + @Operation(summary = "将不是h264的视频通过ffmpeg 转码为h264 + aac") + @Parameter(name = "streamId", description = "视频流ID", required = true) + @PostMapping("/convert/{streamId}") + public JSONObject playConvert(@PathVariable String streamId) { +// StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); + + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, streamId); + if (inviteInfo == null || inviteInfo.getStreamInfo() == null) { + logger.warn("视频转码API调用失败!, 视频流已经停止!"); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到视频流信息, 视频流可能已经停止"); + } + MediaServerItem mediaInfo = mediaServerService.getOne(inviteInfo.getStreamInfo().getMediaServerId()); + JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId); + if (!rtpInfo.getBoolean("exist")) { + logger.warn("视频转码API调用失败!, 视频流已停止推流!"); + 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()); + if (jsonObject != null && jsonObject.getInteger("code") == 0) { + JSONObject data = jsonObject.getJSONObject("data"); + if (data != null) { + 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 { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "转码失败"); + } + } + } + + /** + * 结束转码 + */ + @Operation(summary = "结束转码") + @Parameter(name = "key", description = "视频流key", required = true) + @Parameter(name = "mediaServerId", description = "流媒体服务ID", required = true) + @PostMapping("/convertStop/{key}") + public void playConvertStop(@PathVariable String key, String mediaServerId) { + if (mediaServerId == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "流媒体:" + mediaServerId + "不存在" ); + } + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); + if (mediaInfo == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "使用的流媒体已经停止运行" ); + }else { + JSONObject jsonObject = zlmresTfulUtils.delFFmpegSource(mediaInfo, key); + logger.info(jsonObject.toJSONString()); + if (jsonObject != null && jsonObject.getInteger("code") == 0) { + JSONObject data = jsonObject.getJSONObject("data"); + if (data == null || data.getBoolean("flag") == null || !data.getBoolean("flag")) { + throw new ControllerException(ErrorCode.ERROR100 ); + } + }else { + throw new ControllerException(ErrorCode.ERROR100 ); + } + } + } + + @Operation(summary = "语音广播命令") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @GetMapping("/broadcast/{deviceId}") + @PostMapping("/broadcast/{deviceId}") + public DeferredResult broadcastApi(@PathVariable String deviceId) { + if (logger.isDebugEnabled()) { + logger.debug("语音广播API调用"); + } + Device device = storager.queryVideoDevice(deviceId); + DeferredResult result = new DeferredResult<>(3 * 1000L); + String key = DeferredResultHolder.CALLBACK_CMD_BROADCAST + deviceId; + if (resultHolder.exist(key, null)) { + result.setResult("设备使用中"); + return result; + } + String uuid = UUID.randomUUID().toString(); + if (device == null) { + + resultHolder.put(key, key, result); + 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", "Device 不存在"); + msg.setData(json); + resultHolder.invokeResult(msg); + return result; + } + 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("语音广播操作超时, 设备未返回应答指令"); + 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("Error", "Timeout. Device did not response to broadcast command."); + msg.setData(json); + resultHolder.invokeResult(msg); + }); + resultHolder.put(key, uuid, result); + return result; + } + + @Operation(summary = "获取所有的ssrc") + @GetMapping("/ssrc") + public JSONObject getSSRC() { + if (logger.isDebugEnabled()) { + logger.debug("获取所有的ssrc"); + } + JSONArray objects = new JSONArray(); + List allSsrc = streamSession.getAllSsrc(); + for (SsrcTransaction transaction : allSsrc) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("deviceId", transaction.getDeviceId()); + jsonObject.put("channelId", transaction.getChannelId()); + jsonObject.put("ssrc", transaction.getSsrc()); + jsonObject.put("streamId", transaction.getStream()); + objects.add(jsonObject); + } + + JSONObject jsonObject = new JSONObject(); + jsonObject.put("data", objects); + jsonObject.put("count", objects.size()); + return jsonObject; + } + + @Operation(summary = "获取截图") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "isSubStream", description = "是否子码流(true-子码流,false-主码流),默认为false", required = true) + @GetMapping("/snap") + public DeferredResult getSnap(String deviceId, String channelId,boolean isSubStream) { + if (logger.isDebugEnabled()) { + logger.debug("获取截图: {}/{}", deviceId, channelId); + } + + DeferredResult result = new DeferredResult<>(3 * 1000L); + String key = DeferredResultHolder.CALLBACK_CMD_SNAP + deviceId; + String uuid = UUID.randomUUID().toString(); + resultHolder.put(key, uuid, result); + + RequestMessage message = new RequestMessage(); + message.setKey(key); + message.setId(uuid); + + String fileName = deviceId + "_" + channelId + "_" + DateUtil.getNowForUrl() + "jpg"; + playService.getSnap(deviceId, channelId, fileName, (code, msg, data) -> { + if (code == InviteErrorCode.SUCCESS.getCode()) { + message.setData(data); + }else { + message.setData(WVPResult.fail(code, msg)); + } + resultHolder.invokeResult(message); + }); + return result; + } + +} + 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 new file mode 100644 index 000000000..58d4ce20b --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/bean/PlayResult.java @@ -0,0 +1,39 @@ +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 String uuid; + + private Device device; + + public DeferredResult> getResult() { + return result; + } + + public void setResult(DeferredResult> result) { + this.result = result; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public Device getDevice() { + return device; + } + + public void setDevice(Device device) { + this.device = device; + } +} 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 new file mode 100644 index 000000000..d3225a219 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java @@ -0,0 +1,216 @@ +package com.genersoft.iot.vmp.vmanager.gb28181.playback; + +import com.genersoft.iot.vmp.common.InviteInfo; +import com.genersoft.iot.vmp.common.InviteSessionType; +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.conf.exception.ServiceException; +import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; +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.service.IInviteStreamService; +import com.genersoft.iot.vmp.service.IPlayService; +import com.genersoft.iot.vmp.service.bean.InviteErrorCode; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.bean.StreamContent; +import com.genersoft.iot.vmp.vmanager.bean.WVPResult; +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.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +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.UUID; + +/** + * @author lin + */ +@Tag(name = "视频回放") + +@RestController +@RequestMapping("/api/playback") +public class PlaybackController { + + private final static Logger logger = LoggerFactory.getLogger(PlaybackController.class); + + @Autowired + private SIPCommander cmder; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IInviteStreamService inviteStreamService; + + @Autowired + private IPlayService playService; + + @Autowired + private DeferredResultHolder resultHolder; + + @Autowired + private UserSetting userSetting; + + @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> start(HttpServletRequest request, @PathVariable String deviceId, @PathVariable String channelId, + String startTime, String endTime) { + + if (logger.isDebugEnabled()) { + logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId)); + } + + String uuid = UUID.randomUUID().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId; + DeferredResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue()); + resultHolder.put(key, uuid, result); + + RequestMessage requestMessage = new RequestMessage(); + requestMessage.setKey(key); + requestMessage.setId(uuid); + + playService.playBack(deviceId, channelId, startTime, endTime, + (code, msg, data)->{ + + WVPResult wvpResult = new WVPResult<>(); + if (code == InviteErrorCode.SUCCESS.getCode()) { + wvpResult.setCode(ErrorCode.SUCCESS.getCode()); + wvpResult.setMsg(ErrorCode.SUCCESS.getMsg()); + + if (data != null) { + StreamInfo streamInfo = (StreamInfo)data; + if (userSetting.getUseSourceIpAsStreamIp()) { + streamInfo.channgeStreamIp(request.getLocalAddr()); + } + wvpResult.setData(new StreamContent(streamInfo)); + } + }else { + wvpResult.setCode(code); + wvpResult.setMsg(msg); + } + requestMessage.setData(wvpResult); + resultHolder.invokeResult(requestMessage); + }); + + return result; + } + + + @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); + } + 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()); + } + } + + + @Operation(summary = "回放暂停") + @Parameter(name = "streamId", description = "回放流ID", required = true) + @GetMapping("/pause/{streamId}") + public void playPause(@PathVariable String streamId) { + logger.info("playPause: "+streamId); + + 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()); + } + } + + + @Operation(summary = "回放恢复") + @Parameter(name = "streamId", description = "回放流ID", required = true) + @GetMapping("/resume/{streamId}") + public void playResume(@PathVariable String streamId) { + logger.info("playResume: "+streamId); + 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()); + } + } + + + @Operation(summary = "回放拖动播放") + @Parameter(name = "streamId", description = "回放流ID", required = true) + @Parameter(name = "seekTime", description = "拖动偏移量,单位s", required = true) + @GetMapping("/seek/{streamId}/{seekTime}") + public void playSeek(@PathVariable String streamId, @PathVariable long seekTime) { + logger.info("playSeek: "+streamId+", "+seekTime); + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId); + + if (null == inviteInfo || inviteInfo.getStreamInfo() == null) { + logger.warn("streamId不存在!"); + throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在"); + } + Device device = storager.queryVideoDevice(inviteInfo.getDeviceId()); + try { + cmder.playSeekCmd(device, inviteInfo.getStreamInfo(), seekTime); + } catch (InvalidArgumentException | ParseException | SipException e) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); + } + } + + @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 void playSpeed(@PathVariable String streamId, @PathVariable Double speed) { + logger.info("playSpeed: "+streamId+", "+speed); + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId); + + if (null == inviteInfo || inviteInfo.getStreamInfo() == null) { + logger.warn("streamId不存在!"); + throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在"); + } + if(speed != 0.25 && speed != 0.5 && speed != 1 && speed != 2.0 && speed != 4.0) { + logger.warn("不支持的speed: " + speed); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "不支持的speed(0.25 0.5 1、2、4)"); + } + Device device = storager.queryVideoDevice(inviteInfo.getDeviceId()); + try { + cmder.playSpeedCmd(device, inviteInfo.getStreamInfo(), speed); + } catch (InvalidArgumentException | ParseException | SipException e) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/ptz/PtzController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/ptz/PtzController.java new file mode 100644 index 000000000..283cfe33a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/ptz/PtzController.java @@ -0,0 +1,179 @@ +package com.genersoft.iot.vmp.vmanager.gb28181.ptz; + + +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.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.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; + +@Tag(name = "云台控制") + +@RestController +@RequestMapping("/api/ptz") +public class PtzController { + + private final static Logger logger = LoggerFactory.getLogger(PtzController.class); + + @Autowired + private SIPCommander cmder; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private DeferredResultHolder resultHolder; + + /*** + * 云台控制 + * @param deviceId 设备id + * @param channelId 通道id + * @param command 控制指令 + * @param horizonSpeed 水平移动速度 + * @param verticalSpeed 垂直移动速度 + * @param zoomSpeed 缩放速度 + */ + + @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 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)); + } + Device device = storager.queryVideoDevice(deviceId); + int cmdCode = 0; + switch (command){ + case "left": + cmdCode = 2; + break; + case "right": + cmdCode = 1; + break; + case "up": + cmdCode = 8; + break; + case "down": + cmdCode = 4; + break; + case "upleft": + cmdCode = 10; + break; + case "upright": + cmdCode = 9; + break; + case "downleft": + cmdCode = 6; + break; + case "downright": + cmdCode = 5; + break; + case "zoomin": + cmdCode = 16; + break; + case "zoomout": + cmdCode = 32; + break; + case "stop": + horizonSpeed = 0; + verticalSpeed = 0; + zoomSpeed = 0; + break; + default: + break; + } + 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()); + } + } + + + @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 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); + + 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()); + } + } + + + @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) { + if (logger.isDebugEnabled()) { + logger.debug("设备预置位查询API调用"); + } + Device device = storager.queryVideoDevice(deviceId); + String uuid = UUID.randomUUID().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + (ObjectUtils.isEmpty(channelId) ? deviceId : channelId); + DeferredResult result = new DeferredResult (3 * 1000L); + result.onTimeout(()->{ + logger.warn(String.format("获取设备预置位超时")); + // 释放rtpserver + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData("获取设备预置位超时"); + resultHolder.invokeResult(msg); + }); + if (resultHolder.exist(key, null)) { + return result; + } + 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 new file mode 100644 index 000000000..910e43609 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java @@ -0,0 +1,207 @@ +package com.genersoft.iot.vmp.vmanager.gb28181.record; + +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.RecordInfo; +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; +import com.genersoft.iot.vmp.service.IDeviceService; +import com.genersoft.iot.vmp.service.IPlayService; +import com.genersoft.iot.vmp.service.bean.InviteErrorCode; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.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.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 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.UUID; + +@Tag(name = "国标录像") + +@RestController +@RequestMapping("/api/gb_record") +public class GBRecordController { + + private final static Logger logger = LoggerFactory.getLogger(GBRecordController.class); + + @Autowired + private SIPCommander cmder; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private DeferredResultHolder resultHolder; + + @Autowired + private IPlayService playService; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private UserSetting userSetting; + + @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){ + + if (logger.isDebugEnabled()) { + 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格式为" + DateUtil.PATTERN); + } + if (!DateUtil.verification(endTime, DateUtil.formatter)){ + throw new ControllerException(ErrorCode.ERROR100.getCode(), "endTime格式为" + DateUtil.PATTERN); + } + + Device device = storager.queryVideoDevice(deviceId); + // 指定超时时间 1分钟30秒 + 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); + 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(HttpServletRequest request, @PathVariable String deviceId, @PathVariable String channelId, + String startTime, String endTime, String downloadSpeed) { + + if (logger.isDebugEnabled()) { + logger.debug(String.format("历史媒体下载 API调用,deviceId:%s,channelId:%s,downloadSpeed:%s", deviceId, channelId, downloadSpeed)); + } + + String uuid = UUID.randomUUID().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_DOWNLOAD + deviceId + channelId; + DeferredResult> result = new DeferredResult<>(30000L); + resultHolder.put(key, uuid, result); + RequestMessage requestMessage = new RequestMessage(); + requestMessage.setId(uuid); + requestMessage.setKey(key); + + + playService.download(deviceId, channelId, startTime, endTime, Integer.parseInt(downloadSpeed), + (code, msg, data)->{ + + WVPResult wvpResult = new WVPResult<>(); + if (code == InviteErrorCode.SUCCESS.getCode()) { + wvpResult.setCode(ErrorCode.SUCCESS.getCode()); + wvpResult.setMsg(ErrorCode.SUCCESS.getMsg()); + + if (data != null) { + StreamInfo streamInfo = (StreamInfo)data; + if (userSetting.getUseSourceIpAsStreamIp()) { + streamInfo.channgeStreamIp(request.getLocalAddr()); + } + wvpResult.setData(new StreamContent(streamInfo)); + } + }else { + wvpResult.setCode(code); + wvpResult.setMsg(msg); + } + requestMessage.setData(wvpResult); + resultHolder.invokeResult(requestMessage); + }); + + 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 new file mode 100644 index 000000000..a412b1040 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java @@ -0,0 +1,93 @@ +package com.genersoft.iot.vmp.vmanager.log; + +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.github.pagehelper.PageInfo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; + +@Tag(name = "日志管理") + +@RestController +@RequestMapping("/api/log") +public class LogController { + + private final static Logger logger = LoggerFactory.getLogger(LogController.class); + + @Autowired + private ILogService logService; + + @Autowired + private UserSetting userSetting; + + /** + * 分页查询日志 + * + * @param query 查询内容 + * @param page 当前页 + * @param count 每页查询数量 + * @param type 类型 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return + */ + @GetMapping("/all") + @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, + @RequestParam(required = false) String type, + @RequestParam(required = false) String startTime, + @RequestParam(required = false) String endTime + ) { + if (ObjectUtils.isEmpty(query)) { + query = null; + } + + if (!userSetting.getLogInDatebase()) { + logger.warn("自动记录日志功能已关闭,查询结果可能不完整。"); + } + + if (ObjectUtils.isEmpty(startTime)) { + startTime = null; + }else if (!DateUtil.verification(startTime, DateUtil.formatter) ){ + throw new ControllerException(ErrorCode.ERROR400.getCode(), "startTime格式为" + DateUtil.PATTERN); + } + + if (ObjectUtils.isEmpty(endTime)) { + endTime = null; + }else if (!DateUtil.verification(endTime, DateUtil.formatter) ){ + throw new ControllerException(ErrorCode.ERROR400.getCode(), "endTime格式为" + DateUtil.PATTERN); + } + + return logService.getAll(page, count, query, type, startTime, endTime); + } + + /** + * 清空日志 + * + */ + @Operation(summary = "停止视频回放") + @DeleteMapping("/clear") + public void clear() { + logService.clear(); + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java deleted file mode 100644 index 08e5cc439..000000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java +++ /dev/null @@ -1,253 +0,0 @@ -package com.genersoft.iot.vmp.vmanager.play; - -import com.alibaba.fastjson.JSON; -import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.conf.MediaServerConfig; -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.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.vmanager.service.IPlayService; -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.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; - -@CrossOrigin -@RestController -@RequestMapping("/api") -public class PlayController { - - private final static Logger logger = LoggerFactory.getLogger(PlayController.class); - - @Autowired - private SIPCommander cmder; - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - @Autowired - private ZLMRESTfulUtils zlmresTfulUtils; - - @Autowired - private DeferredResultHolder resultHolder; - - @Autowired - private IPlayService playService; - - @GetMapping("/play/{deviceId}/{channelId}") - public DeferredResult> play(@PathVariable String deviceId, - @PathVariable String channelId) { - - - Device device = storager.queryVideoDevice(deviceId); - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); - - UUID uuid = UUID.randomUUID(); - DeferredResult> result = new DeferredResult>(); - - // 录像查询以channelId作为deviceId查询 - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result); - - if (streamInfo == null) { - // 发送点播消息 - cmder.playStreamCmd(device, channelId, (JSONObject response) -> { - logger.info("收到订阅消息: " + response.toJSONString()); - playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); - }, event -> { - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - Response response = event.getResponse(); - msg.setData(String.format("点播失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); - resultHolder.invokeResult(msg); - }); - } else { - String streamId = streamInfo.getStreamId(); - JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); - if (rtpInfo.getBoolean("exist")) { - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - msg.setData(JSON.toJSONString(streamInfo)); - resultHolder.invokeResult(msg); - } else { - redisCatchStorage.stopPlay(streamInfo); - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); - cmder.playStreamCmd(device, channelId, (JSONObject response) -> { - logger.info("收到订阅消息: " + response.toJSONString()); - playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); - }, event -> { - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - Response response = event.getResponse(); - msg.setData(String.format("点播失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); - resultHolder.invokeResult(msg); - }); - } - } - - // 超时处理 - result.onTimeout(()->{ - logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", deviceId, channelId)); - // 释放rtpserver - cmder.closeRTPServer(device, channelId); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - msg.setData("Timeout"); - resultHolder.invokeResult(msg); - }); - return result; - } - - @PostMapping("/play/{streamId}/stop") - public DeferredResult> playStop(@PathVariable String streamId) { - - logger.debug(String.format("设备预览/回放停止API调用,streamId:%s", streamId)); - - UUID uuid = UUID.randomUUID(); - DeferredResult> result = new DeferredResult>(); - - // 录像查询以channelId作为deviceId查询 - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_STOP + uuid, result); - - cmder.streamByeCmd(streamId, event -> { - StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); - if (streamInfo == null) { - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - msg.setData("streamId not found"); - resultHolder.invokeResult(msg); - }else { - redisCatchStorage.stopPlay(streamInfo); - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_STOP + uuid); - //Response response = event.getResponse(); - msg.setData(String.format("success")); - resultHolder.invokeResult(msg); - } - }); - - if (streamId != null) { - JSONObject json = new JSONObject(); - json.put("streamId", streamId); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - msg.setData(json.toString()); - resultHolder.invokeResult(msg); - } else { - logger.warn("设备预览/回放停止API调用失败!"); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - msg.setData("streamId null"); - resultHolder.invokeResult(msg); - } - - // 超时处理 - result.onTimeout(()->{ - logger.warn(String.format("设备预览/回放停止超时,streamId:%s ", streamId)); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_STOP + uuid); - msg.setData("Timeout"); - resultHolder.invokeResult(msg); - }); - return result; - } - - /** - * 将不是h264的视频通过ffmpeg 转码为h264 + aac - * @param streamId 流ID - * @return - */ - @PostMapping("/play/{streamId}/convert") - public ResponseEntity playConvert(@PathVariable String streamId) { - StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); - if (streamInfo == null) { - logger.warn("视频转码API调用失败!, 视频流已经停止!"); - return new ResponseEntity("未找到视频流信息, 视频流可能已经停止", HttpStatus.OK); - } - JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); - if (!rtpInfo.getBoolean("exist")) { - logger.warn("视频转码API调用失败!, 视频流已停止推流!"); - return new ResponseEntity("推流信息在流媒体中不存在, 视频流可能已停止推流", HttpStatus.OK); - } else { - MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); - 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(srcUrl, dstUrl, "1000000"); - System.out.println(jsonObject); - 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 = new StreamInfo(); - streamInfoResult.setRtmp(dstUrl); - streamInfoResult.setRtsp(String.format("rtsp://%s:%s/convert/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId)); - streamInfoResult.setStreamId(streamId); - streamInfoResult.setFlv(String.format("http://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoResult.setWs_flv(String.format("ws://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoResult.setHls(String.format("http://%s:%s/convert/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoResult.setWs_hls(String.format("ws://%s:%s/convert/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoResult.setFmp4(String.format("http://%s:%s/convert/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoResult.setWs_fmp4(String.format("ws://%s:%s/convert/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoResult.setTs(String.format("http://%s:%s/convert/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoResult.setWs_ts(String.format("ws://%s:%s/convert/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - result.put("data", streamInfoResult); - } - }else { - result.put("code", 1); - result.put("msg", "cover fail"); - } - return new ResponseEntity( result.toJSONString(), HttpStatus.OK); - } - } - - /** - * 结束转码 - * @param key - * @return - */ - @PostMapping("/play/convert/stop/{key}") - public ResponseEntity playConvertStop(@PathVariable String key) { - - JSONObject jsonObject = zlmresTfulUtils.delFFmpegSource(key); - System.out.println(jsonObject); - JSONObject result = new JSONObject(); - 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 { - - } - }else { - result.put("code", 1); - result.put("msg", "delFFmpegSource fail"); - } - return new ResponseEntity( result.toJSONString(), HttpStatus.OK); - } -} - diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java deleted file mode 100644 index 020a26c26..000000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.genersoft.iot.vmp.vmanager.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.ZLMRESTfulUtils; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.vmanager.service.IPlayService; -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; - -@CrossOrigin -@RestController -@RequestMapping("/api") -public class PlaybackController { - - private final static Logger logger = LoggerFactory.getLogger(PlaybackController.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; - - @GetMapping("/playback/{deviceId}/{channelId}") - public DeferredResult> play(@PathVariable String deviceId, @PathVariable String channelId, String startTime, - String endTime) { - - if (logger.isDebugEnabled()) { - logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId)); - } - UUID uuid = UUID.randomUUID(); - DeferredResult> result = new DeferredResult>(); - // 超时处理 - result.onTimeout(()->{ - logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId)); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - msg.setData("Timeout"); - resultHolder.invokeResult(msg); - }); - Device device = storager.queryVideoDevice(deviceId); - StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(deviceId, channelId); - if (streamInfo != null) { - // 停止之前的回放 - cmder.streamByeCmd(streamInfo.getStreamId()); - } - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result); - cmder.playbackStreamCmd(device, channelId, startTime, endTime, (JSONObject response) -> { - logger.info("收到订阅消息: " + response.toJSONString()); - playService.onPublishHandlerForPlayBack(response, deviceId, channelId, uuid.toString()); - }, event -> { - Response response = event.getResponse(); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - msg.setData(String.format("回放失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); - resultHolder.invokeResult(msg); - }); - - return result; - } - - @RequestMapping("/playback/{ssrc}/stop") - public ResponseEntity playStop(@PathVariable String ssrc) { - - cmder.streamByeCmd(ssrc); - - if (logger.isDebugEnabled()) { - logger.debug(String.format("设备录像回放停止 API调用,ssrc:%s", ssrc)); - } - - if (ssrc != null) { - JSONObject json = new JSONObject(); - json.put("ssrc", ssrc); - 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/ptz/PtzController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/ptz/PtzController.java deleted file mode 100644 index 1efdd3d12..000000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/ptz/PtzController.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.genersoft.iot.vmp.vmanager.ptz; - -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.*; -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.gb28181.utils.XmlUtil; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; - -@CrossOrigin -@RestController -@RequestMapping("/api") -public class PtzController { - - private final static Logger logger = LoggerFactory.getLogger(PtzController.class); - - @Autowired - private SIPCommander cmder; - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private DeferredResultHolder resultHolder; - - /*** - * 云台控制 - * @param deviceId 设备id - * @param channelId 通道id - * @param cmdCode 指令码 - * @param horizonSpeed 水平移动速度 - * @param verticalSpeed 垂直移动速度 - * @param zoomSpeed 缩放速度 - * @return String 控制结果 - */ - @PostMapping("/ptz/{deviceId}/{channelId}") - public ResponseEntity ptz(@PathVariable String deviceId,@PathVariable String channelId,int cmdCode, int horizonSpeed, int verticalSpeed, int zoomSpeed){ - - if (logger.isDebugEnabled()) { - logger.debug(String.format("设备云台控制 API调用,deviceId:%s ,channelId:%s ,cmdCode:%d ,horizonSpeed:%d ,verticalSpeed:%d ,zoomSpeed:%d",deviceId, channelId, cmdCode, horizonSpeed, verticalSpeed, zoomSpeed)); - } - Device device = storager.queryVideoDevice(deviceId); - - cmder.frontEndCmd(device, channelId, cmdCode, horizonSpeed, verticalSpeed, zoomSpeed); - return new ResponseEntity("success",HttpStatus.OK); - } - - /** - * 通用前端控制命令API接口 - * - * @param deviceId - * @param channelId - * @param cmdCode - * @param parameter1 - * @param parameter2 - * @param combindCode2 - * @return - */ - @PostMapping("/frontEndCommand/{deviceId}/{channelId}") - public ResponseEntity 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); - } - - /** - * 预置位查询命令API接口 - * - * @param deviceId - * @param channelId - * @return - */ - @GetMapping("/presetQuery/{deviceId}/{channelId}") - public DeferredResult> presetQueryApi(@PathVariable String deviceId, @PathVariable String channelId) { - if (logger.isDebugEnabled()) { - logger.debug("设备预置位查询API调用"); - } - Device device = storager.queryVideoDevice(deviceId); - cmder.presetQuery(device, channelId, event -> { - Response response = event.getResponse(); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); - msg.setData(String.format("获取设备预置位失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); - resultHolder.invokeResult(msg); - }); - DeferredResult> result = new DeferredResult> (3 * 1000L); - result.onTimeout(()->{ - logger.warn(String.format("获取设备预置位超时")); - // 释放rtpserver - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); - msg.setData("获取设备预置位超时"); - resultHolder.invokeResult(msg); - }); - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result); - return result; - } -} 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 519d299eb..6e2aca37a 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,59 +1,51 @@ -package com.genersoft.iot.vmp.vmanager.record; - -import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; -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; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.context.request.async.DeferredResult; - -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; - -@CrossOrigin -@RestController -@RequestMapping("/api") -public class RecordController { - - private final static Logger logger = LoggerFactory.getLogger(RecordController.class); - - @Autowired - private SIPCommander cmder; - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private DeferredResultHolder resultHolder; - - @GetMapping("/record/{deviceId}/{channelId}") - public DeferredResult> recordinfo(@PathVariable String deviceId,@PathVariable String channelId, String startTime, String endTime){ - - if (logger.isDebugEnabled()) { - logger.debug(String.format("录像信息查询 API调用,deviceId:%s ,startTime:%s, startTime:%s",deviceId, startTime, endTime)); - } - - Device device = storager.queryVideoDevice(deviceId); - cmder.recordInfoQuery(device, channelId, startTime, endTime); - // 指定超时时间 1分钟30秒 - DeferredResult> result = new DeferredResult>(90*1000L); - // 录像查询以channelId作为deviceId查询 - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_RECORDINFO+channelId, result); - result.onTimeout(()->{ - RequestMessage msg = new RequestMessage(); - msg.setDeviceId(deviceId); - msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO); - msg.setData("timeout"); - resultHolder.invokeResult(msg); - }); - return result; - } -} +//package com.genersoft.iot.vmp.vmanager.record; +// +//import com.alibaba.fastjson2.JSONObject; +//import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; +//import com.genersoft.iot.vmp.service.IRecordInfoServer; +//import com.genersoft.iot.vmp.storager.dao.dto.RecordInfo; +//import com.genersoft.iot.vmp.vmanager.bean.WVPResult; +//import com.github.pagehelper.PageInfo; +//import io.swagger.annotations.Api; +//import io.swagger.annotations.ApiImplicitParam; +//import io.swagger.annotations.ApiImplicitParams; +//import io.swagger.annotations.ApiOperation; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.web.bind.annotation.*; +// +//@Tag(name = "云端录像") +// +//@RestController +//@RequestMapping("/api/record") +//public class RecordController { +// +// @Autowired +// private IRecordInfoServer recordInfoServer; +// +// //@ApiOperation("录像列表查询") +// @ApiImplicitParams({ +// @ApiImplicitParam(name="page", value = "当前页", required = true, dataTypeClass = Integer.class), +// @ApiImplicitParam(name="count", value = "每页查询数量", required = true, dataTypeClass = Integer.class), +// @ApiImplicitParam(name="query", value = "查询内容", dataTypeClass = String.class), +// }) +// @GetMapping(value = "/app/list") +// @ResponseBody +// public Object list(@RequestParam(required = false)Integer page, +// @RequestParam(required = false)Integer count ){ +// +// PageInfo recordList = recordInfoServer.getRecordList(page - 1, page - 1 + count); +// return recordList; +// } +// +// //@ApiOperation("获取录像详情") +// @ApiImplicitParams({ +// @ApiImplicitParam(name="recordInfo", value = "录像记录", required = true, dataTypeClass = RecordInfo.class) +// }) +// @GetMapping(value = "/detail") +// @ResponseBody +// public JSONObject list(RecordInfo recordInfo, String time ){ +// +// +// return null; +// } +//} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/rtp/RtpController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/rtp/RtpController.java new file mode 100644 index 000000000..2b061b489 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/rtp/RtpController.java @@ -0,0 +1,256 @@ +package com.genersoft.iot.vmp.vmanager.rtp; + +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.UserSetting; +import com.genersoft.iot.vmp.conf.VersionInfo; +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager; +import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory; +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForRtpServerTimeout; +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRtpServerTimeoutHookParam; +import com.genersoft.iot.vmp.service.IDeviceChannelService; +import com.genersoft.iot.vmp.service.IDeviceService; +import com.genersoft.iot.vmp.service.IMediaServerService; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.bean.OtherRtpSendInfo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import okhttp3.OkHttpClient; +import okhttp3.Request; +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.data.redis.core.RedisTemplate; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +@SuppressWarnings("rawtypes") +@Tag(name = "第三方服务对接") + +@RestController +@RequestMapping("/api/rtp") +public class RtpController { + + @Autowired + private ZLMServerFactory zlmServerFactory; + + @Autowired + private SendRtpPortManager sendRtpPortManager; + + private final static Logger logger = LoggerFactory.getLogger(RtpController.class); + + @Autowired + private ZlmHttpHookSubscribe hookSubscribe; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private VersionInfo versionInfo; + + @Autowired + private SipConfig sipConfig; + + @Autowired + private UserSetting userSetting; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private IDeviceChannelService channelService; + + @Autowired + private DynamicTask dynamicTask; + + + @Autowired + private RedisTemplate redisTemplate; + + + @Value("${server.port}") + private int serverPort; + + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + + @GetMapping(value = "/receive/open") + @ResponseBody + @Operation(summary = "开启收流和获取发流信息") + @Parameter(name = "isSend", description = "是否发送,false时只开启收流, true同时返回推流信息", required = true) + @Parameter(name = "callId", description = "整个过程的唯一标识,为了与后续接口关联", required = true) + @Parameter(name = "ssrc", description = "来源流的SSRC,不传则不校验来源ssrc", required = false) + @Parameter(name = "stream", description = "形成的流的ID", required = true) + @Parameter(name = "tcpMode", description = "收流模式, 0为UDP, 1为TCP被动", required = true) + @Parameter(name = "callBack", description = "回调地址,如果收流超时会通道回调通知,回调为get请求,参数为callId", required = true) + public OtherRtpSendInfo openRtpServer(Boolean isSend, @RequestParam(required = false)String ssrc, String callId, String stream, Integer tcpMode, String callBack) { + + logger.info("[第三方服务对接->开启收流和获取发流信息] isSend->{}, ssrc->{}, callId->{}, stream->{}, tcpMode->{}, callBack->{}", + isSend, ssrc, callId, stream, tcpMode==0?"UDP":"TCP被动", callBack); + + MediaServerItem mediaServerItem = mediaServerService.getDefaultMediaServer(); + if (mediaServerItem == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(),"没有可用的MediaServer"); + } + if (stream == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(),"stream参数不可为空"); + } + if (isSend != null && isSend && callId == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(),"isSend为true时,CallID不能为空"); + } + int ssrcInt = 0; + if (ssrc != null) { + try { + ssrcInt = Integer.parseInt(ssrc); + }catch (NumberFormatException e) { + throw new ControllerException(ErrorCode.ERROR100.getCode(),"ssrc格式错误"); + } + + } + int localPort = zlmServerFactory.createRTPServer(mediaServerItem, stream, ssrcInt, null, false, tcpMode); + // 注册回调如果rtp收流超时则通过回调发送通知 + if (callBack != null) { + HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, mediaServerItem.getId()); + // 订阅 zlm启动事件, 新的zlm也会从这里进入系统 + hookSubscribe.addSubscribe(hookSubscribeForRtpServerTimeout, + (mediaServerItemInUse, hookParam)->{ + OnRtpServerTimeoutHookParam serverTimeoutHookParam = (OnRtpServerTimeoutHookParam) hookParam; + if (stream.equals(serverTimeoutHookParam.getStream_id())) { + logger.info("[开启收流和获取发流信息] 等待收流超时 callId->{}, 发送回调", callId); + OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); + OkHttpClient client = httpClientBuilder.build(); + String url = callBack + "?callId=" + callId; + Request request = new Request.Builder().get().url(url).build(); + try { + client.newCall(request).execute(); + } catch (IOException e) { + logger.error("[开启收流和获取发流信息] 等待收流超时 callId->{}, 发送回调失败", callId, e); + } + } + }); + } + String key = VideoManagerConstants.WVP_OTHER_SEND_RTP_INFO + userSetting.getServerId() + callId; + OtherRtpSendInfo otherRtpSendInfo = new OtherRtpSendInfo(); + otherRtpSendInfo.setReceiveIp(mediaServerItem.getSdpIp()); + otherRtpSendInfo.setReceivePort(localPort); + otherRtpSendInfo.setCallId(callId); + otherRtpSendInfo.setStream(stream); + if (isSend != null && isSend) { + int port = sendRtpPortManager.getNextPort(mediaServerItem.getId()); + otherRtpSendInfo.setIp(mediaServerItem.getSdpIp()); + otherRtpSendInfo.setPort(port); + logger.info("[开启收流和获取发流信息] 结果,callId->{}, {}", callId, otherRtpSendInfo); + } + // 将信息写入redis中,以备后用 + redisTemplate.opsForValue().set(key, otherRtpSendInfo, 300, TimeUnit.SECONDS); + return otherRtpSendInfo; + } + + @GetMapping(value = "/receive/close") + @ResponseBody + @Operation(summary = "关闭收流") + @Parameter(name = "stream", description = "流的ID", required = true) + public void closeRtpServer(String stream) { + logger.info("[第三方服务对接->关闭收流] stream->{}", stream); + MediaServerItem mediaServerItem = mediaServerService.getDefaultMediaServer(); + zlmServerFactory.closeRtpServer(mediaServerItem,stream); + } + + @GetMapping(value = "/send/start") + @ResponseBody + @Operation(summary = "发送流") + @Parameter(name = "ssrc", description = "发送流的SSRC", required = true) + @Parameter(name = "ip", description = "目标IP", required = true) + @Parameter(name = "port", description = "目标端口", required = true) + @Parameter(name = "app", description = "待发送应用名", required = true) + @Parameter(name = "stream", description = "待发送流Id", required = true) + @Parameter(name = "callId", description = "整个过程的唯一标识,不传则使用随机端口发流", required = true) + @Parameter(name = "onlyAudio", description = "是否只有音频", required = true) + @Parameter(name = "isUdp", description = "是否为UDP", required = true) + @Parameter(name = "streamType", description = "流类型,1为es流,2为ps流, 默认es流", required = false) + public void sendRTP(String ssrc, String ip, Integer port, String app, String stream, String callId, Boolean onlyAudio, Boolean isUdp, @RequestParam(required = false)Integer streamType) { + logger.info("[第三方服务对接->发送流] ssrc->{}, ip->{}, port->{}, app->{}, stream->{}, callId->{}, onlyAudio->{}, streamType->{}", + ssrc, ip, port, app, stream, callId, onlyAudio, streamType == 1? "ES":"PS"); + if (ObjectUtils.isEmpty(streamType)) { + streamType = 1; + } + MediaServerItem mediaServerItem = mediaServerService.getDefaultMediaServer(); + String key = VideoManagerConstants.WVP_OTHER_SEND_RTP_INFO + userSetting.getServerId() + callId; + OtherRtpSendInfo sendInfo = (OtherRtpSendInfo)redisTemplate.opsForValue().get(key); + if (sendInfo == null) { + sendInfo = new OtherRtpSendInfo(); + } + sendInfo.setPushApp(app); + sendInfo.setPushStream(stream); + sendInfo.setPushSSRC(ssrc); + + Map param = new HashMap<>(12); + param.put("vhost","__defaultVhost__"); + param.put("app",app); + param.put("stream",stream); + param.put("ssrc", ssrc); + + param.put("dst_url",ip); + param.put("dst_port", port); + String is_Udp = isUdp ? "1" : "0"; + param.put("is_udp", is_Udp); + param.put("src_port", sendInfo.getPort()); + param.put("use_ps", streamType==2 ? "1" : "0"); + param.put("only_audio", onlyAudio ? "1" : "0"); + + JSONObject jsonObject = zlmServerFactory.startSendRtpStream(mediaServerItem, param); + if (jsonObject.getInteger("code") == 0) { + logger.info("[第三方服务对接->发送流] 发流成功,callId->{}", callId); + redisTemplate.opsForValue().set(key, sendInfo); + }else { + redisTemplate.delete(key); + logger.info("[第三方服务对接->发送流] 发流失败,callId->{}, {}", callId, jsonObject.getString("msg")); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "[发流失败] " + jsonObject.getString("msg")); + } + } + + + + @GetMapping(value = "/send/stop") + @ResponseBody + @Operation(summary = "关闭发送流") + @Parameter(name = "callId", description = "整个过程的唯一标识,不传则使用随机端口发流", required = true) + public void closeSendRTP(String callId) { + logger.info("[第三方服务对接->关闭发送流] callId->{}", callId); + String key = VideoManagerConstants.WVP_OTHER_SEND_RTP_INFO + userSetting.getServerId() + callId; + OtherRtpSendInfo sendInfo = (OtherRtpSendInfo)redisTemplate.opsForValue().get(key); + if (sendInfo == null){ + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未开启发流"); + } + Map param = new HashMap<>(); + param.put("vhost","__defaultVhost__"); + param.put("app",sendInfo.getPushApp()); + param.put("stream",sendInfo.getPushStream()); + param.put("ssrc",sendInfo.getPushSSRC()); + MediaServerItem mediaServerItem = mediaServerService.getDefaultMediaServer(); + Boolean result = zlmServerFactory.stopSendRtpStream(mediaServerItem, param); + if (!result) { + logger.info("[第三方服务对接->关闭发送流] 失败 callId->{}", callId); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "停止发流失败"); + }else { + logger.info("[第三方服务对接->关闭发送流] 成功 callId->{}", callId); + } + } + +} 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 new file mode 100644 index 000000000..bef7ffb9a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java @@ -0,0 +1,265 @@ +package com.genersoft.iot.vmp.vmanager.server; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +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.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.*; +import com.genersoft.iot.vmp.service.bean.MediaServerLoad; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; +import com.genersoft.iot.vmp.vmanager.bean.ResourceInfo; +import com.genersoft.iot.vmp.vmanager.bean.SystemConfigInfo; +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.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.List; + +@SuppressWarnings("rawtypes") +@Tag(name = "服务控制") + +@RestController +@RequestMapping("/api/server") +public class ServerController { + + @Autowired + private ZlmHttpHookSubscribe zlmHttpHookSubscribe; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private VersionInfo versionInfo; + + @Autowired + private SipConfig sipConfig; + + @Autowired + 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; + + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + + @GetMapping(value = "/media_server/list") + @ResponseBody + @Operation(summary = "流媒体服务列表") + public List getMediaServerList() { + return mediaServerService.getAll(); + } + + @GetMapping(value = "/media_server/online/list") + @ResponseBody + @Operation(summary = "在线流媒体服务列表") + public List getOnlineMediaServerList() { + return mediaServerService.getAllOnline(); + } + + @GetMapping(value = "/media_server/one/{id}") + @ResponseBody + @Operation(summary = "停止视频回放") + @Parameter(name = "id", description = "流媒体服务ID", required = true) + public MediaServerItem getMediaServer(@PathVariable String id) { + return mediaServerService.getOne(id); + } + + @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 MediaServerItem checkMediaServer(@RequestParam String ip, @RequestParam int port, @RequestParam String secret) { + return mediaServerService.checkMediaServer(ip, port, secret); + } + + @Operation(summary = "测试流媒体录像管理服务") + @Parameter(name = "ip", description = "流媒体服务IP", required = true) + @Parameter(name = "port", description = "流媒体服务HTT端口", required = true) + @GetMapping(value = "/media_server/record/check") + @ResponseBody + public void checkMediaRecordServer(@RequestParam String ip, @RequestParam int port) { + boolean checkResult = mediaServerService.checkMediaRecordServer(ip, port); + if (!checkResult) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "连接失败"); + } + } + + @Operation(summary = "保存流媒体服务") + @Parameter(name = "mediaServerItem", description = "流媒体信息", required = true) + @PostMapping(value = "/media_server/save") + @ResponseBody + public void saveMediaServer(@RequestBody MediaServerItem mediaServerItem) { + MediaServerItem mediaServerItemInDatabase = mediaServerService.getOne(mediaServerItem.getId()); + + if (mediaServerItemInDatabase != null) { + mediaServerService.update(mediaServerItem); + } else { + mediaServerService.add(mediaServerItem); + } + } + + @Operation(summary = "移除流媒体服务") + @Parameter(name = "id", description = "流媒体ID", required = true) + @DeleteMapping(value = "/media_server/delete") + @ResponseBody + public void deleteMediaServer(@RequestParam String id) { + if (mediaServerService.getOne(id) == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到此节点"); + } + mediaServerService.delete(id); + mediaServerService.deleteDb(id); + } + + + @Operation(summary = "重启服务") + @GetMapping(value = "/restart") + @ResponseBody + 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()); +// } +// }); + }; + + @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; + } + + @Operation(summary = "获取版本信息") + @GetMapping(value = "/version") + @ResponseBody + public VersionPo VersionPogetVersion() { + return versionInfo.getVersion(); + } + + @GetMapping(value = "/config") + @Operation(summary = "获取配置信息") + @Parameter(name = "type", description = "配置类型(sip, base)", required = true) + @ResponseBody + public JSONObject getVersion(String type) { + + JSONObject jsonObject = new JSONObject(); + jsonObject.put("server.port", serverPort); + if (ObjectUtils.isEmpty(type)) { + jsonObject.put("sip", JSON.toJSON(sipConfig)); + jsonObject.put("base", JSON.toJSON(userSetting)); + } else { + switch (type) { + case "sip": + jsonObject.put("sip", sipConfig); + break; + case "base": + jsonObject.put("base", userSetting); + break; + default: + break; + } + } + 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(); + ResourceBaseInfo deviceInfo = deviceService.getOverview(); + result.setDevice(deviceInfo); + ResourceBaseInfo channelInfo = channelService.getOverview(); + result.setChannel(channelInfo); + ResourceBaseInfo pushInfo = pushService.getOverview(); + result.setPush(pushInfo); + ResourceBaseInfo proxyInfo = proxyService.getOverview(); + result.setProxy(proxyInfo); + + return result; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java b/src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java deleted file mode 100644 index d81bdedd5..000000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.genersoft.iot.vmp.vmanager.service; - -import com.alibaba.fastjson.JSONObject; - -/** - * 点播处理 - */ -public interface IPlayService { - - void onPublishHandlerForPlayBack(JSONObject resonse, String deviceId, String channelId, String uuid); - void onPublishHandlerForPlay(JSONObject resonse, String deviceId, String channelId, String uuid); -} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java deleted file mode 100644 index f08797614..000000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.genersoft.iot.vmp.vmanager.service.impl; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.conf.MediaServerConfig; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; -import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.vmanager.service.IPlayService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -@Service -public class PlayServiceImpl implements IPlayService { - - private final static Logger logger = LoggerFactory.getLogger(PlayServiceImpl.class); - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - @Autowired - private DeferredResultHolder resultHolder; - - @Override - public void onPublishHandlerForPlay(JSONObject resonse, String deviceId, String channelId, String uuid) { - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - StreamInfo streamInfo = onPublishHandler(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()); - } - - redisCatchStorage.startPlay(streamInfo); - msg.setData(JSON.toJSONString(streamInfo)); - resultHolder.invokeResult(msg); - } else { - logger.warn("设备预览API调用失败!"); - msg.setData("设备预览API调用失败!"); - resultHolder.invokeResult(msg); - } - } - - @Override - public void onPublishHandlerForPlayBack(JSONObject resonse, String deviceId, String channelId, String uuid) { - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - StreamInfo streamInfo = onPublishHandler(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 StreamInfo onPublishHandler(JSONObject resonse, String deviceId, String channelId, String uuid) { - String streamId = resonse.getString("id"); - StreamInfo streamInfo = new StreamInfo(); - streamInfo.setStreamId(streamId); - streamInfo.setDeviceID(deviceId); - streamInfo.setChannelId(channelId); - MediaServerConfig mediaServerConfig = redisCatchStorage.getMediaInfo(); - - streamInfo.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); - streamInfo.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); - - streamInfo.setFmp4(String.format("http://%s:%s/rtp/%s.live.mp4", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); - streamInfo.setWs_fmp4(String.format("ws://%s:%s/rtp/%s.live.mp4", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); - - streamInfo.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); - streamInfo.setWs_hls(String.format("ws://%s:%s/rtp/%s/hls.m3u8", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); - - streamInfo.setTs(String.format("http://%s:%s/rtp/%s.live.ts", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); - streamInfo.setWs_ts(String.format("ws://%s:%s/rtp/%s.live.ts", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); - - streamInfo.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaServerConfig.getWanIp(), mediaServerConfig.getRtmpPort(), streamId)); - streamInfo.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaServerConfig.getWanIp(), mediaServerConfig.getRtspPort(), streamId)); - - return streamInfo; - } - -} 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 new file mode 100644 index 000000000..0689f423c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java @@ -0,0 +1,169 @@ +package com.genersoft.iot.vmp.vmanager.streamProxy; + +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.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.media.zlm.dto.StreamProxyItem; +import com.genersoft.iot.vmp.service.IMediaServerService; +import com.genersoft.iot.vmp.service.IStreamProxyService; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.bean.StreamContent; +import com.genersoft.iot.vmp.vmanager.bean.WVPResult; +import com.github.pagehelper.PageInfo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.async.DeferredResult; + +import java.util.UUID; + +@SuppressWarnings("rawtypes") +/** + * 拉流代理接口 + */ +@Tag(name = "拉流代理", description = "") +@Controller + +@RequestMapping(value = "/api/proxy") +public class StreamProxyController { + + private final static Logger logger = LoggerFactory.getLogger(StreamProxyController.class); + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private IStreamProxyService streamProxyService; + + @Autowired + private DeferredResultHolder resultHolder; + + @Autowired + private UserSetting userSetting; + + + @Operation(summary = "分页查询流代理") + @Parameter(name = "page", description = "当前页") + @Parameter(name = "count", description = "每页查询数量") + @Parameter(name = "query", description = "查询内容") + @Parameter(name = "online", description = "是否在线") + @GetMapping(value = "/list") + @ResponseBody + public PageInfo list(@RequestParam(required = false)Integer page, + @RequestParam(required = false)Integer count, + @RequestParam(required = false)String query, + @RequestParam(required = false)Boolean online ){ + + return streamProxyService.getAll(page, count); + } + + @Operation(summary = "保存代理", parameters = { + @Parameter(name = "param", description = "代理参数", required = true), + }) + @PostMapping(value = "/save") + @ResponseBody + public DeferredResult save(@RequestBody StreamProxyItem param){ + logger.info("添加代理: " + JSONObject.toJSONString(param)); + if (ObjectUtils.isEmpty(param.getMediaServerId())) { + param.setMediaServerId("auto"); + } + if (ObjectUtils.isEmpty(param.getType())) { + param.setType("default"); + } + if (ObjectUtils.isEmpty(param.getGbId())) { + param.setGbId(null); + } + + RequestMessage requestMessage = new RequestMessage(); + String key = DeferredResultHolder.CALLBACK_CMD_PROXY + param.getApp() + param.getStream(); + requestMessage.setKey(key); + String uuid = UUID.randomUUID().toString(); + requestMessage.setId(uuid); + DeferredResult result = new DeferredResult<>(userSetting.getPlayTimeout().longValue()); + // 录像查询以channelId作为deviceId查询 + resultHolder.put(key, uuid, result); + result.onTimeout(()->{ + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + wvpResult.setMsg("超时"); + requestMessage.setData(wvpResult); + resultHolder.invokeAllResult(requestMessage); + }); + + streamProxyService.save(param, (code, msg, streamInfo) -> { + logger.info("[拉流代理] {}", code == ErrorCode.SUCCESS.getCode()? "成功":"失败: " + msg); + if (code == ErrorCode.SUCCESS.getCode()) { + requestMessage.setData(new StreamContent(streamInfo)); + }else { + requestMessage.setData(WVPResult.fail(code, msg)); + } + resultHolder.invokeAllResult(requestMessage); + }); + return result; + } + + @GetMapping(value = "/ffmpeg_cmd/list") + @ResponseBody + @Operation(summary = "获取ffmpeg.cmd模板") + @Parameter(name = "mediaServerId", description = "流媒体ID", required = true) + public JSONObject getFFmpegCMDs(@RequestParam String mediaServerId){ + logger.debug("获取节点[ {} ]ffmpeg.cmd模板", mediaServerId ); + + MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); + if (mediaServerItem == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "流媒体: " + mediaServerId + "未找到"); + } + return streamProxyService.getFFmpegCMDs(mediaServerItem); + } + + @DeleteMapping(value = "/del") + @ResponseBody + @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); + if (app == null || stream == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), app == null ?"app不能为null":"stream不能为null"); + }else { + streamProxyService.del(app, stream); + } + } + + @GetMapping(value = "/start") + @ResponseBody + @Operation(summary = "启用代理") + @Parameter(name = "app", description = "应用名", required = true) + @Parameter(name = "stream", description = "流id", required = true) + public void start(String app, String stream){ + logger.info("启用代理: " + app + "/" + stream); + boolean result = streamProxyService.start(app, stream); + if (!result) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + @GetMapping(value = "/stop") + @ResponseBody + @Operation(summary = "停用代理") + @Parameter(name = "app", description = "应用名", required = true) + @Parameter(name = "stream", description = "流id", required = true) + public void stop(String app, String stream){ + logger.info("停用代理: " + app + "/" + stream); + boolean result = streamProxyService.stop(app, stream); + if (!result) { + logger.info("停用代理失败: " + app + "/" + stream); + throw new ControllerException(ErrorCode.ERROR100); + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java new file mode 100644 index 000000000..5c8bf3679 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java @@ -0,0 +1,280 @@ +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.service.impl.StreamPushUploadFileHandler; +import com.genersoft.iot.vmp.vmanager.bean.*; +import com.github.pagehelper.PageInfo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.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; + +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 + +@RequestMapping(value = "/api/push") +public class StreamPushController { + + private final static Logger logger = LoggerFactory.getLogger(StreamPushController.class); + + @Autowired + private IStreamPushService streamPushService; + + @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 pushing, + @RequestParam(required = false)String mediaServerId ){ + + if (ObjectUtils.isEmpty(query)) { + query = null; + } + if (ObjectUtils.isEmpty(mediaServerId)) { + mediaServerId = null; + } + PageInfo pushList = streamPushService.getPushList(page, count, query, pushing, mediaServerId); + return pushList; + } + + @PostMapping(value = "/save_to_gb") + @ResponseBody + @Operation(summary = "将推流添加到国标") + public void saveToGB(@RequestBody GbStream stream){ + if (!streamPushService.saveToGB(stream)){ + throw new ControllerException(ErrorCode.ERROR100); + } + } + + + @DeleteMapping(value = "/remove_form_gb") + @ResponseBody + @Operation(summary = "将推流移出到国标") + public void removeFormGB(@RequestBody GbStream stream){ + if (!streamPushService.removeFromGB(stream)){ + throw new ControllerException(ErrorCode.ERROR100); + } + } + + + @PostMapping(value = "/stop") + @ResponseBody + @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) { + logger.error("未处理的异常 ", e); + } + try { + //传入参数 + ExcelReader excelReader = EasyExcel.read(inputStream, StreamPushExcelDto.class, + new StreamPushUploadFileHandler(streamPushService, mediaServerService.getDefaultMediaServer().getId(), (errorStreams, errorGBs)->{ + logger.info("通道导入成功,存在重复App+Stream为{}个,存在国标ID为{}个", errorStreams.size(), errorGBs.size()); + 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 new file mode 100644 index 000000000..f34f1c4b4 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/user/RoleController.java @@ -0,0 +1,75 @@ +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.storager.dao.dto.Role; +import com.genersoft.iot.vmp.utils.DateUtil; +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.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Tag(name = "角色管理") + +@RestController +@RequestMapping("/api/role") +public class RoleController { + + @Autowired + private IRoleService roleService; + + @PostMapping("/add") + @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){ + // 获取当前登录用户id + int currenRoleId = SecurityUtils.getUserInfo().getRole().getId(); + if (currenRoleId != 1) { + // 只用角色id为1才可以删除和添加用户 + throw new ControllerException(ErrorCode.ERROR403); + } + + Role role = new Role(); + role.setName(name); + role.setAuthority(authority); + role.setCreateTime(DateUtil.getNow()); + role.setUpdateTime(DateUtil.getNow()); + + int addResult = roleService.add(role); + if (addResult <= 0) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + @DeleteMapping("/delete") + @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.ERROR403); + } + int deleteResult = roleService.delete(id); + + if (deleteResult <= 0) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + @GetMapping("/all") + @Operation(summary = "查询角色") + public List all(){ + // 获取当前登录用户id + List allRoles = roleService.getAll(); + 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 5ebfd2d39..5ffb02cb2 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,208 @@ package com.genersoft.iot.vmp.vmanager.user; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.util.StringUtils; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.conf.security.JwtUtils; +import com.genersoft.iot.vmp.conf.security.SecurityUtils; +import com.genersoft.iot.vmp.conf.security.dto.LoginUser; +import com.genersoft.iot.vmp.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 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.security.authentication.AuthenticationManager; +import org.springframework.util.DigestUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; -@CrossOrigin +import javax.security.sasl.AuthenticationException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +@Tag(name = "用户管理") @RestController -@RequestMapping("/api") +@RequestMapping("/api/user") public class UserController { - @Value("${auth.username}") - private String usernameConfig; + @Autowired + private AuthenticationManager authenticationManager; - @Value("${auth.password}") - private String passwordConfig; + @Autowired + private IUserService userService; - @RequestMapping("/user/login") - public String login(String username, String password){ - if (!StringUtils.isEmpty(username) && username.equals(usernameConfig) - && !StringUtils.isEmpty(password) && password.equals(passwordConfig)) { - return "success"; + @Autowired + private IRoleService roleService; + + @GetMapping("/login") + @PostMapping("/login") + @Operation(summary = "登录", description = "登录成功后返回AccessToken, 可以从返回值获取到也可以从响应头中获取到," + + "后续的请求需要添加请求头 'access-token'或者放在参数里") + + @Parameter(name = "username", description = "用户名", required = true) + @Parameter(name = "password", description = "密码(32位md5加密)", required = true) + public LoginUser login(HttpServletRequest request, HttpServletResponse response, @RequestParam String username, @RequestParam String password){ + LoginUser user; + try { + user = SecurityUtils.login(username, password, authenticationManager); + } catch (AuthenticationException e) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); + } + if (user == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "用户名或密码错误"); }else { - return "fail"; + String jwt = JwtUtils.createToken(username, password, user.getRole().getId()); + response.setHeader(JwtUtils.getHeader(), jwt); + user.setAccessToken(jwt); + } + return user; + } + + + @PostMapping("/changePassword") + @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) { + throw new ControllerException(ErrorCode.ERROR100); + } + String username = userInfo.getUsername(); + LoginUser user = null; + try { + user = SecurityUtils.login(username, oldPassword, authenticationManager); + if (user == null) { + throw new ControllerException(ErrorCode.ERROR100); + } + //int userId = SecurityUtils.getUserId(); + boolean result = userService.changePassword(user.getId(), DigestUtils.md5DigestAsHex(password.getBytes())); + if (!result) { + throw new ControllerException(ErrorCode.ERROR100); + } + } catch (AuthenticationException e) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); + } + } + + + @PostMapping("/add") + @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){ + 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才可以删除和添加用户 + 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) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "角色不存在"); + } + user.setRole(role); + user.setCreateTime(DateUtil.getNow()); + user.setUpdateTime(DateUtil.getNow()); + int addResult = userService.addUser(user); + if (addResult <= 0) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + @DeleteMapping("/delete") + @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才可以删除和添加用户 + throw new ControllerException(ErrorCode.ERROR400.getCode(), "用户无权限"); + } + int resetPushKeyResult = userService.changePushKey(userId,pushKey); + if (resetPushKeyResult <= 0) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + @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 + 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/ApiControlController.java b/src/main/java/com/genersoft/iot/vmp/web/ApiControlController.java deleted file mode 100644 index c55824a18..000000000 --- a/src/main/java/com/genersoft/iot/vmp/web/ApiControlController.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.genersoft.iot.vmp.web; - -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.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - -/** - * 兼容LiveGBS的API:设备控制 - */ -@CrossOrigin -@RestController -@RequestMapping(value = "/api/v1/control") -public class ApiControlController { - - private final static Logger logger = LoggerFactory.getLogger(ApiControlController.class); - - @Autowired - private SIPCommander cmder; - - @Autowired - private IVideoManagerStorager storager; - - /** - * 设备控制 - 云台控制 - * @param serial 设备编号 - * @param command 控制指令 允许值: left, right, up, down, upleft, upright, downleft, downright, zoomin, zoomout, stop - * @param channel 通道序号 - * @param code 通道编号 - * @param speed 速度(0~255) 默认值: 129 - * @return - */ - @RequestMapping(value = "/ptz") - private JSONObject list(String serial,String command, - @RequestParam(required = false)Integer channel, - @RequestParam(required = false)String code, - @RequestParam(required = false)Integer speed){ - - if (logger.isDebugEnabled()) { - logger.debug(String.format("模拟接口> 设备云台控制 API调用,deviceId:%s ,channelId:%s ,command:%d ,speed:%d ", - serial, code, command, speed)); - } - Device device = storager.queryVideoDevice(serial); - int leftRight = 0; - int upDown = 0; - int inOut = 0; - switch (command) { - case "left": - leftRight = 1; - break; - case "right": - leftRight = 2; - break; - case "up": - upDown = 1; - break; - case "down": - upDown = 2; - break; - case "upleft": - upDown = 1; - leftRight = 1; - case "upright": - upDown = 1; - leftRight = 2; - break; - case "downleft": - upDown = 2; - leftRight = 1; - break; - case "downright": - upDown = 2; - leftRight = 2; - break; - case "zoomin": - inOut = 2; - break; - case "zoomout": - inOut = 1; - break; - case "stop": - break; - - } - // 默认值 50 - cmder.ptzCmd(device, code, leftRight, upDown, inOut, speed==0 ? 129 : speed, 50); - return null; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/web/ApiDeviceController.java b/src/main/java/com/genersoft/iot/vmp/web/ApiDeviceController.java deleted file mode 100644 index b58a3c5bc..000000000 --- a/src/main/java/com/genersoft/iot/vmp/web/ApiDeviceController.java +++ /dev/null @@ -1,165 +0,0 @@ -package com.genersoft.iot.vmp.web; - -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; -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.transmit.callback.DeferredResultHolder; -// import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.github.pagehelper.PageInfo; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -/** - * 兼容LiveGBS的API:设备信息 - */ -@SuppressWarnings("unchecked") -@CrossOrigin -@RestController -@RequestMapping(value = "/api/v1/device") -public class ApiDeviceController { - - private final static Logger logger = LoggerFactory.getLogger(ApiDeviceController.class); - - @Autowired - private IVideoManagerStorager storager; - - // @Autowired - // private SIPCommander cmder; - - // @Autowired - // private DeferredResultHolder resultHolder; - - // @Autowired - // private DeviceOffLineDetector offLineDetector; - - /** - * 分页获取设备列表 TODO 现在直接返回,尚未实现分页 - * @param start - * @param limit - * @param q - * @param online - * @return - */ - @RequestMapping(value = "/list") - public JSONObject list( @RequestParam(required = false)Integer start, - @RequestParam(required = false)Integer limit, - @RequestParam(required = false)String q, - @RequestParam(required = false)Boolean online ){ - - if (logger.isDebugEnabled()) { - logger.debug("查询所有视频设备API调用"); - } - - logger.debug("查询所有视频设备API调用"); - JSONObject result = new JSONObject(); - List devices; - if (start == null || limit ==null) { - devices = storager.queryVideoDeviceList(); - result.put("DeviceCount", devices.size()); - }else { - PageInfo deviceList = storager.queryVideoDeviceList(start/limit, limit); - result.put("DeviceCount", deviceList.getTotal()); - devices = deviceList.getList(); - } - - JSONArray deviceJSONList = new JSONArray(); - for (Device device : devices) { - JSONObject deviceJsonObject = new JSONObject(); - deviceJsonObject.put("ID", device.getDeviceId()); - deviceJsonObject.put("Name", device.getName()); - deviceJsonObject.put("Type", "GB"); - deviceJsonObject.put("ChannelCount", device.getChannelCount()); - deviceJsonObject.put("RecvStreamIP", ""); - deviceJsonObject.put("CatalogInterval", 3600); // 通道目录抓取周期 - deviceJsonObject.put("SubscribeInterval", 0); // 订阅周期(秒), 0 表示后台不周期订阅 - deviceJsonObject.put("Online", device.getOnline() == 1); - deviceJsonObject.put("Password", ""); - deviceJsonObject.put("MediaTransport", device.getTransport()); - deviceJsonObject.put("RemoteIP", device.getIp()); - deviceJsonObject.put("RemotePort", device.getPort()); - deviceJsonObject.put("LastRegisterAt", ""); - deviceJsonObject.put("LastKeepaliveAt", ""); - deviceJsonObject.put("UpdatedAt", ""); - deviceJsonObject.put("CreatedAt", ""); - deviceJSONList.add(deviceJsonObject); - } - result.put("DeviceList",deviceJSONList); - return result; - } - - @RequestMapping(value = "/channellist") - public JSONObject channellist( String serial, - @RequestParam(required = false)String channel_type, - @RequestParam(required = false)String dir_serial , - @RequestParam(required = false)Integer start, - @RequestParam(required = false)Integer limit, - @RequestParam(required = false)String q, - @RequestParam(required = false)Boolean online ){ - - if (logger.isDebugEnabled()) { - logger.debug("查询所有视频设备API调用"); - } - JSONObject result = new JSONObject(); - // 查询设备是否存在 - Device device = storager.queryVideoDevice(serial); - if (device == null) { - result.put("ChannelCount", 0); - result.put("ChannelList", "[]"); - return result; - } - List deviceChannels; - if (start == null || limit ==null) { - deviceChannels = storager.queryChannelsByDeviceId(serial); - 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(); - } - - JSONArray channleJSONList = new JSONArray(); - for (DeviceChannel deviceChannel : deviceChannels) { - JSONObject deviceJOSNChannel = new JSONObject(); - deviceJOSNChannel.put("ID", deviceChannel.getChannelId()); - deviceJOSNChannel.put("DeviceID", device.getDeviceId()); - deviceJOSNChannel.put("DeviceName", device.getName()); - deviceJOSNChannel.put("DeviceOnline", device.getOnline() == 1); - deviceJOSNChannel.put("Channel", 0); // TODO 自定义序号 - deviceJOSNChannel.put("Name", deviceChannel.getName()); - deviceJOSNChannel.put("Custom", false); - deviceJOSNChannel.put("CustomName", ""); - deviceJOSNChannel.put("SubCount", deviceChannel.getSubCount()); // TODO ? 子节点数, SubCount > 0 表示该通道为子目录 - deviceJOSNChannel.put("SnapURL", ""); - deviceJOSNChannel.put("Manufacturer ", deviceChannel.getManufacture()); - deviceJOSNChannel.put("Model", deviceChannel.getModel()); - deviceJOSNChannel.put("Owner", deviceChannel.getOwner()); - deviceJOSNChannel.put("CivilCode", deviceChannel.getCivilCode()); - deviceJOSNChannel.put("Address", deviceChannel.getAddress()); - deviceJOSNChannel.put("Parental", deviceChannel.getParental()); // 当为通道设备时, 是否有通道子设备, 1-有,0-没有 - deviceJOSNChannel.put("ParentID", deviceChannel.getParentId()); // 直接上级编号 - deviceJOSNChannel.put("Secrecy", deviceChannel.getSecrecy()); - deviceJOSNChannel.put("RegisterWay", 1); // 注册方式, 缺省为1, 允许值: 1, 2, 3 - // 1-IETF RFC3261, - // 2-基于口令的双向认证, - // 3-基于数字证书的双向认证 - deviceJOSNChannel.put("Status", deviceChannel.getStatus()); - deviceJOSNChannel.put("Longitude", deviceChannel.getLongitude()); - deviceJOSNChannel.put("Latitude", deviceChannel.getLatitude()); - deviceJOSNChannel.put("PTZType ", deviceChannel.getPTZType()); // 云台类型, 0 - 未知, 1 - 球机, 2 - 半球, - // 3 - 固定枪机, 4 - 遥控枪机 - deviceJOSNChannel.put("CustomPTZType", ""); - deviceJOSNChannel.put("StreamID", deviceChannel.getStreamId()); // StreamID 直播流ID, 有值表示正在直播 - deviceJOSNChannel.put("NumOutputs ", -1); // 直播在线人数 - channleJSONList.add(deviceJOSNChannel); - } - result.put("ChannelList", channleJSONList); - return result; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java b/src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java deleted file mode 100644 index 5a56bef7c..000000000 --- a/src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java +++ /dev/null @@ -1,191 +0,0 @@ -package com.genersoft.iot.vmp.web; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -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.transmit.cmd.impl.SIPCommander; -// import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.vmanager.play.PlayController; -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.*; -import org.springframework.web.context.request.async.DeferredResult; - -/** - * 兼容LiveGBS的API:实时直播 - */ -@SuppressWarnings(value = {"rawtypes", "unchecked"}) -@CrossOrigin -@RestController -@RequestMapping(value = "/api/v1/stream") -public class ApiStreamController { - - private final static Logger logger = LoggerFactory.getLogger(ApiStreamController.class); - - @Autowired - private SIPCommander cmder; - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - - // @Autowired - // private ZLMRESTfulUtils zlmresTfulUtils; - - - @Autowired - private PlayController playController; - - /** - * 实时直播 - 开始直播 - * @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 transport 流传输模式, 默认 UDP - * @param checkchannelstatus TODO 是否检查通道状态, 默认 false, 表示 拉流前不检查通道状态是否在线 - * @param transportmode TODO 当 transport=TCP 时有效, 指示流传输主被动模式, 默认被动 - * @param timeout TODO 拉流超时(秒), - * @return - */ - @RequestMapping(value = "/start") - private DeferredResult start(String serial , - @RequestParam(required = false)Integer channel , - @RequestParam(required = false)String code, - @RequestParam(required = false)String cdn, - @RequestParam(required = false)String audio, - @RequestParam(required = false)String transport, - @RequestParam(required = false)String checkchannelstatus , - @RequestParam(required = false)String transportmode, - @RequestParam(required = false)String timeout - - ){ - DeferredResult resultDeferredResult = new DeferredResult(); - Device device = storager.queryVideoDevice(serial); - if (device == null ) { - JSONObject result = new JSONObject(); - result.put("error","device[ " + serial + " ]未找到"); - resultDeferredResult.setResult(result); - }else if (device.getOnline() == 0) { - JSONObject result = new JSONObject(); - result.put("error","device[ " + code + " ]offline"); - resultDeferredResult.setResult(result); - } - resultDeferredResult.onTimeout(()->{ - logger.info("播放等待超时"); - JSONObject result = new JSONObject(); - result.put("error","timeout"); - resultDeferredResult.setResult(result); - - // 清理RTP server - }); - - DeviceChannel deviceChannel = storager.queryChannel(serial, code); - if (deviceChannel == null) { - JSONObject result = new JSONObject(); - result.put("error","channel[ " + code + " ]未找到"); - resultDeferredResult.setResult(result); - }else if (deviceChannel.getStatus() == 0) { - JSONObject result = new JSONObject(); - result.put("error","channel[ " + code + " ]offline"); - resultDeferredResult.setResult(result); - } - DeferredResult> play = playController.play(serial, code); - - play.setResultHandler((Object o)->{ - ResponseEntity responseEntity = (ResponseEntity)o; - StreamInfo streamInfo = JSON.parseObject(responseEntity.getBody(), StreamInfo.class); - JSONObject result = new JSONObject(); - result.put("StreamID", streamInfo.getStreamId()); - 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("CDN", ""); - result.put("SnapURL", ""); - result.put("Transport", device.getTransport()); - result.put("StartAt", ""); - result.put("Duration", ""); - result.put("SourceVideoCodecName", ""); - result.put("SourceVideoWidth", ""); - result.put("SourceVideoHeight", ""); - result.put("SourceVideoFrameRate", ""); - result.put("SourceAudioCodecName", ""); - result.put("SourceAudioSampleRate", ""); - result.put("AudioEnable", ""); - result.put("Ondemand", ""); - result.put("InBytes", ""); - result.put("InBitRate", ""); - result.put("OutBytes", ""); - result.put("NumOutputs", ""); - result.put("CascadeSize", ""); - result.put("RelaySize", ""); - result.put("ChannelPTZType", 0); - resultDeferredResult.setResult(result); - }); - return resultDeferredResult; - } - - /** - * 实时直播 - 直播流停止 - * @param serial 设备编号 - * @param channel 通道序号 - * @param code 通道国标编号 - * @param check_outputs - * @return - */ - @RequestMapping(value = "/stop") - @ResponseBody - private JSONObject stop(String serial , - @RequestParam(required = false)Integer channel , - @RequestParam(required = false)String code, - @RequestParam(required = false)String check_outputs - - ){ - - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(serial, code); - if (streamInfo == null) { - JSONObject result = new JSONObject(); - result.put("error","未找到流信息"); - return result; - } - cmder.streamByeCmd(streamInfo.getStreamId()); - redisCatchStorage.stopPlay(streamInfo); - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); - return null; - } - - /** - * 实时直播 - 直播流保活 - * @param serial 设备编号 - * @param channel 通道序号 - * @param code 通道国标编号 - * @return - */ - @RequestMapping(value = "/touch") - @ResponseBody - private JSONObject touch(String serial ,String t, - @RequestParam(required = false)Integer channel , - @RequestParam(required = false)String code, - @RequestParam(required = false)String autorestart, - @RequestParam(required = false)String audio, - @RequestParam(required = false)String cdn - ){ - return null; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/web/AuthController.java b/src/main/java/com/genersoft/iot/vmp/web/AuthController.java deleted file mode 100644 index 702387cbc..000000000 --- a/src/main/java/com/genersoft/iot/vmp/web/AuthController.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.genersoft.iot.vmp.web; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.util.StringUtils; -import org.springframework.web.bind.annotation.*; - -@CrossOrigin -@RestController -@RequestMapping(value = "/auth") -public class AuthController { - - @Value("${auth.username}") - private String username; - - @Value("${auth.password}") - private String password; - - @RequestMapping("/login") - public String devices(String name, String passwd){ - if (!StringUtils.isEmpty(name) && name.equals(username) - && !StringUtils.isEmpty(passwd) && passwd.equals(password)) { - return "success"; - }else { - return "fail"; - } - } -} 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 new file mode 100644 index 000000000..286c8d57a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiControlController.java @@ -0,0 +1,155 @@ +package com.genersoft.iot.vmp.web.gb28181; + +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; + +/** + * API兼容:设备控制 + */ + +@RestController +@RequestMapping(value = "/api/v1/control") +public class ApiControlController { + + private final static Logger logger = LoggerFactory.getLogger(ApiControlController.class); + + @Autowired + private SIPCommander cmder; + + @Autowired + private IVideoManagerStorage storager; + + /** + * 设备控制 - 云台控制 + * @param serial 设备编号 + * @param command 控制指令 允许值: left, right, up, down, upleft, upright, downleft, downright, zoomin, zoomout, stop + * @param channel 通道序号 + * @param code 通道编号 + * @param speed 速度(0~255) 默认值: 129 + * @return + */ + @RequestMapping(value = "/ptz") + private void list(String serial,String command, + @RequestParam(required = false)Integer channel, + @RequestParam(required = false)String code, + @RequestParam(required = false)Integer speed){ + + if (logger.isDebugEnabled()) { + logger.debug("模拟接口> 设备云台控制 API调用,deviceId:{} ,channelId:{} ,command:{} ,speed:{} ", + serial, code, command, speed); + } + if (channel == null) {channel = 0;} + if (speed == null) {speed = 0;} + Device device = storager.queryVideoDevice(serial); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "device[ " + serial + " ]未找到"); + } + int cmdCode = 0; + switch (command){ + case "left": + cmdCode = 2; + break; + case "right": + cmdCode = 1; + break; + case "up": + cmdCode = 8; + break; + case "down": + cmdCode = 4; + break; + case "upleft": + cmdCode = 10; + break; + case "upright": + cmdCode = 9; + break; + case "downleft": + cmdCode = 6; + break; + case "downright": + cmdCode = 5; + break; + case "zoomin": + cmdCode = 16; + break; + case "zoomout": + cmdCode = 32; + break; + case "stop": + cmdCode = 0; + break; + default: + break; + } + // 默认值 50 + 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()); + } + } + + /** + * 设备控制 - 预置位控制 + * @param serial 设备编号 + * @param code 通道编号,通过 /api/v1/device/channellist 获取的 ChannelList.ID, 该参数和 channel 二选一传递即可 + * @param channel 通道序号, 默认值: 1 + * @param command 控制指令 允许值: set, goto, remove + * @param preset 预置位编号(1~255) + * @param name 预置位名称, command=set 时有效 + * @return + */ + @RequestMapping(value = "/preset") + private void list(String serial,String command, + @RequestParam(required = false)Integer channel, + @RequestParam(required = false)String code, + @RequestParam(required = false)String name, + @RequestParam(required = false)Integer preset){ + + if (logger.isDebugEnabled()) { + logger.debug("模拟接口> 预置位控制 API调用,deviceId:{} ,channelId:{} ,command:{} ,name:{} ,preset:{} ", + serial, code, command, name, preset); + } + + if (channel == null) {channel = 0;} + Device device = storager.queryVideoDevice(serial); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "device[ " + serial + " ]未找到"); + } + int cmdCode = 0; + switch (command){ + case "set": + cmdCode = 129; + break; + case "goto": + cmdCode = 130; + break; + case "remove": + cmdCode = 131; + break; + default: + break; + } + try { + cmder.frontEndCmd(device, code, cmdCode, 0, preset, 0); + } 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/ApiController.java b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiController.java similarity index 84% rename from src/main/java/com/genersoft/iot/vmp/web/ApiController.java rename to src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiController.java index 43ae7d972..bd5d5ef3f 100644 --- a/src/main/java/com/genersoft/iot/vmp/web/ApiController.java +++ b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiController.java @@ -1,20 +1,19 @@ -package com.genersoft.iot.vmp.web; +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; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; /** - * 兼容LiveGBS的API:系统接口 + * API兼容:系统接口 */ @Controller -@CrossOrigin + @RequestMapping(value = "/api/v1") public class ApiController { @@ -38,10 +37,10 @@ public class ApiController { result.put("ServerTime","2020-09-02 17:11"); result.put("StartUpTime","2020-09-02 17:11"); result.put("Server",""); - result.put("SIPSerial", sipConfig.getSipId()); - result.put("SIPRealm", sipConfig.getSipDomain()); - result.put("SIPHost", sipConfig.getSipIp()); - result.put("SIPPort", sipConfig.getSipPort()); + result.put("SIPSerial", sipConfig.getId()); + result.put("SIPRealm", sipConfig.getDomain()); + result.put("SIPHost", sipConfig.getIp()); + result.put("SIPPort", sipConfig.getPort()); result.put("ChannelCount","1000"); result.put("VersionType",""); result.put("LogoMiniText",""); @@ -65,10 +64,10 @@ public class ApiController { // result.put("ServerTime","2020-09-02 17:11"); // result.put("StartUpTime","2020-09-02 17:11"); // result.put("Server",""); -// result.put("SIPSerial", sipConfig.getSipId()); -// result.put("SIPRealm", sipConfig.getSipDomain()); -// result.put("SIPHost", sipConfig.getSipIp()); -// result.put("SIPPort", sipConfig.getSipPort()); +// result.put("SIPSerial", sipConfig.getId()); +// result.put("SIPRealm", sipConfig.getDomain()); +// result.put("SIPHost", sipConfig.getIp()); +// result.put("SIPPort", sipConfig.getPort()); // result.put("ChannelCount","1000"); // result.put("VersionType",""); // result.put("LogoMiniText",""); 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 new file mode 100644 index 000000000..d8c209306 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiDeviceController.java @@ -0,0 +1,257 @@ +package com.genersoft.iot.vmp.web.gb28181; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.PresetQuerySipReq; +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; +import com.genersoft.iot.vmp.service.IDeviceService; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.vmanager.bean.DeferredResultEx; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend; +import com.github.pagehelper.PageInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.RequestMapping; +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; +import java.text.ParseException; +import java.util.*; + +/** + * API兼容:设备信息 + */ +@SuppressWarnings("unchecked") + +@RestController +@RequestMapping(value = "/api/v1/device") +public class ApiDeviceController { + + private final static Logger logger = LoggerFactory.getLogger(ApiDeviceController.class); + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommander cmder; + @Autowired + private IDeviceService deviceService; + + @Autowired + private DeferredResultHolder resultHolder; + + + /** + * 分页获取设备列表 现在直接返回,尚未实现分页 + * @param start + * @param limit + * @param q + * @param online + * @return + */ + @RequestMapping(value = "/list") + public JSONObject list( @RequestParam(required = false)Integer start, + @RequestParam(required = false)Integer limit, + @RequestParam(required = false)String q, + @RequestParam(required = false)Boolean online ){ + +// if (logger.isDebugEnabled()) { +// logger.debug("查询所有视频设备API调用"); +// } + JSONObject result = new JSONObject(); + List devices; + if (start == null || limit ==null) { + devices = storager.queryVideoDeviceList(online); + result.put("DeviceCount", devices.size()); + }else { + PageInfo deviceList = storager.queryVideoDeviceList(start/limit, limit,online); + result.put("DeviceCount", deviceList.getTotal()); + devices = deviceList.getList(); + } + + JSONArray deviceJSONList = new JSONArray(); + for (Device device : devices) { + JSONObject deviceJsonObject = new JSONObject(); + deviceJsonObject.put("ID", device.getDeviceId()); + deviceJsonObject.put("Name", device.getName()); + deviceJsonObject.put("Type", "GB"); + deviceJsonObject.put("ChannelCount", device.getChannelCount()); + deviceJsonObject.put("RecvStreamIP", ""); + deviceJsonObject.put("CatalogInterval", 3600); // 通道目录抓取周期 + deviceJsonObject.put("SubscribeInterval", device.getSubscribeCycleForCatalog()); // 订阅周期(秒), 0 表示后台不周期订阅 + deviceJsonObject.put("Online", device.isOnLine()); + deviceJsonObject.put("Password", ""); + deviceJsonObject.put("MediaTransport", device.getTransport()); + deviceJsonObject.put("RemoteIP", device.getIp()); + deviceJsonObject.put("RemotePort", device.getPort()); + deviceJsonObject.put("LastRegisterAt", ""); + deviceJsonObject.put("LastKeepaliveAt", ""); + deviceJsonObject.put("UpdatedAt", ""); + deviceJsonObject.put("CreatedAt", ""); + deviceJSONList.add(deviceJsonObject); + } + result.put("DeviceList",deviceJSONList); + return result; + } + + @RequestMapping(value = "/channellist") + public JSONObject channellist( String serial, + @RequestParam(required = false)String channel_type, + @RequestParam(required = false)String code , + @RequestParam(required = false)String dir_serial , + @RequestParam(required = false)Integer start, + @RequestParam(required = false)Integer limit, + @RequestParam(required = false)String q, + @RequestParam(required = false)Boolean online ){ + +// if (logger.isDebugEnabled()) { +// logger.debug("查询所有视频设备API调用"); +// } + JSONObject result = new JSONObject(); + // 查询设备是否存在 +// Device device = storager.queryVideoDevice(serial); +// if (device == null) { +// result.put("ChannelCount", 0); +// result.put("ChannelList", "[]"); +// 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,channelIds,online); + if (start == null || limit ==null) { + deviceChannels = allDeviceChannelList; + result.put("ChannelCount", deviceChannels.size()); + }else { + deviceChannels = storager.queryChannelsByDeviceIdWithStartAndLimit(serial,channelIds, null, null, online,start, limit); + int total = allDeviceChannelList.size(); + result.put("ChannelCount", total); + } + + JSONArray channleJSONList = new JSONArray(); + for (DeviceChannelExtend deviceChannelExtend : deviceChannels) { + JSONObject deviceJOSNChannel = new JSONObject(); + deviceJOSNChannel.put("ID", deviceChannelExtend.getChannelId()); + deviceJOSNChannel.put("DeviceID", deviceChannelExtend.getDeviceId()); + deviceJOSNChannel.put("DeviceName", deviceChannelExtend.getDeviceName()); + deviceJOSNChannel.put("DeviceOnline", deviceChannelExtend.isDeviceOnline()); + deviceJOSNChannel.put("Channel", 0); // TODO 自定义序号 + deviceJOSNChannel.put("Name", deviceChannelExtend.getName()); + deviceJOSNChannel.put("Custom", false); + deviceJOSNChannel.put("CustomName", ""); + deviceJOSNChannel.put("SubCount", deviceChannelExtend.getSubCount()); // TODO ? 子节点数, SubCount > 0 表示该通道为子目录 + deviceJOSNChannel.put("SnapURL", ""); + deviceJOSNChannel.put("Manufacturer ", deviceChannelExtend.getManufacture()); + deviceJOSNChannel.put("Model", deviceChannelExtend.getModel()); + deviceJOSNChannel.put("Owner", deviceChannelExtend.getOwner()); + deviceJOSNChannel.put("CivilCode", deviceChannelExtend.getCivilCode()); + deviceJOSNChannel.put("Address", deviceChannelExtend.getAddress()); + deviceJOSNChannel.put("Parental", deviceChannelExtend.getParental()); // 当为通道设备时, 是否有通道子设备, 1-有,0-没有 + deviceJOSNChannel.put("ParentID", deviceChannelExtend.getParentId()); // 直接上级编号 + deviceJOSNChannel.put("Secrecy", deviceChannelExtend.getSecrecy()); + deviceJOSNChannel.put("RegisterWay", 1); // 注册方式, 缺省为1, 允许值: 1, 2, 3 + // 1-IETF RFC3261, + // 2-基于口令的双向认证, + // 3-基于数字证书的双向认证 + deviceJOSNChannel.put("Status", deviceChannelExtend.isStatus() ? "ON":"OFF"); + deviceJOSNChannel.put("Longitude", deviceChannelExtend.getLongitude()); + deviceJOSNChannel.put("Latitude", deviceChannelExtend.getLatitude()); + deviceJOSNChannel.put("PTZType ", deviceChannelExtend.getPTZType()); // 云台类型, 0 - 未知, 1 - 球机, 2 - 半球, + // 3 - 固定枪机, 4 - 遥控枪机 + deviceJOSNChannel.put("CustomPTZType", ""); + deviceJOSNChannel.put("StreamID", deviceChannelExtend.getStreamId()); // StreamID 直播流ID, 有值表示正在直播 + deviceJOSNChannel.put("NumOutputs ", -1); // 直播在线人数 + channleJSONList.add(deviceJOSNChannel); + } + result.put("ChannelList", channleJSONList); + return result; + } + + /** + * 设备信息 - 获取下级通道预置位 + * @param serial 设备编号 + * @param code 通道编号,通过 /api/v1/device/channellist 获取的 ChannelList.ID, 该参数和 channel 二选一传递即可 + * @param channel 通道序号, 默认值: 1 + * @param fill 是否填充空置预置位,当下级返回预置位,但不够255个时,自动填充空置预置位到255个, 默认值: true, 允许值: true, false + * @param timeout 超时时间(秒) 默认值: 15 + * @return + */ + @RequestMapping(value = "/fetchpreset") + private DeferredResult list(String serial, + @RequestParam(required = false)Integer channel, + @RequestParam(required = false)String code, + @RequestParam(required = false)Boolean fill, + @RequestParam(required = false)Integer timeout){ + + if (logger.isDebugEnabled()) { + logger.debug("<模拟接口> 获取下级通道预置位 API调用,deviceId:{} ,channel:{} ,code:{} ,fill:{} ,timeout:{} ", + serial, channel, code, fill, timeout); + } + + Device device = storager.queryVideoDevice(serial); + String uuid = UUID.randomUUID().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + (ObjectUtils.isEmpty(code) ? serial : code); + DeferredResult result = new DeferredResult<> (timeout * 1000L); + DeferredResultEx deferredResultEx = new DeferredResultEx<>(result); + result.onTimeout(()->{ + logger.warn("<模拟接口> 获取设备预置位超时"); + // 释放rtpserver + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData("wait for presetquery timeout["+timeout+"s]"); + resultHolder.invokeResult(msg); + }); + if (resultHolder.exist(key, null)) { + return result; + } + + deferredResultEx.setFilter(filterResult->{ + List presetQuerySipReqList = (List)filterResult; + HashMap resultMap = new HashMap<>(); + resultMap.put("DeviceID", code); + resultMap.put("Result", "OK"); + resultMap.put("SumNum", presetQuerySipReqList.size()); + ArrayList> presetItemList = new ArrayList<>(presetQuerySipReqList.size()); + for (PresetQuerySipReq presetQuerySipReq : presetQuerySipReqList) { + Map item = new HashMap<>(); + item.put("PresetID", presetQuerySipReq.getPresetId()); + item.put("PresetName", presetQuerySipReq.getPresetName()); + item.put("PresetEnable", true); + presetItemList.add(item); + } + resultMap.put("PresetItemList",presetItemList ); + return resultMap; + }); + + resultHolder.put(key, uuid, deferredResultEx); + + try { + cmder.presetQuery(device, code, 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/web/gb28181/ApiStreamController.java b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java new file mode 100644 index 000000000..556adf62c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java @@ -0,0 +1,232 @@ +package com.genersoft.iot.vmp.web.gb28181; + +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.common.InviteInfo; +import com.genersoft.iot.vmp.common.InviteSessionType; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +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.IInviteStreamService; +import com.genersoft.iot.vmp.service.IPlayService; +import com.genersoft.iot.vmp.service.bean.InviteErrorCode; +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.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.request.async.DeferredResult; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; + +/** + * API兼容:实时直播 + */ +@SuppressWarnings(value = {"rawtypes", "unchecked"}) + +@RestController +@RequestMapping(value = "/api/v1/stream") +public class ApiStreamController { + + private final static Logger logger = LoggerFactory.getLogger(ApiStreamController.class); + + @Autowired + private SIPCommander cmder; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private UserSetting userSetting; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IInviteStreamService inviteStreamService; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private IPlayService playService; + + /** + * 实时直播 - 开始直播 + * @param serial 设备编号 + * @param channel 通道序号 默认值: 1 + * @param code 通道编号,通过 /api/v1/device/channellist 获取的 ChannelList.ID, 该参数和 channel 二选一传递即可 + * @param cdn 转推 CDN 地址, 形如: [rtmp|rtsp]://xxx, encodeURIComponent + * @param audio 是否开启音频, 默认 开启 + * @param transport 流传输模式, 默认 UDP + * @param checkchannelstatus 是否检查通道状态, 默认 false, 表示 拉流前不检查通道状态是否在线 + * @param transportmode 当 transport=TCP 时有效, 指示流传输主被动模式, 默认被动 + * @param timeout 拉流超时(秒), + * @return + */ + @RequestMapping(value = "/start") + private DeferredResult start(String serial , + @RequestParam(required = false)Integer channel , + @RequestParam(required = false)String code, + @RequestParam(required = false)String cdn, + @RequestParam(required = false)String audio, + @RequestParam(required = false)String transport, + @RequestParam(required = false)String checkchannelstatus , + @RequestParam(required = false)String transportmode, + @RequestParam(required = false)String timeout + + ){ + 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.isOnLine()) { + JSONObject result = new JSONObject(); + result.put("error","device[ " + code + " ]offline"); + resultDeferredResult.setResult(result); + return resultDeferredResult; + } + resultDeferredResult.onTimeout(()->{ + logger.info("播放等待超时"); + JSONObject result = new JSONObject(); + result.put("error","timeout"); + resultDeferredResult.setResult(result); + + // 清理RTP server + }); + + DeviceChannel deviceChannel = storager.queryChannel(serial, code); + if (deviceChannel == null) { + JSONObject result = new JSONObject(); + result.put("error","channel[ " + code + " ]未找到"); + resultDeferredResult.setResult(result); + return resultDeferredResult; + }else if (!deviceChannel.isStatus()) { + JSONObject result = new JSONObject(); + result.put("error","channel[ " + code + " ]offline"); + resultDeferredResult.setResult(result); + return resultDeferredResult; + } + MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device); + + + playService.play(newMediaServerItem, serial, code, (errorCode, msg, data) -> { + if (errorCode == InviteErrorCode.SUCCESS.getCode()) { + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, serial, code); + if (inviteInfo != null && inviteInfo.getStreamInfo() != null) { + JSONObject result = new JSONObject(); + result.put("StreamID", inviteInfo.getStreamInfo().getStream()); + result.put("DeviceID", device.getDeviceId()); + result.put("ChannelID", code); + result.put("ChannelName", deviceChannel.getName()); + result.put("ChannelCustomName", ""); + result.put("FLV", inviteInfo.getStreamInfo().getFlv().getUrl()); + result.put("WS_FLV", inviteInfo.getStreamInfo().getWs_flv().getUrl()); + result.put("RTMP", inviteInfo.getStreamInfo().getRtmp().getUrl()); + result.put("HLS", inviteInfo.getStreamInfo().getHls().getUrl()); + result.put("RTSP", inviteInfo.getStreamInfo().getRtsp().getUrl()); + result.put("WEBRTC", inviteInfo.getStreamInfo().getRtc().getUrl()); + result.put("CDN", ""); + result.put("SnapURL", ""); + result.put("Transport", device.getTransport()); + result.put("StartAt", ""); + result.put("Duration", ""); + result.put("SourceVideoCodecName", ""); + result.put("SourceVideoWidth", ""); + result.put("SourceVideoHeight", ""); + result.put("SourceVideoFrameRate", ""); + result.put("SourceAudioCodecName", ""); + result.put("SourceAudioSampleRate", ""); + result.put("AudioEnable", ""); + result.put("Ondemand", ""); + result.put("InBytes", ""); + result.put("InBitRate", ""); + result.put("OutBytes", ""); + result.put("NumOutputs", ""); + result.put("CascadeSize", ""); + result.put("RelaySize", ""); + result.put("ChannelPTZType", "0"); + resultDeferredResult.setResult(result); + } + }else { + JSONObject result = new JSONObject(); + result.put("error", "channel[ " + code + " ] " + msg); + resultDeferredResult.setResult(result); + } + }); + + return resultDeferredResult; + } + + /** + * 实时直播 - 直播流停止 + * @param serial 设备编号 + * @param channel 通道序号 + * @param code 通道国标编号 + * @param check_outputs + * @return + */ + @RequestMapping(value = "/stop") + @ResponseBody + private JSONObject stop(String serial , + @RequestParam(required = false)Integer channel , + @RequestParam(required = false)String code, + @RequestParam(required = false)String check_outputs + + ){ + + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, serial, code); + if (inviteInfo == null) { + JSONObject result = new JSONObject(); + result.put("error","未找到流信息"); + return result; + } + Device device = deviceService.getDevice(serial); + if (device == null) { + JSONObject result = new JSONObject(); + result.put("error","未找到设备"); + return result; + } + try { + cmder.streamByeCmd(device, code, inviteInfo.getStream(), null); + } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { + JSONObject result = new JSONObject(); + result.put("error","发送BYE失败:" + e.getMessage()); + return result; + } + inviteStreamService.removeInviteInfo(inviteInfo); + storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId()); + return null; + } + + /** + * 实时直播 - 直播流保活 + * @param serial 设备编号 + * @param channel 通道序号 + * @param code 通道国标编号 + * @return + */ + @RequestMapping(value = "/touch") + @ResponseBody + private JSONObject touch(String serial ,String t, + @RequestParam(required = false)Integer channel , + @RequestParam(required = false)String code, + @RequestParam(required = false)String autorestart, + @RequestParam(required = false)String audio, + @RequestParam(required = false)String cdn + ){ + return null; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/web/gb28181/AuthController.java b/src/main/java/com/genersoft/iot/vmp/web/gb28181/AuthController.java new file mode 100644 index 000000000..d503f1531 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/web/gb28181/AuthController.java @@ -0,0 +1,25 @@ +package com.genersoft.iot.vmp.web.gb28181; + +import com.genersoft.iot.vmp.service.IUserService; +import com.genersoft.iot.vmp.storager.dao.dto.User; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +@RestController +@RequestMapping(value = "/auth") +public class AuthController { + + @Autowired + private IUserService userService; + + @RequestMapping("/login") + public String devices(String name, String passwd){ + User user = userService.getUser(name, passwd); + if (user != null) { + return "success"; + }else { + return "fail"; + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/web/gb28181/dto/DeviceChannelExtend.java b/src/main/java/com/genersoft/iot/vmp/web/gb28181/dto/DeviceChannelExtend.java new file mode 100644 index 000000000..5e76cb701 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/web/gb28181/dto/DeviceChannelExtend.java @@ -0,0 +1,555 @@ +package com.genersoft.iot.vmp.web.gb28181.dto; + +public class DeviceChannelExtend { + + + /** + * 数据库自增ID + */ + private int id; + + /** + * 通道id + */ + private String channelId; + + /** + * 设备id + */ + private String deviceId; + + /** + * 通道名 + */ + private String name; + + private String deviceName; + + private boolean deviceOnline; + + /** + * 生产厂商 + */ + private String manufacture; + + /** + * 型号 + */ + private String model; + + /** + * 设备归属 + */ + private String owner; + + /** + * 行政区域 + */ + private String civilCode; + + /** + * 警区 + */ + private String block; + + /** + * 安装地址 + */ + private String address; + + /** + * 是否有子设备 1有, 0没有 + */ + private int parental; + + /** + * 父级id + */ + private String parentId; + + /** + * 信令安全模式 缺省为0; 0:不采用; 2: S/MIME签名方式; 3: S/ MIME加密签名同时采用方式; 4:数字摘要方式 + */ + private int safetyWay; + + /** + * 注册方式 缺省为1;1:符合IETFRFC3261标准的认证注册模 式; 2:基于口令的双向认证注册模式; 3:基于数字证书的双向认证注册模式 + */ + private int registerWay; + + /** + * 证书序列号 + */ + private String certNum; + + /** + * 证书有效标识 缺省为0;证书有效标识:0:无效1: 有效 + */ + private int certifiable; + + /** + * 证书无效原因码 + */ + private int errCode; + + /** + * 证书终止有效期 + */ + private String endTime; + + /** + * 保密属性 缺省为0; 0:不涉密, 1:涉密 + */ + private String secrecy; + + /** + * IP地址 + */ + private String ipAddress; + + /** + * 端口号 + */ + private int port; + + /** + * 密码 + */ + private String password; + + /** + * 云台类型 + */ + private int PTZType; + + /** + * 云台类型描述字符串 + */ + private String PTZTypeText; + + /** + * 创建时间 + */ + private String createTime; + + /** + * 更新时间 + */ + private String updateTime; + + /** + * 在线/离线 + * 1在线,0离线 + * 默认在线 + * 信令: + * ON + * OFF + * 遇到过NVR下的IPC下发信令可以推流, 但是 Status 响应 OFF + */ + private boolean status; + + /** + * 经度 + */ + private double longitude; + + /** + * 纬度 + */ + private double latitude; + + /** + * 经度 GCJ02 + */ + private double longitudeGcj02; + + /** + * 纬度 GCJ02 + */ + private double latitudeGcj02; + + /** + * 经度 WGS84 + */ + private double longitudeWgs84; + + /** + * 纬度 WGS84 + */ + private double latitudeWgs84; + + /** + * 子设备数 + */ + private int subCount; + + /** + * 流唯一编号,存在表示正在直播 + */ + private String streamId; + + /** + * 是否含有音频 + */ + private boolean hasAudio; + + /** + * 标记通道的类型,0->国标通道 1->直播流通道 2->业务分组/虚拟组织/行政区划 + */ + private int channelType; + + /** + * 业务分组 + */ + private String businessGroupId; + + /** + * GPS的更新时间 + */ + private String gpsTime; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public void setPTZType(int PTZType) { + this.PTZType = PTZType; + switch (PTZType) { + case 0: + this.PTZTypeText = "未知"; + break; + case 1: + this.PTZTypeText = "球机"; + break; + case 2: + this.PTZTypeText = "半球"; + break; + case 3: + this.PTZTypeText = "固定枪机"; + break; + case 4: + this.PTZTypeText = "遥控枪机"; + break; + } + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getManufacture() { + return manufacture; + } + + public void setManufacture(String manufacture) { + this.manufacture = manufacture; + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public String getCivilCode() { + return civilCode; + } + + public void setCivilCode(String civilCode) { + this.civilCode = civilCode; + } + + public String getBlock() { + return block; + } + + public void setBlock(String block) { + this.block = block; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public int getParental() { + return parental; + } + + public void setParental(int parental) { + this.parental = parental; + } + + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + public int getSafetyWay() { + return safetyWay; + } + + public void setSafetyWay(int safetyWay) { + this.safetyWay = safetyWay; + } + + public int getRegisterWay() { + return registerWay; + } + + public void setRegisterWay(int registerWay) { + this.registerWay = registerWay; + } + + public String getCertNum() { + return certNum; + } + + public void setCertNum(String certNum) { + this.certNum = certNum; + } + + public int getCertifiable() { + return certifiable; + } + + public void setCertifiable(int certifiable) { + this.certifiable = certifiable; + } + + public int getErrCode() { + return errCode; + } + + public void setErrCode(int errCode) { + this.errCode = errCode; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public String getSecrecy() { + return secrecy; + } + + public void setSecrecy(String secrecy) { + this.secrecy = secrecy; + } + + public String getIpAddress() { + return ipAddress; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public int getPTZType() { + return PTZType; + } + + public String getPTZTypeText() { + return PTZTypeText; + } + + public void setPTZTypeText(String PTZTypeText) { + this.PTZTypeText = PTZTypeText; + } + + public boolean isStatus() { + return status; + } + + public void setStatus(boolean status) { + this.status = status; + } + + public double getLongitude() { + return longitude; + } + + public void setLongitude(double longitude) { + this.longitude = longitude; + } + + public double getLatitude() { + return latitude; + } + + public void setLatitude(double latitude) { + this.latitude = latitude; + } + + public double getLongitudeGcj02() { + return longitudeGcj02; + } + + public void setLongitudeGcj02(double longitudeGcj02) { + this.longitudeGcj02 = longitudeGcj02; + } + + public double getLatitudeGcj02() { + return latitudeGcj02; + } + + public void setLatitudeGcj02(double latitudeGcj02) { + this.latitudeGcj02 = latitudeGcj02; + } + + public double getLongitudeWgs84() { + return longitudeWgs84; + } + + public void setLongitudeWgs84(double longitudeWgs84) { + this.longitudeWgs84 = longitudeWgs84; + } + + public double getLatitudeWgs84() { + return latitudeWgs84; + } + + public void setLatitudeWgs84(double latitudeWgs84) { + this.latitudeWgs84 = latitudeWgs84; + } + + public int getSubCount() { + return subCount; + } + + public void setSubCount(int subCount) { + this.subCount = subCount; + } + + public boolean isHasAudio() { + return hasAudio; + } + + public void setHasAudio(boolean hasAudio) { + this.hasAudio = hasAudio; + } + + public String getStreamId() { + return streamId; + } + + public void setStreamId(String streamId) { + this.streamId = streamId; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public int getChannelType() { + return channelType; + } + + public void setChannelType(int channelType) { + this.channelType = channelType; + } + + public String getBusinessGroupId() { + return businessGroupId; + } + + public void setBusinessGroupId(String businessGroupId) { + this.businessGroupId = businessGroupId; + } + + public String getGpsTime() { + return gpsTime; + } + + public void setGpsTime(String gpsTime) { + this.gpsTime = gpsTime; + } + + public String getDeviceName() { + return deviceName; + } + + public void setDeviceName(String deviceName) { + this.deviceName = deviceName; + } + + public boolean isDeviceOnline() { + return deviceOnline; + } + + public void setDeviceOnline(boolean deviceOnline) { + this.deviceOnline = deviceOnline; + } +} diff --git a/src/main/resources/all-application.yml b/src/main/resources/all-application.yml new file mode 100644 index 000000000..ef247f7fc --- /dev/null +++ b/src/main/resources/all-application.yml @@ -0,0 +1,223 @@ + + + +# 此配置文件只是用作展示所有配置项, 不可直接使用 + + +spring: + # 设置接口超时时间 + mvc: + async: + request-timeout: 20000 + # [可选]上传文件大小限制 + servlet: + multipart: + max-file-size: 10MB + max-request-size: 100MB + # REDIS数据库配置 + redis: + # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1 + host: 127.0.0.1 + # [必须修改] 端口号 + port: 6379 + # [可选] 数据库 DB + database: 6 + # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 + password: + # [可选] 超时时间 + timeout: 10000 + # [可选] 一个pool最多可分配多少个jedis实例 + poolMaxTotal: 1000 + # [可选] 一个pool最多有多少个状态为idle(空闲)的jedis实例 + poolMaxIdle: 500 + # [可选] 最大的等待时间(秒) + poolMaxWait: 5 + # [必选] jdbc数据库配置 + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true + username: root + password: root123 + hikari: + connection-timeout: 20000 # 是客户端等待连接池连接的最大毫秒数 + initialSize: 10 # 连接池初始化连接数 + maximum-pool-size: 200 # 连接池最大连接数 + minimum-idle: 5 # 连接池最小空闲连接数 + idle-timeout: 300000 # 允许连接在连接池中空闲的最长时间(以毫秒为单位) + max-lifetime: 1200000 # 是池中连接关闭后的最长生命周期(以毫秒为单位) + + +# 修改分页插件为 postgresql, 数据库类型为mysql不需要 +#pagehelper: +# helper-dialect: postgresql + +# [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 +server: + port: 18080 + # [可选] HTTPS配置, 默认不开启 + ssl: + # [可选] 是否开启HTTPS访问 + enabled: false + # [可选] 证书文件路径,放置在resource/目录下即可,修改xxx为文件名 + key-store: classpath:xxx.jks + # [可选] 证书密码 + key-store-password: password + # [可选] 证书类型, 默认为jks,根据实际修改 + key-store-type: JKS + # 配置证书可以使用如下两项,如上面二选一即可 + # PEM 编码证书 + certificate: xx.pem + # 私钥文件 + certificate-private-key: xx.key + +# 作为28181服务器的配置 +sip: + # [必须修改] 本机的IP,对应你的网卡,监听什么ip就是使用什么网卡, + # 如果要监听多张网卡,可以使用逗号分隔多个IP, 例如: 192.168.1.4,10.0.0.4 + # 如果不明白,就使用0.0.0.0,大部分情况都是可以的 + # 请不要使用127.0.0.1,任何包括localhost在内的域名都是不可以的。 + ip: 0.0.0.0 + # [可选] 没有任何业务需求,仅仅是在前端展示的时候用 + show-ip: 192.168.0.100 + # [可选] 28181服务监听的端口 + port: 5060 + # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) + # 后两位为行业编码,定义参照附录D.3 + # 3701020049标识山东济南历下区 信息行业接入 + # [可选] + domain: 4401020049 + # [可选] + id: 44010200492000000001 + # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验 + password: admin123 + # [可选] 国标级联注册失败,再次发起注册的时间间隔。 默认60秒 + register-time-interval: 60 + # [可选] 云台控制速度 + ptz-speed: 50 + # TODO [可选] 收到心跳后自动上线, 重启服务后会将所有设备置为离线,默认false,等待注册后上线。设置为true则收到心跳设置为上线。 + # keepalliveToOnline: false + # 是否存储alarm信息 + alarm: false + +# 做为JT1078服务器的配置 +jt1078: + #[必须修改] 是否开启1078的服务 + enable: true + #[必修修改] 1708设备接入的端口 + port: 21078 + #[可选] 设备鉴权的密码 + password: admin123 + +#zlm 默认服务器配置 +media: + # [必须修改] zlm服务器唯一id,用于触发hook时区别是哪台服务器,general.mediaServerId + id: + # [必须修改] zlm服务器的内网IP + ip: 192.168.0.100 + # [可选] 返回流地址时的ip,置空使用 media.ip + stream-ip: + # [可选] wvp在国标信令中使用的ip,此ip为摄像机可以访问到的ip, 置空使用 media.ip + sdp-ip: + # [可选] zlm服务器的hook所使用的IP, 默认使用sip.ip + hook-ip: + # [必须修改] zlm服务器的http.port + http-port: 80 + # [可选] zlm服务器的http.sslport, 置空使用zlm配置文件配置 + http-ssl-port: + # [可选] zlm服务器的rtmp.port, 置空使用zlm配置文件配置 + rtmp-port: + # [可选] zlm服务器的rtmp.sslport, 置空使用zlm配置文件配置 + rtmp-ssl-port: + # [可选] zlm服务器的 rtp_proxy.port, 置空使用zlm配置文件配置 + rtp-proxy-port: + # [可选] zlm服务器的 rtsp.port, 置空使用zlm配置文件配置 + rtsp-port: + # [可选] zlm服务器的 rtsp.sslport, 置空使用zlm配置文件配置 + rtsp-ssl-port: + # [可选] 是否自动配置ZLM, 如果希望手动配置ZLM, 可以设为false, 不建议新接触的用户修改 + auto-config: true + # [可选] zlm服务器的hook.admin_params=secret + secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc + # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 + rtp: + # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 + enable: true + # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功 + port-range: 30000,30500 # 端口范围 + # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 + record-assist-port: 0 + +# [可选] 日志配置, 一般不需要改 +logging: + config: classpath:logback-spring-local.xml + +# [根据业务需求配置] +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: 18000 + # 上级点播等待超时时间,单位:毫秒 + platform-play-timeout: 60000 + # 是否开启接口鉴权 + interface-authentication: true + # 接口鉴权例外的接口, 即不进行接口鉴权的接口,尽量详细书写,尽量不用/**,至少两级目录 + interface-authentication-excludes: + - /api/v1/** + # 推流直播是否录制 + record-push-live: true + # 国标是否录制 + record-sip: true + # 是否将日志存储进数据库 + logInDatebase: true + # 使用推流状态作为推流通道状态 + 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 + # 是否开启sql日志 + sql-log: true + # 消息通道功能-缺少国标ID是否给所有上级发送消息 + send-to-platforms-when-id-lost: true + # 保持通道状态,不接受notify通道状态变化, 兼容海康平台发送错误消息 + refuse-channel-status-channel-form-notify: false + # 设置notify缓存队列最大长度,超过此长度的数据将返回486 BUSY_HERE,消息丢弃, 默认10000 + max-notify-count-queue: 10000 + # 设备/通道状态变化时发送消息 + device-status-notify: false + # 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式 + use-custom-ssrc-for-parent-invite: true + # 国标级联离线后多久重试一次注册 + register-again-after-time: 60 + # 国标续订方式,true为续订,每次注册在同一个会话里,false为重新注册,每次使用新的会话 + register-keep-int-dialog: false + # 跨域配置,配置你访问前端页面的地址即可, 可以配置多个 + allowed-origins: + - http://localhost:8008 + - http://192.168.1.3:8008 + +# 关闭在线文档(生产环境建议关闭) +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 26f5d084a..504c9c66a 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,99 +1,116 @@ spring: - # REDIS数据库配置 - redis: - # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1 - host: 127.0.0.1 - # [必须修改] 端口号 - port: 6379 - # [可选] 数据库 DB - database: 6 - # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 - password: - # [可选] 超时时间 - timeout: 10000 - # [不可用] jdbc数据库配置, 暂不支持 - datasource: - #name: eiot - #url: jdbc:mysql://127.0.0.1:3306/eiot?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true - #username: - #password: - #type: com.alibaba.druid.pool.DruidDataSource - #driver-class-name: com.mysql.jdbc.Driver - name: eiot - url: jdbc:sqlite::resource:wvp.sqlite - username: - password: - type: com.alibaba.druid.pool.DruidDataSource - driver-class-name: org.sqlite.JDBC - max-active: 1 - min-idle: 1 - -# [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 + # 设置接口超时时间 + mvc: + async: + request-timeout: 20000 + thymeleaf: + cache: false + # [可选]上传文件大小限制 + servlet: + multipart: + max-file-size: 10MB + max-request-size: 100MB + # REDIS数据库配置 + redis: + # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1 + host: 127.0.0.1 + # [必须修改] 端口号 + port: 6379 + # [可选] 数据库 DB + database: 7 + # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 + password: + # [可选] 超时时间 + timeout: 10000 + # mysql数据源 + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/test_gb-89wulian?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true + username: root + password: root + hikari: + connection-timeout: 20000 # 是客户端等待连接池连接的最大毫秒数 + initialSize: 10 # 连接池初始化连接数 + maximum-pool-size: 200 # 连接池最大连接数 + minimum-idle: 5 # 连接池最小空闲连接数 + idle-timeout: 300000 # 允许连接在连接池中空闲的最长时间(以毫秒为单位) + max-lifetime: 1200000 # 是池中连接关闭后的最长生命周期(以毫秒为单位) +#[可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 server: - port: 18080 + port: 18978 + # [可选] HTTPS配置, 默认不开启 + ssl: + # [可选] 是否开启HTTPS访问 + enabled: false + # [可选] 证书文件路径,放置在resource/目录下即可,修改xxx为文件名 + key-store: classpath:test.monitor.89iot.cn.jks + # [可选] 证书密码 + key-store-password: gpf64qmw + # [可选] 证书类型, 默认为jks,根据实际修改 + key-store-type: JKS # 作为28181服务器的配置 sip: - # [必须修改] 本机的IP, 必须是网卡上的IP - ip: 192.168.0.100 - # [可选] 28181服务监听的端口 - port: 5060 - # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) - # 后两位为行业编码,定义参照附录D.3 - # 3701020049标识山东济南历下区 信息行业接入 - # [可选] - domain: 4401020049 - # [可选] - id: 44010200492000000001 - # [可选] 默认设备认证密码,后续扩展使用设备单独密码 - password: admin123 + # [必须修改] 本机的IP,对应你的网卡,监听什么ip就是使用什么网卡, + # 如果要监听多张网卡,可以使用逗号分隔多个IP, 例如: 192.168.1.4,10.0.0.4 + # 如果不明白,就使用0.0.0.0,大部分情况都是可以的 + # 请不要使用127.0.0.1,任何包括localhost在内的域名都是不可以的。 + ip: 192.168.1.18 + # [可选] 28181服务监听的端口 + port: 8116 + # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) + # 后两位为行业编码,定义参照附录D.3 + # 3701020049标识山东济南历下区 信息行业接入 + # [可选] + domain: 4101050000 + # [可选] + id: 41010500002000000001 + # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验 + password: bajiuwulian1006 + # 是否存储alarm信息 + alarm: true -# 登陆的用户名密码 -auth: - # [可选] 用户名 - username: admin - # [可选] 密码, 默认为admin - password: 21232f297a57a5a743894a0e4a801fc3 - -#zlm服务器配置 +#zlm 默认服务器配置 media: - # [必须修改] zlm服务器的内网IP - ip: 192.168.0.100 - # [可选] zlm服务器的公网IP, 内网部署置空即可 - wanIp: - # [可选] zlm服务器的hook所使用的IP, 默认使用sip.ip - hookIp: - # [必须修改] zlm服务器的http.port - port: 80 - # [可选] 是否自动配置ZLM, 如果希望手动配置ZLM, 可以设为false, 不建议新接触的用户修改 - autoConfig: true - # [可选] zlm服务器的hook.admin_params=secret - secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc - # [可选] zlm服务器的general.streamNoneReaderDelayMS - streamNoneReaderDelayMS: 18000 # 无人观看多久自动关闭流, -1表示永不自动关闭,即 关闭按需拉流 - # [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true - autoApplyPlay: false - # [可选] 部分设备需要扩展SDP,需要打开此设置 - seniorSdp: false - # 启用udp多端口模式, 详细解释参考: https://github.com/xia-chu/ZLMediaKit/wiki/GB28181%E6%8E%A8%E6%B5%81 下的高阶使用 - rtp: - # [可选] 是否启用udp多端口模式, 开启后会在udpPortRange范围内选择端口用于媒体流传输 - enable: true - # [可选] 在此范围内选择端口用于媒体流传输, 不只是udp, 使用TCP被动传输模式时,也是从这个范围内选择端口 - udpPortRange: 30000,30500 # 端口范围 - + id: 89wulian-one + # [必须修改] zlm服务器的内网IP + ip: 192.168.1.18 + # [必须修改] zlm服务器的http.port + http-port: 80 + # [可选] 返回流地址时的ip,置空使用 media.ip + stream-ip: 192.168.1.18 + # [可选] wvp在国标信令中使用的ip,此ip为摄像机可以访问到的ip, 置空使用 media.ip + sdp-ip: 192.168.1.18 + # [可选] zlm服务器的hook所使用的IP, 默认使用sip.ip + hook-ip: 192.168.1.18 + # [可选] zlm服务器的http.sslport, 置空使用zlm配置文件配置 + http-ssl-port: 443 + # [可选] zlm服务器的hook.admin_params=secret + secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc + # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 + rtp: + # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 + enable: true + # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功 + port-range: 50000,50300 # 端口范围 + # [可选] 国标级联在此范围内选择端口发送媒体流, + send-port-range: 50000,50300 # 端口范围 + # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 + record-assist-port: 18081 +# [根据业务需求配置] +user-settings: + # 点播/录像回放 等待超时时间,单位:毫秒 + play-timeout: 180000 + # [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true + auto-apply-play: true + # 设备/通道状态变化时发送消息 + device-status-notify: true + # 跨域配置,配置你访问前端页面的地址即可, 可以配置多个 + allowed-origins: + - http://localhost:8080 + - http://127.0.0.1:8080 # [可选] 日志配置, 一般不需要改 logging: - file: - name: logs/wvp.log - max-history: 30 - max-size: 10MB - total-size-cap: 300MB - level: - com: - genersoft: - iot: debug -# [根据业务需求配置] -userSettings: - # 保存移动位置历史轨迹:true:保留历史数据,false:仅保留最后的位置(默认) - savePositionHistory: false \ No newline at end of file + config: classpath:logback-spring-local.xml + diff --git a/src/main/resources/application-docker.yml b/src/main/resources/application-docker.yml new file mode 100644 index 000000000..0e0c0ad35 --- /dev/null +++ b/src/main/resources/application-docker.yml @@ -0,0 +1,83 @@ +spring: + # 上传文件大小限制 + servlet: + multipart: + max-file-size: 10MB + max-request-size: 100MB + # REDIS数据库配置 + redis: + # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1 + host: ${REDIS_HOST:127.0.0.1} + # [必须修改] 端口号 + port: ${REDIS_PORT:6379} + # [可选] 数据库 DB + database: ${REDIS_DB:6} + # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 + password: ${REDIS_PWD:root} + # [可选] 超时时间 + timeout: 10000 + # [必选] jdbc数据库配置 + datasource: + # 使用mysql 打开23-28行注释, 删除29-36行 + name: wvp + url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&allowMultiQueries=true&useSSL=false&allowMultiQueries=true + username: root + password: root + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + +# [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 +server: + port: 18080 + +# 作为28181服务器的配置 +sip: + # [必须修改] 本机的IP,对应你的网卡,监听什么ip就是使用什么网卡, + # 如果要监听多张网卡,可以使用逗号分隔多个IP, 例如: 192.168.1.4,10.0.0.4 + # 如果不明白,就使用0.0.0.0,大部分情况都是可以的 + # 请不要使用127.0.0.1,任何包括localhost在内的域名都是不可以的。 + ip: ${WVP_HOST:127.0.0.1} + # [可选] 28181服务监听的端口 + port: ${WVP_PORT:5060} + # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) + # 后两位为行业编码,定义参照附录D.3 + # 3701020049标识山东济南历下区 信息行业接入 + # [可选] + domain: ${WVP_DOMAIN:4401020049} + # [可选] + id: ${WVP_ID:44010200492000000001} + # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验 + password: ${WVP_PWD:admin123} + +#zlm 默认服务器配置 +media: + # [必须修改] zlm服务器唯一id,用于触发hook时区别是哪台服务器,general.mediaServerId + id: + # [必须修改] zlm服务器的内网IP + ip: ${ZLM_HOST:127.0.0.1} + # [必须修改] zlm服务器的http.port + http-port: ${ZLM_PORT:80} + # [可选] 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 + sdp-ip: ${sip.ip} + stream-ip: ${sip.ip} +# [可选] 日志配置, 一般不需要改 +# [可选] 日志配置, 一般不需要改 +logging: + config: classpath:logback-spring-local.xml + +# [根据业务需求配置] +user-settings: + # 推流直播是否录制 + record-push-live: true + auto-apply-play: true diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index caf4dfcd6..3f478442e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,3 +1,5 @@ spring: + application: + name: wvp profiles: - active: dev \ No newline at end of file + active: local \ No newline at end of file diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt new file mode 100644 index 000000000..7a75e1c67 --- /dev/null +++ b/src/main/resources/banner.txt @@ -0,0 +1,7 @@ + ___ __ ___ ___ ________ ________ ________ ________ +|\ \ |\ \|\ \ / /|\ __ \ |\ __ \|\ __ \|\ __ \ +\ \ \ \ \ \ \ \ / / | \ \|\ \ ____________\ \ \|\ \ \ \|\ \ \ \|\ \ + \ \ \ __\ \ \ \ \/ / / \ \ ____\\____________\ \ ____\ \ _ _\ \ \\\ \ + \ \ \|\__\_\ \ \ / / \ \ \___\|____________|\ \ \___|\ \ \\ \\ \ \\\ \ + \ \____________\ \__/ / \ \__\ \ \__\ \ \__\\ _\\ \_______\ + \|____________|\|__|/ \|__| \|__| \|__|\|__|\|_______| diff --git a/src/main/resources/civilCode.csv b/src/main/resources/civilCode.csv new file mode 100644 index 000000000..7785924fa --- /dev/null +++ b/src/main/resources/civilCode.csv @@ -0,0 +1,3220 @@ +编号,名称,上级 +11,北京市, +110101,东城区,11 +110102,西城区,11 +110105,朝阳区,11 +110106,丰台区,11 +110107,石景山区,11 +110108,海淀区,11 +110109,门头沟区,11 +110111,房山区,11 +110112,通州区,11 +110113,顺义区,11 +110114,昌平区,11 +110115,大兴区,11 +110116,怀柔区,11 +110117,平谷区,11 +110118,密云区,11 +110119,延庆区,11 +12,天津市, +120101,和平区,12 +120102,河东区,12 +120103,河西区,12 +120104,南开区,12 +120105,河北区,12 +120106,红桥区,12 +120110,东丽区,12 +120111,西青区,12 +120112,津南区,12 +120113,北辰区,12 +120114,武清区,12 +120115,宝坻区,12 +120116,滨海新区,12 +120117,宁河区,12 +120118,静海区,12 +120119,蓟州区,12 +13,河北省, +1301,石家庄市,13 +130102,长安区,1301 +130104,桥西区,1301 +130105,新华区,1301 +130107,井陉矿区,1301 +130108,裕华区,1301 +130109,藁城区,1301 +130110,鹿泉区,1301 +130111,栾城区,1301 +130121,井陉县,1301 +130123,正定县,1301 +130125,行唐县,1301 +130126,灵寿县,1301 +130127,高邑县,1301 +130128,深泽县,1301 +130129,赞皇县,1301 +130130,无极县,1301 +130131,平山县,1301 +130132,元氏县,1301 +130133,赵县,1301 +130181,辛集市,1301 +130183,晋州市,1301 +130184,新乐市,1301 +1302,唐山市,13 +130202,路南区,1302 +130203,路北区,1302 +130204,古冶区,1302 +130205,开平区,1302 +130207,丰南区,1302 +130208,丰润区,1302 +130209,曹妃甸区,1302 +130224,滦南县,1302 +130225,乐亭县,1302 +130227,迁西县,1302 +130229,玉田县,1302 +130281,遵化市,1302 +130283,迁安市,1302 +130284,滦州市,1302 +1303,秦皇岛市,13 +130302,海港区,1303 +130303,山海关区,1303 +130304,北戴河区,1303 +130306,抚宁区,1303 +130321,青龙满族自治县,1303 +130322,昌黎县,1303 +130324,卢龙县,1303 +1304,邯郸市,13 +130402,邯山区,1304 +130403,丛台区,1304 +130404,复兴区,1304 +130406,峰峰矿区,1304 +130407,肥乡区,1304 +130408,永年区,1304 +130423,临漳县,1304 +130424,成安县,1304 +130425,大名县,1304 +130426,涉县,1304 +130427,磁县,1304 +130430,邱县,1304 +130431,鸡泽县,1304 +130432,广平县,1304 +130433,馆陶县,1304 +130434,魏县,1304 +130435,曲周县,1304 +130481,武安市,1304 +1305,邢台市,13 +130502,桥东区,1305 +130503,桥西区,1305 +130521,邢台县,1305 +130522,临城县,1305 +130523,内丘县,1305 +130524,柏乡县,1305 +130525,隆尧县,1305 +130526,任县,1305 +130527,南和县,1305 +130528,宁晋县,1305 +130529,巨鹿县,1305 +130530,新河县,1305 +130531,广宗县,1305 +130532,平乡县,1305 +130533,威县,1305 +130534,清河县,1305 +130535,临西县,1305 +130581,南宫市,1305 +130582,沙河市,1305 +1306,保定市,13 +130602,竞秀区,1306 +130606,莲池区,1306 +130607,满城区,1306 +130608,清苑区,1306 +130609,徐水区,1306 +130623,涞水县,1306 +130624,阜平县,1306 +130626,定兴县,1306 +130627,唐县,1306 +130628,高阳县,1306 +130629,容城县,1306 +130630,涞源县,1306 +130631,望都县,1306 +130632,安新县,1306 +130633,易县,1306 +130634,曲阳县,1306 +130635,蠡县,1306 +130636,顺平县,1306 +130637,博野县,1306 +130638,雄县,1306 +130681,涿州市,1306 +130682,定州市,1306 +130683,安国市,1306 +130684,高碑店市,1306 +1307,张家口市,13 +130702,桥东区,1307 +130703,桥西区,1307 +130705,宣化区,1307 +130706,下花园区,1307 +130708,万全区,1307 +130709,崇礼区,1307 +130722,张北县,1307 +130723,康保县,1307 +130724,沽源县,1307 +130725,尚义县,1307 +130726,蔚县,1307 +130727,阳原县,1307 +130728,怀安县,1307 +130730,怀来县,1307 +130731,涿鹿县,1307 +130732,赤城县,1307 +1308,承德市,13 +130802,双桥区,1308 +130803,双滦区,1308 +130804,鹰手营子矿区,1308 +130821,承德县,1308 +130822,兴隆县,1308 +130824,滦平县,1308 +130825,隆化县,1308 +130826,丰宁满族自治县,1308 +130827,宽城满族自治县,1308 +130828,围场满族蒙古族自治县,1308 +130881,平泉市,1308 +1309,沧州市,13 +130902,新华区,1309 +130903,运河区,1309 +130921,沧县,1309 +130922,青县,1309 +130923,东光县,1309 +130924,海兴县,1309 +130925,盐山县,1309 +130926,肃宁县,1309 +130927,南皮县,1309 +130928,吴桥县,1309 +130929,献县,1309 +130930,孟村回族自治县,1309 +130981,泊头市,1309 +130982,任丘市,1309 +130983,黄骅市,1309 +130984,河间市,1309 +1310,廊坊市,13 +131002,安次区,1310 +131003,广阳区,1310 +131022,固安县,1310 +131023,永清县,1310 +131024,香河县,1310 +131025,大城县,1310 +131026,文安县,1310 +131028,大厂回族自治县,1310 +131081,霸州市,1310 +131082,三河市,1310 +1311,衡水市,13 +131102,桃城区,1311 +131103,冀州区,1311 +131121,枣强县,1311 +131122,武邑县,1311 +131123,武强县,1311 +131124,饶阳县,1311 +131125,安平县,1311 +131126,故城县,1311 +131127,景县,1311 +131128,阜城县,1311 +131182,深州市,1311 +14,山西省, +1401,太原市,14 +140105,小店区,1401 +140106,迎泽区,1401 +140107,杏花岭区,1401 +140108,尖草坪区,1401 +140109,万柏林区,1401 +140110,晋源区,1401 +140121,清徐县,1401 +140122,阳曲县,1401 +140123,娄烦县,1401 +140181,古交市,1401 +1402,大同市,14 +140212,新荣区,1402 +140213,平城区,1402 +140214,云冈区,1402 +140215,云州区,1402 +140221,阳高县,1402 +140222,天镇县,1402 +140223,广灵县,1402 +140224,灵丘县,1402 +140225,浑源县,1402 +140226,左云县,1402 +1403,阳泉市,14 +140302,城区,1403 +140303,矿区,1403 +140311,郊区,1403 +140321,平定县,1403 +140322,盂县,1403 +1404,长治市,14 +140403,潞州区,1404 +140404,上党区,1404 +140405,屯留区,1404 +140406,潞城区,1404 +140423,襄垣县,1404 +140425,平顺县,1404 +140426,黎城县,1404 +140427,壶关县,1404 +140428,长子县,1404 +140429,武乡县,1404 +140430,沁县,1404 +140431,沁源县,1404 +1405,晋城市,14 +140502,城区,1405 +140521,沁水县,1405 +140522,阳城县,1405 +140524,陵川县,1405 +140525,泽州县,1405 +140581,高平市,1405 +1406,朔州市,14 +140602,朔城区,1406 +140603,平鲁区,1406 +140621,山阴县,1406 +140622,应县,1406 +140623,右玉县,1406 +140681,怀仁市,1406 +1407,晋中市,14 +140702,榆次区,1407 +140721,榆社县,1407 +140722,左权县,1407 +140723,和顺县,1407 +140724,昔阳县,1407 +140725,寿阳县,1407 +140726,太谷县,1407 +140727,祁县,1407 +140728,平遥县,1407 +140729,灵石县,1407 +140781,介休市,1407 +1408,运城市,14 +140802,盐湖区,1408 +140821,临猗县,1408 +140822,万荣县,1408 +140823,闻喜县,1408 +140824,稷山县,1408 +140825,新绛县,1408 +140826,绛县,1408 +140827,垣曲县,1408 +140828,夏县,1408 +140829,平陆县,1408 +140830,芮城县,1408 +140881,永济市,1408 +140882,河津市,1408 +1409,忻州市,14 +140902,忻府区,1409 +140921,定襄县,1409 +140922,五台县,1409 +140923,代县,1409 +140924,繁峙县,1409 +140925,宁武县,1409 +140926,静乐县,1409 +140927,神池县,1409 +140928,五寨县,1409 +140929,岢岚县,1409 +140930,河曲县,1409 +140931,保德县,1409 +140932,偏关县,1409 +140981,原平市,1409 +1410,临汾市,14 +141002,尧都区,1410 +141021,曲沃县,1410 +141022,翼城县,1410 +141023,襄汾县,1410 +141024,洪洞县,1410 +141025,古县,1410 +141026,安泽县,1410 +141027,浮山县,1410 +141028,吉县,1410 +141029,乡宁县,1410 +141030,大宁县,1410 +141031,隰县,1410 +141032,永和县,1410 +141033,蒲县,1410 +141034,汾西县,1410 +141081,侯马市,1410 +141082,霍州市,1410 +1411,吕梁市,14 +141102,离石区,1411 +141121,文水县,1411 +141122,交城县,1411 +141123,兴县,1411 +141124,临县,1411 +141125,柳林县,1411 +141126,石楼县,1411 +141127,岚县,1411 +141128,方山县,1411 +141129,中阳县,1411 +141130,交口县,1411 +141181,孝义市,1411 +141182,汾阳市,1411 +15,内蒙古自治区, +1501,呼和浩特市,15 +150102,新城区,1501 +150103,回民区,1501 +150104,玉泉区,1501 +150105,赛罕区,1501 +150121,土默特左旗,1501 +150122,托克托县,1501 +150123,和林格尔县,1501 +150124,清水河县,1501 +150125,武川县,1501 +1502,包头市,15 +150202,东河区,1502 +150203,昆都仑区,1502 +150204,青山区,1502 +150205,石拐区,1502 +150206,白云鄂博矿区,1502 +150207,九原区,1502 +150221,土默特右旗,1502 +150222,固阳县,1502 +150223,达尔罕茂明安联合旗,1502 +1503,乌海市,15 +150302,海勃湾区,1503 +150303,海南区,1503 +150304,乌达区,1503 +1504,赤峰市,15 +150402,红山区,1504 +150403,元宝山区,1504 +150404,松山区,1504 +150421,阿鲁科尔沁旗,1504 +150422,巴林左旗,1504 +150423,巴林右旗,1504 +150424,林西县,1504 +150425,克什克腾旗,1504 +150426,翁牛特旗,1504 +150428,喀喇沁旗,1504 +150429,宁城县,1504 +150430,敖汉旗,1504 +1505,通辽市,15 +150502,科尔沁区,1505 +150521,科尔沁左翼中旗,1505 +150522,科尔沁左翼后旗,1505 +150523,开鲁县,1505 +150524,库伦旗,1505 +150525,奈曼旗,1505 +150526,扎鲁特旗,1505 +150581,霍林郭勒市,1505 +1506,鄂尔多斯市,15 +150602,东胜区,1506 +150603,康巴什区,1506 +150621,达拉特旗,1506 +150622,准格尔旗,1506 +150623,鄂托克前旗,1506 +150624,鄂托克旗,1506 +150625,杭锦旗,1506 +150626,乌审旗,1506 +150627,伊金霍洛旗,1506 +1507,呼伦贝尔市,15 +150702,海拉尔区,1507 +150703,扎赉诺尔区,1507 +150721,阿荣旗,1507 +150722,莫力达瓦达斡尔族自治旗,1507 +150723,鄂伦春自治旗,1507 +150724,鄂温克族自治旗,1507 +150725,陈巴尔虎旗,1507 +150726,新巴尔虎左旗,1507 +150727,新巴尔虎右旗,1507 +150781,满洲里市,1507 +150782,牙克石市,1507 +150783,扎兰屯市,1507 +150784,额尔古纳市,1507 +150785,根河市,1507 +1508,巴彦淖尔市,15 +150802,临河区,1508 +150821,五原县,1508 +150822,磴口县,1508 +150823,乌拉特前旗,1508 +150824,乌拉特中旗,1508 +150825,乌拉特后旗,1508 +150826,杭锦后旗,1508 +1509,乌兰察布市,15 +150902,集宁区,1509 +150921,卓资县,1509 +150922,化德县,1509 +150923,商都县,1509 +150924,兴和县,1509 +150925,凉城县,1509 +150926,察哈尔右翼前旗,1509 +150927,察哈尔右翼中旗,1509 +150928,察哈尔右翼后旗,1509 +150929,四子王旗,1509 +150981,丰镇市,1509 +1522,兴安盟,15 +152201,乌兰浩特市,1522 +152202,阿尔山市,1522 +152221,科尔沁右翼前旗,1522 +152222,科尔沁右翼中旗,1522 +152223,扎赉特旗,1522 +152224,突泉县,1522 +1525,锡林郭勒盟,15 +152501,二连浩特市,1525 +152502,锡林浩特市,1525 +152522,阿巴嘎旗,1525 +152523,苏尼特左旗,1525 +152524,苏尼特右旗,1525 +152525,东乌珠穆沁旗,1525 +152526,西乌珠穆沁旗,1525 +152527,太仆寺旗,1525 +152528,镶黄旗,1525 +152529,正镶白旗,1525 +152530,正蓝旗,1525 +152531,多伦县,1525 +1529,阿拉善盟,15 +152921,阿拉善左旗,1529 +152922,阿拉善右旗,1529 +152923,额济纳旗,1529 +21,辽宁省, +2101,沈阳市,21 +210102,和平区,2101 +210103,沈河区,2101 +210104,大东区,2101 +210105,皇姑区,2101 +210106,铁西区,2101 +210111,苏家屯区,2101 +210112,浑南区,2101 +210113,沈北新区,2101 +210114,于洪区,2101 +210115,辽中区,2101 +210123,康平县,2101 +210124,法库县,2101 +210181,新民市,2101 +2102,大连市,21 +210202,中山区,2102 +210203,西岗区,2102 +210204,沙河口区,2102 +210211,甘井子区,2102 +210212,旅顺口区,2102 +210213,金州区,2102 +210214,普兰店区,2102 +210224,长海县,2102 +210281,瓦房店市,2102 +210283,庄河市,2102 +2103,鞍山市,21 +210302,铁东区,2103 +210303,铁西区,2103 +210304,立山区,2103 +210311,千山区,2103 +210321,台安县,2103 +210323,岫岩满族自治县,2103 +210381,海城市,2103 +2104,抚顺市,21 +210402,新抚区,2104 +210403,东洲区,2104 +210404,望花区,2104 +210411,顺城区,2104 +210421,抚顺县,2104 +210422,新宾满族自治县,2104 +210423,清原满族自治县,2104 +2105,本溪市,21 +210502,平山区,2105 +210503,溪湖区,2105 +210504,明山区,2105 +210505,南芬区,2105 +210521,本溪满族自治县,2105 +210522,桓仁满族自治县,2105 +2106,丹东市,21 +210602,元宝区,2106 +210603,振兴区,2106 +210604,振安区,2106 +210624,宽甸满族自治县,2106 +210681,东港市,2106 +210682,凤城市,2106 +2107,锦州市,21 +210702,古塔区,2107 +210703,凌河区,2107 +210711,太和区,2107 +210726,黑山县,2107 +210727,义县,2107 +210781,凌海市,2107 +210782,北镇市,2107 +2108,营口市,21 +210802,站前区,2108 +210803,西市区,2108 +210804,鲅鱼圈区,2108 +210811,老边区,2108 +210881,盖州市,2108 +210882,大石桥市,2108 +2109,阜新市,21 +210902,海州区,2109 +210903,新邱区,2109 +210904,太平区,2109 +210905,清河门区,2109 +210911,细河区,2109 +210921,阜新蒙古族自治县,2109 +210922,彰武县,2109 +2110,辽阳市,21 +211002,白塔区,2110 +211003,文圣区,2110 +211004,宏伟区,2110 +211005,弓长岭区,2110 +211011,太子河区,2110 +211021,辽阳县,2110 +211081,灯塔市,2110 +2111,盘锦市,21 +211102,双台子区,2111 +211103,兴隆台区,2111 +211104,大洼区,2111 +211122,盘山县,2111 +2112,铁岭市,21 +211202,银州区,2112 +211204,清河区,2112 +211221,铁岭县,2112 +211223,西丰县,2112 +211224,昌图县,2112 +211281,调兵山市,2112 +211282,开原市,2112 +2113,朝阳市,21 +211302,双塔区,2113 +211303,龙城区,2113 +211321,朝阳县,2113 +211322,建平县,2113 +211324,喀喇沁左翼蒙古族自治县,2113 +211381,北票市,2113 +211382,凌源市,2113 +2114,葫芦岛市,21 +211402,连山区,2114 +211403,龙港区,2114 +211404,南票区,2114 +211421,绥中县,2114 +211422,建昌县,2114 +211481,兴城市,2114 +22,吉林省, +2201,长春市,22 +220102,南关区,2201 +220103,宽城区,2201 +220104,朝阳区,2201 +220105,二道区,2201 +220106,绿园区,2201 +220112,双阳区,2201 +220113,九台区,2201 +220122,农安县,2201 +220182,榆树市,2201 +220183,德惠市,2201 +2202,吉林市,22 +220202,昌邑区,2202 +220203,龙潭区,2202 +220204,船营区,2202 +220211,丰满区,2202 +220221,永吉县,2202 +220281,蛟河市,2202 +220282,桦甸市,2202 +220283,舒兰市,2202 +220284,磐石市,2202 +2203,四平市,22 +220302,铁西区,2203 +220303,铁东区,2203 +220322,梨树县,2203 +220323,伊通满族自治县,2203 +220381,公主岭市,2203 +220382,双辽市,2203 +2204,辽源市,22 +220402,龙山区,2204 +220403,西安区,2204 +220421,东丰县,2204 +220422,东辽县,2204 +2205,通化市,22 +220502,东昌区,2205 +220503,二道江区,2205 +220521,通化县,2205 +220523,辉南县,2205 +220524,柳河县,2205 +220581,梅河口市,2205 +220582,集安市,2205 +2206,白山市,22 +220602,浑江区,2206 +220605,江源区,2206 +220621,抚松县,2206 +220622,靖宇县,2206 +220623,长白朝鲜族自治县,2206 +220681,临江市,2206 +2207,松原市,22 +220702,宁江区,2207 +220721,前郭尔罗斯蒙古族自治县,2207 +220722,长岭县,2207 +220723,乾安县,2207 +220781,扶余市,2207 +2208,白城市,22 +220802,洮北区,2208 +220821,镇赉县,2208 +220822,通榆县,2208 +220881,洮南市,2208 +220882,大安市,2208 +2224,延边朝鲜族自治州,22 +222401,延吉市,2224 +222402,图们市,2224 +222403,敦化市,2224 +222404,珲春市,2224 +222405,龙井市,2224 +222406,和龙市,2224 +222424,汪清县,2224 +222426,安图县,2224 +23,黑龙江省, +2301,哈尔滨市,23 +230102,道里区,2301 +230103,南岗区,2301 +230104,道外区,2301 +230108,平房区,2301 +230109,松北区,2301 +230110,香坊区,2301 +230111,呼兰区,2301 +230112,阿城区,2301 +230113,双城区,2301 +230123,依兰县,2301 +230124,方正县,2301 +230125,宾县,2301 +230126,巴彦县,2301 +230127,木兰县,2301 +230128,通河县,2301 +230129,延寿县,2301 +230183,尚志市,2301 +230184,五常市,2301 +2302,齐齐哈尔市,23 +230202,龙沙区,2302 +230203,建华区,2302 +230204,铁锋区,2302 +230205,昂昂溪区,2302 +230206,富拉尔基区,2302 +230207,碾子山区,2302 +230208,梅里斯达斡尔族区,2302 +230221,龙江县,2302 +230223,依安县,2302 +230224,泰来县,2302 +230225,甘南县,2302 +230227,富裕县,2302 +230229,克山县,2302 +230230,克东县,2302 +230231,拜泉县,2302 +230281,讷河市,2302 +2303,鸡西市,23 +230302,鸡冠区,2303 +230303,恒山区,2303 +230304,滴道区,2303 +230305,梨树区,2303 +230306,城子河区,2303 +230307,麻山区,2303 +230321,鸡东县,2303 +230381,虎林市,2303 +230382,密山市,2303 +2304,鹤岗市,23 +230402,向阳区,2304 +230403,工农区,2304 +230404,南山区,2304 +230405,兴安区,2304 +230406,东山区,2304 +230407,兴山区,2304 +230421,萝北县,2304 +230422,绥滨县,2304 +2305,双鸭山市,23 +230502,尖山区,2305 +230503,岭东区,2305 +230505,四方台区,2305 +230506,宝山区,2305 +230521,集贤县,2305 +230522,友谊县,2305 +230523,宝清县,2305 +230524,饶河县,2305 +2306,大庆市,23 +230602,萨尔图区,2306 +230603,龙凤区,2306 +230604,让胡路区,2306 +230605,红岗区,2306 +230606,大同区,2306 +230621,肇州县,2306 +230622,肇源县,2306 +230623,林甸县,2306 +230624,杜尔伯特蒙古族自治县,2306 +2307,伊春市,23 +230702,伊春区,2307 +230703,南岔区,2307 +230704,友好区,2307 +230705,西林区,2307 +230706,翠峦区,2307 +230707,新青区,2307 +230708,美溪区,2307 +230709,金山屯区,2307 +230710,五营区,2307 +230711,乌马河区,2307 +230712,汤旺河区,2307 +230713,带岭区,2307 +230714,乌伊岭区,2307 +230715,红星区,2307 +230716,上甘岭区,2307 +230722,嘉荫县,2307 +230781,铁力市,2307 +2308,佳木斯市,23 +230803,向阳区,2308 +230804,前进区,2308 +230805,东风区,2308 +230811,郊区,2308 +230822,桦南县,2308 +230826,桦川县,2308 +230828,汤原县,2308 +230881,同江市,2308 +230882,富锦市,2308 +230883,抚远市,2308 +2309,七台河市,23 +230902,新兴区,2309 +230903,桃山区,2309 +230904,茄子河区,2309 +230921,勃利县,2309 +2310,牡丹江市,23 +231002,东安区,2310 +231003,阳明区,2310 +231004,爱民区,2310 +231005,西安区,2310 +231025,林口县,2310 +231081,绥芬河市,2310 +231083,海林市,2310 +231084,宁安市,2310 +231085,穆棱市,2310 +231086,东宁市,2310 +2311,黑河市,23 +231102,爱辉区,2311 +231121,嫩江县,2311 +231123,逊克县,2311 +231124,孙吴县,2311 +231181,北安市,2311 +231182,五大连池市,2311 +2312,绥化市,23 +231202,北林区,2312 +231221,望奎县,2312 +231222,兰西县,2312 +231223,青冈县,2312 +231224,庆安县,2312 +231225,明水县,2312 +231226,绥棱县,2312 +231281,安达市,2312 +231282,肇东市,2312 +231283,海伦市,2312 +2327,大兴安岭地区,23 +232701,漠河市,2327 +232721,呼玛县,2327 +232722,塔河县,2327 +31,上海市, +310101,黄浦区,31 +310104,徐汇区,31 +310105,长宁区,31 +310106,静安区,31 +310107,普陀区,31 +310109,虹口区,31 +310110,杨浦区,31 +310112,闵行区,31 +310113,宝山区,31 +310114,嘉定区,31 +310115,浦东新区,31 +310116,金山区,31 +310117,松江区,31 +310118,青浦区,31 +310120,奉贤区,31 +310151,崇明区,31 +32,江苏省, +3201,南京市,32 +320102,玄武区,3201 +320104,秦淮区,3201 +320105,建邺区,3201 +320106,鼓楼区,3201 +320111,浦口区,3201 +320113,栖霞区,3201 +320114,雨花台区,3201 +320115,江宁区,3201 +320116,六合区,3201 +320117,溧水区,3201 +320118,高淳区,3201 +3202,无锡市,32 +320205,锡山区,3202 +320206,惠山区,3202 +320211,滨湖区,3202 +320213,梁溪区,3202 +320214,新吴区,3202 +320281,江阴市,3202 +320282,宜兴市,3202 +3203,徐州市,32 +320302,鼓楼区,3203 +320303,云龙区,3203 +320305,贾汪区,3203 +320311,泉山区,3203 +320312,铜山区,3203 +320321,丰县,3203 +320322,沛县,3203 +320324,睢宁县,3203 +320381,新沂市,3203 +320382,邳州市,3203 +3204,常州市,32 +320402,天宁区,3204 +320404,钟楼区,3204 +320411,新北区,3204 +320412,武进区,3204 +320413,金坛区,3204 +320481,溧阳市,3204 +3205,苏州市,32 +320505,虎丘区,3205 +320506,吴中区,3205 +320507,相城区,3205 +320508,姑苏区,3205 +320509,吴江区,3205 +320581,常熟市,3205 +320582,张家港市,3205 +320583,昆山市,3205 +320585,太仓市,3205 +3206,南通市,32 +320602,崇川区,3206 +320611,港闸区,3206 +320612,通州区,3206 +320623,如东县,3206 +320681,启东市,3206 +320682,如皋市,3206 +320684,海门市,3206 +320685,海安市,3206 +3207,连云港市,32 +320703,连云区,3207 +320706,海州区,3207 +320707,赣榆区,3207 +320722,东海县,3207 +320723,灌云县,3207 +320724,灌南县,3207 +3208,淮安市,32 +320803,淮安区,3208 +320804,淮阴区,3208 +320812,清江浦区,3208 +320813,洪泽区,3208 +320826,涟水县,3208 +320830,盱眙县,3208 +320831,金湖县,3208 +3209,盐城市,32 +320902,亭湖区,3209 +320903,盐都区,3209 +320904,大丰区,3209 +320921,响水县,3209 +320922,滨海县,3209 +320923,阜宁县,3209 +320924,射阳县,3209 +320925,建湖县,3209 +320981,东台市,3209 +3210,扬州市,32 +321002,广陵区,3210 +321003,邗江区,3210 +321012,江都区,3210 +321023,宝应县,3210 +321081,仪征市,3210 +321084,高邮市,3210 +3211,镇江市,32 +321102,京口区,3211 +321111,润州区,3211 +321112,丹徒区,3211 +321181,丹阳市,3211 +321182,扬中市,3211 +321183,句容市,3211 +3212,泰州市,32 +321202,海陵区,3212 +321203,高港区,3212 +321204,姜堰区,3212 +321281,兴化市,3212 +321282,靖江市,3212 +321283,泰兴市,3212 +3213,宿迁市,32 +321302,宿城区,3213 +321311,宿豫区,3213 +321322,沭阳县,3213 +321323,泗阳县,3213 +321324,泗洪县,3213 +33,浙江省, +3301,杭州市,33 +330102,上城区,3301 +330103,下城区,3301 +330104,江干区,3301 +330105,拱墅区,3301 +330106,西湖区,3301 +330108,滨江区,3301 +330109,萧山区,3301 +330110,余杭区,3301 +330111,富阳区,3301 +330112,临安区,3301 +330122,桐庐县,3301 +330127,淳安县,3301 +330182,建德市,3301 +3302,宁波市,33 +330203,海曙区,3302 +330205,江北区,3302 +330206,北仑区,3302 +330211,镇海区,3302 +330212,鄞州区,3302 +330213,奉化区,3302 +330225,象山县,3302 +330226,宁海县,3302 +330281,余姚市,3302 +330282,慈溪市,3302 +3303,温州市,33 +330302,鹿城区,3303 +330303,龙湾区,3303 +330304,瓯海区,3303 +330305,洞头区,3303 +330324,永嘉县,3303 +330326,平阳县,3303 +330327,苍南县,3303 +330328,文成县,3303 +330329,泰顺县,3303 +330381,瑞安市,3303 +330382,乐清市,3303 +3304,嘉兴市,33 +330402,南湖区,3304 +330411,秀洲区,3304 +330421,嘉善县,3304 +330424,海盐县,3304 +330481,海宁市,3304 +330482,平湖市,3304 +330483,桐乡市,3304 +3305,湖州市,33 +330502,吴兴区,3305 +330503,南浔区,3305 +330521,德清县,3305 +330522,长兴县,3305 +330523,安吉县,3305 +3306,绍兴市,33 +330602,越城区,3306 +330603,柯桥区,3306 +330604,上虞区,3306 +330624,新昌县,3306 +330681,诸暨市,3306 +330683,嵊州市,3306 +3307,金华市,33 +330702,婺城区,3307 +330703,金东区,3307 +330723,武义县,3307 +330726,浦江县,3307 +330727,磐安县,3307 +330781,兰溪市,3307 +330782,义乌市,3307 +330783,东阳市,3307 +330784,永康市,3307 +3308,衢州市,33 +330802,柯城区,3308 +330803,衢江区,3308 +330822,常山县,3308 +330824,开化县,3308 +330825,龙游县,3308 +330881,江山市,3308 +3309,舟山市,33 +330902,定海区,3309 +330903,普陀区,3309 +330921,岱山县,3309 +330922,嵊泗县,3309 +3310,台州市,33 +331002,椒江区,3310 +331003,黄岩区,3310 +331004,路桥区,3310 +331022,三门县,3310 +331023,天台县,3310 +331024,仙居县,3310 +331081,温岭市,3310 +331082,临海市,3310 +331083,玉环市,3310 +3311,丽水市,33 +331102,莲都区,3311 +331121,青田县,3311 +331122,缙云县,3311 +331123,遂昌县,3311 +331124,松阳县,3311 +331125,云和县,3311 +331126,庆元县,3311 +331127,景宁畲族自治县,3311 +331181,龙泉市,3311 +34,安徽省, +3401,合肥市,34 +340102,瑶海区,3401 +340103,庐阳区,3401 +340104,蜀山区,3401 +340111,包河区,3401 +340121,长丰县,3401 +340122,肥东县,3401 +340123,肥西县,3401 +340124,庐江县,3401 +340181,巢湖市,3401 +3402,芜湖市,34 +340202,镜湖区,3402 +340203,弋江区,3402 +340207,鸠江区,3402 +340208,三山区,3402 +340221,芜湖县,3402 +340222,繁昌县,3402 +340223,南陵县,3402 +340225,无为县,3402 +3403,蚌埠市,34 +340302,龙子湖区,3403 +340303,蚌山区,3403 +340304,禹会区,3403 +340311,淮上区,3403 +340321,怀远县,3403 +340322,五河县,3403 +340323,固镇县,3403 +3404,淮南市,34 +340402,大通区,3404 +340403,田家庵区,3404 +340404,谢家集区,3404 +340405,八公山区,3404 +340406,潘集区,3404 +340421,凤台县,3404 +340422,寿县,3404 +3405,马鞍山市,34 +340503,花山区,3405 +340504,雨山区,3405 +340506,博望区,3405 +340521,当涂县,3405 +340522,含山县,3405 +340523,和县,3405 +3406,淮北市,34 +340602,杜集区,3406 +340603,相山区,3406 +340604,烈山区,3406 +340621,濉溪县,3406 +3407,铜陵市,34 +340705,铜官区,3407 +340706,义安区,3407 +340711,郊区,3407 +340722,枞阳县,3407 +3408,安庆市,34 +340802,迎江区,3408 +340803,大观区,3408 +340811,宜秀区,3408 +340822,怀宁县,3408 +340825,太湖县,3408 +340826,宿松县,3408 +340827,望江县,3408 +340828,岳西县,3408 +340881,桐城市,3408 +340882,潜山市,3408 +3410,黄山市,34 +341002,屯溪区,3410 +341003,黄山区,3410 +341004,徽州区,3410 +341021,歙县,3410 +341022,休宁县,3410 +341023,黟县,3410 +341024,祁门县,3410 +3411,滁州市,34 +341102,琅琊区,3411 +341103,南谯区,3411 +341122,来安县,3411 +341124,全椒县,3411 +341125,定远县,3411 +341126,凤阳县,3411 +341181,天长市,3411 +341182,明光市,3411 +3412,阜阳市,34 +341202,颍州区,3412 +341203,颍东区,3412 +341204,颍泉区,3412 +341221,临泉县,3412 +341222,太和县,3412 +341225,阜南县,3412 +341226,颍上县,3412 +341282,界首市,3412 +3413,宿州市,34 +341302,埇桥区,3413 +341321,砀山县,3413 +341322,萧县,3413 +341323,灵璧县,3413 +341324,泗县,3413 +3415,六安市,34 +341502,金安区,3415 +341503,裕安区,3415 +341504,叶集区,3415 +341522,霍邱县,3415 +341523,舒城县,3415 +341524,金寨县,3415 +341525,霍山县,3415 +3416,亳州市,34 +341602,谯城区,3416 +341621,涡阳县,3416 +341622,蒙城县,3416 +341623,利辛县,3416 +3417,池州市,34 +341702,贵池区,3417 +341721,东至县,3417 +341722,石台县,3417 +341723,青阳县,3417 +3418,宣城市,34 +341802,宣州区,3418 +341821,郎溪县,3418 +341822,广德县,3418 +341823,泾县,3418 +341824,绩溪县,3418 +341825,旌德县,3418 +341881,宁国市,3418 +35,福建省, +3501,福州市,35 +350102,鼓楼区,3501 +350103,台江区,3501 +350104,仓山区,3501 +350105,马尾区,3501 +350111,晋安区,3501 +350112,长乐区,3501 +350121,闽侯县,3501 +350122,连江县,3501 +350123,罗源县,3501 +350124,闽清县,3501 +350125,永泰县,3501 +350128,平潭县,3501 +350181,福清市,3501 +3502,厦门市,35 +350203,思明区,3502 +350205,海沧区,3502 +350206,湖里区,3502 +350211,集美区,3502 +350212,同安区,3502 +350213,翔安区,3502 +3503,莆田市,35 +350302,城厢区,3503 +350303,涵江区,3503 +350304,荔城区,3503 +350305,秀屿区,3503 +350322,仙游县,3503 +3504,三明市,35 +350402,梅列区,3504 +350403,三元区,3504 +350421,明溪县,3504 +350423,清流县,3504 +350424,宁化县,3504 +350425,大田县,3504 +350426,尤溪县,3504 +350427,沙县,3504 +350428,将乐县,3504 +350429,泰宁县,3504 +350430,建宁县,3504 +350481,永安市,3504 +3505,泉州市,35 +350502,鲤城区,3505 +350503,丰泽区,3505 +350504,洛江区,3505 +350505,泉港区,3505 +350521,惠安县,3505 +350524,安溪县,3505 +350525,永春县,3505 +350526,德化县,3505 +350527,金门县,3505 +350581,石狮市,3505 +350582,晋江市,3505 +350583,南安市,3505 +3506,漳州市,35 +350602,芗城区,3506 +350603,龙文区,3506 +350622,云霄县,3506 +350623,漳浦县,3506 +350624,诏安县,3506 +350625,长泰县,3506 +350626,东山县,3506 +350627,南靖县,3506 +350628,平和县,3506 +350629,华安县,3506 +350681,龙海市,3506 +3507,南平市,35 +350702,延平区,3507 +350703,建阳区,3507 +350721,顺昌县,3507 +350722,浦城县,3507 +350723,光泽县,3507 +350724,松溪县,3507 +350725,政和县,3507 +350781,邵武市,3507 +350782,武夷山市,3507 +350783,建瓯市,3507 +3508,龙岩市,35 +350802,新罗区,3508 +350803,永定区,3508 +350821,长汀县,3508 +350823,上杭县,3508 +350824,武平县,3508 +350825,连城县,3508 +350881,漳平市,3508 +3509,宁德市,35 +350902,蕉城区,3509 +350921,霞浦县,3509 +350922,古田县,3509 +350923,屏南县,3509 +350924,寿宁县,3509 +350925,周宁县,3509 +350926,柘荣县,3509 +350981,福安市,3509 +350982,福鼎市,3509 +36,江西省, +3601,南昌市,36 +360102,东湖区,3601 +360103,西湖区,3601 +360104,青云谱区,3601 +360105,湾里区,3601 +360111,青山湖区,3601 +360112,新建区,3601 +360121,南昌县,3601 +360123,安义县,3601 +360124,进贤县,3601 +3602,景德镇市,36 +360202,昌江区,3602 +360203,珠山区,3602 +360222,浮梁县,3602 +360281,乐平市,3602 +3603,萍乡市,36 +360302,安源区,3603 +360313,湘东区,3603 +360321,莲花县,3603 +360322,上栗县,3603 +360323,芦溪县,3603 +3604,九江市,36 +360402,濂溪区,3604 +360403,浔阳区,3604 +360404,柴桑区,3604 +360423,武宁县,3604 +360424,修水县,3604 +360425,永修县,3604 +360426,德安县,3604 +360428,都昌县,3604 +360429,湖口县,3604 +360430,彭泽县,3604 +360481,瑞昌市,3604 +360482,共青城市,3604 +360483,庐山市,3604 +3605,新余市,36 +360502,渝水区,3605 +360521,分宜县,3605 +3606,鹰潭市,36 +360602,月湖区,3606 +360603,余江区,3606 +360681,贵溪市,3606 +3607,赣州市,36 +360702,章贡区,3607 +360703,南康区,3607 +360704,赣县区,3607 +360722,信丰县,3607 +360723,大余县,3607 +360724,上犹县,3607 +360725,崇义县,3607 +360726,安远县,3607 +360727,龙南县,3607 +360728,定南县,3607 +360729,全南县,3607 +360730,宁都县,3607 +360731,于都县,3607 +360732,兴国县,3607 +360733,会昌县,3607 +360734,寻乌县,3607 +360735,石城县,3607 +360781,瑞金市,3607 +3608,吉安市,36 +360802,吉州区,3608 +360803,青原区,3608 +360821,吉安县,3608 +360822,吉水县,3608 +360823,峡江县,3608 +360824,新干县,3608 +360825,永丰县,3608 +360826,泰和县,3608 +360827,遂川县,3608 +360828,万安县,3608 +360829,安福县,3608 +360830,永新县,3608 +360881,井冈山市,3608 +3609,宜春市,36 +360902,袁州区,3609 +360921,奉新县,3609 +360922,万载县,3609 +360923,上高县,3609 +360924,宜丰县,3609 +360925,靖安县,3609 +360926,铜鼓县,3609 +360981,丰城市,3609 +360982,樟树市,3609 +360983,高安市,3609 +3610,抚州市,36 +361002,临川区,3610 +361003,东乡区,3610 +361021,南城县,3610 +361022,黎川县,3610 +361023,南丰县,3610 +361024,崇仁县,3610 +361025,乐安县,3610 +361026,宜黄县,3610 +361027,金溪县,3610 +361028,资溪县,3610 +361030,广昌县,3610 +3611,上饶市,36 +361102,信州区,3611 +361103,广丰区,3611 +361121,上饶县,3611 +361123,玉山县,3611 +361124,铅山县,3611 +361125,横峰县,3611 +361126,弋阳县,3611 +361127,余干县,3611 +361128,鄱阳县,3611 +361129,万年县,3611 +361130,婺源县,3611 +361181,德兴市,3611 +37,山东省, +3701,济南市,37 +370102,历下区,3701 +370103,市中区,3701 +370104,槐荫区,3701 +370105,天桥区,3701 +370112,历城区,3701 +370113,长清区,3701 +370114,章丘区,3701 +370115,济阳区,3701 +370124,平阴县,3701 +370126,商河县,3701 +3702,青岛市,37 +370202,市南区,3702 +370203,市北区,3702 +370211,黄岛区,3702 +370212,崂山区,3702 +370213,李沧区,3702 +370214,城阳区,3702 +370215,即墨区,3702 +370281,胶州市,3702 +370283,平度市,3702 +370285,莱西市,3702 +3703,淄博市,37 +370302,淄川区,3703 +370303,张店区,3703 +370304,博山区,3703 +370305,临淄区,3703 +370306,周村区,3703 +370321,桓台县,3703 +370322,高青县,3703 +370323,沂源县,3703 +3704,枣庄市,37 +370402,市中区,3704 +370403,薛城区,3704 +370404,峄城区,3704 +370405,台儿庄区,3704 +370406,山亭区,3704 +370481,滕州市,3704 +3705,东营市,37 +370502,东营区,3705 +370503,河口区,3705 +370505,垦利区,3705 +370522,利津县,3705 +370523,广饶县,3705 +3706,烟台市,37 +370602,芝罘区,3706 +370611,福山区,3706 +370612,牟平区,3706 +370613,莱山区,3706 +370634,长岛县,3706 +370681,龙口市,3706 +370682,莱阳市,3706 +370683,莱州市,3706 +370684,蓬莱市,3706 +370685,招远市,3706 +370686,栖霞市,3706 +370687,海阳市,3706 +3707,潍坊市,37 +370702,潍城区,3707 +370703,寒亭区,3707 +370704,坊子区,3707 +370705,奎文区,3707 +370724,临朐县,3707 +370725,昌乐县,3707 +370781,青州市,3707 +370782,诸城市,3707 +370783,寿光市,3707 +370784,安丘市,3707 +370785,高密市,3707 +370786,昌邑市,3707 +3708,济宁市,37 +370811,任城区,3708 +370812,兖州区,3708 +370826,微山县,3708 +370827,鱼台县,3708 +370828,金乡县,3708 +370829,嘉祥县,3708 +370830,汶上县,3708 +370831,泗水县,3708 +370832,梁山县,3708 +370881,曲阜市,3708 +370883,邹城市,3708 +3709,泰安市,37 +370902,泰山区,3709 +370911,岱岳区,3709 +370921,宁阳县,3709 +370923,东平县,3709 +370982,新泰市,3709 +370983,肥城市,3709 +3710,威海市,37 +371002,环翠区,3710 +371003,文登区,3710 +371082,荣成市,3710 +371083,乳山市,3710 +3711,日照市,37 +371102,东港区,3711 +371103,岚山区,3711 +371121,五莲县,3711 +371122,莒县,3711 +3712,莱芜市,37 +371202,莱城区,3712 +371203,钢城区,3712 +3713,临沂市,37 +371302,兰山区,3713 +371311,罗庄区,3713 +371312,河东区,3713 +371321,沂南县,3713 +371322,郯城县,3713 +371323,沂水县,3713 +371324,兰陵县,3713 +371325,费县,3713 +371326,平邑县,3713 +371327,莒南县,3713 +371328,蒙阴县,3713 +371329,临沭县,3713 +3714,德州市,37 +371402,德城区,3714 +371403,陵城区,3714 +371422,宁津县,3714 +371423,庆云县,3714 +371424,临邑县,3714 +371425,齐河县,3714 +371426,平原县,3714 +371427,夏津县,3714 +371428,武城县,3714 +371481,乐陵市,3714 +371482,禹城市,3714 +3715,聊城市,37 +371502,东昌府区,3715 +371521,阳谷县,3715 +371522,莘县,3715 +371523,茌平县,3715 +371524,东阿县,3715 +371525,冠县,3715 +371526,高唐县,3715 +371581,临清市,3715 +3716,滨州市,37 +371602,滨城区,3716 +371603,沾化区,3716 +371621,惠民县,3716 +371622,阳信县,3716 +371623,无棣县,3716 +371625,博兴县,3716 +371681,邹平市,3716 +3717,菏泽市,37 +371702,牡丹区,3717 +371703,定陶区,3717 +371721,曹县,3717 +371722,单县,3717 +371723,成武县,3717 +371724,巨野县,3717 +371725,郓城县,3717 +371726,鄄城县,3717 +371728,东明县,3717 +41,河南省, +4101,郑州市,41 +410102,中原区,4101 +410103,二七区,4101 +410104,管城回族区,4101 +410105,金水区,4101 +410106,上街区,4101 +410108,惠济区,4101 +410122,中牟县,4101 +410181,巩义市,4101 +410182,荥阳市,4101 +410183,新密市,4101 +410184,新郑市,4101 +410185,登封市,4101 +4102,开封市,41 +410202,龙亭区,4102 +410203,顺河回族区,4102 +410204,鼓楼区,4102 +410205,禹王台区,4102 +410212,祥符区,4102 +410221,杞县,4102 +410222,通许县,4102 +410223,尉氏县,4102 +410225,兰考县,4102 +4103,洛阳市,41 +410302,老城区,4103 +410303,西工区,4103 +410304,瀍河回族区,4103 +410305,涧西区,4103 +410306,吉利区,4103 +410311,洛龙区,4103 +410322,孟津县,4103 +410323,新安县,4103 +410324,栾川县,4103 +410325,嵩县,4103 +410326,汝阳县,4103 +410327,宜阳县,4103 +410328,洛宁县,4103 +410329,伊川县,4103 +410381,偃师市,4103 +4104,平顶山市,41 +410402,新华区,4104 +410403,卫东区,4104 +410404,石龙区,4104 +410411,湛河区,4104 +410421,宝丰县,4104 +410422,叶县,4104 +410423,鲁山县,4104 +410425,郏县,4104 +410481,舞钢市,4104 +410482,汝州市,4104 +4105,安阳市,41 +410502,文峰区,4105 +410503,北关区,4105 +410505,殷都区,4105 +410506,龙安区,4105 +410522,安阳县,4105 +410523,汤阴县,4105 +410526,滑县,4105 +410527,内黄县,4105 +410581,林州市,4105 +4106,鹤壁市,41 +410602,鹤山区,4106 +410603,山城区,4106 +410611,淇滨区,4106 +410621,浚县,4106 +410622,淇县,4106 +4107,新乡市,41 +410702,红旗区,4107 +410703,卫滨区,4107 +410704,凤泉区,4107 +410711,牧野区,4107 +410721,新乡县,4107 +410724,获嘉县,4107 +410725,原阳县,4107 +410726,延津县,4107 +410727,封丘县,4107 +410728,长垣县,4107 +410781,卫辉市,4107 +410782,辉县市,4107 +4108,焦作市,41 +410802,解放区,4108 +410803,中站区,4108 +410804,马村区,4108 +410811,山阳区,4108 +410821,修武县,4108 +410822,博爱县,4108 +410823,武陟县,4108 +410825,温县,4108 +410882,沁阳市,4108 +410883,孟州市,4108 +4109,濮阳市,41 +410902,华龙区,4109 +410922,清丰县,4109 +410923,南乐县,4109 +410926,范县,4109 +410927,台前县,4109 +410928,濮阳县,4109 +4110,许昌市,41 +411002,魏都区,4110 +411003,建安区,4110 +411024,鄢陵县,4110 +411025,襄城县,4110 +411081,禹州市,4110 +411082,长葛市,4110 +4111,漯河市,41 +411102,源汇区,4111 +411103,郾城区,4111 +411104,召陵区,4111 +411121,舞阳县,4111 +411122,临颍县,4111 +4112,三门峡市,41 +411202,湖滨区,4112 +411203,陕州区,4112 +411221,渑池县,4112 +411224,卢氏县,4112 +411281,义马市,4112 +411282,灵宝市,4112 +4113,南阳市,41 +411302,宛城区,4113 +411303,卧龙区,4113 +411321,南召县,4113 +411322,方城县,4113 +411323,西峡县,4113 +411324,镇平县,4113 +411325,内乡县,4113 +411326,淅川县,4113 +411327,社旗县,4113 +411328,唐河县,4113 +411329,新野县,4113 +411330,桐柏县,4113 +411381,邓州市,4113 +4114,商丘市,41 +411402,梁园区,4114 +411403,睢阳区,4114 +411421,民权县,4114 +411422,睢县,4114 +411423,宁陵县,4114 +411424,柘城县,4114 +411425,虞城县,4114 +411426,夏邑县,4114 +411481,永城市,4114 +4115,信阳市,41 +411502,浉河区,4115 +411503,平桥区,4115 +411521,罗山县,4115 +411522,光山县,4115 +411523,新县,4115 +411524,商城县,4115 +411525,固始县,4115 +411526,潢川县,4115 +411527,淮滨县,4115 +411528,息县,4115 +4116,周口市,41 +411602,川汇区,4116 +411621,扶沟县,4116 +411622,西华县,4116 +411623,商水县,4116 +411624,沈丘县,4116 +411625,郸城县,4116 +411626,淮阳县,4116 +411627,太康县,4116 +411628,鹿邑县,4116 +411681,项城市,4116 +4117,驻马店市,41 +411702,驿城区,4117 +411721,西平县,4117 +411722,上蔡县,4117 +411723,平舆县,4117 +411724,正阳县,4117 +411725,确山县,4117 +411726,泌阳县,4117 +411727,汝南县,4117 +411728,遂平县,4117 +411729,新蔡县,4117 +419001,济源市,41 +42,湖北省, +4201,武汉市,42 +420102,江岸区,4201 +420103,江汉区,4201 +420104,硚口区,4201 +420105,汉阳区,4201 +420106,武昌区,4201 +420107,青山区,4201 +420111,洪山区,4201 +420112,东西湖区,4201 +420113,汉南区,4201 +420114,蔡甸区,4201 +420115,江夏区,4201 +420116,黄陂区,4201 +420117,新洲区,4201 +4202,黄石市,42 +420202,黄石港区,4202 +420203,西塞山区,4202 +420204,下陆区,4202 +420205,铁山区,4202 +420222,阳新县,4202 +420281,大冶市,4202 +4203,十堰市,42 +420302,茅箭区,4203 +420303,张湾区,4203 +420304,郧阳区,4203 +420322,郧西县,4203 +420323,竹山县,4203 +420324,竹溪县,4203 +420325,房县,4203 +420381,丹江口市,4203 +4205,宜昌市,42 +420502,西陵区,4205 +420503,伍家岗区,4205 +420504,点军区,4205 +420505,猇亭区,4205 +420506,夷陵区,4205 +420525,远安县,4205 +420526,兴山县,4205 +420527,秭归县,4205 +420528,长阳土家族自治县,4205 +420529,五峰土家族自治县,4205 +420581,宜都市,4205 +420582,当阳市,4205 +420583,枝江市,4205 +4206,襄阳市,42 +420602,襄城区,4206 +420606,樊城区,4206 +420607,襄州区,4206 +420624,南漳县,4206 +420625,谷城县,4206 +420626,保康县,4206 +420682,老河口市,4206 +420683,枣阳市,4206 +420684,宜城市,4206 +4207,鄂州市,42 +420702,梁子湖区,4207 +420703,华容区,4207 +420704,鄂城区,4207 +4208,荆门市,42 +420802,东宝区,4208 +420804,掇刀区,4208 +420822,沙洋县,4208 +420881,钟祥市,4208 +420882,京山市,4208 +4209,孝感市,42 +420902,孝南区,4209 +420921,孝昌县,4209 +420922,大悟县,4209 +420923,云梦县,4209 +420981,应城市,4209 +420982,安陆市,4209 +420984,汉川市,4209 +4210,荆州市,42 +421002,沙市区,4210 +421003,荆州区,4210 +421022,公安县,4210 +421023,监利县,4210 +421024,江陵县,4210 +421081,石首市,4210 +421083,洪湖市,4210 +421087,松滋市,4210 +4211,黄冈市,42 +421102,黄州区,4211 +421121,团风县,4211 +421122,红安县,4211 +421123,罗田县,4211 +421124,英山县,4211 +421125,浠水县,4211 +421126,蕲春县,4211 +421127,黄梅县,4211 +421181,麻城市,4211 +421182,武穴市,4211 +4212,咸宁市,42 +421202,咸安区,4212 +421221,嘉鱼县,4212 +421222,通城县,4212 +421223,崇阳县,4212 +421224,通山县,4212 +421281,赤壁市,4212 +4213,随州市,42 +421303,曾都区,4213 +421321,随县,4213 +421381,广水市,4213 +4228,恩施土家族苗族自治州,42 +422801,恩施市,4228 +422802,利川市,4228 +422822,建始县,4228 +422823,巴东县,4228 +422825,宣恩县,4228 +422826,咸丰县,4228 +422827,来凤县,4228 +422828,鹤峰县,4228 +429004,仙桃市,42 +429005,潜江市,42 +429006,天门市,42 +429021,神农架林区,42 +43,湖南省, +4301,长沙市,43 +430102,芙蓉区,4301 +430103,天心区,4301 +430104,岳麓区,4301 +430105,开福区,4301 +430111,雨花区,4301 +430112,望城区,4301 +430121,长沙县,4301 +430181,浏阳市,4301 +430182,宁乡市,4301 +4302,株洲市,43 +430202,荷塘区,4302 +430203,芦淞区,4302 +430204,石峰区,4302 +430211,天元区,4302 +430212,渌口区,4302 +430223,攸县,4302 +430224,茶陵县,4302 +430225,炎陵县,4302 +430281,醴陵市,4302 +4303,湘潭市,43 +430302,雨湖区,4303 +430304,岳塘区,4303 +430321,湘潭县,4303 +430381,湘乡市,4303 +430382,韶山市,4303 +4304,衡阳市,43 +430405,珠晖区,4304 +430406,雁峰区,4304 +430407,石鼓区,4304 +430408,蒸湘区,4304 +430412,南岳区,4304 +430421,衡阳县,4304 +430422,衡南县,4304 +430423,衡山县,4304 +430424,衡东县,4304 +430426,祁东县,4304 +430481,耒阳市,4304 +430482,常宁市,4304 +4305,邵阳市,43 +430502,双清区,4305 +430503,大祥区,4305 +430511,北塔区,4305 +430521,邵东县,4305 +430522,新邵县,4305 +430523,邵阳县,4305 +430524,隆回县,4305 +430525,洞口县,4305 +430527,绥宁县,4305 +430528,新宁县,4305 +430529,城步苗族自治县,4305 +430581,武冈市,4305 +4306,岳阳市,43 +430602,岳阳楼区,4306 +430603,云溪区,4306 +430611,君山区,4306 +430621,岳阳县,4306 +430623,华容县,4306 +430624,湘阴县,4306 +430626,平江县,4306 +430681,汨罗市,4306 +430682,临湘市,4306 +4307,常德市,43 +430702,武陵区,4307 +430703,鼎城区,4307 +430721,安乡县,4307 +430722,汉寿县,4307 +430723,澧县,4307 +430724,临澧县,4307 +430725,桃源县,4307 +430726,石门县,4307 +430781,津市市,4307 +4308,张家界市,43 +430802,永定区,4308 +430811,武陵源区,4308 +430821,慈利县,4308 +430822,桑植县,4308 +4309,益阳市,43 +430902,资阳区,4309 +430903,赫山区,4309 +430921,南县,4309 +430922,桃江县,4309 +430923,安化县,4309 +430981,沅江市,4309 +4310,郴州市,43 +431002,北湖区,4310 +431003,苏仙区,4310 +431021,桂阳县,4310 +431022,宜章县,4310 +431023,永兴县,4310 +431024,嘉禾县,4310 +431025,临武县,4310 +431026,汝城县,4310 +431027,桂东县,4310 +431028,安仁县,4310 +431081,资兴市,4310 +4311,永州市,43 +431102,零陵区,4311 +431103,冷水滩区,4311 +431121,祁阳县,4311 +431122,东安县,4311 +431123,双牌县,4311 +431124,道县,4311 +431125,江永县,4311 +431126,宁远县,4311 +431127,蓝山县,4311 +431128,新田县,4311 +431129,江华瑶族自治县,4311 +4312,怀化市,43 +431202,鹤城区,4312 +431221,中方县,4312 +431222,沅陵县,4312 +431223,辰溪县,4312 +431224,溆浦县,4312 +431225,会同县,4312 +431226,麻阳苗族自治县,4312 +431227,新晃侗族自治县,4312 +431228,芷江侗族自治县,4312 +431229,靖州苗族侗族自治县,4312 +431230,通道侗族自治县,4312 +431281,洪江市,4312 +4313,娄底市,43 +431302,娄星区,4313 +431321,双峰县,4313 +431322,新化县,4313 +431381,冷水江市,4313 +431382,涟源市,4313 +4331,湘西土家族苗族自治州,43 +433101,吉首市,4331 +433122,泸溪县,4331 +433123,凤凰县,4331 +433124,花垣县,4331 +433125,保靖县,4331 +433126,古丈县,4331 +433127,永顺县,4331 +433130,龙山县,4331 +44,广东省, +4401,广州市,44 +440103,荔湾区,4401 +440104,越秀区,4401 +440105,海珠区,4401 +440106,天河区,4401 +440111,白云区,4401 +440112,黄埔区,4401 +440113,番禺区,4401 +440114,花都区,4401 +440115,南沙区,4401 +440117,从化区,4401 +440118,增城区,4401 +4402,韶关市,44 +440203,武江区,4402 +440204,浈江区,4402 +440205,曲江区,4402 +440222,始兴县,4402 +440224,仁化县,4402 +440229,翁源县,4402 +440232,乳源瑶族自治县,4402 +440233,新丰县,4402 +440281,乐昌市,4402 +440282,南雄市,4402 +4403,深圳市,44 +440303,罗湖区,4403 +440304,福田区,4403 +440305,南山区,4403 +440306,宝安区,4403 +440307,龙岗区,4403 +440308,盐田区,4403 +440309,龙华区,4403 +440310,坪山区,4403 +440311,光明区,4403 +4404,珠海市,44 +440402,香洲区,4404 +440403,斗门区,4404 +440404,金湾区,4404 +4405,汕头市,44 +440507,龙湖区,4405 +440511,金平区,4405 +440512,濠江区,4405 +440513,潮阳区,4405 +440514,潮南区,4405 +440515,澄海区,4405 +440523,南澳县,4405 +4406,佛山市,44 +440604,禅城区,4406 +440605,南海区,4406 +440606,顺德区,4406 +440607,三水区,4406 +440608,高明区,4406 +4407,江门市,44 +440703,蓬江区,4407 +440704,江海区,4407 +440705,新会区,4407 +440781,台山市,4407 +440783,开平市,4407 +440784,鹤山市,4407 +440785,恩平市,4407 +4408,湛江市,44 +440802,赤坎区,4408 +440803,霞山区,4408 +440804,坡头区,4408 +440811,麻章区,4408 +440823,遂溪县,4408 +440825,徐闻县,4408 +440881,廉江市,4408 +440882,雷州市,4408 +440883,吴川市,4408 +4409,茂名市,44 +440902,茂南区,4409 +440904,电白区,4409 +440981,高州市,4409 +440982,化州市,4409 +440983,信宜市,4409 +4412,肇庆市,44 +441202,端州区,4412 +441203,鼎湖区,4412 +441204,高要区,4412 +441223,广宁县,4412 +441224,怀集县,4412 +441225,封开县,4412 +441226,德庆县,4412 +441284,四会市,4412 +4413,惠州市,44 +441302,惠城区,4413 +441303,惠阳区,4413 +441322,博罗县,4413 +441323,惠东县,4413 +441324,龙门县,4413 +4414,梅州市,44 +441402,梅江区,4414 +441403,梅县区,4414 +441422,大埔县,4414 +441423,丰顺县,4414 +441424,五华县,4414 +441426,平远县,4414 +441427,蕉岭县,4414 +441481,兴宁市,4414 +4415,汕尾市,44 +441502,城区,4415 +441521,海丰县,4415 +441523,陆河县,4415 +441581,陆丰市,4415 +4416,河源市,44 +441602,源城区,4416 +441621,紫金县,4416 +441622,龙川县,4416 +441623,连平县,4416 +441624,和平县,4416 +441625,东源县,4416 +4417,阳江市,44 +441702,江城区,4417 +441704,阳东区,4417 +441721,阳西县,4417 +441781,阳春市,4417 +4418,清远市,44 +441802,清城区,4418 +441803,清新区,4418 +441821,佛冈县,4418 +441823,阳山县,4418 +441825,连山壮族瑶族自治县,4418 +441826,连南瑶族自治县,4418 +441881,英德市,4418 +441882,连州市,4418 +4419,东莞市,44 +4420,中山市,44 +4451,潮州市,44 +445102,湘桥区,4451 +445103,潮安区,4451 +445122,饶平县,4451 +4452,揭阳市,44 +445202,榕城区,4452 +445203,揭东区,4452 +445222,揭西县,4452 +445224,惠来县,4452 +445281,普宁市,4452 +4453,云浮市,44 +445302,云城区,4453 +445303,云安区,4453 +445321,新兴县,4453 +445322,郁南县,4453 +445381,罗定市,4453 +45,广西壮族自治区, +4501,南宁市,45 +450102,兴宁区,4501 +450103,青秀区,4501 +450105,江南区,4501 +450107,西乡塘区,4501 +450108,良庆区,4501 +450109,邕宁区,4501 +450110,武鸣区,4501 +450123,隆安县,4501 +450124,马山县,4501 +450125,上林县,4501 +450126,宾阳县,4501 +450127,横县,4501 +4502,柳州市,45 +450202,城中区,4502 +450203,鱼峰区,4502 +450204,柳南区,4502 +450205,柳北区,4502 +450206,柳江区,4502 +450222,柳城县,4502 +450223,鹿寨县,4502 +450224,融安县,4502 +450225,融水苗族自治县,4502 +450226,三江侗族自治县,4502 +4503,桂林市,45 +450302,秀峰区,4503 +450303,叠彩区,4503 +450304,象山区,4503 +450305,七星区,4503 +450311,雁山区,4503 +450312,临桂区,4503 +450321,阳朔县,4503 +450323,灵川县,4503 +450324,全州县,4503 +450325,兴安县,4503 +450326,永福县,4503 +450327,灌阳县,4503 +450328,龙胜各族自治县,4503 +450329,资源县,4503 +450330,平乐县,4503 +450332,恭城瑶族自治县,4503 +450381,荔浦市,4503 +4504,梧州市,45 +450403,万秀区,4504 +450405,长洲区,4504 +450406,龙圩区,4504 +450421,苍梧县,4504 +450422,藤县,4504 +450423,蒙山县,4504 +450481,岑溪市,4504 +4505,北海市,45 +450502,海城区,4505 +450503,银海区,4505 +450512,铁山港区,4505 +450521,合浦县,4505 +4506,防城港市,45 +450602,港口区,4506 +450603,防城区,4506 +450621,上思县,4506 +450681,东兴市,4506 +4507,钦州市,45 +450702,钦南区,4507 +450703,钦北区,4507 +450721,灵山县,4507 +450722,浦北县,4507 +4508,贵港市,45 +450802,港北区,4508 +450803,港南区,4508 +450804,覃塘区,4508 +450821,平南县,4508 +450881,桂平市,4508 +4509,玉林市,45 +450902,玉州区,4509 +450903,福绵区,4509 +450921,容县,4509 +450922,陆川县,4509 +450923,博白县,4509 +450924,兴业县,4509 +450981,北流市,4509 +4510,百色市,45 +451002,右江区,4510 +451021,田阳县,4510 +451022,田东县,4510 +451023,平果县,4510 +451024,德保县,4510 +451026,那坡县,4510 +451027,凌云县,4510 +451028,乐业县,4510 +451029,田林县,4510 +451030,西林县,4510 +451031,隆林各族自治县,4510 +451081,靖西市,4510 +4511,贺州市,45 +451102,八步区,4511 +451103,平桂区,4511 +451121,昭平县,4511 +451122,钟山县,4511 +451123,富川瑶族自治县,4511 +4512,河池市,45 +451202,金城江区,4512 +451203,宜州区,4512 +451221,南丹县,4512 +451222,天峨县,4512 +451223,凤山县,4512 +451224,东兰县,4512 +451225,罗城仫佬族自治县,4512 +451226,环江毛南族自治县,4512 +451227,巴马瑶族自治县,4512 +451228,都安瑶族自治县,4512 +451229,大化瑶族自治县,4512 +4513,来宾市,45 +451302,兴宾区,4513 +451321,忻城县,4513 +451322,象州县,4513 +451323,武宣县,4513 +451324,金秀瑶族自治县,4513 +451381,合山市,4513 +4514,崇左市,45 +451402,江州区,4514 +451421,扶绥县,4514 +451422,宁明县,4514 +451423,龙州县,4514 +451424,大新县,4514 +451425,天等县,4514 +451481,凭祥市,4514 +46,海南省, +4601,海口市,46 +460105,秀英区,4601 +460106,龙华区,4601 +460107,琼山区,4601 +460108,美兰区,4601 +4602,三亚市,46 +460202,海棠区,4602 +460203,吉阳区,4602 +460204,天涯区,4602 +460205,崖州区,4602 +4603,三沙市,46 +4604,儋州市,46 +469001,五指山市,46 +469002,琼海市,46 +469005,文昌市,46 +469006,万宁市,46 +469007,东方市,46 +469021,定安县,46 +469022,屯昌县,46 +469023,澄迈县,46 +469024,临高县,46 +469025,白沙黎族自治县,46 +469026,昌江黎族自治县,46 +469027,乐东黎族自治县,46 +469028,陵水黎族自治县,46 +469029,保亭黎族苗族自治县,46 +469030,琼中黎族苗族自治县,46 +50,重庆市, +500101,万州区,50 +500102,涪陵区,50 +500103,渝中区,50 +500104,大渡口区,50 +500105,江北区,50 +500106,沙坪坝区,50 +500107,九龙坡区,50 +500108,南岸区,50 +500109,北碚区,50 +500110,綦江区,50 +500111,大足区,50 +500112,渝北区,50 +500113,巴南区,50 +500114,黔江区,50 +500115,长寿区,50 +500116,江津区,50 +500117,合川区,50 +500118,永川区,50 +500119,南川区,50 +500120,璧山区,50 +500151,铜梁区,50 +500152,潼南区,50 +500153,荣昌区,50 +500154,开州区,50 +500155,梁平区,50 +500156,武隆区,50 +500229,城口县,50 +500230,丰都县,50 +500231,垫江县,50 +500233,忠县,50 +500235,云阳县,50 +500236,奉节县,50 +500237,巫山县,50 +500238,巫溪县,50 +500240,石柱土家族自治县,50 +500241,秀山土家族苗族自治县,50 +500242,酉阳土家族苗族自治县,50 +500243,彭水苗族土家族自治县,50 +51,四川省, +5101,成都市,51 +510104,锦江区,5101 +510105,青羊区,5101 +510106,金牛区,5101 +510107,武侯区,5101 +510108,成华区,5101 +510112,龙泉驿区,5101 +510113,青白江区,5101 +510114,新都区,5101 +510115,温江区,5101 +510116,双流区,5101 +510117,郫都区,5101 +510121,金堂县,5101 +510129,大邑县,5101 +510131,蒲江县,5101 +510132,新津县,5101 +510181,都江堰市,5101 +510182,彭州市,5101 +510183,邛崃市,5101 +510184,崇州市,5101 +510185,简阳市,5101 +5103,自贡市,51 +510302,自流井区,5103 +510303,贡井区,5103 +510304,大安区,5103 +510311,沿滩区,5103 +510321,荣县,5103 +510322,富顺县,5103 +5104,攀枝花市,51 +510402,东区,5104 +510403,西区,5104 +510411,仁和区,5104 +510421,米易县,5104 +510422,盐边县,5104 +5105,泸州市,51 +510502,江阳区,5105 +510503,纳溪区,5105 +510504,龙马潭区,5105 +510521,泸县,5105 +510522,合江县,5105 +510524,叙永县,5105 +510525,古蔺县,5105 +5106,德阳市,51 +510603,旌阳区,5106 +510604,罗江区,5106 +510623,中江县,5106 +510681,广汉市,5106 +510682,什邡市,5106 +510683,绵竹市,5106 +5107,绵阳市,51 +510703,涪城区,5107 +510704,游仙区,5107 +510705,安州区,5107 +510722,三台县,5107 +510723,盐亭县,5107 +510725,梓潼县,5107 +510726,北川羌族自治县,5107 +510727,平武县,5107 +510781,江油市,5107 +5108,广元市,51 +510802,利州区,5108 +510811,昭化区,5108 +510812,朝天区,5108 +510821,旺苍县,5108 +510822,青川县,5108 +510823,剑阁县,5108 +510824,苍溪县,5108 +5109,遂宁市,51 +510903,船山区,5109 +510904,安居区,5109 +510921,蓬溪县,5109 +510922,射洪县,5109 +510923,大英县,5109 +5110,内江市,51 +511002,市中区,5110 +511011,东兴区,5110 +511024,威远县,5110 +511025,资中县,5110 +511083,隆昌市,5110 +5111,乐山市,51 +511102,市中区,5111 +511111,沙湾区,5111 +511112,五通桥区,5111 +511113,金口河区,5111 +511123,犍为县,5111 +511124,井研县,5111 +511126,夹江县,5111 +511129,沐川县,5111 +511132,峨边彝族自治县,5111 +511133,马边彝族自治县,5111 +511181,峨眉山市,5111 +5113,南充市,51 +511302,顺庆区,5113 +511303,高坪区,5113 +511304,嘉陵区,5113 +511321,南部县,5113 +511322,营山县,5113 +511323,蓬安县,5113 +511324,仪陇县,5113 +511325,西充县,5113 +511381,阆中市,5113 +5114,眉山市,51 +511402,东坡区,5114 +511403,彭山区,5114 +511421,仁寿县,5114 +511423,洪雅县,5114 +511424,丹棱县,5114 +511425,青神县,5114 +5115,宜宾市,51 +511502,翠屏区,5115 +511503,南溪区,5115 +511504,叙州区,5115 +511523,江安县,5115 +511524,长宁县,5115 +511525,高县,5115 +511526,珙县,5115 +511527,筠连县,5115 +511528,兴文县,5115 +511529,屏山县,5115 +5116,广安市,51 +511602,广安区,5116 +511603,前锋区,5116 +511621,岳池县,5116 +511622,武胜县,5116 +511623,邻水县,5116 +511681,华蓥市,5116 +5117,达州市,51 +511702,通川区,5117 +511703,达川区,5117 +511722,宣汉县,5117 +511723,开江县,5117 +511724,大竹县,5117 +511725,渠县,5117 +511781,万源市,5117 +5118,雅安市,51 +511802,雨城区,5118 +511803,名山区,5118 +511822,荥经县,5118 +511823,汉源县,5118 +511824,石棉县,5118 +511825,天全县,5118 +511826,芦山县,5118 +511827,宝兴县,5118 +5119,巴中市,51 +511902,巴州区,5119 +511903,恩阳区,5119 +511921,通江县,5119 +511922,南江县,5119 +511923,平昌县,5119 +5120,资阳市,51 +512002,雁江区,5120 +512021,安岳县,5120 +512022,乐至县,5120 +5132,阿坝藏族羌族自治州,51 +513201,马尔康市,5132 +513221,汶川县,5132 +513222,理县,5132 +513223,茂县,5132 +513224,松潘县,5132 +513225,九寨沟县,5132 +513226,金川县,5132 +513227,小金县,5132 +513228,黑水县,5132 +513230,壤塘县,5132 +513231,阿坝县,5132 +513232,若尔盖县,5132 +513233,红原县,5132 +5133,甘孜藏族自治州,51 +513301,康定市,5133 +513322,泸定县,5133 +513323,丹巴县,5133 +513324,九龙县,5133 +513325,雅江县,5133 +513326,道孚县,5133 +513327,炉霍县,5133 +513328,甘孜县,5133 +513329,新龙县,5133 +513330,德格县,5133 +513331,白玉县,5133 +513332,石渠县,5133 +513333,色达县,5133 +513334,理塘县,5133 +513335,巴塘县,5133 +513336,乡城县,5133 +513337,稻城县,5133 +513338,得荣县,5133 +5134,凉山彝族自治州,51 +513401,西昌市,5134 +513422,木里藏族自治县,5134 +513423,盐源县,5134 +513424,德昌县,5134 +513425,会理县,5134 +513426,会东县,5134 +513427,宁南县,5134 +513428,普格县,5134 +513429,布拖县,5134 +513430,金阳县,5134 +513431,昭觉县,5134 +513432,喜德县,5134 +513433,冕宁县,5134 +513434,越西县,5134 +513435,甘洛县,5134 +513436,美姑县,5134 +513437,雷波县,5134 +52,贵州省, +5201,贵阳市,52 +520102,南明区,5201 +520103,云岩区,5201 +520111,花溪区,5201 +520112,乌当区,5201 +520113,白云区,5201 +520115,观山湖区,5201 +520121,开阳县,5201 +520122,息烽县,5201 +520123,修文县,5201 +520181,清镇市,5201 +5202,六盘水市,52 +520201,钟山区,5202 +520203,六枝特区,5202 +520221,水城县,5202 +520281,盘州市,5202 +5203,遵义市,52 +520302,红花岗区,5203 +520303,汇川区,5203 +520304,播州区,5203 +520322,桐梓县,5203 +520323,绥阳县,5203 +520324,正安县,5203 +520325,道真仡佬族苗族自治县,5203 +520326,务川仡佬族苗族自治县,5203 +520327,凤冈县,5203 +520328,湄潭县,5203 +520329,余庆县,5203 +520330,习水县,5203 +520381,赤水市,5203 +520382,仁怀市,5203 +5204,安顺市,52 +520402,西秀区,5204 +520403,平坝区,5204 +520422,普定县,5204 +520423,镇宁布依族苗族自治县,5204 +520424,关岭布依族苗族自治县,5204 +520425,紫云苗族布依族自治县,5204 +5205,毕节市,52 +520502,七星关区,5205 +520521,大方县,5205 +520522,黔西县,5205 +520523,金沙县,5205 +520524,织金县,5205 +520525,纳雍县,5205 +520526,威宁彝族回族苗族自治县,5205 +520527,赫章县,5205 +5206,铜仁市,52 +520602,碧江区,5206 +520603,万山区,5206 +520621,江口县,5206 +520622,玉屏侗族自治县,5206 +520623,石阡县,5206 +520624,思南县,5206 +520625,印江土家族苗族自治县,5206 +520626,德江县,5206 +520627,沿河土家族自治县,5206 +520628,松桃苗族自治县,5206 +5223,黔西南布依族苗族自治州,52 +522301,兴义市,5223 +522302,兴仁市,5223 +522323,普安县,5223 +522324,晴隆县,5223 +522325,贞丰县,5223 +522326,望谟县,5223 +522327,册亨县,5223 +522328,安龙县,5223 +5226,黔东南苗族侗族自治州,52 +522601,凯里市,5226 +522622,黄平县,5226 +522623,施秉县,5226 +522624,三穗县,5226 +522625,镇远县,5226 +522626,岑巩县,5226 +522627,天柱县,5226 +522628,锦屏县,5226 +522629,剑河县,5226 +522630,台江县,5226 +522631,黎平县,5226 +522632,榕江县,5226 +522633,从江县,5226 +522634,雷山县,5226 +522635,麻江县,5226 +522636,丹寨县,5226 +5227,黔南布依族苗族自治州,52 +522701,都匀市,5227 +522702,福泉市,5227 +522722,荔波县,5227 +522723,贵定县,5227 +522725,瓮安县,5227 +522726,独山县,5227 +522727,平塘县,5227 +522728,罗甸县,5227 +522729,长顺县,5227 +522730,龙里县,5227 +522731,惠水县,5227 +522732,三都水族自治县,5227 +53,云南省, +5301,昆明市,53 +530102,五华区,5301 +530103,盘龙区,5301 +530111,官渡区,5301 +530112,西山区,5301 +530113,东川区,5301 +530114,呈贡区,5301 +530115,晋宁区,5301 +530124,富民县,5301 +530125,宜良县,5301 +530126,石林彝族自治县,5301 +530127,嵩明县,5301 +530128,禄劝彝族苗族自治县,5301 +530129,寻甸回族彝族自治县,5301 +530181,安宁市,5301 +5303,曲靖市,53 +530302,麒麟区,5303 +530303,沾益区,5303 +530304,马龙区,5303 +530322,陆良县,5303 +530323,师宗县,5303 +530324,罗平县,5303 +530325,富源县,5303 +530326,会泽县,5303 +530381,宣威市,5303 +5304,玉溪市,53 +530402,红塔区,5304 +530403,江川区,5304 +530422,澄江县,5304 +530423,通海县,5304 +530424,华宁县,5304 +530425,易门县,5304 +530426,峨山彝族自治县,5304 +530427,新平彝族傣族自治县,5304 +530428,元江哈尼族彝族傣族自治县,5304 +5305,保山市,53 +530502,隆阳区,5305 +530521,施甸县,5305 +530523,龙陵县,5305 +530524,昌宁县,5305 +530581,腾冲市,5305 +5306,昭通市,53 +530602,昭阳区,5306 +530621,鲁甸县,5306 +530622,巧家县,5306 +530623,盐津县,5306 +530624,大关县,5306 +530625,永善县,5306 +530626,绥江县,5306 +530627,镇雄县,5306 +530628,彝良县,5306 +530629,威信县,5306 +530681,水富市,5306 +5307,丽江市,53 +530702,古城区,5307 +530721,玉龙纳西族自治县,5307 +530722,永胜县,5307 +530723,华坪县,5307 +530724,宁蒗彝族自治县,5307 +5308,普洱市,53 +530802,思茅区,5308 +530821,宁洱哈尼族彝族自治县,5308 +530822,墨江哈尼族自治县,5308 +530823,景东彝族自治县,5308 +530824,景谷傣族彝族自治县,5308 +530825,镇沅彝族哈尼族拉祜族自治县,5308 +530826,江城哈尼族彝族自治县,5308 +530827,孟连傣族拉祜族佤族自治县,5308 +530828,澜沧拉祜族自治县,5308 +530829,西盟佤族自治县,5308 +5309,临沧市,53 +530902,临翔区,5309 +530921,凤庆县,5309 +530922,云县,5309 +530923,永德县,5309 +530924,镇康县,5309 +530925,双江拉祜族佤族布朗族傣族自治县,5309 +530926,耿马傣族佤族自治县,5309 +530927,沧源佤族自治县,5309 +5323,楚雄彝族自治州,53 +532301,楚雄市,5323 +532322,双柏县,5323 +532323,牟定县,5323 +532324,南华县,5323 +532325,姚安县,5323 +532326,大姚县,5323 +532327,永仁县,5323 +532328,元谋县,5323 +532329,武定县,5323 +532331,禄丰县,5323 +5325,红河哈尼族彝族自治州,53 +532501,个旧市,5325 +532502,开远市,5325 +532503,蒙自市,5325 +532504,弥勒市,5325 +532523,屏边苗族自治县,5325 +532524,建水县,5325 +532525,石屏县,5325 +532527,泸西县,5325 +532528,元阳县,5325 +532529,红河县,5325 +532530,金平苗族瑶族傣族自治县,5325 +532531,绿春县,5325 +532532,河口瑶族自治县,5325 +5326,文山壮族苗族自治州,53 +532601,文山市,5326 +532622,砚山县,5326 +532623,西畴县,5326 +532624,麻栗坡县,5326 +532625,马关县,5326 +532626,丘北县,5326 +532627,广南县,5326 +532628,富宁县,5326 +5328,西双版纳傣族自治州,53 +532801,景洪市,5328 +532822,勐海县,5328 +532823,勐腊县,5328 +5329,大理白族自治州,53 +532901,大理市,5329 +532922,漾濞彝族自治县,5329 +532923,祥云县,5329 +532924,宾川县,5329 +532925,弥渡县,5329 +532926,南涧彝族自治县,5329 +532927,巍山彝族回族自治县,5329 +532928,永平县,5329 +532929,云龙县,5329 +532930,洱源县,5329 +532931,剑川县,5329 +532932,鹤庆县,5329 +5331,德宏傣族景颇族自治州,53 +533102,瑞丽市,5331 +533103,芒市,5331 +533122,梁河县,5331 +533123,盈江县,5331 +533124,陇川县,5331 +5333,怒江傈僳族自治州,53 +533301,泸水市,5333 +533323,福贡县,5333 +533324,贡山独龙族怒族自治县,5333 +533325,兰坪白族普米族自治县,5333 +5334,迪庆藏族自治州,53 +533401,香格里拉市,5334 +533422,德钦县,5334 +533423,维西傈僳族自治县,5334 +54,西藏自治区, +5401,拉萨市,54 +540102,城关区,5401 +540103,堆龙德庆区,5401 +540104,达孜区,5401 +540121,林周县,5401 +540122,当雄县,5401 +540123,尼木县,5401 +540124,曲水县,5401 +540127,墨竹工卡县,5401 +5402,日喀则市,54 +540202,桑珠孜区,5402 +540221,南木林县,5402 +540222,江孜县,5402 +540223,定日县,5402 +540224,萨迦县,5402 +540225,拉孜县,5402 +540226,昂仁县,5402 +540227,谢通门县,5402 +540228,白朗县,5402 +540229,仁布县,5402 +540230,康马县,5402 +540231,定结县,5402 +540232,仲巴县,5402 +540233,亚东县,5402 +540234,吉隆县,5402 +540235,聂拉木县,5402 +540236,萨嘎县,5402 +540237,岗巴县,5402 +5403,昌都市,54 +540302,卡若区,5403 +540321,江达县,5403 +540322,贡觉县,5403 +540323,类乌齐县,5403 +540324,丁青县,5403 +540325,察雅县,5403 +540326,八宿县,5403 +540327,左贡县,5403 +540328,芒康县,5403 +540329,洛隆县,5403 +540330,边坝县,5403 +5404,林芝市,54 +540402,巴宜区,5404 +540421,工布江达县,5404 +540422,米林县,5404 +540423,墨脱县,5404 +540424,波密县,5404 +540425,察隅县,5404 +540426,朗县,5404 +5405,山南市,54 +540502,乃东区,5405 +540521,扎囊县,5405 +540522,贡嘎县,5405 +540523,桑日县,5405 +540524,琼结县,5405 +540525,曲松县,5405 +540526,措美县,5405 +540527,洛扎县,5405 +540528,加查县,5405 +540529,隆子县,5405 +540530,错那县,5405 +540531,浪卡子县,5405 +5406,那曲市,54 +540602,色尼区,5406 +540621,嘉黎县,5406 +540622,比如县,5406 +540623,聂荣县,5406 +540624,安多县,5406 +540625,申扎县,5406 +540626,索县,5406 +540627,班戈县,5406 +540628,巴青县,5406 +540629,尼玛县,5406 +540630,双湖县,5406 +5425,阿里地区,54 +542521,普兰县,5425 +542522,札达县,5425 +542523,噶尔县,5425 +542524,日土县,5425 +542525,革吉县,5425 +542526,改则县,5425 +542527,措勤县,5425 +61,陕西省, +6101,西安市,61 +610102,新城区,6101 +610103,碑林区,6101 +610104,莲湖区,6101 +610111,灞桥区,6101 +610112,未央区,6101 +610113,雁塔区,6101 +610114,阎良区,6101 +610115,临潼区,6101 +610116,长安区,6101 +610117,高陵区,6101 +610118,鄠邑区,6101 +610122,蓝田县,6101 +610124,周至县,6101 +6102,铜川市,61 +610202,王益区,6102 +610203,印台区,6102 +610204,耀州区,6102 +610222,宜君县,6102 +6103,宝鸡市,61 +610302,渭滨区,6103 +610303,金台区,6103 +610304,陈仓区,6103 +610322,凤翔县,6103 +610323,岐山县,6103 +610324,扶风县,6103 +610326,眉县,6103 +610327,陇县,6103 +610328,千阳县,6103 +610329,麟游县,6103 +610330,凤县,6103 +610331,太白县,6103 +6104,咸阳市,61 +610402,秦都区,6104 +610403,杨陵区,6104 +610404,渭城区,6104 +610422,三原县,6104 +610423,泾阳县,6104 +610424,乾县,6104 +610425,礼泉县,6104 +610426,永寿县,6104 +610428,长武县,6104 +610429,旬邑县,6104 +610430,淳化县,6104 +610431,武功县,6104 +610481,兴平市,6104 +610482,彬州市,6104 +6105,渭南市,61 +610502,临渭区,6105 +610503,华州区,6105 +610522,潼关县,6105 +610523,大荔县,6105 +610524,合阳县,6105 +610525,澄城县,6105 +610526,蒲城县,6105 +610527,白水县,6105 +610528,富平县,6105 +610581,韩城市,6105 +610582,华阴市,6105 +6106,延安市,61 +610602,宝塔区,6106 +610603,安塞区,6106 +610621,延长县,6106 +610622,延川县,6106 +610623,子长县,6106 +610625,志丹县,6106 +610626,吴起县,6106 +610627,甘泉县,6106 +610628,富县,6106 +610629,洛川县,6106 +610630,宜川县,6106 +610631,黄龙县,6106 +610632,黄陵县,6106 +6107,汉中市,61 +610702,汉台区,6107 +610703,南郑区,6107 +610722,城固县,6107 +610723,洋县,6107 +610724,西乡县,6107 +610725,勉县,6107 +610726,宁强县,6107 +610727,略阳县,6107 +610728,镇巴县,6107 +610729,留坝县,6107 +610730,佛坪县,6107 +6108,榆林市,61 +610802,榆阳区,6108 +610803,横山区,6108 +610822,府谷县,6108 +610824,靖边县,6108 +610825,定边县,6108 +610826,绥德县,6108 +610827,米脂县,6108 +610828,佳县,6108 +610829,吴堡县,6108 +610830,清涧县,6108 +610831,子洲县,6108 +610881,神木市,6108 +6109,安康市,61 +610902,汉滨区,6109 +610921,汉阴县,6109 +610922,石泉县,6109 +610923,宁陕县,6109 +610924,紫阳县,6109 +610925,岚皋县,6109 +610926,平利县,6109 +610927,镇坪县,6109 +610928,旬阳县,6109 +610929,白河县,6109 +6110,商洛市,61 +611002,商州区,6110 +611021,洛南县,6110 +611022,丹凤县,6110 +611023,商南县,6110 +611024,山阳县,6110 +611025,镇安县,6110 +611026,柞水县,6110 +62,甘肃省, +6201,兰州市,62 +620102,城关区,6201 +620103,七里河区,6201 +620104,西固区,6201 +620105,安宁区,6201 +620111,红古区,6201 +620121,永登县,6201 +620122,皋兰县,6201 +620123,榆中县,6201 +6202,嘉峪关市,62 +6203,金昌市,62 +620302,金川区,6203 +620321,永昌县,6203 +6204,白银市,62 +620402,白银区,6204 +620403,平川区,6204 +620421,靖远县,6204 +620422,会宁县,6204 +620423,景泰县,6204 +6205,天水市,62 +620502,秦州区,6205 +620503,麦积区,6205 +620521,清水县,6205 +620522,秦安县,6205 +620523,甘谷县,6205 +620524,武山县,6205 +620525,张家川回族自治县,6205 +6206,武威市,62 +620602,凉州区,6206 +620621,民勤县,6206 +620622,古浪县,6206 +620623,天祝藏族自治县,6206 +6207,张掖市,62 +620702,甘州区,6207 +620721,肃南裕固族自治县,6207 +620722,民乐县,6207 +620723,临泽县,6207 +620724,高台县,6207 +620725,山丹县,6207 +6208,平凉市,62 +620802,崆峒区,6208 +620821,泾川县,6208 +620822,灵台县,6208 +620823,崇信县,6208 +620825,庄浪县,6208 +620826,静宁县,6208 +620881,华亭市,6208 +6209,酒泉市,62 +620902,肃州区,6209 +620921,金塔县,6209 +620922,瓜州县,6209 +620923,肃北蒙古族自治县,6209 +620924,阿克塞哈萨克族自治县,6209 +620981,玉门市,6209 +620982,敦煌市,6209 +6210,庆阳市,62 +621002,西峰区,6210 +621021,庆城县,6210 +621022,环县,6210 +621023,华池县,6210 +621024,合水县,6210 +621025,正宁县,6210 +621026,宁县,6210 +621027,镇原县,6210 +6211,定西市,62 +621102,安定区,6211 +621121,通渭县,6211 +621122,陇西县,6211 +621123,渭源县,6211 +621124,临洮县,6211 +621125,漳县,6211 +621126,岷县,6211 +6212,陇南市,62 +621202,武都区,6212 +621221,成县,6212 +621222,文县,6212 +621223,宕昌县,6212 +621224,康县,6212 +621225,西和县,6212 +621226,礼县,6212 +621227,徽县,6212 +621228,两当县,6212 +6229,临夏回族自治州,62 +622901,临夏市,6229 +622921,临夏县,6229 +622922,康乐县,6229 +622923,永靖县,6229 +622924,广河县,6229 +622925,和政县,6229 +622926,东乡族自治县,6229 +622927,积石山保安族东乡族撒拉族自治县,6229 +6230,甘南藏族自治州,62 +623001,合作市,6230 +623021,临潭县,6230 +623022,卓尼县,6230 +623023,舟曲县,6230 +623024,迭部县,6230 +623025,玛曲县,6230 +623026,碌曲县,6230 +623027,夏河县,6230 +63,青海省, +6301,西宁市,63 +630102,城东区,6301 +630103,城中区,6301 +630104,城西区,6301 +630105,城北区,6301 +630121,大通回族土族自治县,6301 +630122,湟中县,6301 +630123,湟源县,6301 +6302,海东市,63 +630202,乐都区,6302 +630203,平安区,6302 +630222,民和回族土族自治县,6302 +630223,互助土族自治县,6302 +630224,化隆回族自治县,6302 +630225,循化撒拉族自治县,6302 +6322,海北藏族自治州,63 +632221,门源回族自治县,6322 +632222,祁连县,6322 +632223,海晏县,6322 +632224,刚察县,6322 +6323,黄南藏族自治州,63 +632321,同仁县,6323 +632322,尖扎县,6323 +632323,泽库县,6323 +632324,河南蒙古族自治县,6323 +6325,海南藏族自治州,63 +632521,共和县,6325 +632522,同德县,6325 +632523,贵德县,6325 +632524,兴海县,6325 +632525,贵南县,6325 +6326,果洛藏族自治州,63 +632621,玛沁县,6326 +632622,班玛县,6326 +632623,甘德县,6326 +632624,达日县,6326 +632625,久治县,6326 +632626,玛多县,6326 +6327,玉树藏族自治州,63 +632701,玉树市,6327 +632722,杂多县,6327 +632723,称多县,6327 +632724,治多县,6327 +632725,囊谦县,6327 +632726,曲麻莱县,6327 +6328,海西蒙古族藏族自治州,63 +632801,格尔木市,6328 +632802,德令哈市,6328 +632803,茫崖市,6328 +632821,乌兰县,6328 +632822,都兰县,6328 +632823,天峻县,6328 +64,宁夏回族自治区, +6401,银川市,64 +640104,兴庆区,6401 +640105,西夏区,6401 +640106,金凤区,6401 +640121,永宁县,6401 +640122,贺兰县,6401 +640181,灵武市,6401 +6402,石嘴山市,64 +640202,大武口区,6402 +640205,惠农区,6402 +640221,平罗县,6402 +6403,吴忠市,64 +640302,利通区,6403 +640303,红寺堡区,6403 +640323,盐池县,6403 +640324,同心县,6403 +640381,青铜峡市,6403 +6404,固原市,64 +640402,原州区,6404 +640422,西吉县,6404 +640423,隆德县,6404 +640424,泾源县,6404 +640425,彭阳县,6404 +6405,中卫市,64 +640502,沙坡头区,6405 +640521,中宁县,6405 +640522,海原县,6405 +65,新疆维吾尔自治区, +6501,乌鲁木齐市,65 +650102,天山区,6501 +650103,沙依巴克区,6501 +650104,新市区,6501 +650105,水磨沟区,6501 +650106,头屯河区,6501 +650107,达坂城区,6501 +650109,米东区,6501 +650121,乌鲁木齐县,6501 +6502,克拉玛依市,65 +650202,独山子区,6502 +650203,克拉玛依区,6502 +650204,白碱滩区,6502 +650205,乌尔禾区,6502 +6504,吐鲁番市,65 +650402,高昌区,6504 +650421,鄯善县,6504 +650422,托克逊县,6504 +6505,哈密市,65 +650502,伊州区,6505 +650521,巴里坤哈萨克自治县,6505 +650522,伊吾县,6505 +6523,昌吉回族自治州,65 +652301,昌吉市,6523 +652302,阜康市,6523 +652323,呼图壁县,6523 +652324,玛纳斯县,6523 +652325,奇台县,6523 +652327,吉木萨尔县,6523 +652328,木垒哈萨克自治县,6523 +6527,博尔塔拉蒙古自治州,65 +652701,博乐市,6527 +652702,阿拉山口市,6527 +652722,精河县,6527 +652723,温泉县,6527 +6528,巴音郭楞蒙古自治州,65 +652801,库尔勒市,6528 +652822,轮台县,6528 +652823,尉犁县,6528 +652824,若羌县,6528 +652825,且末县,6528 +652826,焉耆回族自治县,6528 +652827,和静县,6528 +652828,和硕县,6528 +652829,博湖县,6528 +6529,阿克苏地区,65 +652901,阿克苏市,6529 +652922,温宿县,6529 +652923,库车县,6529 +652924,沙雅县,6529 +652925,新和县,6529 +652926,拜城县,6529 +652927,乌什县,6529 +652928,阿瓦提县,6529 +652929,柯坪县,6529 +6530,克孜勒苏柯尔克孜自治州,65 +653001,阿图什市,6530 +653022,阿克陶县,6530 +653023,阿合奇县,6530 +653024,乌恰县,6530 +6531,喀什地区,65 +653101,喀什市,6531 +653121,疏附县,6531 +653122,疏勒县,6531 +653123,英吉沙县,6531 +653124,泽普县,6531 +653125,莎车县,6531 +653126,叶城县,6531 +653127,麦盖提县,6531 +653128,岳普湖县,6531 +653129,伽师县,6531 +653130,巴楚县,6531 +653131,塔什库尔干塔吉克自治县,6531 +6532,和田地区,65 +653201,和田市,6532 +653221,和田县,6532 +653222,墨玉县,6532 +653223,皮山县,6532 +653224,洛浦县,6532 +653225,策勒县,6532 +653226,于田县,6532 +653227,民丰县,6532 +6540,伊犁哈萨克自治州,65 +654002,伊宁市,6540 +654003,奎屯市,6540 +654004,霍尔果斯市,6540 +654021,伊宁县,6540 +654022,察布查尔锡伯自治县,6540 +654023,霍城县,6540 +654024,巩留县,6540 +654025,新源县,6540 +654026,昭苏县,6540 +654027,特克斯县,6540 +654028,尼勒克县,6540 +6542,塔城地区,65 +654201,塔城市,6542 +654202,乌苏市,6542 +654221,额敏县,6542 +654223,沙湾县,6542 +654224,托里县,6542 +654225,裕民县,6542 +654226,和布克赛尔蒙古自治县,6542 +6543,阿勒泰地区,65 +654301,阿勒泰市,6543 +654321,布尔津县,6543 +654322,富蕴县,6543 +654323,福海县,6543 +654324,哈巴河县,6543 +654325,青河县,6543 +654326,吉木乃县,6543 +659001,石河子市,65 +659002,阿拉尔市,65 +659003,图木舒克市,65 +659004,五家渠市,65 +659005,北屯市,65 +659006,铁门关市,65 +659007,双河市,65 +659008,可克达拉市,65 +659009,昆玉市,65 +71,台湾省, +81,香港特别行政区, +82,澳门特别行政区, \ 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 000000000..dfda37e2c --- /dev/null +++ b/src/main/resources/logback-spring-local.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + ${log.pattern} + UTF-8 + + + + + 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 + UTF-8 + + + + + 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 + UTF-8 + + + + WARN + + + + + + + + ${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 + UTF-8 + + + + + + + + + + + + + + + + + + + \ 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 ff76bcd66..000000000 Binary files a/src/main/resources/wvp.sqlite and /dev/null differ diff --git a/src/main/resources/wvpssl.jks b/src/main/resources/wvpssl.jks new file mode 100644 index 000000000..92b98b77a Binary files /dev/null and b/src/main/resources/wvpssl.jks differ diff --git a/src/test/java/com/genersoft/iot/vmp/jt1078/JT1078ServerTest.java b/src/test/java/com/genersoft/iot/vmp/jt1078/JT1078ServerTest.java new file mode 100644 index 000000000..8986f91e6 --- /dev/null +++ b/src/test/java/com/genersoft/iot/vmp/jt1078/JT1078ServerTest.java @@ -0,0 +1,103 @@ +package com.genersoft.iot.vmp.jt1078; + +import com.genersoft.iot.vmp.jt1078.cmd.JT1078Template; +import com.genersoft.iot.vmp.jt1078.codec.netty.TcpServer; +import com.genersoft.iot.vmp.jt1078.proc.response.J9102; +import com.genersoft.iot.vmp.jt1078.proc.response.J9201; +import com.genersoft.iot.vmp.jt1078.proc.response.J9202; +import com.genersoft.iot.vmp.jt1078.proc.response.J9205; + +import java.util.Scanner; + +/** + * @author QingtaiJiang + * @date 2023/4/28 14:22 + * @email qingtaij@163.com + */ +public class JT1078ServerTest { + + private static final JT1078Template jt1078Template = new JT1078Template(); + + public static void main(String[] args) { + System.out.println("Starting jt1078 server..."); + TcpServer tcpServer = new TcpServer(21078); + tcpServer.start(); + System.out.println("Start jt1078 server success!"); + + + Scanner s = new Scanner(System.in); + while (true) { + String code = s.nextLine(); + switch (code) { + case "1": + test9102(); + break; + case "2": + test9201(); + break; + case "3": + test9202(); + break; + case "4": + test9205(); + break; + default: + break; + } + } + } + + private static void test9102() { + J9102 j9102 = new J9102(); + j9102.setChannel(1); + j9102.setCommand(0); + j9102.setCloseType(0); + j9102.setStreamType(0); + + String s = jt1078Template.stopLive("18864197066", j9102, 6); + System.out.println(s); + } + + private static void test9201() { + J9201 j9201 = new J9201(); + j9201.setIp("192.168.1.1"); + j9201.setChannel(1); + j9201.setTcpPort(7618); + j9201.setUdpPort(7618); + j9201.setType(0); + j9201.setRate(0); + j9201.setStorageType(0); + j9201.setPlaybackType(0); + j9201.setPlaybackSpeed(0); + j9201.setStartTime("230428134100"); + j9201.setEndTime("230428134200"); + + String s = jt1078Template.startBackLive("18864197066", j9201, 6); + System.out.println(s); + } + + private static void test9202() { + J9202 j9202 = new J9202(); + + j9202.setChannel(1); + j9202.setPlaybackType(2); + j9202.setPlaybackSpeed(0); + j9202.setPlaybackTime("230428134100"); + + String s = jt1078Template.controlBackLive("18864197066", j9202, 6); + System.out.println(s); + } + + private static void test9205() { + J9205 j9205 = new J9205(); + j9205.setChannelId(1); + j9205.setStartTime("230428134100"); + j9205.setEndTime("230428134100"); + j9205.setMediaType(0); + j9205.setStreamType(0); + j9205.setStorageType(0); + + String s = jt1078Template.queryBackTime("18864197066", j9205, 6); + System.out.println(s); + } +} diff --git a/web_src/build/webpack.base.conf.js b/web_src/build/webpack.base.conf.js index a07e68360..72539e37a 100644 --- a/web_src/build/webpack.base.conf.js +++ b/web_src/build/webpack.base.conf.js @@ -27,6 +27,7 @@ module.exports = { alias: { 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src'), + '@static': resolve('static'), } }, module: { diff --git a/web_src/build/webpack.dev.conf.js b/web_src/build/webpack.dev.conf.js index e33cb0178..ad7c53065 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 @@ -63,9 +64,12 @@ const devWebpackConfig = merge(baseWebpackConfig, { from: path.resolve(__dirname, '../static'), to: config.dev.assetsSubDirectory, ignore: ['.*'] - }, - { from: 'node_modules/@easydarwin/easywasmplayer/libDecoder.wasm'}, - { from: 'node_modules/@easydarwin/easywasmplayer/EasyWasmPlayer.js', to: 'js/'} + } + ]), + 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 13d373dbc..e4324a94d 100644 --- a/web_src/build/webpack.prod.conf.js +++ b/web_src/build/webpack.prod.conf.js @@ -46,7 +46,7 @@ const webpackConfig = merge(baseWebpackConfig, { filename: utils.assetsPath('css/[name].[contenthash].css'), // Setting the following option to `false` will not extract CSS from codesplit chunks. // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. - // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, + // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 allChunks: true, }), @@ -114,9 +114,12 @@ const webpackConfig = merge(baseWebpackConfig, { from: path.resolve(__dirname, '../static'), to: config.build.assetsSubDirectory, ignore: ['.*'] - }, - { from: 'node_modules/@easydarwin/easywasmplayer/libDecoder.wasm'}, - { from: 'node_modules/@easydarwin/easywasmplayer/EasyWasmPlayer.js', to: 'js/'} + } + ]), + 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 a16611c0a..b1e1cbe14 100644 --- a/web_src/config/index.js +++ b/web_src/config/index.js @@ -17,15 +17,25 @@ module.exports = { pathRewrite: { '^/debug': '/' } - } + }, + '/static/snap': { + target: 'http://localhost:18080', + changeOrigin: true, + // pathRewrite: { + // '^/static/snap': '/static/snap' + // } + }, + }, // 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- @@ -50,7 +60,7 @@ module.exports = { // Paths assetsRoot: path.resolve(__dirname, '../../src/main/resources/static/'), - assetsSubDirectory: '.', + assetsSubDirectory: './static', assetsPublicPath: '/', /** diff --git a/web_src/index.html b/web_src/index.html index 8b26e729f..bd66d3923 100644 --- a/web_src/index.html +++ b/web_src/index.html @@ -4,11 +4,17 @@ 国标28181 + + + - - + + + + +
    - + diff --git a/web_src/package-lock.json b/web_src/package-lock.json index 54840f6c2..dd0df1d94 100644 --- a/web_src/package-lock.json +++ b/web_src/package-lock.json @@ -1,20 +1,14454 @@ { "name": "gb_web", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, - "dependencies": { - "@easydarwin/easywasmplayer": { + "packages": { + "": { + "name": "gb_web", + "version": "1.0.0", + "dependencies": { + "@liveqing/liveplayer": "^2.7.10", + "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.8.3", + "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.10", + "resolved": "https://registry.npmmirror.com/@liveqing/liveplayer/-/liveplayer-2.7.10.tgz", + "integrity": "sha512-OrRzy2t0WZvO/h+hxMYL2+Ips7ON75l+PH8rjZDT7sps1zKW6dct6qPJm3EsVQuttUQ0dVGu3UXaSUm5gEM4dQ==" + }, + "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": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "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.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "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/@easydarwin/easywasmplayer/download/@easydarwin/easywasmplayer-4.0.7.tgz", - "integrity": "sha1-FNtIUXbdwIWdalvIMEaH0+zUGx4=" + "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": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "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/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.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "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/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/schema-utils/node_modules/ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha512-Ajr4IcMXq/2QmMkEmSvxqfLN5zGmJ92gHXAeOXq1OekoH2rfDNsgdDoL2f7QaRCy7G/E6TpxBVdRuNraMztGHw==", + "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/schema-utils/node_modules/fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha512-fueX787WZKCV0Is4/T2cyAdM4+x1S3MXXOAhavE1ys/W42SHAPacLTQhucja22QBYrfGw50M2sRiXPtTGv9Ymw==", + "dev": true + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha512-4JD/Ivzg7PoW8NzdrBSr3UFwC9mHgvI7Z6z3QGBsSHgKaRTUDmyZAAKJo2UbG1kUVfS9WS8bi36N49U1xw43DA==", + "dev": true + }, + "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/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/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.8.3", + "resolved": "https://registry.npmmirror.com/vue-cookies/-/vue-cookies-1.8.3.tgz", + "integrity": "sha512-VBRsyRMVdahBgFfh389TMHPmDdr4URDJNMk4FKSCfuNITs7+jitBDhwyL4RJd3WUsfOYNNjPAkfbehyH9AFuoA==" + }, + "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/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/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.10", + "resolved": "https://registry.npmmirror.com/@liveqing/liveplayer/-/liveplayer-2.7.10.tgz", + "integrity": "sha512-OrRzy2t0WZvO/h+hxMYL2+Ips7ON75l+PH8rjZDT7sps1zKW6dct6qPJm3EsVQuttUQ0dVGu3UXaSUm5gEM4dQ==" + }, + "@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.npm.taobao.org/@types/q/download/@types/q-1.5.4.tgz", - "integrity": "sha1-FZJUFOCtLNdlv+9YhC9+JqesyyQ=", + "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", @@ -49,22 +14483,23 @@ } }, "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=", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, "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 + "dev": true, + "requires": {} }, "align-text": { "version": "0.1.4", @@ -267,8 +14702,8 @@ }, "async-validator": { "version": "1.8.5", - "resolved": "https://registry.npm.taobao.org/async-validator/download/async-validator-1.8.5.tgz?cache=0&sync_timestamp=1596625258256&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fasync-validator%2Fdownload%2Fasync-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" } @@ -294,28 +14729,17 @@ } }, "axios": { - "version": "0.19.2", - "resolved": "https://registry.npm.taobao.org/axios/download/axios-0.19.2.tgz?cache=0&sync_timestamp=1597979791211&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Faxios%2Fdownload%2Faxios-0.19.2.tgz", - "integrity": "sha1-PqNsXYgY0NX4qKl6bTa4bNwAyyc=", + "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==" } } }, @@ -1269,34 +15693,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", @@ -1528,8 +15924,8 @@ }, "cacache": { "version": "10.0.4", - "resolved": "https://registry.npm.taobao.org/cacache/download/cacache-10.0.4.tgz?cache=0&sync_timestamp=1594428006433&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcacache%2Fdownload%2Fcacache-10.0.4.tgz", - "integrity": "sha1-ZFI2eZnv+dQYiu/ZoU6dfGomNGA=", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", "dev": true, "requires": { "bluebird": "^3.5.1", @@ -1564,6 +15960,16 @@ "unset-value": "^1.0.0" } }, + "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, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, "caller-callsite": { "version": "2.0.0", "resolved": "https://registry.npm.taobao.org/caller-callsite/download/caller-callsite-2.0.0.tgz", @@ -1647,15 +16053,15 @@ } }, "caniuse-db": { - "version": "1.0.30001146", - "resolved": "https://registry.npm.taobao.org/caniuse-db/download/caniuse-db-1.0.30001146.tgz?cache=0&sync_timestamp=1602133173904&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcaniuse-db%2Fdownload%2Fcaniuse-db-1.0.30001146.tgz", - "integrity": "sha1-poOLhbXEFSLrDXsetgeHFsBQ1q4=", + "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 }, "caniuse-lite": { - "version": "1.0.30001146", - "resolved": "https://registry.npm.taobao.org/caniuse-lite/download/caniuse-lite-1.0.30001146.tgz", - "integrity": "sha1-xh/LFHRSDBRikTaJIB+ykrpvRHw=", + "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 }, "center-align": { @@ -1829,8 +16235,8 @@ }, "co": { "version": "4.6.0", - "resolved": "https://registry.npm.taobao.org/co/download/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true }, "coa": { @@ -1891,6 +16297,12 @@ "color-name": "^1.0.0" } }, + "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 + }, "colormin": { "version": "1.1.2", "resolved": "https://registry.npm.taobao.org/colormin/download/colormin-1.1.2.tgz", @@ -2061,8 +16473,8 @@ }, "copy-webpack-plugin": { "version": "4.6.0", - "resolved": "https://registry.npm.taobao.org/copy-webpack-plugin/download/copy-webpack-plugin-4.6.0.tgz", - "integrity": "sha1-5/QN2KaEd9QF3Rt6hUquMksVi64=", + "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, "requires": { "cacache": "^10.0.4", @@ -2199,8 +16611,8 @@ }, "css-declaration-sorter": { "version": "4.0.1", - "resolved": "https://registry.npm.taobao.org/css-declaration-sorter/download/css-declaration-sorter-4.0.1.tgz?cache=0&sync_timestamp=1602012852731&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcss-declaration-sorter%2Fdownload%2Fcss-declaration-sorter-4.0.1.tgz", - "integrity": "sha1-wZiUD2OnbX42wecQGLABchBUyyI=", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", "dev": true, "requires": { "postcss": "^7.0.1", @@ -2209,8 +16621,8 @@ "dependencies": { "postcss": { "version": "7.0.35", - "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-7.0.35.tgz", - "integrity": "sha1-0r4AuZj38hHYonaXQHny6SuXDiQ=", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -2220,8 +16632,8 @@ }, "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=", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -2231,7 +16643,7 @@ }, "css-loader": { "version": "0.28.11", - "resolved": "https://registry.npm.taobao.org/css-loader/download/css-loader-0.28.11.tgz", + "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, "requires": { @@ -2313,22 +16725,10 @@ } } }, - "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.npm.taobao.org/css-select-base-adapter/download/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha1-Oy/0lyzDYquIVhUHqVQIoUMhNdc=", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", "dev": true }, "css-selector-tokenizer": { @@ -2343,19 +16743,18 @@ }, "css-tree": { "version": "1.0.0-alpha.37", - "resolved": "https://registry.npm.taobao.org/css-tree/download/css-tree-1.0.0-alpha.37.tgz", - "integrity": "sha1-mL69YsTB2flg7DQM+fdSLjBwmiI=", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", "dev": true, "requires": { "mdn-data": "2.0.4", "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", @@ -2490,9 +16889,9 @@ } }, "cssnano-preset-default": { - "version": "4.0.7", - "resolved": "https://registry.npm.taobao.org/cssnano-preset-default/download/cssnano-preset-default-4.0.7.tgz?cache=0&sync_timestamp=1599670481433&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcssnano-preset-default%2Fdownload%2Fcssnano-preset-default-4.0.7.tgz", - "integrity": "sha1-UexmLM/KD4izltzZZ5zbkxvhf3Y=", + "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, "requires": { "css-declaration-sorter": "^4.0.1", @@ -2523,26 +16922,27 @@ "postcss-ordered-values": "^4.1.2", "postcss-reduce-initial": "^4.0.3", "postcss-reduce-transforms": "^4.0.2", - "postcss-svgo": "^4.0.2", + "postcss-svgo": "^4.0.3", "postcss-unique-selectors": "^4.0.1" }, "dependencies": { "browserslist": { - "version": "4.14.5", - "resolved": "https://registry.npm.taobao.org/browserslist/download/browserslist-4.14.5.tgz", - "integrity": "sha1-HHUUYaEC3cYOQJk2ObcJvn8sQBU=", + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001135", - "electron-to-chromium": "^1.3.571", - "escalade": "^3.1.0", - "node-releases": "^1.1.61" + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" } }, "caniuse-api": { "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/caniuse-api/download/caniuse-api-3.0.0.tgz", - "integrity": "sha1-Xk2Q4idJYdRikZl99Znj7QCO5MA=", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -2553,8 +16953,8 @@ }, "coa": { "version": "2.0.2", - "resolved": "https://registry.npm.taobao.org/coa/download/coa-2.0.2.tgz", - "integrity": "sha1-Q/bCEVG07yv1cYfbDXPeIp4+fsM=", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", "dev": true, "requires": { "@types/q": "^1.5.1", @@ -2564,8 +16964,8 @@ }, "color": { "version": "3.1.3", - "resolved": "https://registry.npm.taobao.org/color/download/color-3.1.3.tgz", - "integrity": "sha1-ymf7TnuX1hHc3jns7tQiBn2RWW4=", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz", + "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==", "dev": true, "requires": { "color-convert": "^1.9.1", @@ -2573,9 +16973,9 @@ } }, "color-string": { - "version": "1.5.4", - "resolved": "https://registry.npm.taobao.org/color-string/download/color-string-1.5.4.tgz?cache=0&sync_timestamp=1602228058149&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcolor-string%2Fdownload%2Fcolor-string-1.5.4.tgz", - "integrity": "sha1-3VHNJc/ulT0Tj+QAI3LMPQ5QTLY=", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz", + "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==", "dev": true, "requires": { "color-name": "^1.0.0", @@ -2584,8 +16984,8 @@ }, "css-select": { "version": "2.1.0", - "resolved": "https://registry.npm.taobao.org/css-select/download/css-select-2.1.0.tgz?cache=0&sync_timestamp=1601657992127&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcss-select%2Fdownload%2Fcss-select-2.1.0.tgz", - "integrity": "sha1-ajRlM1ZjWTSoG6ymjQJVQyEF2+8=", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", "dev": true, "requires": { "boolbase": "^1.0.0", @@ -2596,26 +16996,26 @@ }, "css-what": { "version": "3.4.2", - "resolved": "https://registry.npm.taobao.org/css-what/download/css-what-3.4.2.tgz", - "integrity": "sha1-6nAm/LAXd+295SEk4h8yfnrpUOQ=", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", "dev": true }, "csso": { - "version": "4.0.3", - "resolved": "https://registry.npm.taobao.org/csso/download/csso-4.0.3.tgz", - "integrity": "sha1-DZmF3IUsfMKyys+74QeQFNGo6QM=", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", "dev": true, "requires": { - "css-tree": "1.0.0-alpha.39" + "css-tree": "^1.1.2" }, "dependencies": { "css-tree": { - "version": "1.0.0-alpha.39", - "resolved": "https://registry.npm.taobao.org/css-tree/download/css-tree-1.0.0-alpha.39.tgz", - "integrity": "sha1-K/8//huz93bPfu/ZHuXLp3oUnus=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", "dev": true, "requires": { - "mdn-data": "2.0.6", + "mdn-data": "2.0.14", "source-map": "^0.6.1" } } @@ -2623,8 +17023,8 @@ }, "domutils": { "version": "1.7.0", - "resolved": "https://registry.npm.taobao.org/domutils/download/domutils-1.7.0.tgz", - "integrity": "sha1-Vuo0HoNOBuZ0ivehyyXaZ+qfjCo=", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", "dev": true, "requires": { "dom-serializer": "0", @@ -2633,23 +17033,14 @@ }, "esprima": { "version": "4.0.1", - "resolved": "https://registry.npm.taobao.org/esprima/download/esprima-4.0.1.tgz", - "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, - "is-svg": { - "version": "3.0.0", - "resolved": "https://registry.npm.taobao.org/is-svg/download/is-svg-3.0.0.tgz", - "integrity": "sha1-kyHb0pwhLlypnE+peUxxS8r6L3U=", - "dev": true, - "requires": { - "html-comment-regex": "^1.1.0" - } - }, "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=", + "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, "requires": { "argparse": "^1.0.7", @@ -2657,21 +17048,21 @@ } }, "mdn-data": { - "version": "2.0.6", - "resolved": "https://registry.npm.taobao.org/mdn-data/download/mdn-data-2.0.6.tgz", - "integrity": "sha1-hS3GD8ql2qLoz2yRicRA7T4EKXg=", + "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 }, "normalize-url": { "version": "3.3.0", - "resolved": "https://registry.npm.taobao.org/normalize-url/download/normalize-url-3.3.0.tgz?cache=0&sync_timestamp=1601463025217&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnormalize-url%2Fdownload%2Fnormalize-url-3.3.0.tgz", - "integrity": "sha1-suHE3E98bVd0PfczpPWXjRhlBVk=", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", "dev": true }, "postcss": { "version": "7.0.35", - "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-7.0.35.tgz", - "integrity": "sha1-0r4AuZj38hHYonaXQHny6SuXDiQ=", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -2681,19 +17072,27 @@ }, "postcss-calc": { "version": "7.0.5", - "resolved": "https://registry.npm.taobao.org/postcss-calc/download/postcss-calc-7.0.5.tgz?cache=0&sync_timestamp=1601732624081&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-calc%2Fdownload%2Fpostcss-calc-7.0.5.tgz", - "integrity": "sha1-+KbpnxLmGcLrwjz2xIb9wVhgkz4=", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", + "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", "dev": true, "requires": { "postcss": "^7.0.27", "postcss-selector-parser": "^6.0.2", "postcss-value-parser": "^4.0.2" + }, + "dependencies": { + "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 + } } }, "postcss-colormin": { "version": "4.0.3", - "resolved": "https://registry.npm.taobao.org/postcss-colormin/download/postcss-colormin-4.0.3.tgz?cache=0&sync_timestamp=1599670485775&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-colormin%2Fdownload%2Fpostcss-colormin-4.0.3.tgz", - "integrity": "sha1-rgYLzpPteUrHEmTwgTLVUJVr04E=", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -2701,38 +17100,22 @@ "has": "^1.0.0", "postcss": "^7.0.0", "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "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 - } } }, "postcss-convert-values": { "version": "4.0.1", - "resolved": "https://registry.npm.taobao.org/postcss-convert-values/download/postcss-convert-values-4.0.1.tgz?cache=0&sync_timestamp=1599674174819&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-convert-values%2Fdownload%2Fpostcss-convert-values-4.0.1.tgz", - "integrity": "sha1-yjgT7U2g+BL51DcDWE5Enr4Ymn8=", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", "dev": true, "requires": { "postcss": "^7.0.0", "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "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 - } } }, "postcss-discard-comments": { "version": "4.0.2", - "resolved": "https://registry.npm.taobao.org/postcss-discard-comments/download/postcss-discard-comments-4.0.2.tgz?cache=0&sync_timestamp=1599674175141&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-discard-comments%2Fdownload%2Fpostcss-discard-comments-4.0.2.tgz", - "integrity": "sha1-H7q9LCRr/2qq15l7KwkY9NevQDM=", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", "dev": true, "requires": { "postcss": "^7.0.0" @@ -2740,8 +17123,8 @@ }, "postcss-discard-duplicates": { "version": "4.0.2", - "resolved": "https://registry.npm.taobao.org/postcss-discard-duplicates/download/postcss-discard-duplicates-4.0.2.tgz?cache=0&sync_timestamp=1599674175412&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-discard-duplicates%2Fdownload%2Fpostcss-discard-duplicates-4.0.2.tgz", - "integrity": "sha1-P+EzzTyCKC5VD8myORdqkge3hOs=", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", "dev": true, "requires": { "postcss": "^7.0.0" @@ -2749,8 +17132,8 @@ }, "postcss-discard-empty": { "version": "4.0.1", - "resolved": "https://registry.npm.taobao.org/postcss-discard-empty/download/postcss-discard-empty-4.0.1.tgz?cache=0&sync_timestamp=1599670482017&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-discard-empty%2Fdownload%2Fpostcss-discard-empty-4.0.1.tgz", - "integrity": "sha1-yMlR6fc+2UKAGUWERKAq2Qu592U=", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", "dev": true, "requires": { "postcss": "^7.0.0" @@ -2758,8 +17141,8 @@ }, "postcss-discard-overridden": { "version": "4.0.1", - "resolved": "https://registry.npm.taobao.org/postcss-discard-overridden/download/postcss-discard-overridden-4.0.1.tgz?cache=0&sync_timestamp=1599670482109&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-discard-overridden%2Fdownload%2Fpostcss-discard-overridden-4.0.1.tgz", - "integrity": "sha1-ZSrvipZybwKfXj4AFG7npOdV/1c=", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", "dev": true, "requires": { "postcss": "^7.0.0" @@ -2767,28 +17150,20 @@ }, "postcss-merge-longhand": { "version": "4.0.11", - "resolved": "https://registry.npm.taobao.org/postcss-merge-longhand/download/postcss-merge-longhand-4.0.11.tgz?cache=0&sync_timestamp=1599670482411&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-merge-longhand%2Fdownload%2Fpostcss-merge-longhand-4.0.11.tgz", - "integrity": "sha1-YvSaE+Sg7gTnuY9CuxYGLKJUniQ=", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", "dev": true, "requires": { "css-color-names": "0.0.4", "postcss": "^7.0.0", "postcss-value-parser": "^3.0.0", "stylehacks": "^4.0.0" - }, - "dependencies": { - "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 - } } }, "postcss-merge-rules": { "version": "4.0.3", - "resolved": "https://registry.npm.taobao.org/postcss-merge-rules/download/postcss-merge-rules-4.0.3.tgz?cache=0&sync_timestamp=1599670482522&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-merge-rules%2Fdownload%2Fpostcss-merge-rules-4.0.3.tgz", - "integrity": "sha1-NivqT/Wh+Y5AdacTxsslrv75plA=", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -2801,8 +17176,8 @@ "dependencies": { "postcss-selector-parser": { "version": "3.1.2", - "resolved": "https://registry.npm.taobao.org/postcss-selector-parser/download/postcss-selector-parser-3.1.2.tgz?cache=0&sync_timestamp=1601045323543&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-selector-parser%2Fdownload%2Fpostcss-selector-parser-3.1.2.tgz", - "integrity": "sha1-sxD1xMD9r3b5SQK7qjDbaqhPUnA=", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", "dev": true, "requires": { "dot-prop": "^5.2.0", @@ -2814,46 +17189,30 @@ }, "postcss-minify-font-values": { "version": "4.0.2", - "resolved": "https://registry.npm.taobao.org/postcss-minify-font-values/download/postcss-minify-font-values-4.0.2.tgz?cache=0&sync_timestamp=1599670482669&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-minify-font-values%2Fdownload%2Fpostcss-minify-font-values-4.0.2.tgz", - "integrity": "sha1-zUw0TM5HQ0P6xdgiBqssvLiv1aY=", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", "dev": true, "requires": { "postcss": "^7.0.0", "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "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 - } } }, "postcss-minify-gradients": { "version": "4.0.2", - "resolved": "https://registry.npm.taobao.org/postcss-minify-gradients/download/postcss-minify-gradients-4.0.2.tgz?cache=0&sync_timestamp=1599670482761&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-minify-gradients%2Fdownload%2Fpostcss-minify-gradients-4.0.2.tgz", - "integrity": "sha1-k7KcL/UJnFNe7NpWxKpuZlpmNHE=", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", "dev": true, "requires": { "cssnano-util-get-arguments": "^4.0.0", "is-color-stop": "^1.0.0", "postcss": "^7.0.0", "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "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 - } } }, "postcss-minify-params": { "version": "4.0.2", - "resolved": "https://registry.npm.taobao.org/postcss-minify-params/download/postcss-minify-params-4.0.2.tgz?cache=0&sync_timestamp=1599670482880&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-minify-params%2Fdownload%2Fpostcss-minify-params-4.0.2.tgz", - "integrity": "sha1-a5zvAwwR41Jh+V9hjJADbWgNuHQ=", + "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, "requires": { "alphanum-sort": "^1.0.0", @@ -2862,20 +17221,12 @@ "postcss": "^7.0.0", "postcss-value-parser": "^3.0.0", "uniqs": "^2.0.0" - }, - "dependencies": { - "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 - } } }, "postcss-minify-selectors": { "version": "4.0.2", - "resolved": "https://registry.npm.taobao.org/postcss-minify-selectors/download/postcss-minify-selectors-4.0.2.tgz?cache=0&sync_timestamp=1599670483082&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-minify-selectors%2Fdownload%2Fpostcss-minify-selectors-4.0.2.tgz", - "integrity": "sha1-4uXrQL/uUA0M2SQ1APX46kJi+9g=", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", "dev": true, "requires": { "alphanum-sort": "^1.0.0", @@ -2886,8 +17237,8 @@ "dependencies": { "postcss-selector-parser": { "version": "3.1.2", - "resolved": "https://registry.npm.taobao.org/postcss-selector-parser/download/postcss-selector-parser-3.1.2.tgz?cache=0&sync_timestamp=1601045323543&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-selector-parser%2Fdownload%2Fpostcss-selector-parser-3.1.2.tgz", - "integrity": "sha1-sxD1xMD9r3b5SQK7qjDbaqhPUnA=", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", "dev": true, "requires": { "dot-prop": "^5.2.0", @@ -2899,8 +17250,8 @@ }, "postcss-normalize-charset": { "version": "4.0.1", - "resolved": "https://registry.npm.taobao.org/postcss-normalize-charset/download/postcss-normalize-charset-4.0.1.tgz?cache=0&sync_timestamp=1599670482967&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-normalize-charset%2Fdownload%2Fpostcss-normalize-charset-4.0.1.tgz", - "integrity": "sha1-izWt067oOhNrBHHg1ZvlilAoXdQ=", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", "dev": true, "requires": { "postcss": "^7.0.0" @@ -2908,47 +17259,31 @@ }, "postcss-normalize-url": { "version": "4.0.1", - "resolved": "https://registry.npm.taobao.org/postcss-normalize-url/download/postcss-normalize-url-4.0.1.tgz?cache=0&sync_timestamp=1599670483753&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-normalize-url%2Fdownload%2Fpostcss-normalize-url-4.0.1.tgz", - "integrity": "sha1-EOQ3+GvHx+WPe5ZS7YeNqqlfquE=", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", "dev": true, "requires": { "is-absolute-url": "^2.0.0", "normalize-url": "^3.0.0", "postcss": "^7.0.0", "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "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 - } } }, "postcss-ordered-values": { "version": "4.1.2", - "resolved": "https://registry.npm.taobao.org/postcss-ordered-values/download/postcss-ordered-values-4.1.2.tgz?cache=0&sync_timestamp=1599670483946&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-ordered-values%2Fdownload%2Fpostcss-ordered-values-4.1.2.tgz", - "integrity": "sha1-DPdcgg7H1cTSgBiVWeC1ceusDu4=", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", "dev": true, "requires": { "cssnano-util-get-arguments": "^4.0.0", "postcss": "^7.0.0", "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "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 - } } }, "postcss-reduce-initial": { "version": "4.0.3", - "resolved": "https://registry.npm.taobao.org/postcss-reduce-initial/download/postcss-reduce-initial-4.0.3.tgz?cache=0&sync_timestamp=1599670484036&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-reduce-initial%2Fdownload%2Fpostcss-reduce-initial-4.0.3.tgz", - "integrity": "sha1-f9QuvqXpyBRgljniwuhK4nC6SN8=", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -2959,60 +17294,41 @@ }, "postcss-reduce-transforms": { "version": "4.0.2", - "resolved": "https://registry.npm.taobao.org/postcss-reduce-transforms/download/postcss-reduce-transforms-4.0.2.tgz?cache=0&sync_timestamp=1599670484213&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-reduce-transforms%2Fdownload%2Fpostcss-reduce-transforms-4.0.2.tgz", - "integrity": "sha1-F++kBerMbge+NBSlyi0QdGgdTik=", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", "dev": true, "requires": { "cssnano-util-get-match": "^4.0.0", "has": "^1.0.0", "postcss": "^7.0.0", "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "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 - } } }, "postcss-selector-parser": { - "version": "6.0.4", - "resolved": "https://registry.npm.taobao.org/postcss-selector-parser/download/postcss-selector-parser-6.0.4.tgz?cache=0&sync_timestamp=1601045323543&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-selector-parser%2Fdownload%2Fpostcss-selector-parser-6.0.4.tgz", - "integrity": "sha1-VgdaE4CgRgTDiwY+p3Z6Epr1wrM=", + "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, "requires": { "cssesc": "^3.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1", "util-deprecate": "^1.0.2" } }, "postcss-svgo": { - "version": "4.0.2", - "resolved": "https://registry.npm.taobao.org/postcss-svgo/download/postcss-svgo-4.0.2.tgz?cache=0&sync_timestamp=1599670484324&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-svgo%2Fdownload%2Fpostcss-svgo-4.0.2.tgz", - "integrity": "sha1-F7mXvHEbMzurFDqu07jT1uPTglg=", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.3.tgz", + "integrity": "sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw==", "dev": true, "requires": { - "is-svg": "^3.0.0", "postcss": "^7.0.0", "postcss-value-parser": "^3.0.0", "svgo": "^1.0.0" - }, - "dependencies": { - "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 - } } }, "postcss-unique-selectors": { "version": "4.0.1", - "resolved": "https://registry.npm.taobao.org/postcss-unique-selectors/download/postcss-unique-selectors-4.0.1.tgz?cache=0&sync_timestamp=1599670484407&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-unique-selectors%2Fdownload%2Fpostcss-unique-selectors-4.0.1.tgz", - "integrity": "sha1-lEaRHzKJv9ZMbWgPBzwDsfnuS6w=", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", "dev": true, "requires": { "alphanum-sort": "^1.0.0", @@ -3020,16 +17336,10 @@ "uniqs": "^2.0.0" } }, - "postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npm.taobao.org/postcss-value-parser/download/postcss-value-parser-4.1.0.tgz", - "integrity": "sha1-RD9qIM7WSBor2k+oUypuVdeJoss=", - "dev": true - }, "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=", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -3037,8 +17347,8 @@ }, "svgo": { "version": "1.3.2", - "resolved": "https://registry.npm.taobao.org/svgo/download/svgo-1.3.2.tgz", - "integrity": "sha1-ttxRHAYzRsnkFbgeQ0ARRbltQWc=", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", "dev": true, "requires": { "chalk": "^2.4.1", @@ -3060,20 +17370,20 @@ }, "cssnano-util-get-arguments": { "version": "4.0.0", - "resolved": "https://registry.npm.taobao.org/cssnano-util-get-arguments/download/cssnano-util-get-arguments-4.0.0.tgz", + "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=", "dev": true }, "cssnano-util-get-match": { "version": "4.0.0", - "resolved": "https://registry.npm.taobao.org/cssnano-util-get-match/download/cssnano-util-get-match-4.0.0.tgz", + "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=", "dev": true }, "cssnano-util-raw-cache": { "version": "4.0.1", - "resolved": "https://registry.npm.taobao.org/cssnano-util-raw-cache/download/cssnano-util-raw-cache-4.0.1.tgz", - "integrity": "sha1-sm1f1fcqEd/np4RvtMZyYPlr8oI=", + "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", "dev": true, "requires": { "postcss": "^7.0.0" @@ -3081,8 +17391,8 @@ "dependencies": { "postcss": { "version": "7.0.35", - "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-7.0.35.tgz", - "integrity": "sha1-0r4AuZj38hHYonaXQHny6SuXDiQ=", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -3092,8 +17402,8 @@ }, "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=", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -3103,8 +17413,8 @@ }, "cssnano-util-same-parent": { "version": "4.0.1", - "resolved": "https://registry.npm.taobao.org/cssnano-util-same-parent/download/cssnano-util-same-parent-4.0.1.tgz", - "integrity": "sha1-V0CC+yhZ0ttDOFWDXZqEVuoYu/M=", + "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 }, "csso": { @@ -3199,8 +17509,8 @@ }, "deepmerge": { "version": "1.5.2", - "resolved": "https://registry.npm.taobao.org/deepmerge/download/deepmerge-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", @@ -3378,9 +17688,9 @@ "dev": true }, "dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npm.taobao.org/dns-packet/download/dns-packet-1.3.1.tgz", - "integrity": "sha1-EqpCaYEHW+UAuRDu3NC0fdfe2lo=", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", "dev": true, "requires": { "ip": "^1.1.0", @@ -3435,29 +17745,10 @@ "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.npm.taobao.org/dot-prop/download/dot-prop-5.3.0.tgz?cache=0&sync_timestamp=1602107326908&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdot-prop%2Fdownload%2Fdot-prop-5.3.0.tgz", - "integrity": "sha1-kMzOcIzZzYLMTcjD3dmr3VWyDog=", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", "dev": true, "requires": { "is-obj": "^2.0.0" @@ -3483,12 +17774,28 @@ }, "echarts": { "version": "4.9.0", - "resolved": "https://registry.npm.taobao.org/echarts/download/echarts-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", @@ -3502,15 +17809,15 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.578", - "resolved": "https://registry.npm.taobao.org/electron-to-chromium/download/electron-to-chromium-1.3.578.tgz", - "integrity": "sha1-5mcZNvRXGodOsm4ugzqgssC3duA=", + "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 }, "element-ui": { - "version": "2.10.1", - "resolved": "https://registry.npm.taobao.org/element-ui/download/element-ui-2.10.1.tgz?cache=0&sync_timestamp=1589795164194&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Felement-ui%2Fdownload%2Felement-ui-2.10.1.tgz", - "integrity": "sha1-EBEBblDnb12ub7+chuU7blP8ues=", + "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", @@ -3521,18 +17828,18 @@ } }, "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npm.taobao.org/elliptic/download/elliptic-6.5.3.tgz?cache=0&sync_timestamp=1592492844326&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Felliptic%2Fdownload%2Felliptic-6.5.3.tgz", - "integrity": "sha1-y1nrLv2vc6C9eMzXAVpirW4Pk9Y=", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", "dev": true, "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", + "bn.js": "^4.11.9", + "brorand": "^1.1.0", "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" }, "dependencies": { "bn.js": { @@ -3610,22 +17917,27 @@ } }, "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npm.taobao.org/es-abstract/download/es-abstract-1.17.7.tgz", - "integrity": "sha1-pN5hsvZpifx0IWdsHLl4dXOs5Uw=", + "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, "requires": { + "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.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", + "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.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" } }, "es-to-primitive": { @@ -3723,9 +18035,9 @@ } }, "escalade": { - "version": "3.1.0", - "resolved": "https://registry.npm.taobao.org/escalade/download/escalade-3.1.0.tgz?cache=0&sync_timestamp=1600103337623&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fescalade%2Fdownload%2Fescalade-3.1.0.tgz", - "integrity": "sha1-6OLXx6i3b27mTCGB1rgVFEFgLU4=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, "escape-html": { @@ -4043,9 +18355,9 @@ } }, "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=", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, "fast-json-stable-stringify": { @@ -4079,30 +18391,6 @@ "schema-utils": "^0.4.5" }, "dependencies": { - "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, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "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 - }, - "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 - }, "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", @@ -4306,12 +18594,44 @@ "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", "integrity": "sha1-+Xj6TJDR3+f/LWvtoqUV5xO9z0o=", "dev": true }, + "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, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-stdin": { "version": "4.0.1", "resolved": "https://registry.npm.taobao.org/get-stdin/download/get-stdin-4.0.1.tgz", @@ -4345,9 +18665,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": { @@ -4428,15 +18748,21 @@ "ansi-regex": "^2.0.0" } }, + "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 + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/has-symbols/download/has-symbols-1.0.1.tgz", - "integrity": "sha1-n1IUdYpEGWxAbZvXbOv4HsLdMeg=", + "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 }, "has-value": { @@ -4545,8 +18871,8 @@ }, "hex-color-regex": { "version": "1.1.0", - "resolved": "https://registry.npm.taobao.org/hex-color-regex/download/hex-color-regex-1.1.0.tgz", - "integrity": "sha1-TAb8y0YC/iYCs8k9+C1+fb8aio4=", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", "dev": true }, "hmac-drbg": { @@ -4571,9 +18897,9 @@ } }, "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-2.8.8.tgz?cache=0&sync_timestamp=1594427968487&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhosted-git-info%2Fdownload%2Fhosted-git-info-2.8.8.tgz", - "integrity": "sha1-dTm9S8Hg4KiVgVouAmJCCxKFhIg=", + "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 }, "hpack.js": { @@ -4590,13 +18916,13 @@ }, "hsl-regex": { "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/hsl-regex/download/hsl-regex-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", "dev": true }, "hsla-regex": { "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/hsla-regex/download/hsla-regex-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", "dev": true }, @@ -4667,39 +18993,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", @@ -4789,8 +19082,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", @@ -4948,6 +19240,12 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "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 + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npm.taobao.org/is-binary-path/download/is-binary-path-2.1.0.tgz", @@ -4958,6 +19256,15 @@ "binary-extensions": "^2.0.0" } }, + "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, + "requires": { + "call-bind": "^1.0.2" + } + }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npm.taobao.org/is-buffer/download/is-buffer-1.1.6.tgz", @@ -4965,14 +19272,14 @@ "dev": true }, "is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npm.taobao.org/is-callable/download/is-callable-1.2.2.tgz?cache=0&sync_timestamp=1600721424274&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-callable%2Fdownload%2Fis-callable-1.2.2.tgz", - "integrity": "sha1-x8ZxXNItTdtI0+GZcCI6zquwgNk=", + "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 }, "is-color-stop": { "version": "1.1.0", - "resolved": "https://registry.npm.taobao.org/is-color-stop/download/is-color-stop-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", "dev": true, "requires": { @@ -5058,9 +19365,9 @@ } }, "is-negative-zero": { - "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/is-negative-zero/download/is-negative-zero-2.0.0.tgz", - "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", + "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 }, "is-number": { @@ -5070,10 +19377,16 @@ "dev": true, "optional": true }, + "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 + }, "is-obj": { "version": "2.0.0", - "resolved": "https://registry.npm.taobao.org/is-obj/download/is-obj-2.0.0.tgz?cache=0&sync_timestamp=1597763179449&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-obj%2Fdownload%2Fis-obj-2.0.0.tgz", - "integrity": "sha1-Rz+wXZc3BeP9liBUUBjKjiLvSYI=", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", "dev": true }, "is-path-cwd": { @@ -5116,12 +19429,13 @@ } }, "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npm.taobao.org/is-regex/download/is-regex-1.1.1.tgz?cache=0&sync_timestamp=1596555650246&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-regex%2Fdownload%2Fis-regex-1.1.1.tgz", - "integrity": "sha1-xvmKrMVG9s7FRooHt7FTq1ZKV7k=", + "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, "requires": { - "has-symbols": "^1.0.1" + "call-bind": "^1.0.2", + "has-symbols": "^1.0.2" } }, "is-resolvable": { @@ -5137,9 +19451,9 @@ "dev": true }, "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npm.taobao.org/is-string/download/is-string-1.0.5.tgz", - "integrity": "sha1-QEk+0ZjvP/R3uMf5L2ROyCpc06Y=", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", + "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==", "dev": true }, "is-svg": { @@ -5196,6 +19510,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", @@ -5210,7 +19529,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": { @@ -5237,11 +19556,16 @@ "dev": true }, "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=", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "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", @@ -5294,13 +19618,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", @@ -5370,9 +19691,9 @@ } }, "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npm.taobao.org/lodash/download/lodash-4.17.20.tgz?cache=0&sync_timestamp=1597336097104&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash%2Fdownload%2Flodash-4.17.20.tgz", - "integrity": "sha1-tEqbYpe8tpjxxRo1RaKzs2jVnFI=", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "lodash.camelcase": { @@ -5479,29 +19800,15 @@ "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.2.22", - "resolved": "https://registry.npm.taobao.org/math-expression-evaluator/download/math-expression-evaluator-1.2.22.tgz", - "integrity": "sha1-wU3LPYtNFQ5dzqnGjI2tgDCbDV4=", + "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 }, "md5.js": { @@ -5517,15 +19824,10 @@ }, "mdn-data": { "version": "2.0.4", - "resolved": "https://registry.npm.taobao.org/mdn-data/download/mdn-data-2.0.4.tgz", - "integrity": "sha1-aZs8OKxvHXKAkaZGULZdOIUC/Vs=", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "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", @@ -5851,8 +20153,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", @@ -5924,7 +20225,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", @@ -6062,9 +20364,9 @@ } }, "node-releases": { - "version": "1.1.61", - "resolved": "https://registry.npm.taobao.org/node-releases/download/node-releases-1.1.61.tgz?cache=0&sync_timestamp=1599612952711&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnode-releases%2Fdownload%2Fnode-releases-1.1.61.tgz", - "integrity": "sha1-cHsPypzk4ReDYSukovy6CQR68W4=", + "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 }, "normalize-package-data": { @@ -6138,6 +20440,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", @@ -6167,9 +20474,9 @@ } }, "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npm.taobao.org/object-inspect/download/object-inspect-1.8.0.tgz?cache=0&sync_timestamp=1592545152574&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fobject-inspect%2Fdownload%2Fobject-inspect-1.8.0.tgz", - "integrity": "sha1-34B+Xs9TpgnMa/6T6sPMe+WzqdA=", + "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 }, "object-is": { @@ -6180,28 +20487,6 @@ "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.18.0-next.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npm.taobao.org/es-abstract/download/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha1-bjoKS9pxflAjqzuOkL7DYQjSLGg=", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } } }, "object-keys": { @@ -6220,47 +20505,26 @@ } }, "object.assign": { - "version": "4.1.1", - "resolved": "https://registry.npm.taobao.org/object.assign/download/object.assign-4.1.1.tgz?cache=0&sync_timestamp=1599844895115&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fobject.assign%2Fdownload%2Fobject.assign-4.1.1.tgz", - "integrity": "sha1-MDhnpmbN1Bk27N7fsfjz4ypHjN0=", + "version": "4.1.2", + "resolved": "https://registry.nlark.com/object.assign/download/object.assign-4.1.2.tgz", + "integrity": "sha1-DtVKNC7Os3s4/3brgxoOeIy2OUA=", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.0", "has-symbols": "^1.0.1", "object-keys": "^1.1.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npm.taobao.org/es-abstract/download/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha1-bjoKS9pxflAjqzuOkL7DYQjSLGg=", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } } }, "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npm.taobao.org/object.getownpropertydescriptors/download/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha1-Npvx+VktiridcS3O1cuBx8U1Jkk=", + "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, "requires": { + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" + "es-abstract": "^1.18.0-next.2" } }, "object.pick": { @@ -6273,15 +20537,14 @@ } }, "object.values": { - "version": "1.1.1", - "resolved": "https://registry.npm.taobao.org/object.values/download/object.values-1.1.1.tgz", - "integrity": "sha1-aKmezeNWt+kpWjxeDOMdyMlT3l4=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", + "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", "dev": true, "requires": { + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "has": "^1.0.3" + "es-abstract": "^1.18.2" } }, "obuf": { @@ -6290,6 +20553,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", @@ -6349,21 +20633,21 @@ }, "dependencies": { "cssnano": { - "version": "4.1.10", - "resolved": "https://registry.npm.taobao.org/cssnano/download/cssnano-4.1.10.tgz?cache=0&sync_timestamp=1599670481279&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcssnano%2Fdownload%2Fcssnano-4.1.10.tgz", - "integrity": "sha1-CsQfCxPRPUZUh+ERt3jULaYxuLI=", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.11.tgz", + "integrity": "sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g==", "dev": true, "requires": { "cosmiconfig": "^5.0.0", - "cssnano-preset-default": "^4.0.7", + "cssnano-preset-default": "^4.0.8", "is-resolvable": "^1.0.0", "postcss": "^7.0.0" } }, "postcss": { "version": "7.0.35", - "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-7.0.35.tgz", - "integrity": "sha1-0r4AuZj38hHYonaXQHny6SuXDiQ=", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -6417,8 +20701,8 @@ }, "os-locale": { "version": "2.1.0", - "resolved": "https://registry.npm.taobao.org/os-locale/download/os-locale-2.1.0.tgz", - "integrity": "sha1-QrwpAKa1uL0XN2yOiCtlr8zyS/I=", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "dev": true, "requires": { "execa": "^0.7.0", @@ -6507,6 +20791,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", @@ -6566,9 +20855,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": { @@ -6586,6 +20875,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", @@ -7432,30 +21730,6 @@ "schema-utils": "^0.4.0" }, "dependencies": { - "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, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "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 - }, - "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 - }, "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", @@ -8108,8 +22382,8 @@ }, "postcss-normalize-display-values": { "version": "4.0.2", - "resolved": "https://registry.npm.taobao.org/postcss-normalize-display-values/download/postcss-normalize-display-values-4.0.2.tgz?cache=0&sync_timestamp=1599670483192&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-normalize-display-values%2Fdownload%2Fpostcss-normalize-display-values-4.0.2.tgz", - "integrity": "sha1-Db4EpM6QY9RmftK+R2u4MMglk1o=", + "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, "requires": { "cssnano-util-get-match": "^4.0.0", @@ -8119,8 +22393,8 @@ "dependencies": { "postcss": { "version": "7.0.35", - "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-7.0.35.tgz", - "integrity": "sha1-0r4AuZj38hHYonaXQHny6SuXDiQ=", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -8130,8 +22404,8 @@ }, "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=", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -8141,8 +22415,8 @@ }, "postcss-normalize-positions": { "version": "4.0.2", - "resolved": "https://registry.npm.taobao.org/postcss-normalize-positions/download/postcss-normalize-positions-4.0.2.tgz?cache=0&sync_timestamp=1599670483273&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-normalize-positions%2Fdownload%2Fpostcss-normalize-positions-4.0.2.tgz", - "integrity": "sha1-BfdX+E8mBDc3g2ipH4ky1LECkX8=", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", "dev": true, "requires": { "cssnano-util-get-arguments": "^4.0.0", @@ -8153,8 +22427,8 @@ "dependencies": { "postcss": { "version": "7.0.35", - "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-7.0.35.tgz", - "integrity": "sha1-0r4AuZj38hHYonaXQHny6SuXDiQ=", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -8164,8 +22438,8 @@ }, "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=", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -8175,8 +22449,8 @@ }, "postcss-normalize-repeat-style": { "version": "4.0.2", - "resolved": "https://registry.npm.taobao.org/postcss-normalize-repeat-style/download/postcss-normalize-repeat-style-4.0.2.tgz?cache=0&sync_timestamp=1599670483443&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-normalize-repeat-style%2Fdownload%2Fpostcss-normalize-repeat-style-4.0.2.tgz", - "integrity": "sha1-xOu8KJ85kaAo1EdRy90RkYsXkQw=", + "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, "requires": { "cssnano-util-get-arguments": "^4.0.0", @@ -8187,8 +22461,8 @@ "dependencies": { "postcss": { "version": "7.0.35", - "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-7.0.35.tgz", - "integrity": "sha1-0r4AuZj38hHYonaXQHny6SuXDiQ=", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -8198,8 +22472,8 @@ }, "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=", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -8209,8 +22483,8 @@ }, "postcss-normalize-string": { "version": "4.0.2", - "resolved": "https://registry.npm.taobao.org/postcss-normalize-string/download/postcss-normalize-string-4.0.2.tgz?cache=0&sync_timestamp=1599670483365&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-normalize-string%2Fdownload%2Fpostcss-normalize-string-4.0.2.tgz", - "integrity": "sha1-zUTECrB6DHo23F6Zqs4eyk7CaQw=", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", "dev": true, "requires": { "has": "^1.0.0", @@ -8220,8 +22494,8 @@ "dependencies": { "postcss": { "version": "7.0.35", - "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-7.0.35.tgz", - "integrity": "sha1-0r4AuZj38hHYonaXQHny6SuXDiQ=", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -8231,8 +22505,8 @@ }, "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=", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -8242,8 +22516,8 @@ }, "postcss-normalize-timing-functions": { "version": "4.0.2", - "resolved": "https://registry.npm.taobao.org/postcss-normalize-timing-functions/download/postcss-normalize-timing-functions-4.0.2.tgz?cache=0&sync_timestamp=1599670483533&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-normalize-timing-functions%2Fdownload%2Fpostcss-normalize-timing-functions-4.0.2.tgz", - "integrity": "sha1-jgCcoqOUnNr4rSPmtquZy159KNk=", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", "dev": true, "requires": { "cssnano-util-get-match": "^4.0.0", @@ -8253,8 +22527,8 @@ "dependencies": { "postcss": { "version": "7.0.35", - "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-7.0.35.tgz", - "integrity": "sha1-0r4AuZj38hHYonaXQHny6SuXDiQ=", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -8264,8 +22538,8 @@ }, "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=", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -8275,8 +22549,8 @@ }, "postcss-normalize-unicode": { "version": "4.0.1", - "resolved": "https://registry.npm.taobao.org/postcss-normalize-unicode/download/postcss-normalize-unicode-4.0.1.tgz?cache=0&sync_timestamp=1599670483640&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-normalize-unicode%2Fdownload%2Fpostcss-normalize-unicode-4.0.1.tgz", - "integrity": "sha1-hBvUj9zzAZrUuqdJOj02O1KuHPs=", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -8285,21 +22559,22 @@ }, "dependencies": { "browserslist": { - "version": "4.14.5", - "resolved": "https://registry.npm.taobao.org/browserslist/download/browserslist-4.14.5.tgz", - "integrity": "sha1-HHUUYaEC3cYOQJk2ObcJvn8sQBU=", + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001135", - "electron-to-chromium": "^1.3.571", - "escalade": "^3.1.0", - "node-releases": "^1.1.61" + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" } }, "postcss": { "version": "7.0.35", - "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-7.0.35.tgz", - "integrity": "sha1-0r4AuZj38hHYonaXQHny6SuXDiQ=", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -8309,8 +22584,8 @@ }, "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=", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -8394,8 +22669,8 @@ }, "postcss-normalize-whitespace": { "version": "4.0.2", - "resolved": "https://registry.npm.taobao.org/postcss-normalize-whitespace/download/postcss-normalize-whitespace-4.0.2.tgz?cache=0&sync_timestamp=1599670483848&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-normalize-whitespace%2Fdownload%2Fpostcss-normalize-whitespace-4.0.2.tgz", - "integrity": "sha1-vx1AcP5Pzqh9E0joJdjMDF+qfYI=", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", "dev": true, "requires": { "postcss": "^7.0.0", @@ -8404,8 +22679,8 @@ "dependencies": { "postcss": { "version": "7.0.35", - "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-7.0.35.tgz", - "integrity": "sha1-0r4AuZj38hHYonaXQHny6SuXDiQ=", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -8415,8 +22690,8 @@ }, "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=", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -9036,6 +23311,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", @@ -9147,6 +23427,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", @@ -9184,6 +23474,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", @@ -9400,16 +23698,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": { @@ -9459,8 +23836,8 @@ }, "resize-observer-polyfill": { "version": "1.5.1", - "resolved": "https://registry.npm.taobao.org/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", @@ -9486,6 +23863,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", @@ -9510,13 +23895,13 @@ }, "rgb-regex": { "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/rgb-regex/download/rgb-regex-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", "dev": true }, "rgba-regex": { "version": "1.0.0", - "resolved": "https://registry.npm.taobao.org/rgba-regex/download/rgba-regex-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", "dev": true }, @@ -9557,6 +23942,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", @@ -9591,6 +23981,32 @@ "dev": true, "requires": { "ajv": "^5.0.0" + }, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha512-Ajr4IcMXq/2QmMkEmSvxqfLN5zGmJ92gHXAeOXq1OekoH2rfDNsgdDoL2f7QaRCy7G/E6TpxBVdRuNraMztGHw==", + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha512-fueX787WZKCV0Is4/T2cyAdM4+x1S3MXXOAhavE1ys/W42SHAPacLTQhucja22QBYrfGw50M2sRiXPtTGv9Ymw==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha512-4JD/Ivzg7PoW8NzdrBSr3UFwC9mHgvI7Z6z3QGBsSHgKaRTUDmyZAAKJo2UbG1kUVfS9WS8bi36N49U1xw43DA==", + "dev": true + } } }, "select": { @@ -9650,8 +24066,8 @@ }, "serialize-javascript": { "version": "1.9.1", - "resolved": "https://registry.npm.taobao.org/serialize-javascript/download/serialize-javascript-1.9.1.tgz?cache=0&sync_timestamp=1599740666792&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fserialize-javascript%2Fdownload%2Fserialize-javascript-1.9.1.tgz", - "integrity": "sha1-z8IArvd7YAxH2pu4FJyUPnmML9s=", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz", + "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==", "dev": true }, "serve-index": { @@ -9774,9 +24190,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", @@ -9798,7 +24214,7 @@ }, "simple-swizzle": { "version": "0.2.2", - "resolved": "https://registry.npm.taobao.org/simple-swizzle/download/simple-swizzle-0.2.2.tgz", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", "dev": true, "requires": { @@ -9807,8 +24223,8 @@ "dependencies": { "is-arrayish": { "version": "0.3.2", - "resolved": "https://registry.npm.taobao.org/is-arrayish/download/is-arrayish-0.3.2.tgz", - "integrity": "sha1-RXSirlb3qyBolvtDHq7tBm/fjwM=", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", "dev": true } } @@ -9935,6 +24351,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": { @@ -9962,6 +24386,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", @@ -9971,6 +24405,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", @@ -10134,12 +24577,13 @@ "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", - "resolved": "https://registry.npm.taobao.org/ssri/download/ssri-5.3.0.tgz", - "integrity": "sha1-ujhyycbTOgcEp9cf8EXl7EiZnQY=", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", + "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", "dev": true, "requires": { "safe-buffer": "^5.1.1" @@ -10147,8 +24591,8 @@ }, "stable": { "version": "0.1.8", - "resolved": "https://registry.npm.taobao.org/stable/download/stable-0.1.8.tgz", - "integrity": "sha1-g26zyDgv4pNv6vVEYxAXzn1Ho88=", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", "dev": true }, "stackframe": { @@ -10229,6 +24673,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", @@ -10257,32 +24710,23 @@ } }, "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/string.prototype.trimend/download/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha1-hYEqa4R6wAInD1gIFGBkyZX7aRM=", + "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, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" } }, "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/string.prototype.trimstart/download/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha1-FK9tnzSwU/fPyJty+PLuFLkDmlQ=", + "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, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "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" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" } }, "strip-ansi": { @@ -10316,58 +24760,39 @@ } }, "stylehacks": { - "version": "4.0.3", - "resolved": "https://registry.npm.taobao.org/stylehacks/download/stylehacks-4.0.3.tgz?cache=0&sync_timestamp=1599670484593&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstylehacks%2Fdownload%2Fstylehacks-4.0.3.tgz", - "integrity": "sha1-Zxj8r00eB9ihMYaQiB6NlnJqcdU=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.0.tgz", + "integrity": "sha1-ZLMjlRxKJOX8ey7AbBN78y0VXoo=", "dev": true, "requires": { "browserslist": "^4.0.0", - "postcss": "^7.0.0", + "postcss": "^6.0.0", "postcss-selector-parser": "^3.0.0" }, "dependencies": { "browserslist": { - "version": "4.14.5", - "resolved": "https://registry.npm.taobao.org/browserslist/download/browserslist-4.14.5.tgz", - "integrity": "sha1-HHUUYaEC3cYOQJk2ObcJvn8sQBU=", + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001135", - "electron-to-chromium": "^1.3.571", - "escalade": "^3.1.0", - "node-releases": "^1.1.61" - } - }, - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-7.0.35.tgz", - "integrity": "sha1-0r4AuZj38hHYonaXQHny6SuXDiQ=", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" } }, "postcss-selector-parser": { "version": "3.1.2", - "resolved": "https://registry.npm.taobao.org/postcss-selector-parser/download/postcss-selector-parser-3.1.2.tgz?cache=0&sync_timestamp=1601045323543&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-selector-parser%2Fdownload%2Fpostcss-selector-parser-3.1.2.tgz", - "integrity": "sha1-sxD1xMD9r3b5SQK7qjDbaqhPUnA=", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", "dev": true, "requires": { "dot-prop": "^5.2.0", "indexes-of": "^1.0.1", "uniq": "^1.0.1" } - }, - "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, - "requires": { - "has-flag": "^3.0.0" - } } } }, @@ -10392,6 +24817,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": { @@ -10402,8 +24839,8 @@ }, "throttle-debounce": { "version": "1.1.0", - "resolved": "https://registry.npm.taobao.org/throttle-debounce/download/throttle-debounce-1.1.0.tgz?cache=0&sync_timestamp=1597223490354&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fthrottle-debounce%2Fdownload%2Fthrottle-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", @@ -10438,7 +24875,7 @@ }, "timsort": { "version": "0.3.0", - "resolved": "https://registry.npm.taobao.org/timsort/download/timsort-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", "dev": true }, @@ -10548,11 +24985,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", @@ -10594,36 +25026,12 @@ "worker-farm": "^1.5.2" }, "dependencies": { - "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, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, "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 }, - "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 - }, - "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 - }, "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", @@ -10646,6 +25054,18 @@ } } }, + "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, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npm.taobao.org/union-value/download/union-value-1.0.1.tgz", @@ -10696,7 +25116,7 @@ }, "unquote": { "version": "1.1.1", - "resolved": "https://registry.npm.taobao.org/unquote/download/unquote-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", "dev": true }, @@ -10787,8 +25207,8 @@ }, "url-loader": { "version": "0.5.9", - "resolved": "https://registry.npm.taobao.org/url-loader/download/url-loader-0.5.9.tgz?cache=0&sync_timestamp=1602252665628&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Furl-loader%2Fdownload%2Furl-loader-0.5.9.tgz", - "integrity": "sha1-zI/qgse5Bud3cBklCGnlaemVwpU=", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.5.9.tgz", + "integrity": "sha512-B7QYFyvv+fOBqBVeefsxv6koWWtjmHaMFT6KZWti4KRw8YUD/hOU+3AECvXuzyVawIBx3z7zQRejXCDSO5kk1Q==", "dev": true, "requires": { "loader-utils": "^1.0.2", @@ -10804,9 +25224,9 @@ } }, "url-parse": { - "version": "1.4.7", - "resolved": "https://registry.npm.taobao.org/url-parse/download/url-parse-1.4.7.tgz", - "integrity": "sha1-qKg1NejACjFuQDpdtKwbm4U64ng=", + "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", @@ -10844,8 +25264,8 @@ }, "util.promisify": { "version": "1.0.1", - "resolved": "https://registry.npm.taobao.org/util.promisify/download/util.promisify-1.0.1.tgz", - "integrity": "sha1-a693dLgO6w91INi4HQeYKlmruu4=", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", "dev": true, "requires": { "define-properties": "^1.1.3", @@ -10860,6 +25280,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", @@ -10867,10 +25292,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", @@ -10905,18 +25341,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", @@ -10925,10 +25349,44 @@ "clipboard": "^2.0.0" } }, + "vue-clipboards": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/vue-clipboards/-/vue-clipboards-1.3.0.tgz", + "integrity": "sha512-VMDYHpLQH0EUmqfk9b5XMrkvSu/HjNsLW2EBR4OS6JZHcv/PxmWYdoTBPVlp5eYrhWy07La8nWpRwAh09Mgufw==", + "requires": { + "clipboard": "^1.7.1" + }, + "dependencies": { + "clipboard": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-1.7.1.tgz", + "integrity": "sha1-Ng1taUbpmnof7zleQrqStem1oWs=", + "requires": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + } + } + }, + "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=" + "version": "1.8.3", + "resolved": "https://registry.npmmirror.com/vue-cookies/-/vue-cookies-1.8.3.tgz", + "integrity": "sha512-VBRsyRMVdahBgFfh389TMHPmDdr4URDJNMk4FKSCfuNITs7+jitBDhwyL4RJd3WUsfOYNNjPAkfbehyH9AFuoA==" + }, + "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", @@ -11026,6 +25484,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", @@ -11229,10 +25692,20 @@ "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.npm.taobao.org/webpack/download/webpack-3.12.0.tgz?cache=0&sync_timestamp=1602283335551&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fwebpack%2Fdownload%2Fwebpack-3.12.0.tgz", - "integrity": "sha1-P540NgNwYC/PY56Xk520hvTsDXQ=", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.12.0.tgz", + "integrity": "sha512-Sw7MdIIOv/nkzPzee4o0EdvCuPmxT98+vVpIvwtcwcF1Q4SDSNp92vwcKc4REe7NItH9f1S4ra9FuQ7yuYZ8bQ==", "dev": true, "requires": { "acorn": "^5.0.0", @@ -11259,36 +25732,12 @@ "yargs": "^8.0.2" }, "dependencies": { - "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, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "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 - }, "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 }, - "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 - }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", @@ -11344,8 +25793,8 @@ }, "webpack-bundle-analyzer": { "version": "2.13.1", - "resolved": "https://registry.npm.taobao.org/webpack-bundle-analyzer/download/webpack-bundle-analyzer-2.13.1.tgz", - "integrity": "sha1-B9IXbG6Gw83OTCPlb64qe2tK1SY=", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.13.1.tgz", + "integrity": "sha512-rwxyfecTAxoarCC9VlHlIpfQCmmJ/qWD5bpbjkof+7HrNhTNZIwZITxN6CdlYL2axGmwNUQ+tFgcSOiNXMf/sQ==", "dev": true, "requires": { "acorn": "^5.3.0", @@ -11377,8 +25826,8 @@ }, "webpack-dev-server": { "version": "2.11.5", - "resolved": "https://registry.npm.taobao.org/webpack-dev-server/download/webpack-dev-server-2.11.5.tgz", - "integrity": "sha1-QW+96g4E7r5EpibnkdWi6zf+jEg=", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.11.5.tgz", + "integrity": "sha512-7TdOKKt7G3sWEhPKV0zP+nD0c4V9YKUJ3wDdBwQsZNo58oZIRoVIu66pg7PYkBW8A74msP9C2kLwmxGHndz/pw==", "dev": true, "requires": { "ansi-html": "0.0.7", @@ -11723,9 +26172,9 @@ "dev": true }, "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npm.taobao.org/y18n/download/y18n-3.2.1.tgz?cache=0&sync_timestamp=1601576779692&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fy18n%2Fdownload%2Fy18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", "dev": true }, "yargs": { @@ -11811,6 +26260,19 @@ "isexe": "^2.0.0" } }, + "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, + "requires": { + "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" + } + }, "which-module": { "version": "2.0.0", "resolved": "https://registry.npm.taobao.org/which-module/download/which-module-2.0.0.tgz", @@ -11886,6 +26348,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", @@ -11902,9 +26369,9 @@ } }, "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npm.taobao.org/y18n/download/y18n-4.0.0.tgz?cache=0&sync_timestamp=1601576779692&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fy18n%2Fdownload%2Fy18n-4.0.0.tgz", - "integrity": "sha1-le+U+F7MgdAHwmThkKEg8KPIVms=", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "yallist": { @@ -11974,9 +26441,9 @@ } }, "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npm.taobao.org/y18n/download/y18n-3.2.1.tgz?cache=0&sync_timestamp=1601576779692&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fy18n%2Fdownload%2Fy18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", "dev": true } } diff --git a/web_src/package.json b/web_src/package.json index 4a9f76600..255e78878 100644 --- a/web_src/package.json +++ b/web_src/package.json @@ -10,19 +10,25 @@ "build": "node build/build.js" }, "dependencies": { - "@easydarwin/easywasmplayer": "^4.0.7", - "axios": "^0.19.2", + "@liveqing/liveplayer": "^2.7.10", + "axios": "^0.24.0", "core-js": "^2.6.5", - "echarts": "^4.7.0", - "element-ui": "2.10.1", + "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-baidu-map": "^0.21.22", "vue-clipboard2": "^0.3.1", - "vue-cookies": "^1.7.4", - "vue-router": "^3.1.6" + "vue-clipboards": "^1.3.0", + "vue-contextmenujs": "^1.3.13", + "vue-cookies": "^1.8.3", + "vue-giant-tree": "^0.1.5", + "vue-router": "^3.1.6", + "vue-ztree-2.0": "^1.0.4" }, "devDependencies": { "autoprefixer": "^7.1.2", @@ -36,7 +42,7 @@ "babel-preset-stage-2": "^6.22.0", "chalk": "^2.0.1", "copy-webpack-plugin": "^4.6.0", - "css-loader": "^0.28.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", @@ -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 e1145454b..103cbe1a0 100644 --- a/web_src/src/App.vue +++ b/web_src/src/App.vue @@ -5,11 +5,13 @@ + + diff --git a/web_src/src/components/CloudRecordDetail.vue b/web_src/src/components/CloudRecordDetail.vue new file mode 100644 index 000000000..6983b543f --- /dev/null +++ b/web_src/src/components/CloudRecordDetail.vue @@ -0,0 +1,615 @@ + + + + + + diff --git a/web_src/src/components/DeviceList.vue b/web_src/src/components/DeviceList.vue new file mode 100644 index 000000000..5375e5e38 --- /dev/null +++ b/web_src/src/components/DeviceList.vue @@ -0,0 +1,355 @@ + + + + + diff --git a/web_src/src/components/GBRecordDetail.vue b/web_src/src/components/GBRecordDetail.vue new file mode 100644 index 000000000..f9fce20d8 --- /dev/null +++ b/web_src/src/components/GBRecordDetail.vue @@ -0,0 +1,525 @@ + + + + + + diff --git a/web_src/src/components/Login.vue b/web_src/src/components/Login.vue index 2a9188657..64a3cbd31 100644 --- a/web_src/src/components/Login.vue +++ b/web_src/src/components/Login.vue @@ -1,29 +1,48 @@ - diff --git a/web_src/src/components/MediaServerManger.vue b/web_src/src/components/MediaServerManger.vue new file mode 100644 index 000000000..1d3c057ca --- /dev/null +++ b/web_src/src/components/MediaServerManger.vue @@ -0,0 +1,181 @@ + + + + + diff --git a/web_src/src/components/ParentPlatformList.vue b/web_src/src/components/ParentPlatformList.vue new file mode 100644 index 000000000..c3c9735f0 --- /dev/null +++ b/web_src/src/components/ParentPlatformList.vue @@ -0,0 +1,193 @@ + + + + diff --git a/web_src/src/components/PushVideoList.vue b/web_src/src/components/PushVideoList.vue new file mode 100644 index 000000000..6aed98a30 --- /dev/null +++ b/web_src/src/components/PushVideoList.vue @@ -0,0 +1,353 @@ + + + + + diff --git a/web_src/src/components/StreamProxyList.vue b/web_src/src/components/StreamProxyList.vue new file mode 100644 index 000000000..ff079cb87 --- /dev/null +++ b/web_src/src/components/StreamProxyList.vue @@ -0,0 +1,379 @@ + + + + + diff --git a/web_src/src/components/UiHeader.vue b/web_src/src/components/UiHeader.vue deleted file mode 100644 index 8b84ef246..000000000 --- a/web_src/src/components/UiHeader.vue +++ /dev/null @@ -1,76 +0,0 @@ - - - diff --git a/web_src/src/components/UserManager.vue b/web_src/src/components/UserManager.vue new file mode 100644 index 000000000..c0fa695a4 --- /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 4ee2b7c18..a8e843395 100644 --- a/web_src/src/components/channelList.vue +++ b/web_src/src/components/channelList.vue @@ -1,353 +1,491 @@ diff --git a/web_src/src/components/common/ h265web.vue b/web_src/src/components/common/ h265web.vue new file mode 100644 index 000000000..4049721c0 --- /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 000000000..c701bf0fd --- /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 000000000..6c02bccaa --- /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 000000000..fb091ad6a --- /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 000000000..c02960e6c --- /dev/null +++ b/web_src/src/components/common/jessibuca.vue @@ -0,0 +1,347 @@ + + + + + diff --git a/web_src/src/components/console.vue b/web_src/src/components/console.vue new file mode 100644 index 000000000..e192fb1b7 --- /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 000000000..5aed07ed4 --- /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 000000000..ed69cc3f6 --- /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 000000000..566b469b5 --- /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 000000000..a842b5038 --- /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 000000000..22d4f343c --- /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 000000000..0596c41ee --- /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 000000000..3aa77715e --- /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 51bcb0a40..000000000 --- a/web_src/src/components/control.vue +++ /dev/null @@ -1,353 +0,0 @@ - - - - - diff --git a/web_src/src/components/devicePosition.vue b/web_src/src/components/devicePosition.vue deleted file mode 100644 index f6cf19e40..000000000 --- a/web_src/src/components/devicePosition.vue +++ /dev/null @@ -1,388 +0,0 @@ - - - - - diff --git a/web_src/src/components/dialog/MediaServerEdit.vue b/web_src/src/components/dialog/MediaServerEdit.vue new file mode 100644 index 000000000..9808a1c2e --- /dev/null +++ b/web_src/src/components/dialog/MediaServerEdit.vue @@ -0,0 +1,379 @@ + + + diff --git a/web_src/src/components/dialog/StreamProxyEdit.vue b/web_src/src/components/dialog/StreamProxyEdit.vue new file mode 100644 index 000000000..7ed893a7e --- /dev/null +++ b/web_src/src/components/dialog/StreamProxyEdit.vue @@ -0,0 +1,294 @@ + + + diff --git a/web_src/src/components/dialog/SyncChannelProgress.vue b/web_src/src/components/dialog/SyncChannelProgress.vue new file mode 100644 index 000000000..e1c9fe01b --- /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 000000000..8dc56827a --- /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 000000000..9a5e3a364 --- /dev/null +++ b/web_src/src/components/dialog/catalogEdit.vue @@ -0,0 +1,158 @@ + + + diff --git a/web_src/src/components/dialog/changePassword.vue b/web_src/src/components/dialog/changePassword.vue new file mode 100644 index 000000000..5ab2d2f54 --- /dev/null +++ b/web_src/src/components/dialog/changePassword.vue @@ -0,0 +1,132 @@ + + + diff --git a/web_src/src/components/dialog/changePasswordForAdmin.vue b/web_src/src/components/dialog/changePasswordForAdmin.vue new file mode 100644 index 000000000..5b9135778 --- /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 000000000..0b9834ef8 --- /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 000000000..2ef0e5295 --- /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 new file mode 100644 index 000000000..86bea8314 --- /dev/null +++ b/web_src/src/components/dialog/chooseChannel.vue @@ -0,0 +1,126 @@ + + + + + diff --git a/web_src/src/components/dialog/chooseChannelForCatalog.vue b/web_src/src/components/dialog/chooseChannelForCatalog.vue new file mode 100644 index 000000000..ae79e644f --- /dev/null +++ b/web_src/src/components/dialog/chooseChannelForCatalog.vue @@ -0,0 +1,317 @@ + + + + + + diff --git a/web_src/src/components/dialog/chooseChannelForGb.vue b/web_src/src/components/dialog/chooseChannelForGb.vue new file mode 100644 index 000000000..bc71577d9 --- /dev/null +++ b/web_src/src/components/dialog/chooseChannelForGb.vue @@ -0,0 +1,351 @@ + + + + + diff --git a/web_src/src/components/dialog/chooseChannelForStream.vue b/web_src/src/components/dialog/chooseChannelForStream.vue new file mode 100644 index 000000000..b522a63c9 --- /dev/null +++ b/web_src/src/components/dialog/chooseChannelForStream.vue @@ -0,0 +1,281 @@ + + + + + diff --git a/web_src/src/components/dialog/configInfo.vue b/web_src/src/components/dialog/configInfo.vue new file mode 100644 index 000000000..4ac90cc1c --- /dev/null +++ b/web_src/src/components/dialog/configInfo.vue @@ -0,0 +1,59 @@ + + + diff --git a/web_src/src/components/dialog/deviceEdit.vue b/web_src/src/components/dialog/deviceEdit.vue new file mode 100644 index 000000000..d833b132c --- /dev/null +++ b/web_src/src/components/dialog/deviceEdit.vue @@ -0,0 +1,158 @@ + + + diff --git a/web_src/src/components/gb28181/devicePlayer.vue b/web_src/src/components/dialog/devicePlayer.vue similarity index 57% rename from web_src/src/components/gb28181/devicePlayer.vue rename to web_src/src/components/dialog/devicePlayer.vue index 234183197..9e4eea00b 100644 --- a/web_src/src/components/gb28181/devicePlayer.vue +++ b/web_src/src/components/dialog/devicePlayer.vue @@ -1,116 +1,218 @@ diff --git a/web_src/src/components/dialog/easyPlayer.vue b/web_src/src/components/dialog/easyPlayer.vue new file mode 100644 index 000000000..daf72079f --- /dev/null +++ b/web_src/src/components/dialog/easyPlayer.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/web_src/src/components/dialog/getCatalog.vue b/web_src/src/components/dialog/getCatalog.vue new file mode 100644 index 000000000..fdc26de16 --- /dev/null +++ b/web_src/src/components/dialog/getCatalog.vue @@ -0,0 +1,167 @@ + + + + + + diff --git a/web_src/src/components/dialog/importChannel.vue b/web_src/src/components/dialog/importChannel.vue new file mode 100644 index 000000000..e61b642ef --- /dev/null +++ b/web_src/src/components/dialog/importChannel.vue @@ -0,0 +1,131 @@ + + + + diff --git a/web_src/src/components/dialog/importChannelShowErrorData.vue b/web_src/src/components/dialog/importChannelShowErrorData.vue new file mode 100644 index 000000000..5194b7eee --- /dev/null +++ b/web_src/src/components/dialog/importChannelShowErrorData.vue @@ -0,0 +1,64 @@ + + + + diff --git a/web_src/src/components/dialog/onvifEdit.vue b/web_src/src/components/dialog/onvifEdit.vue new file mode 100644 index 000000000..f36ce4a0d --- /dev/null +++ b/web_src/src/components/dialog/onvifEdit.vue @@ -0,0 +1,121 @@ + + + diff --git a/web_src/src/components/dialog/platformEdit.vue b/web_src/src/components/dialog/platformEdit.vue new file mode 100644 index 000000000..0158cf742 --- /dev/null +++ b/web_src/src/components/dialog/platformEdit.vue @@ -0,0 +1,517 @@ + + + + + diff --git a/web_src/src/components/dialog/pushStreamEdit.vue b/web_src/src/components/dialog/pushStreamEdit.vue new file mode 100644 index 000000000..de4e7bcc8 --- /dev/null +++ b/web_src/src/components/dialog/pushStreamEdit.vue @@ -0,0 +1,180 @@ + + + diff --git a/web_src/src/components/dialog/queryTrace.vue b/web_src/src/components/dialog/queryTrace.vue new file mode 100644 index 000000000..5063ad4e7 --- /dev/null +++ b/web_src/src/components/dialog/queryTrace.vue @@ -0,0 +1,104 @@ + + + diff --git a/web_src/src/components/dialog/recordDownload.vue b/web_src/src/components/dialog/recordDownload.vue new file mode 100644 index 000000000..7a945400d --- /dev/null +++ b/web_src/src/components/dialog/recordDownload.vue @@ -0,0 +1,215 @@ + + + + + + diff --git a/web_src/src/components/dialog/rtcPlayer.vue b/web_src/src/components/dialog/rtcPlayer.vue new file mode 100644 index 000000000..f957df711 --- /dev/null +++ b/web_src/src/components/dialog/rtcPlayer.vue @@ -0,0 +1,114 @@ + + + + + diff --git a/web_src/src/components/gb28181/player.vue b/web_src/src/components/gb28181/player.vue deleted file mode 100644 index 0abf5c05b..000000000 --- a/web_src/src/components/gb28181/player.vue +++ /dev/null @@ -1,57 +0,0 @@ - - - - - \ No newline at end of file diff --git a/web_src/src/components/live.vue b/web_src/src/components/live.vue new file mode 100644 index 000000000..4a7af491e --- /dev/null +++ b/web_src/src/components/live.vue @@ -0,0 +1,311 @@ + + + + + diff --git a/web_src/src/components/map.vue b/web_src/src/components/map.vue new file mode 100644 index 000000000..248426cea --- /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 000000000..85d36f8b3 --- /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 new file mode 100644 index 000000000..d4446f06f --- /dev/null +++ b/web_src/src/components/service/MediaServer.js @@ -0,0 +1,99 @@ +import axios from 'axios'; + +class MediaServer{ + + constructor() { + this.$axios = axios; + } + + getOnlineMediaServerList(callback){ + this.$axios({ + method: 'get', + url:`/api/server/media_server/online/list`, + }).then((res) => { + if (typeof (callback) == "function") callback(res.data) + }).catch((error) => { + console.log(error); + }); + } + getMediaServerList(callback){ + this.$axios({ + method: 'get', + url:`/api/server/media_server/list`, + }).then(function (res) { + if (typeof (callback) == "function") callback(res.data) + }).catch(function (error) { + console.log(error); + }); + } + + getMediaServer(id, callback){ + this.$axios({ + method: 'get', + url:`/api/server/media_server/one/` + id, + }).then(function (res) { + if (typeof (callback) == "function") callback(res.data) + }).catch(function (error) { + console.log(error); + }); + } + + checkServer(param, callback){ + this.$axios({ + method: 'get', + url:`/api/server/media_server/check`, + params: { + ip: param.ip, + port: param.httpPort, + secret: param.secret + } + }).then(function (res) { + if (typeof (callback) == "function") callback(res.data) + }).catch(function (error) { + console.log(error); + }); + } + + checkRecordServer(param, callback){ + this.$axios({ + method: 'get', + url:`/api/server/media_server/record/check`, + params: { + ip: param.ip, + port: param.recordAssistPort + } + }).then(function (res) { + if (typeof (callback) == "function") callback(res.data) + }).catch(function (error) { + console.log(error); + }); + } + + addServer(param, callback){ + this.$axios({ + method: 'post', + url:`/api/server/media_server/save`, + data: param + }).then(function (res) { + if (typeof (callback) == "function") callback(res.data) + }).catch(function (error) { + console.log(error); + }); + } + + delete(id, callback) { + this.$axios({ + method: 'delete', + url:`/api/server/media_server/delete`, + params: { + id: id + } + }).then(function (res) { + if (typeof (callback) == "function") callback(res.data) + }).catch(function (error) { + console.log(error); + }); + } +} + +export default MediaServer; diff --git a/web_src/src/components/service/UserService.js b/web_src/src/components/service/UserService.js new file mode 100644 index 000000000..0212dc87a --- /dev/null +++ b/web_src/src/components/service/UserService.js @@ -0,0 +1,42 @@ + +export default { + + /** + * 存储用户信息 + * @param username + * @param token + */ + setUser(user){ + localStorage.setItem("wvp-user", JSON.stringify(user)); + }, + + /** + * 获取用户 + */ + getUser(){ + return JSON.parse(localStorage.getItem("wvp-user")); + }, + + + /** + * 获取登录token + */ + getToken(){ + return localStorage.getItem("wvp-token"); + }, + + /** + * 清理用户信息 + */ + clearUserInfo(){ + localStorage.removeItem("wvp-user"); + localStorage.removeItem("wvp-token"); + }, + /** + * 更新token + * @param header + */ + setToken(token) { + localStorage.setItem("wvp-token", token); + } +} diff --git a/web_src/src/components/setting/Media.vue b/web_src/src/components/setting/Media.vue new file mode 100644 index 000000000..664260106 --- /dev/null +++ b/web_src/src/components/setting/Media.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/web_src/src/components/setting/Sip.vue b/web_src/src/components/setting/Sip.vue new file mode 100644 index 000000000..f74e1f8ee --- /dev/null +++ b/web_src/src/components/setting/Sip.vue @@ -0,0 +1,70 @@ + + + + + diff --git a/web_src/src/components/setting/Web.vue b/web_src/src/components/setting/Web.vue new file mode 100644 index 000000000..591e50360 --- /dev/null +++ b/web_src/src/components/setting/Web.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/web_src/src/components/videoList.vue b/web_src/src/components/videoList.vue deleted file mode 100644 index 8a3f1c0e6..000000000 --- a/web_src/src/components/videoList.vue +++ /dev/null @@ -1,283 +0,0 @@ - - - - - diff --git a/web_src/src/layout/UiHeader.vue b/web_src/src/layout/UiHeader.vue new file mode 100644 index 000000000..2cdca02c0 --- /dev/null +++ b/web_src/src/layout/UiHeader.vue @@ -0,0 +1,177 @@ + + + + diff --git a/web_src/src/layout/index.vue b/web_src/src/layout/index.vue new file mode 100644 index 000000000..d6afcf644 --- /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 5e0696b99..a6c6e1bf7 100644 --- a/web_src/src/main.js +++ b/web_src/src/main.js @@ -1,5 +1,6 @@ import Vue from 'vue'; import App from './App.vue'; + Vue.config.productionTip = false; import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; @@ -7,20 +8,25 @@ 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 {Notification} from 'element-ui'; import Fingerprint2 from 'fingerprintjs2'; +import VueClipboards from 'vue-clipboards'; +import Contextmenu from "vue-contextmenujs" +import userService from "./components/service/UserService" + // 生成唯一ID -Fingerprint2.get(function(components) { - const values = components.map(function(component,index) { +Fingerprint2.get(function (components) { + const values = components.map(function (component, index) { if (index === 0) { //把微信浏览器里UA的wifi或4G等网络替换成空,不然切换网络会ID不一样 return component.value.replace(/\bNetType\/\w+\b/, ''); } return component.value; }) - //console.log(values) //使用的浏览器信息npm + //console.log(values) //使用的浏览器信息npm // 生成最终id let port = window.location.port; console.log(port); @@ -32,15 +38,46 @@ Fingerprint2.get(function(components) { Vue.use(VueClipboard); Vue.use(ElementUI); Vue.use(VueCookies); -Vue.prototype.$axios = axios; +Vue.use(VueClipboards); + Vue.prototype.$notify = Notification; +Vue.use(Contextmenu); +Vue.use(VCharts); -axios.defaults.baseURL = (process.env.NODE_ENV === 'development') ? process.env.BASE_API : ""; +axios.defaults.baseURL = (process.env.NODE_ENV === 'development') ? process.env.BASE_API : (window.baseUrl ? window.baseUrl : ""); +axios.defaults.withCredentials = true; +// api 返回401自动回登陆页面 +axios.interceptors.response.use((response) => { + // 对响应数据做点什么 + let token = response.headers["access-token"]; + if (token) { + userService.setToken(token) + } + return response; +}, (error) => { + // 对响应错误做点什么 + if (error.response.status === 401) { + console.log("Received 401 Response") + router.push('/login'); + } + return Promise.reject(error); +}); +axios.interceptors.request.use( + config => { + if (userService.getToken() != null && config.url !== "/api/user/login") { + config.headers['access-token'] = `${userService.getToken()}`; + } + return config; + }, + error => { + return Promise.reject(error); + } +); +Vue.prototype.$axios = axios; Vue.prototype.$cookies.config(60*30); - new Vue({ - router: router, - render: h => h(App), + router: router, + render: h => h(App), }).$mount('#app') diff --git a/web_src/src/router/index.js b/web_src/src/router/index.js index e8ff1053e..23c0a8250 100644 --- a/web_src/src/router/index.js +++ b/web_src/src/router/index.js @@ -1,11 +1,27 @@ import Vue from 'vue' import VueRouter from 'vue-router' +import Layout from "../layout/index.vue" -import control from '../components/control.vue' -import videoList from '../components/videoList.vue' +import console from '../components/console.vue' +import deviceList from '../components/DeviceList.vue' import channelList from '../components/channelList.vue' -import devicePosition from '../components/devicePosition.vue' +import gbRecordDetail from '../components/GBRecordDetail.vue' +import pushVideoList from '../components/PushVideoList.vue' +import streamProxyList from '../components/StreamProxyList.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 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/common/jessibuca.vue' +import rtcPlayer from '../components/dialog/rtcPlayer.vue' const originalPush = VueRouter.prototype.push VueRouter.prototype.push = function push(location) { @@ -20,11 +36,86 @@ export default new VueRouter({ routes: [ { path: '/', - component: control, - }, - { - path: '/videoList', - component: videoList, + 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', @@ -32,14 +123,19 @@ export default new VueRouter({ component: login, }, { - path: '/channelList/:deviceId/:parentChannelId/:count/:page', - name: 'channelList', - component: channelList, + path: '/test', + name: 'deviceTree', + component: deviceTree, }, { - path: '/devicePosition/:deviceId/:parentChannelId/:count/:page', - name: 'devicePosition', - component: devicePosition, + path: '/play/wasm/:url', + name: 'wasmPlayer', + component: wasmPlayer, + }, + { + path: '/play/rtc/:url', + name: 'rtcPlayer', + component: rtcPlayer, }, ] }) diff --git a/web_src/static/EasyPlayer.swf b/web_src/static/EasyPlayer.swf new file mode 100755 index 000000000..eaa53fb17 Binary files /dev/null and b/web_src/static/EasyPlayer.swf differ diff --git a/web_src/static/css/iconfont.css b/web_src/static/css/iconfont.css new file mode 100644 index 000000000..4636a730e --- /dev/null +++ b/web_src/static/css/iconfont.css @@ -0,0 +1,1995 @@ +@font-face { + font-family: "iconfont"; /* Project id 1291092 */ + src: url('iconfont.woff2?t=1673251105600') format('woff2'), + url('iconfont.woff?t=1673251105600') format('woff'), + url('iconfont.ttf?t=1673251105600') format('truetype'); +} + +.iconfont { + font-family: "iconfont" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -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"; +} + +.icon-shanchu3:before { + content: "\e79b"; +} + +.icon-chehui:before { + content: "\e79c"; +} + +.icon-wenben:before { + content: "\e79d"; +} + +.icon-zhongzuo:before { + content: "\e79e"; +} + +.icon-jianqie:before { + content: "\e79f"; +} + +.icon-fangda:before { + content: "\e7a0"; +} + +.icon-fangdazhanshi:before { + content: "\e7a1"; +} + +.icon-qianjin:before { + content: "\e7a2"; +} + +.icon-houtui:before { + content: "\e7a3"; +} + +.icon-diyigeshipin:before { + content: "\e7a4"; +} + +.icon-kuaijin:before { + content: "\e7a5"; +} + +.icon-kaishi:before { + content: "\e7a7"; +} + +.icon-zuihouyigeshipin:before { + content: "\e7a8"; +} + +.icon-zanting:before { + content: "\e7a9"; +} + +.icon-zhankai:before { + content: "\e7aa"; +} + +.icon-bendisucai:before { + content: "\e7ab"; +} + +.icon-luzhi:before { + content: "\e7ac"; +} + +.icon-ossziyuan:before { + content: "\e7ad"; +} + +.icon-chuangjianzhinengfenxirenwu:before { + content: "\e7ae"; +} + +.icon-sousuo3:before { + content: "\e7af"; +} + +.icon-gengduo:before { + content: "\e7b0"; +} + +.icon-tianjia:before { + content: "\e7b1"; +} + +.icon-xiazai:before { + content: "\e7b2"; +} + +.icon-biaojibeifen:before { + content: "\e7b3"; +} + +.icon-bendisucaibeifen:before { + content: "\e7b4"; +} + +.icon-luzhibeifen:before { + content: "\e7b5"; +} + +.icon-ossziyuanbeifen:before { + content: "\e7b6"; +} + +.icon-bianji3:before { + content: "\e7b7"; +} + +.icon-cuti:before { + content: "\e7b8"; +} + +.icon-xieti:before { + content: "\e7b9"; +} + +.icon-xiahuaxian:before { + content: "\e7ba"; +} + +.icon-wuxiaoguo:before { + content: "\e7bb"; +} + +.icon-sousuo4:before { + content: "\e7bc"; +} + +.icon-gouwuche:before { + content: "\e7bd"; +} + +.icon-shuaxin2:before { + content: "\e7be"; +} + +.icon-xiaoxi:before { + content: "\e7bf"; +} + +.icon-wushouquan:before { + content: "\e7c0"; +} + +.icon-tishi2:before { + content: "\e7c1"; +} + +.icon-tishi1:before { + content: "\e7c2"; +} + +.icon-shouquanchenggong:before { + content: "\e7c3"; +} + +.icon-sousuo5:before { + content: "\e7c4"; +} + +.icon-shuaxin3:before { + content: "\e7c5"; +} + +.icon-xiazai1:before { + content: "\e7c6"; +} + +.icon-shangchuan:before { + content: "\e7c7"; +} + +.icon-guanbi:before { + content: "\e7c8"; +} + +.icon-wangye-loading:before { + content: "\e7c9"; +} + +.icon-bianzubeifen3:before { + content: "\e7ca"; +} + +.icon-xingzhuangbeifen:before { + content: "\e7cb"; +} + +.icon-bianzubeifen:before { + content: "\e7cc"; +} + +.icon-zhuanchang:before { + content: "\e7cd"; +} + +.icon-meizi:before { + content: "\e7ce"; +} + +.icon-daimabeifen:before { + content: "\e7cf"; +} + +.icon-suoxiao1:before { + content: "\e7d0"; +} + +.icon-ai19:before { + content: "\e799"; +} + +.icon-online:before { + content: "\e600"; +} + +.icon-xiangqing2:before { + content: "\e798"; +} + +.icon-record:before { + content: "\e7a6"; +} + +.icon-audio-mute:before { + content: "\e792"; +} + +.icon-audio-high:before { + content: "\e793"; +} + +.icon-record1:before { + content: "\e7f8"; +} + +.icon-audio-line:before { + content: "\e794"; +} + +.icon-record2:before { + content: "\e795"; +} + +.icon-audio-fill:before { + content: "\e796"; +} + +.icon-PTZ:before { + content: "\e797"; +} + +.icon-camera1196054easyiconnet:before { + content: "\e791"; +} + +.icon-weibiaoti10:before { + content: "\e78f"; +} + +.icon-weibiaoti11:before { + content: "\e790"; +} + +.icon-page-next1:before { + content: "\e69c"; +} + +.icon-page-last1:before { + content: "\e69d"; +} + +.icon-ptz-down1:before { + content: "\e69e"; +} + +.icon-file-search1:before { + content: "\e69f"; +} + +.icon-page-first1:before { + content: "\e6a0"; +} + +.icon-fork1:before { + content: "\e6a1"; +} + +.icon-ptz-middle1:before { + content: "\e6a2"; +} + +.icon-ptz-upright1:before { + content: "\e6a3"; +} + +.icon-ptz-downleft1:before { + content: "\e6a4"; +} + +.icon-window-restore1:before { + content: "\e6a5"; +} + +.icon-plus1:before { + content: "\e6a6"; +} + +.icon-ptz-right1:before { + content: "\e6a7"; +} + +.icon-stop:before { + content: "\e6a8"; +} + +.icon-refresh1:before { + content: "\e6a9"; +} + +.icon-tool-polyline1:before { + content: "\e6aa"; +} + +.icon-tool-point1:before { + content: "\e6ab"; +} + +.icon-minus1:before { + content: "\e6ac"; +} + +.icon-ptz-wiper1:before { + content: "\e6ad"; +} + +.icon-tool-select1:before { + content: "\e6ae"; +} + +.icon-tool-polygon1:before { + content: "\e6af"; +} + +.icon-settings1:before { + content: "\e6b0"; +} + +.icon-search1:before { + content: "\e6b1"; +} + +.icon-ir-vis1:before { + content: "\e6b2"; +} + +.icon-ptz-light1:before { + content: "\e6b3"; +} + +.icon-ptz-up1:before { + content: "\e6b4"; +} + +.icon-ptz-upleft1:before { + content: "\e6b5"; +} + +.icon-temp-stream1:before { + content: "\e6b6"; +} + +.icon-tool-mouse1:before { + content: "\e6b7"; +} + +.icon-zhongyingwenyingwen-01:before { + content: "\e6b8"; +} + +.icon-zhongyingwenyingwen02-01:before { + content: "\e6b9"; +} + +.icon-crop2:before { + content: "\e6ba"; +} + +.icon-expander-down2:before { + content: "\e6bb"; +} + +.icon-window-restore2:before { + content: "\e6bc"; +} + +.icon-file-jpg2:before { + content: "\e6bd"; +} + +.icon-asterisk3:before { + content: "\e6be"; +} + +.icon-ffc2:before { + content: "\e6bf"; +} + +.icon-file-record2:before { + content: "\e6c0"; +} + +.icon-file-stream2:before { + content: "\e6c1"; +} + +.icon-fork2:before { + content: "\e6c2"; +} + +.icon-file-mp42:before { + content: "\e6c3"; +} + +.icon-ir-vis2:before { + content: "\e6c4"; +} + +.icon-file-search2:before { + content: "\e6c5"; +} + +.icon-pause:before { + content: "\e6c6"; +} + +.icon-play1:before { + content: "\e6c7"; +} + +.icon-page-previous2:before { + content: "\e6c8"; +} + +.icon-page-next2:before { + content: "\e6c9"; +} + +.icon-minus2:before { + content: "\e6ca"; +} + +.icon-page-last2:before { + content: "\e6cb"; +} + +.icon-page-first2:before { + content: "\e6cc"; +} + +.icon-ptz-downleft2:before { + content: "\e6cd"; +} + +.icon-ptz-downright2:before { + content: "\e6ce"; +} + +.icon-ptz-middle2:before { + content: "\e6cf"; +} + +.icon-ptz-down2:before { + content: "\e6d0"; +} + +.icon-plus2:before { + content: "\e6d1"; +} + +.icon-ptz-left2:before { + content: "\e6d2"; +} + +.icon-ptz-up2:before { + content: "\e6d3"; +} + +.icon-ptz-right2:before { + content: "\e6d4"; +} + +.icon-ptz-light2:before { + content: "\e6d5"; +} + +.icon-ptz-wiper2:before { + content: "\e6d6"; +} + +.icon-ptz-upright2:before { + content: "\e6d7"; +} + +.icon-search2:before { + content: "\e6d8"; +} + +.icon-refresh2:before { + content: "\e6d9"; +} + +.icon-ptz-upleft2:before { + content: "\e6da"; +} + +.icon-stop1:before { + content: "\e6db"; +} + +.icon-tool-mouse2:before { + content: "\e6dc"; +} + +.icon-settings2:before { + content: "\e6dd"; +} + +.icon-tool-polygon2:before { + content: "\e6de"; +} + +.icon-tool-point2:before { + content: "\e6df"; +} + +.icon-temp-stream2:before { + content: "\e6e0"; +} + +.icon-tool-polyline2:before { + content: "\e6e1"; +} + +.icon-window-maximize2:before { + content: "\e6e2"; +} + +.icon-window-minimize2:before { + content: "\e6e3"; +} + +.icon-tool-select2:before { + content: "\e6e4"; +} + +.icon-video-stream2:before { + content: "\e6e5"; +} + +.icon-bianji1:before { + content: "\e6e6"; +} + +.icon-caidanzhankai1:before { + content: "\e6e7"; +} + +.icon-cha11:before { + content: "\e6e8"; +} + +.icon-caidanshouqi1:before { + content: "\e6e9"; +} + +.icon-zhongyingwen2zhongwen1:before { + content: "\e6ea"; +} + +.icon-bofang011:before { + content: "\e6eb"; +} + +.icon-zuo:before { + content: "\e6ec"; +} + +.icon-baojing1:before { + content: "\e6ed"; +} + +.icon-fuxuankuang-true1:before { + content: "\e6ee"; +} + +.icon-bofang2:before { + content: "\e6ef"; +} + +.icon-baojingshezhi1:before { + content: "\e6f0"; +} + +.icon-jiahao2:before { + content: "\e6f1"; +} + +.icon-huifangxuanzhong1:before { + content: "\e6f2"; +} + +.icon-cewen1:before { + content: "\e6f3"; +} + +.icon-baojingjilu2:before { + content: "\e6f4"; +} + +.icon-danxuan1:before { + content: "\e6f5"; +} + +.icon-pingmufenge1:before { + content: "\e6f6"; +} + +.icon-luxiangguanli1:before { + content: "\e6f7"; +} + +.icon-goukuang:before { + content: "\e6f8"; +} + +.icon-shanchu11:before { + content: "\e6f9"; +} + +.icon-cha02:before { + content: "\e6fa"; +} + +.icon-huifang1:before { + content: "\e6fb"; +} + +.icon-rili1:before { + content: "\e6fc"; +} + +.icon-quanping1:before { + content: "\e6fd"; +} + +.icon-jianhao1:before { + content: "\e6fe"; +} + +.icon-shijian1:before { + content: "\e6ff"; +} + +.icon-shishiyulanxuanzhong1:before { + content: "\e700"; +} + +.icon-shouji1:before { + content: "\e701"; +} + +.icon-shouyexuanzhong1:before { + content: "\e702"; +} + +.icon-luxiang01:before { + content: "\e703"; +} + +.icon-shishiyulan:before { + content: "\e704"; +} + +.icon-quxiao:before { + content: "\e601"; +} + +.icon-sousuo1:before { + content: "\e705"; +} + +.icon-file-record:before { + content: "\e602"; +} + +.icon-shebeiguanli1:before { + content: "\e706"; +} + +.icon-play:before { + content: "\e603"; +} + +.icon-suo1:before { + content: "\e707"; +} + +.icon-file-stream:before { + content: "\e604"; +} + +.icon-tuichudenglu1:before { + content: "\e708"; +} + +.icon-ptz-middle:before { + content: "\e606"; +} + +.icon-wenhao1:before { + content: "\e709"; +} + +.icon-minus:before { + content: "\e607"; +} + +.icon-shezhixuanzhong:before { + content: "\e70a"; +} + +.icon-fork:before { + content: "\e608"; +} + +.icon-shezhiweixuanzhong1:before { + content: "\e70b"; +} + +.icon-ptz-up:before { + content: "\e609"; +} + +.icon-shuju2:before { + content: "\e70c"; +} + +.icon-file-jpg:before { + content: "\e60a"; +} + +.icon-xiazai011:before { + content: "\e70d"; +} + +.icon-ptz-left:before { + content: "\e60b"; +} + +.icon-xiala11:before { + content: "\e70e"; +} + +.icon-ptz-down:before { + content: "\e60c"; +} + +.icon-shuaxin:before { + content: "\e70f"; +} + +.icon-file-search:before { + content: "\e60d"; +} + +.icon-pingmufenge01:before { + content: "\e710"; +} + +.icon-crop:before { + content: "\e60e"; +} + +.icon-yonghu1:before { + content: "\e711"; +} + +.icon-asterisk:before { + content: "\e60f"; +} + +.icon-wenhao01:before { + content: "\e712"; +} + +.icon-expander-down:before { + content: "\e610"; +} + +.icon-you:before { + content: "\e713"; +} + +.icon-ptz-right:before { + content: "\e611"; +} + +.icon-shujuxuanzhong1:before { + content: "\e714"; +} + +.icon-ptz-wiper:before { + content: "\e612"; +} + +.icon-kuangxuan1:before { + content: "\e715"; +} + +.icon-ir-vis:before { + content: "\e613"; +} + +.icon-yonghuguanli1:before { + content: "\e716"; +} + +.icon-ptz-upleft:before { + content: "\e614"; +} + +.icon-zhongyingwenyingwen:before { + content: "\e717"; +} + +.icon-ptz-downright:before { + content: "\e615"; +} + +.icon-xiala2:before { + content: "\e718"; +} + +.icon-search:before { + content: "\e616"; +} + +.icon-luxiang:before { + content: "\e719"; +} + +.icon-ptz-upright:before { + content: "\e617"; +} + +.icon-zanting2:before { + content: "\e71a"; +} + +.icon-ptz-downleft:before { + content: "\e618"; +} + +.icon-kefu:before { + content: "\e71b"; +} + +.icon-tool-point:before { + content: "\e619"; +} + +.icon-jiqiren:before { + content: "\e71c"; +} + +.icon-ptz-light:before { + content: "\e61a"; +} + +.icon-huanliuzhan:before { + content: "\e71d"; +} + +.icon-tool-polyline:before { + content: "\e61b"; +} + +.icon-shouji2:before { + content: "\e71e"; +} + +.icon-file-mp4:before { + content: "\e61c"; +} + +.icon-cangku:before { + content: "\e71f"; +} + +.icon-window-maximize:before { + content: "\e61d"; +} + +.icon-shuaxin11:before { + content: "\e720"; +} + +.icon-page-next:before { + content: "\e61e"; +} + +.icon-weixiu:before { + content: "\e721"; +} + +.icon-ffc:before { + content: "\e61f"; +} + +.icon-biandianzhan:before { + content: "\e722"; +} + +.icon-tool-mouse:before { + content: "\e620"; +} + +.icon-youxiang:before { + content: "\e723"; +} + +.icon-settings:before { + content: "\e621"; +} + +.icon-qq:before { + content: "\e724"; +} + +.icon-page-last:before { + content: "\e622"; +} + +.icon-dianhua01:before { + content: "\e725"; +} + +.icon-window-restore:before { + content: "\e624"; +} + +.icon-fasongyoujian:before { + content: "\e726"; +} + +.icon-tool-select:before { + content: "\e625"; +} + +.icon-gaotieyunhangcopy:before { + content: "\e727"; +} + +.icon-video-stream:before { + content: "\e627"; +} + +.icon-dizhi:before { + content: "\e728"; +} + +.icon-page-first:before { + content: "\e628"; +} + +.icon-anfangbaojingmian:before { + content: "\e729"; +} + +.icon-page-previous:before { + content: "\e629"; +} + +.icon-piliangcaozuo1:before { + content: "\e72a"; +} + +.icon-refresh:before { + content: "\e62a"; +} + +.icon-qiyeguanli1:before { + content: "\e72b"; +} + +.icon-temp-stream:before { + content: "\e62b"; +} + +.icon-luxiangguanli2:before { + content: "\e72c"; +} + +.icon-tool-polygon:before { + content: "\e62c"; +} + +.icon-quanxianguanli1:before { + content: "\e72d"; +} + +.icon-window-minimize:before { + content: "\e62d"; +} + +.icon-shezhi1:before { + content: "\e72e"; +} + +.icon-plus:before { + content: "\e62e"; +} + +.icon-shishi1:before { + content: "\e72f"; +} + +.icon-qiyeguanli:before { + content: "\e62f"; +} + +.icon-shujuquanxian1:before { + content: "\e730"; +} + +.icon-quanxianguanli:before { + content: "\e630"; +} + +.icon-shishiyulanxuanzhong2:before { + content: "\e731"; +} + +.icon-shujuquanxian:before { + content: "\e631"; +} + +.icon-renzheng:before { + content: "\e732"; +} + +.icon--_baojinglianxiren:before { + content: "\e632"; +} + +.icon-shuju3:before { + content: "\e733"; +} + +.icon-yuechi:before { + content: "\e633"; +} + +.icon-shouye1:before { + content: "\e734"; +} + +.icon-xitongguanli:before { + content: "\e634"; +} + +.icon-zuzhi1:before { + content: "\e735"; +} + +.icon-zuzhi:before { + content: "\e635"; +} + +.icon-zuzhiguanli1:before { + content: "\e736"; +} + +.icon-renzheng6:before { + content: "\e636"; +} + +.icon-xitongguanli1:before { + content: "\e737"; +} + +.icon-yonghuguanli01:before { + content: "\e637"; +} + +.icon-yuechi1:before { + content: "\e738"; +} + +.icon-baojingmoban:before { + content: "\e638"; +} + +.icon-baojinglianxiren:before { + content: "\e739"; +} + +.icon-zuzhiguanli:before { + content: "\e639"; +} + +.icon-baojingjilu3:before { + content: "\e73a"; +} + +.icon-yonghuguanli:before { + content: "\e63a"; +} + +.icon-huifangxuanzhong2:before { + content: "\e73b"; +} + +.icon-bumenguanli:before { + content: "\e63b"; +} + +.icon-caiwu1:before { + content: "\e73c"; +} + +.icon-shishi:before { + content: "\e63c"; +} + +.icon-baojingguize1:before { + content: "\e73d"; +} + +.icon-baojing:before { + content: "\e63d"; +} + +.icon-bumenguanli1:before { + content: "\e73e"; +} + +.icon-shezhi:before { + content: "\e63e"; +} + +.icon-baojing2:before { + content: "\e73f"; +} + +.icon-huifangxuanzhong:before { + content: "\e63f"; +} + +.icon-yonghuguanli2:before { + content: "\e740"; +} + +.icon-luxiangguanli:before { + content: "\e640"; +} + +.icon-huifang2:before { + content: "\e741"; +} + +.icon-huifang:before { + content: "\e642"; +} + +.icon-baojingmoban1:before { + content: "\e742"; +} + +.icon-shouye:before { + content: "\e643"; +} + +.icon-dingdanxiangqing1:before { + content: "\e743"; +} + +.icon-shishiyulanxuanzhong:before { + content: "\e644"; +} + +.icon-fapiaoguanli1:before { + content: "\e744"; +} + +.icon-caiwu:before { + content: "\e645"; +} + +.icon-shiyonggaikuang1:before { + content: "\e745"; +} + +.icon-baojingjilu:before { + content: "\e646"; +} + +.icon-zengzhifuwu1:before { + content: "\e746"; +} + +.icon-baojingguize:before { + content: "\e647"; +} + +.icon-yiguanzhu:before { + content: "\e747"; +} + +.icon-shuju:before { + content: "\e648"; +} + +.icon-baojingtuisongshezhi1:before { + content: "\e748"; +} + +.icon-piliangcaozuo:before { + content: "\e649"; +} + +.icon-quxiao1:before { + content: "\e749"; +} + +.icon-suo:before { + content: "\e64a"; +} + +.icon-xiangqing1:before { + content: "\e74a"; +} + +.icon-yonghu:before { + content: "\e64b"; +} + +.icon-xufei1:before { + content: "\e74b"; +} + +.icon-shouji:before { + content: "\e64c"; +} + +.icon-zhifu1:before { + content: "\e74c"; +} + +.icon-tianjiadian:before { + content: "\e64d"; +} + +.icon-kuang:before { + content: "\e74d"; +} + +.icon-tianjiaxian:before { + content: "\e64e"; +} + +.icon-shouzhimingxi:before { + content: "\e74e"; +} + +.icon-tianjiaxuanqu:before { + content: "\e64f"; +} + +.icon-shouzhimingxi1:before { + content: "\e74f"; +} + +.icon-xuanzeduixiang:before { + content: "\e650"; +} + +.icon-daochu:before { + content: "\e750"; +} + +.icon-baojing01:before { + content: "\e651"; +} + +.icon-daochu1:before { + content: "\e751"; +} + +.icon-baojingjilu1:before { + content: "\e652"; +} + +.icon-daping:before { + content: "\e752"; +} + +.icon-baojingshezhi:before { + content: "\e653"; +} + +.icon-shaixuan:before { + content: "\e753"; +} + +.icon-cewen:before { + content: "\e654"; +} + +.icon-zhifu2:before { + content: "\e754"; +} + +.icon-tuichudenglu:before { + content: "\e655"; +} + +.icon-shaixuan1:before { + content: "\e755"; +} + +.icon-shezhiweixuanzhong:before { + content: "\e656"; +} + +.icon-zhifu3:before { + content: "\e756"; +} + +.icon-shezhixuanzhong1:before { + content: "\e657"; +} + +.icon-xia:before { + content: "\e757"; +} + +.icon-shouyexuanzhong:before { + content: "\e658"; +} + +.icon-xia1:before { + content: "\e758"; +} + +.icon-shujuxuanzhong:before { + content: "\e659"; +} + +.icon-yanzhengma:before { + content: "\e759"; +} + +.icon-shuju1:before { + content: "\e65a"; +} + +.icon-tongxunlu:before { + content: "\e75a"; +} + +.icon-bianji:before { + content: "\e65b"; +} + +.icon-yanzhengma1:before { + content: "\e75b"; +} + +.icon-rili:before { + content: "\e65c"; +} + +.icon-tongxunlu1:before { + content: "\e75c"; +} + +.icon-shanchu:before { + content: "\e65d"; +} + +.icon-yingyongbangding:before { + content: "\e75d"; +} + +.icon-jiahao:before { + content: "\e65e"; +} + +.icon-yingyongbangding1:before { + content: "\e75e"; +} + +.icon-wenhao:before { + content: "\e65f"; +} + +.icon-yingyongbangding2:before { + content: "\e75f"; +} + +.icon-zhongyingwen:before { + content: "\e660"; +} + +.icon-dapingzhanshi:before { + content: "\e760"; +} + +.icon-kuangxuan:before { + content: "\e661"; +} + +.icon-jiankong:before { + content: "\e761"; +} + +.icon-cha1:before { + content: "\e662"; +} + +.icon-touxiang:before { + content: "\e762"; +} + +.icon-bofang01:before { + content: "\e663"; +} + +.icon-lou:before { + content: "\e763"; +} + +.icon-caidanzhankai:before { + content: "\e664"; +} + +.icon-jiankong1:before { + content: "\e764"; +} + +.icon-caidanshouqi:before { + content: "\e665"; +} + +.icon-lou1:before { + content: "\e765"; +} + +.icon-danxuan:before { + content: "\e666"; +} + +.icon-dapingzhanshi1:before { + content: "\e766"; +} + +.icon-fuxuankuangxuanzhong:before { + content: "\e667"; +} + +.icon-touxiang1:before { + content: "\e767"; +} + +.icon-fuxuankuang-true:before { + content: "\e668"; +} + +.icon-shebei:before { + content: "\e768"; +} + +.icon-jianhao:before { + content: "\e669"; +} + +.icon-shebeii:before { + content: "\e769"; +} + +.icon-shanchu1:before { + content: "\e66a"; +} + +.icon-bianji11:before { + content: "\e76a"; +} + +.icon-shijian:before { + content: "\e66b"; +} + +.icon-jilu:before { + content: "\e76b"; +} + +.icon-jiahao1:before { + content: "\e66c"; +} + +.icon-yun:before { + content: "\e76c"; +} + +.icon-sousuo:before { + content: "\e66d"; +} + +.icon-baojing3:before { + content: "\e76d"; +} + +.icon-zhongyingwen2zhongwen:before { + content: "\e66e"; +} + +.icon-zhinengyangan:before { + content: "\e76e"; +} + +.icon-xiala:before { + content: "\e66f"; +} + +.icon-yongdiananquan:before { + content: "\e76f"; +} + +.icon-xiala1:before { + content: "\e670"; +} + +.icon-zhinengmensuo:before { + content: "\e770"; +} + +.icon-xiazai01:before { + content: "\e671"; +} + +.icon-xiaokongyujing:before { + content: "\e771"; +} + +.icon-pingmufenge02:before { + content: "\e672"; +} + +.icon-zhinengdianbiao:before { + content: "\e772"; +} + +.icon-shezhi01:before { + content: "\e673"; +} + +.icon-zhinengshuibiao:before { + content: "\e773"; +} + +.icon-zuixiaohuaxi:before { + content: "\e674"; +} + +.icon-shuiyajiance01:before { + content: "\e774"; +} + +.icon-zuidahuaxi:before { + content: "\e675"; +} + +.icon-zhinengzhaoming:before { + content: "\e775"; +} + +.icon-huifuxi:before { + content: "\e676"; +} + +.icon-zhinengmenjin:before { + content: "\e776"; +} + +.icon-guanbixi:before { + content: "\e677"; +} + +.icon-tingchechang:before { + content: "\e777"; +} + +.icon-baocunJPG:before { + content: "\e678"; +} + +.icon-xiala3:before { + content: "\e778"; +} + +.icon-quxian:before { + content: "\e679"; +} + +.icon-zhinengkongtiao:before { + content: "\e779"; +} + +.icon-tingzhiyulan:before { + content: "\e67a"; +} + +.icon-sousuo2:before { + content: "\e77a"; +} + +.icon-wenduliuluzhi:before { + content: "\e67b"; +} + +.icon-shang1:before { + content: "\e77b"; +} + +.icon-shuaxin1:before { + content: "\e67c"; +} + +.icon-1_jingdianchuwuweixuanzhong:before { + content: "\e77c"; +} + +.icon-shangjiantou:before { + content: "\e67d"; +} + +.icon-dianti:before { + content: "\e77d"; +} + +.icon-shang:before { + content: "\e67e"; +} + +.icon-zhuangtai:before { + content: "\e77e"; +} + +.icon-zixun:before { + content: "\e67f"; +} + +.icon-keshi:before { + content: "\e77f"; +} + +.icon-youxiang01:before { + content: "\e680"; +} + +.icon-chongzhijilu:before { + content: "\e780"; +} + +.icon-QQ:before { + content: "\e681"; +} + +.icon-jingshi:before { + content: "\e781"; +} + +.icon-dianhua:before { + content: "\e682"; +} + +.icon-bianji2:before { + content: "\e782"; +} + +.icon-pingmufenge:before { + content: "\e683"; +} + +.icon-fuzhi:before { + content: "\e783"; +} + +.icon-gou:before { + content: "\e684"; +} + +.icon-guanyu:before { + content: "\e784"; +} + +.icon-dingdanxiangqing:before { + content: "\e685"; +} + +.icon-shishiyulan-01:before { + content: "\e785"; +} + +.icon-shiyonggaikuang:before { + content: "\e686"; +} + +.icon-shujuchakan:before { + content: "\e786"; +} + +.icon-fapiaoguanli:before { + content: "\e687"; +} + +.icon-shanchu2:before { + content: "\e787"; +} + +.icon-xiangqing:before { + content: "\e688"; +} + +.icon-xitongpeizhi:before { + content: "\e788"; +} + +.icon-baojingtuisongshezhi:before { + content: "\e689"; +} + +.icon-tezhengwendu:before { + content: "\e789"; +} + +.icon-zhifu:before { + content: "\e68a"; +} + +.icon-quanzhenwendu:before { + content: "\e78a"; +} + +.icon-zengzhifuwu:before { + content: "\e68b"; +} + +.icon-fenxiang:before { + content: "\e78b"; +} + +.icon-xufei:before { + content: "\e68c"; +} + +.icon-fenxiang01:before { + content: "\e78c"; +} + +.icon-asterisk1:before { + content: "\e68d"; +} + +.icon-wenhao2:before { + content: "\e78d"; +} + +.icon-window-maximize1:before { + content: "\e68e"; +} + +.icon-dian:before { + content: "\e78e"; +} + +.icon-crop1:before { + content: "\e68f"; +} + +.icon-asterisk2:before { + content: "\e690"; +} + +.icon-file-record1:before { + content: "\e691"; +} + +.icon-ffc1:before { + content: "\e692"; +} + +.icon-file-mp41:before { + content: "\e693"; +} + +.icon-window-minimize1:before { + content: "\e694"; +} + +.icon-ptz-downright1:before { + content: "\e695"; +} + +.icon-video-stream1:before { + content: "\e696"; +} + +.icon-file-jpg1:before { + content: "\e697"; +} + +.icon-file-stream1:before { + content: "\e698"; +} + +.icon-page-previous1:before { + content: "\e699"; +} + +.icon-expander-down1:before { + content: "\e69a"; +} + +.icon-ptz-left1:before { + content: "\e69b"; +} + +.icon-yinpinwenjian1:before { + content: "\e623"; +} + +.icon-yinpinwenjian2:before { + content: "\e626"; +} + +.icon-xiazaiyinpinwenjian:before { + content: "\e605"; +} + +.icon-yinpinwenjian:before { + content: "\e641"; +} + diff --git a/web_src/static/css/iconfont.woff2 b/web_src/static/css/iconfont.woff2 new file mode 100644 index 000000000..f1cb24cf3 Binary files /dev/null and b/web_src/static/css/iconfont.woff2 differ diff --git a/web_src/static/css/login.css b/web_src/static/css/login.css new file mode 100755 index 000000000..e0f049405 --- /dev/null +++ b/web_src/static/css/login.css @@ -0,0 +1,420 @@ + + + + +/*////////////////////////////////////////////////////////////////// +[ FONT ]*/ + +@font-face { + font-family: Poppins-Regular; + src: url('../fonts/poppins/Poppins-Regular.ttf'); +} + +@font-face { + font-family: Poppins-Medium; + src: url('../fonts/poppins/Poppins-Medium.ttf'); +} + +@font-face { + font-family: Poppins-Bold; + src: url('../fonts/poppins/Poppins-Bold.ttf'); +} + +@font-face { + font-family: Poppins-SemiBold; + src: url('../fonts/poppins/Poppins-SemiBold.ttf'); +} + +.limiter { + width: 100%; + margin: 0 auto; + font-family: Poppins-Regular, sans-serif; +} + +.container-login100 { + width: 100%; + min-height: 100vh; + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; + background: #f2f2f2; +} + +.wrap-login100 { + background: #fff; + border-radius: 10px; + overflow: hidden; + padding: 77px 55px 33px 55px; + + box-shadow: 0 5px 10px 0px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 5px 10px 0px rgba(0, 0, 0, 0.1); + -webkit-box-shadow: 0 5px 10px 0px rgba(0, 0, 0, 0.1); + -o-box-shadow: 0 5px 10px 0px rgba(0, 0, 0, 0.1); + -ms-box-shadow: 0 5px 10px 0px rgba(0, 0, 0, 0.1); +} + + +/*------------------------------------------------------------------ +[ Form ]*/ + +.login100-form { + width: 100%; +} + +.login100-form-title { + display: block; + font-family: Poppins-Bold; + font-size: 30px; + color: #333333; + line-height: 1.2; + text-align: center; + +} +.login100-form-title i { + font-size: 60px; +} + +/*------------------------------------------------------------------ +[ Input ]*/ + +.wrap-input100 { + width: 100%; + position: relative; + border-bottom: 2px solid #adadad; + margin-bottom: 37px; +} + +.input100 { + font-family: Poppins-Regular; + font-size: 15px; + color: #555555; + line-height: 1.2; + + display: block; + width: 100%; + height: 45px; + background: transparent; + padding: 0 5px; + outline: none; + border: none; +} +.input100::-webkit-input-placeholder { color: #adadad;} +.input100:-moz-placeholder { color: #adadad;} +.input100::-moz-placeholder { color: #adadad;} +.input100:-ms-input-placeholder { color: #adadad;} + +/*---------------------------------------------*/ +.focus-input100 { + position: absolute; + display: block; + width: 100%; + height: 100%; + top: 0; + left: 0; + pointer-events: none; + color:transparent; +} + +.focus-input100::before { + content: ""; + display: block; + position: absolute; + bottom: -2px; + left: 0; + width: 0; + height: 2px; + + -webkit-transition: all 0.4s; + -o-transition: all 0.4s; + -moz-transition: all 0.4s; + transition: all 0.4s; + + background: #6a7dfe; + background: -webkit-linear-gradient(left, #21d4fd, #b721ff); + background: -o-linear-gradient(left, #21d4fd, #b721ff); + background: -moz-linear-gradient(left, #21d4fd, #b721ff); + background: linear-gradient(left, #21d4fd, #b721ff); +} + +.focus-input100::after { + font-family: Poppins-Regular; + font-size: 15px; + color: #999999; + line-height: 1.2; + + content: attr(data-placeholder); + display: block; + width: 100%; + position: absolute; + top: 16px; + left: 0px; + padding-left: 5px; + + -webkit-transition: all 0.4s; + -o-transition: all 0.4s; + -moz-transition: all 0.4s; + transition: all 0.4s; +} + +.input100:focus + .focus-input100::after { + top: -15px; +} + +.input100:focus + .focus-input100::before { + width: 100%; +} + +.has-val.input100 + .focus-input100::after { + top: -15px; +} + +.has-val.input100 + .focus-input100::before { + width: 100%; +} + +/*---------------------------------------------*/ +.btn-show-pass { + font-size: 15px; + color: #999999; + + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + align-items: center; + position: absolute; + height: 100%; + top: 0; + right: 0; + padding-right: 5px; + cursor: pointer; + -webkit-transition: all 0.4s; + -o-transition: all 0.4s; + -moz-transition: all 0.4s; + transition: all 0.4s; +} + +.btn-show-pass:hover { + color: #6a7dfe; + color: -webkit-linear-gradient(left, #21d4fd, #b721ff); + color: -o-linear-gradient(left, #21d4fd, #b721ff); + color: -moz-linear-gradient(left, #21d4fd, #b721ff); + color: linear-gradient(left, #21d4fd, #b721ff); +} + +.btn-show-pass.active { + color: #6a7dfe; + color: -webkit-linear-gradient(left, #21d4fd, #b721ff); + color: -o-linear-gradient(left, #21d4fd, #b721ff); + color: -moz-linear-gradient(left, #21d4fd, #b721ff); + color: linear-gradient(left, #21d4fd, #b721ff); +} + + + +/*------------------------------------------------------------------ +[ Button ]*/ +.container-login100-form-btn { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + flex-wrap: wrap; + justify-content: center; + padding-top: 13px; +} + +.wrap-login100-form-btn { + width: 100%; + display: block; + position: relative; + z-index: 1; + border-radius: 25px; + overflow: hidden; + margin: 0 auto; +} + +.login100-form-bgbtn { + position: absolute; + z-index: -1; + width: 300%; + height: 100%; + background: #a64bf4; + background: -webkit-linear-gradient(right, #21d4fd, #b721ff, #21d4fd, #b721ff); + background: -o-linear-gradient(right, #21d4fd, #b721ff, #21d4fd, #b721ff); + background: -moz-linear-gradient(right, #21d4fd, #b721ff, #21d4fd, #b721ff); + background: linear-gradient(right, #21d4fd, #b721ff, #21d4fd, #b721ff); + top: 0; + left: -100%; + + -webkit-transition: all 0.4s; + -o-transition: all 0.4s; + -moz-transition: all 0.4s; + transition: all 0.4s; +} + +.login100-form-btn { + font-family: Poppins-Medium; + font-size: 15px; + color: #fff; + line-height: 1.2; + text-transform: uppercase; + + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + justify-content: center; + align-items: center; + padding: 0 20px; + width: 100%; + height: 50px; + outline: none !important; + border: none; + background: transparent; +} + +.login100-form-btn:hover { + cursor: pointer; +} + +.wrap-login100-form-btn:hover .login100-form-bgbtn { + left: 0; +} + + +/*------------------------------------------------------------------ +[ Responsive ]*/ + +@media (max-width: 576px) { + .wrap-login100 { + padding: 77px 15px 33px 15px; + } +} + + + +/*------------------------------------------------------------------ +[ Alert validate ]*/ + +.validate-input { + position: relative; +} + +.alert-validate::before { + content: attr(data-validate); + position: absolute; + max-width: 70%; + background-color: #fff; + border: 1px solid #c80000; + border-radius: 2px; + padding: 4px 25px 4px 10px; + top: 50%; + -webkit-transform: translateY(-50%); + -moz-transform: translateY(-50%); + -ms-transform: translateY(-50%); + -o-transform: translateY(-50%); + transform: translateY(-50%); + right: 0px; + pointer-events: none; + + font-family: Poppins-Regular; + color: #c80000; + font-size: 13px; + line-height: 1.4; + text-align: left; + + visibility: hidden; + opacity: 0; + + -webkit-transition: opacity 0.4s; + -o-transition: opacity 0.4s; + -moz-transition: opacity 0.4s; + transition: opacity 0.4s; +} + +.alert-validate::after { + content: "\f06a"; + font-family: FontAwesome; + font-size: 16px; + color: #c80000; + + display: block; + position: absolute; + background-color: #fff; + top: 50%; + -webkit-transform: translateY(-50%); + -moz-transform: translateY(-50%); + -ms-transform: translateY(-50%); + -o-transform: translateY(-50%); + transform: translateY(-50%); + right: 5px; +} + +.alert-validate:hover:before { + visibility: visible; + opacity: 1; +} + +@media (max-width: 992px) { + .alert-validate::before { + visibility: visible; + opacity: 1; + } +} + +/** util **/ +.p-b-26 {padding-bottom: 26px;} +.p-b-48 {padding-bottom: 48px;} +.p-t-115 {padding-top: 115px;} + +@keyframes login { + from {width: 100%} + to {width: 50px} +} + +.login-loading{ + width: 50px; + animation-name: login; + animation-duration: 0.5s; +} +.login-loading > .login100-form-btn { + visibility: hidden !important; +} +.login-loading-class{ + stroke: rgb(255, 255, 255) !important; + background-color: transparent !important; +} + +.login-loading-class > .el-loading-spinner .path { + stroke: rgb(255, 255, 255) !important; +} + + + + +/**font**/ +/* .zmdi { + display: inline-block; + font: normal normal normal 14px/1 'Material-Design-Iconic-Font'; + font-size: 14px; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.zmdi-font::before { + content: '\f16a'; +} +.login100-form-title i { + font-size: 60px; +} */ + diff --git a/web_src/static/favicon.ico b/web_src/static/favicon.ico new file mode 100644 index 000000000..2d672689a 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 000000000..495702f4b Binary files /dev/null and b/web_src/static/file/推流通道导入.zip differ diff --git a/web_src/static/fonts/poppins/Poppins-Bold.ttf b/web_src/static/fonts/poppins/Poppins-Bold.ttf new file mode 100755 index 000000000..44313ca44 Binary files /dev/null and b/web_src/static/fonts/poppins/Poppins-Bold.ttf differ diff --git a/web_src/static/fonts/poppins/Poppins-Medium.ttf b/web_src/static/fonts/poppins/Poppins-Medium.ttf new file mode 100755 index 000000000..5b46f1985 Binary files /dev/null and b/web_src/static/fonts/poppins/Poppins-Medium.ttf differ diff --git a/web_src/static/fonts/poppins/Poppins-Regular.ttf b/web_src/static/fonts/poppins/Poppins-Regular.ttf new file mode 100755 index 000000000..246a861a7 Binary files /dev/null and b/web_src/static/fonts/poppins/Poppins-Regular.ttf differ diff --git a/web_src/static/fonts/poppins/Poppins-SemiBold.ttf b/web_src/static/fonts/poppins/Poppins-SemiBold.ttf new file mode 100755 index 000000000..3bbad2a8b Binary files /dev/null and b/web_src/static/fonts/poppins/Poppins-SemiBold.ttf differ diff --git a/web_src/static/images/arrow.png b/web_src/static/images/arrow.png new file mode 100644 index 000000000..4d8df4625 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 000000000..67eb0fc74 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 000000000..a93bd5517 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 000000000..597209bf1 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 000000000..e5f2b5ff3 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 000000000..4ddae239d 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 000000000..073bceb47 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 000000000..f05c2a341 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 000000000..b40f67be0 Binary files /dev/null and b/web_src/static/images/gis/camera3.png differ diff --git a/web_src/static/images/zlm-logo.png b/web_src/static/images/zlm-logo.png new file mode 100644 index 000000000..5f492dcdf Binary files /dev/null and b/web_src/static/images/zlm-logo.png differ diff --git a/web_src/static/js/EasyWasmPlayer.js b/web_src/static/js/EasyWasmPlayer.js new file mode 100644 index 000000000..f7b0bacfb --- /dev/null +++ b/web_src/static/js/EasyWasmPlayer.js @@ -0,0 +1,19 @@ +!function(A,M){"object"==typeof exports&&"object"==typeof module?module.exports=M():"function"==typeof define&&define.amd?define([],M):"object"==typeof exports?exports.WasmPlayer=M():A.WasmPlayer=M()}(this,(function(){return function(A){var M={};function t(g){if(M[g])return M[g].exports;var I=M[g]={i:g,l:!1,exports:{}};return A[g].call(I.exports,I,I.exports,t),I.l=!0,I.exports}return t.m=A,t.c=M,t.d=function(A,M,g){t.o(A,M)||Object.defineProperty(A,M,{enumerable:!0,get:g})},t.r=function(A){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(A,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(A,"__esModule",{value:!0})},t.t=function(A,M){if(1&M&&(A=t(A)),8&M)return A;if(4&M&&"object"==typeof A&&A&&A.__esModule)return A;var g=Object.create(null);if(t.r(g),Object.defineProperty(g,"default",{enumerable:!0,value:A}),2&M&&"string"!=typeof A)for(var I in A)t.d(g,I,function(M){return A[M]}.bind(null,I));return g},t.n=function(A){var M=A&&A.__esModule?function(){return A.default}:function(){return A};return t.d(M,"a",M),M},t.o=function(A,M){return Object.prototype.hasOwnProperty.call(A,M)},t.p="",t(t.s=222)}([function(A,M,t){"use strict";t.d(M,"D",(function(){return g})),t.d(M,"B",(function(){return I})),t.d(M,"C",(function(){return e})),t.d(M,"u",(function(){return i})),t.d(M,"k",(function(){return T})),t.d(M,"o",(function(){return E})),t.d(M,"w",(function(){return N})),t.d(M,"t",(function(){return n})),t.d(M,"j",(function(){return D})),t.d(M,"q",(function(){return C})),t.d(M,"r",(function(){return r})),t.d(M,"l",(function(){return c})),t.d(M,"A",(function(){return o})),t.d(M,"v",(function(){return B})),t.d(M,"y",(function(){return Q})),t.d(M,"z",(function(){return a})),t.d(M,"s",(function(){return h})),t.d(M,"p",(function(){return s})),t.d(M,"n",(function(){return y})),t.d(M,"x",(function(){return w})),t.d(M,"m",(function(){return j})),t.d(M,"b",(function(){return L})),t.d(M,"a",(function(){return d})),t.d(M,"e",(function(){return x})),t.d(M,"h",(function(){return u})),t.d(M,"c",(function(){return S})),t.d(M,"g",(function(){return l})),t.d(M,"i",(function(){return z})),t.d(M,"d",(function(){return U})),t.d(M,"f",(function(){return f}));var g=1,I=2,e=3,i=1,T=2,E=3,N=4,n=6,D=7,C=10,r=11,c=12,o=13,B=14,Q=15,a=16,h=20,s=21,y=102,w=103,j=200,L=1,d=2,x=3,Y=!0;function u(A){Y=A}function S(){return Y}function l(A){A}function z(A){return new Promise((function(M){return setTimeout(M,A)}))}function U(A){var M,t=new Date,g=t.getFullYear(),I=t.getMonth()+1,e=t.getDate();t.getDay();M=g+"-"+I+"-"+e+" "+t.getHours()+":"+t.getMinutes()+":"+t.getSeconds(),console.log("["+M+"]: "+A)}function f(){for(var A=window.location.href.split("/"),M="",t=0;t<3;t++)M+=A[t],M+="/";return M}},function(A,M,t){"use strict";var g=t(7),I=t.n(g),e=function(){function A(){}return A.e=function(M,t){M&&!A.FORCE_GLOBAL_TAG||(M=A.GLOBAL_TAG);var g="["+M+"] > "+t;A.ENABLE_CALLBACK&&A.emitter.emit("log","error",g),A.ENABLE_ERROR&&(console.error?console.error(g):console.warn?console.warn(g):console.log(g))},A.i=function(M,t){M&&!A.FORCE_GLOBAL_TAG||(M=A.GLOBAL_TAG);var g="["+M+"] > "+t;A.ENABLE_CALLBACK&&A.emitter.emit("log","info",g),A.ENABLE_INFO&&(console.info?console.info(g):console.log(g))},A.w=function(M,t){M&&!A.FORCE_GLOBAL_TAG||(M=A.GLOBAL_TAG);var g="["+M+"] > "+t;A.ENABLE_CALLBACK&&A.emitter.emit("log","warn",g),A.ENABLE_WARN&&(console.warn?console.warn(g):console.log(g))},A.d=function(M,t){M&&!A.FORCE_GLOBAL_TAG||(M=A.GLOBAL_TAG);var g="["+M+"] > "+t;A.ENABLE_CALLBACK&&A.emitter.emit("log","debug",g),A.ENABLE_DEBUG&&(console.debug?console.debug(g):console.log(g))},A.v=function(M,t){M&&!A.FORCE_GLOBAL_TAG||(M=A.GLOBAL_TAG);var g="["+M+"] > "+t;A.ENABLE_CALLBACK&&A.emitter.emit("log","verbose",g),A.ENABLE_VERBOSE&&console.log(g)},A}();e.GLOBAL_TAG="EasyPlayer.js",e.FORCE_GLOBAL_TAG=!1,e.ENABLE_ERROR=!0,e.ENABLE_INFO=!0,e.ENABLE_WARN=!0,e.ENABLE_DEBUG=!0,e.ENABLE_VERBOSE=!0,e.ENABLE_CALLBACK=!1,e.emitter=new I.a,M.a=e},function(A,M,t){"use strict";function g(A,M){A.prototype=Object.create(M.prototype),A.prototype.constructor=A,A.__proto__=M}function I(A,M){for(var t=0;t */ +var g=t(9),I=g.Buffer;function e(A,M){for(var t in A)M[t]=A[t]}function i(A,M,t){return I(A,M,t)}I.from&&I.alloc&&I.allocUnsafe&&I.allocUnsafeSlow?A.exports=g:(e(g,M),M.Buffer=i),i.prototype=Object.create(I.prototype),e(I,i),i.from=function(A,M,t){if("number"==typeof A)throw new TypeError("Argument must not be a number");return I(A,M,t)},i.alloc=function(A,M,t){if("number"!=typeof A)throw new TypeError("Argument must be a number");var g=I(A);return void 0!==M?"string"==typeof t?g.fill(M,t):g.fill(M):g.fill(0),g},i.allocUnsafe=function(A){if("number"!=typeof A)throw new TypeError("Argument must be a number");return I(A)},i.allocUnsafeSlow=function(A){if("number"!=typeof A)throw new TypeError("Argument must be a number");return g.SlowBuffer(A)}},function(A,M,t){"use strict";M.a={OK:"OK",FORMAT_ERROR:"FormatError",FORMAT_UNSUPPORTED:"FormatUnsupported",CODEC_UNSUPPORTED:"CodecUnsupported"}},function(A,M,t){(function(A){!function(A,M){"use strict";function g(A,M){if(!A)throw new Error(M||"Assertion failed")}function I(A,M){A.super_=M;var t=function(){};t.prototype=M.prototype,A.prototype=new t,A.prototype.constructor=A}function e(A,M,t){if(e.isBN(A))return A;this.negative=0,this.words=null,this.length=0,this.red=null,null!==A&&("le"!==M&&"be"!==M||(t=M,M=10),this._init(A||0,M||10,t||"be"))}var i;"object"==typeof A?A.exports=e:M.BN=e,e.BN=e,e.wordSize=26;try{i=t(165).Buffer}catch(A){}function T(A,M,t){for(var g=0,I=Math.min(A.length,t),e=M;e=49&&i<=54?i-49+10:i>=17&&i<=22?i-17+10:15&i}return g}function E(A,M,t,g){for(var I=0,e=Math.min(A.length,t),i=M;i=49?T-49+10:T>=17?T-17+10:T}return I}e.isBN=function(A){return A instanceof e||null!==A&&"object"==typeof A&&A.constructor.wordSize===e.wordSize&&Array.isArray(A.words)},e.max=function(A,M){return A.cmp(M)>0?A:M},e.min=function(A,M){return A.cmp(M)<0?A:M},e.prototype._init=function(A,M,t){if("number"==typeof A)return this._initNumber(A,M,t);if("object"==typeof A)return this._initArray(A,M,t);"hex"===M&&(M=16),g(M===(0|M)&&M>=2&&M<=36);var I=0;"-"===(A=A.toString().replace(/\s+/g,""))[0]&&I++,16===M?this._parseHex(A,I):this._parseBase(A,M,I),"-"===A[0]&&(this.negative=1),this.strip(),"le"===t&&this._initArray(this.toArray(),M,t)},e.prototype._initNumber=function(A,M,t){A<0&&(this.negative=1,A=-A),A<67108864?(this.words=[67108863&A],this.length=1):A<4503599627370496?(this.words=[67108863&A,A/67108864&67108863],this.length=2):(g(A<9007199254740992),this.words=[67108863&A,A/67108864&67108863,1],this.length=3),"le"===t&&this._initArray(this.toArray(),M,t)},e.prototype._initArray=function(A,M,t){if(g("number"==typeof A.length),A.length<=0)return this.words=[0],this.length=1,this;this.length=Math.ceil(A.length/3),this.words=new Array(this.length);for(var I=0;I=0;I-=3)i=A[I]|A[I-1]<<8|A[I-2]<<16,this.words[e]|=i<>>26-T&67108863,(T+=24)>=26&&(T-=26,e++);else if("le"===t)for(I=0,e=0;I>>26-T&67108863,(T+=24)>=26&&(T-=26,e++);return this.strip()},e.prototype._parseHex=function(A,M){this.length=Math.ceil((A.length-M)/6),this.words=new Array(this.length);for(var t=0;t=M;t-=6)I=T(A,t,t+6),this.words[g]|=I<>>26-e&4194303,(e+=24)>=26&&(e-=26,g++);t+6!==M&&(I=T(A,M,t+6),this.words[g]|=I<>>26-e&4194303),this.strip()},e.prototype._parseBase=function(A,M,t){this.words=[0],this.length=1;for(var g=0,I=1;I<=67108863;I*=M)g++;g--,I=I/M|0;for(var e=A.length-t,i=e%g,T=Math.min(e,e-i)+t,N=0,n=t;n1&&0===this.words[this.length-1];)this.length--;return this._normSign()},e.prototype._normSign=function(){return 1===this.length&&0===this.words[0]&&(this.negative=0),this},e.prototype.inspect=function(){return(this.red?""};var N=["","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"],n=[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],D=[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 C(A,M,t){t.negative=M.negative^A.negative;var g=A.length+M.length|0;t.length=g,g=g-1|0;var I=0|A.words[0],e=0|M.words[0],i=I*e,T=67108863&i,E=i/67108864|0;t.words[0]=T;for(var N=1;N>>26,D=67108863&E,C=Math.min(N,M.length-1),r=Math.max(0,N-A.length+1);r<=C;r++){var c=N-r|0;n+=(i=(I=0|A.words[c])*(e=0|M.words[r])+D)/67108864|0,D=67108863&i}t.words[N]=0|D,E=0|n}return 0!==E?t.words[N]=0|E:t.length--,t.strip()}e.prototype.toString=function(A,M){var t;if(M=0|M||1,16===(A=A||10)||"hex"===A){t="";for(var I=0,e=0,i=0;i>>24-I&16777215)||i!==this.length-1?N[6-E.length]+E+t:E+t,(I+=2)>=26&&(I-=26,i--)}for(0!==e&&(t=e.toString(16)+t);t.length%M!=0;)t="0"+t;return 0!==this.negative&&(t="-"+t),t}if(A===(0|A)&&A>=2&&A<=36){var C=n[A],r=D[A];t="";var c=this.clone();for(c.negative=0;!c.isZero();){var o=c.modn(r).toString(A);t=(c=c.idivn(r)).isZero()?o+t:N[C-o.length]+o+t}for(this.isZero()&&(t="0"+t);t.length%M!=0;)t="0"+t;return 0!==this.negative&&(t="-"+t),t}g(!1,"Base should be between 2 and 36")},e.prototype.toNumber=function(){var A=this.words[0];return 2===this.length?A+=67108864*this.words[1]:3===this.length&&1===this.words[2]?A+=4503599627370496+67108864*this.words[1]:this.length>2&&g(!1,"Number can only safely store up to 53 bits"),0!==this.negative?-A:A},e.prototype.toJSON=function(){return this.toString(16)},e.prototype.toBuffer=function(A,M){return g(void 0!==i),this.toArrayLike(i,A,M)},e.prototype.toArray=function(A,M){return this.toArrayLike(Array,A,M)},e.prototype.toArrayLike=function(A,M,t){var I=this.byteLength(),e=t||Math.max(1,I);g(I<=e,"byte array longer than desired length"),g(e>0,"Requested array length <= 0"),this.strip();var i,T,E="le"===M,N=new A(e),n=this.clone();if(E){for(T=0;!n.isZero();T++)i=n.andln(255),n.iushrn(8),N[T]=i;for(;T=4096&&(t+=13,M>>>=13),M>=64&&(t+=7,M>>>=7),M>=8&&(t+=4,M>>>=4),M>=2&&(t+=2,M>>>=2),t+M},e.prototype._zeroBits=function(A){if(0===A)return 26;var M=A,t=0;return 0==(8191&M)&&(t+=13,M>>>=13),0==(127&M)&&(t+=7,M>>>=7),0==(15&M)&&(t+=4,M>>>=4),0==(3&M)&&(t+=2,M>>>=2),0==(1&M)&&t++,t},e.prototype.bitLength=function(){var A=this.words[this.length-1],M=this._countBits(A);return 26*(this.length-1)+M},e.prototype.zeroBits=function(){if(this.isZero())return 0;for(var A=0,M=0;MA.length?this.clone().ior(A):A.clone().ior(this)},e.prototype.uor=function(A){return this.length>A.length?this.clone().iuor(A):A.clone().iuor(this)},e.prototype.iuand=function(A){var M;M=this.length>A.length?A:this;for(var t=0;tA.length?this.clone().iand(A):A.clone().iand(this)},e.prototype.uand=function(A){return this.length>A.length?this.clone().iuand(A):A.clone().iuand(this)},e.prototype.iuxor=function(A){var M,t;this.length>A.length?(M=this,t=A):(M=A,t=this);for(var g=0;gA.length?this.clone().ixor(A):A.clone().ixor(this)},e.prototype.uxor=function(A){return this.length>A.length?this.clone().iuxor(A):A.clone().iuxor(this)},e.prototype.inotn=function(A){g("number"==typeof A&&A>=0);var M=0|Math.ceil(A/26),t=A%26;this._expand(M),t>0&&M--;for(var I=0;I0&&(this.words[I]=~this.words[I]&67108863>>26-t),this.strip()},e.prototype.notn=function(A){return this.clone().inotn(A)},e.prototype.setn=function(A,M){g("number"==typeof A&&A>=0);var t=A/26|0,I=A%26;return this._expand(t+1),this.words[t]=M?this.words[t]|1<A.length?(t=this,g=A):(t=A,g=this);for(var I=0,e=0;e>>26;for(;0!==I&&e>>26;if(this.length=t.length,0!==I)this.words[this.length]=I,this.length++;else if(t!==this)for(;eA.length?this.clone().iadd(A):A.clone().iadd(this)},e.prototype.isub=function(A){if(0!==A.negative){A.negative=0;var M=this.iadd(A);return A.negative=1,M._normSign()}if(0!==this.negative)return this.negative=0,this.iadd(A),this.negative=1,this._normSign();var t,g,I=this.cmp(A);if(0===I)return this.negative=0,this.length=1,this.words[0]=0,this;I>0?(t=this,g=A):(t=A,g=this);for(var e=0,i=0;i>26,this.words[i]=67108863&M;for(;0!==e&&i>26,this.words[i]=67108863&M;if(0===e&&i>>13,r=0|i[1],c=8191&r,o=r>>>13,B=0|i[2],Q=8191&B,a=B>>>13,h=0|i[3],s=8191&h,y=h>>>13,w=0|i[4],j=8191&w,L=w>>>13,d=0|i[5],x=8191&d,Y=d>>>13,u=0|i[6],S=8191&u,l=u>>>13,z=0|i[7],U=8191&z,f=z>>>13,O=0|i[8],F=8191&O,m=O>>>13,R=0|i[9],k=8191&R,p=R>>>13,b=0|T[0],G=8191&b,J=b>>>13,H=0|T[1],X=8191&H,v=H>>>13,V=0|T[2],W=8191&V,P=V>>>13,Z=0|T[3],K=8191&Z,q=Z>>>13,_=0|T[4],$=8191&_,AA=_>>>13,MA=0|T[5],tA=8191&MA,gA=MA>>>13,IA=0|T[6],eA=8191&IA,iA=IA>>>13,TA=0|T[7],EA=8191&TA,NA=TA>>>13,nA=0|T[8],DA=8191&nA,CA=nA>>>13,rA=0|T[9],cA=8191&rA,oA=rA>>>13;t.negative=A.negative^M.negative,t.length=19;var BA=(N+(g=Math.imul(D,G))|0)+((8191&(I=(I=Math.imul(D,J))+Math.imul(C,G)|0))<<13)|0;N=((e=Math.imul(C,J))+(I>>>13)|0)+(BA>>>26)|0,BA&=67108863,g=Math.imul(c,G),I=(I=Math.imul(c,J))+Math.imul(o,G)|0,e=Math.imul(o,J);var QA=(N+(g=g+Math.imul(D,X)|0)|0)+((8191&(I=(I=I+Math.imul(D,v)|0)+Math.imul(C,X)|0))<<13)|0;N=((e=e+Math.imul(C,v)|0)+(I>>>13)|0)+(QA>>>26)|0,QA&=67108863,g=Math.imul(Q,G),I=(I=Math.imul(Q,J))+Math.imul(a,G)|0,e=Math.imul(a,J),g=g+Math.imul(c,X)|0,I=(I=I+Math.imul(c,v)|0)+Math.imul(o,X)|0,e=e+Math.imul(o,v)|0;var aA=(N+(g=g+Math.imul(D,W)|0)|0)+((8191&(I=(I=I+Math.imul(D,P)|0)+Math.imul(C,W)|0))<<13)|0;N=((e=e+Math.imul(C,P)|0)+(I>>>13)|0)+(aA>>>26)|0,aA&=67108863,g=Math.imul(s,G),I=(I=Math.imul(s,J))+Math.imul(y,G)|0,e=Math.imul(y,J),g=g+Math.imul(Q,X)|0,I=(I=I+Math.imul(Q,v)|0)+Math.imul(a,X)|0,e=e+Math.imul(a,v)|0,g=g+Math.imul(c,W)|0,I=(I=I+Math.imul(c,P)|0)+Math.imul(o,W)|0,e=e+Math.imul(o,P)|0;var hA=(N+(g=g+Math.imul(D,K)|0)|0)+((8191&(I=(I=I+Math.imul(D,q)|0)+Math.imul(C,K)|0))<<13)|0;N=((e=e+Math.imul(C,q)|0)+(I>>>13)|0)+(hA>>>26)|0,hA&=67108863,g=Math.imul(j,G),I=(I=Math.imul(j,J))+Math.imul(L,G)|0,e=Math.imul(L,J),g=g+Math.imul(s,X)|0,I=(I=I+Math.imul(s,v)|0)+Math.imul(y,X)|0,e=e+Math.imul(y,v)|0,g=g+Math.imul(Q,W)|0,I=(I=I+Math.imul(Q,P)|0)+Math.imul(a,W)|0,e=e+Math.imul(a,P)|0,g=g+Math.imul(c,K)|0,I=(I=I+Math.imul(c,q)|0)+Math.imul(o,K)|0,e=e+Math.imul(o,q)|0;var sA=(N+(g=g+Math.imul(D,$)|0)|0)+((8191&(I=(I=I+Math.imul(D,AA)|0)+Math.imul(C,$)|0))<<13)|0;N=((e=e+Math.imul(C,AA)|0)+(I>>>13)|0)+(sA>>>26)|0,sA&=67108863,g=Math.imul(x,G),I=(I=Math.imul(x,J))+Math.imul(Y,G)|0,e=Math.imul(Y,J),g=g+Math.imul(j,X)|0,I=(I=I+Math.imul(j,v)|0)+Math.imul(L,X)|0,e=e+Math.imul(L,v)|0,g=g+Math.imul(s,W)|0,I=(I=I+Math.imul(s,P)|0)+Math.imul(y,W)|0,e=e+Math.imul(y,P)|0,g=g+Math.imul(Q,K)|0,I=(I=I+Math.imul(Q,q)|0)+Math.imul(a,K)|0,e=e+Math.imul(a,q)|0,g=g+Math.imul(c,$)|0,I=(I=I+Math.imul(c,AA)|0)+Math.imul(o,$)|0,e=e+Math.imul(o,AA)|0;var yA=(N+(g=g+Math.imul(D,tA)|0)|0)+((8191&(I=(I=I+Math.imul(D,gA)|0)+Math.imul(C,tA)|0))<<13)|0;N=((e=e+Math.imul(C,gA)|0)+(I>>>13)|0)+(yA>>>26)|0,yA&=67108863,g=Math.imul(S,G),I=(I=Math.imul(S,J))+Math.imul(l,G)|0,e=Math.imul(l,J),g=g+Math.imul(x,X)|0,I=(I=I+Math.imul(x,v)|0)+Math.imul(Y,X)|0,e=e+Math.imul(Y,v)|0,g=g+Math.imul(j,W)|0,I=(I=I+Math.imul(j,P)|0)+Math.imul(L,W)|0,e=e+Math.imul(L,P)|0,g=g+Math.imul(s,K)|0,I=(I=I+Math.imul(s,q)|0)+Math.imul(y,K)|0,e=e+Math.imul(y,q)|0,g=g+Math.imul(Q,$)|0,I=(I=I+Math.imul(Q,AA)|0)+Math.imul(a,$)|0,e=e+Math.imul(a,AA)|0,g=g+Math.imul(c,tA)|0,I=(I=I+Math.imul(c,gA)|0)+Math.imul(o,tA)|0,e=e+Math.imul(o,gA)|0;var wA=(N+(g=g+Math.imul(D,eA)|0)|0)+((8191&(I=(I=I+Math.imul(D,iA)|0)+Math.imul(C,eA)|0))<<13)|0;N=((e=e+Math.imul(C,iA)|0)+(I>>>13)|0)+(wA>>>26)|0,wA&=67108863,g=Math.imul(U,G),I=(I=Math.imul(U,J))+Math.imul(f,G)|0,e=Math.imul(f,J),g=g+Math.imul(S,X)|0,I=(I=I+Math.imul(S,v)|0)+Math.imul(l,X)|0,e=e+Math.imul(l,v)|0,g=g+Math.imul(x,W)|0,I=(I=I+Math.imul(x,P)|0)+Math.imul(Y,W)|0,e=e+Math.imul(Y,P)|0,g=g+Math.imul(j,K)|0,I=(I=I+Math.imul(j,q)|0)+Math.imul(L,K)|0,e=e+Math.imul(L,q)|0,g=g+Math.imul(s,$)|0,I=(I=I+Math.imul(s,AA)|0)+Math.imul(y,$)|0,e=e+Math.imul(y,AA)|0,g=g+Math.imul(Q,tA)|0,I=(I=I+Math.imul(Q,gA)|0)+Math.imul(a,tA)|0,e=e+Math.imul(a,gA)|0,g=g+Math.imul(c,eA)|0,I=(I=I+Math.imul(c,iA)|0)+Math.imul(o,eA)|0,e=e+Math.imul(o,iA)|0;var jA=(N+(g=g+Math.imul(D,EA)|0)|0)+((8191&(I=(I=I+Math.imul(D,NA)|0)+Math.imul(C,EA)|0))<<13)|0;N=((e=e+Math.imul(C,NA)|0)+(I>>>13)|0)+(jA>>>26)|0,jA&=67108863,g=Math.imul(F,G),I=(I=Math.imul(F,J))+Math.imul(m,G)|0,e=Math.imul(m,J),g=g+Math.imul(U,X)|0,I=(I=I+Math.imul(U,v)|0)+Math.imul(f,X)|0,e=e+Math.imul(f,v)|0,g=g+Math.imul(S,W)|0,I=(I=I+Math.imul(S,P)|0)+Math.imul(l,W)|0,e=e+Math.imul(l,P)|0,g=g+Math.imul(x,K)|0,I=(I=I+Math.imul(x,q)|0)+Math.imul(Y,K)|0,e=e+Math.imul(Y,q)|0,g=g+Math.imul(j,$)|0,I=(I=I+Math.imul(j,AA)|0)+Math.imul(L,$)|0,e=e+Math.imul(L,AA)|0,g=g+Math.imul(s,tA)|0,I=(I=I+Math.imul(s,gA)|0)+Math.imul(y,tA)|0,e=e+Math.imul(y,gA)|0,g=g+Math.imul(Q,eA)|0,I=(I=I+Math.imul(Q,iA)|0)+Math.imul(a,eA)|0,e=e+Math.imul(a,iA)|0,g=g+Math.imul(c,EA)|0,I=(I=I+Math.imul(c,NA)|0)+Math.imul(o,EA)|0,e=e+Math.imul(o,NA)|0;var LA=(N+(g=g+Math.imul(D,DA)|0)|0)+((8191&(I=(I=I+Math.imul(D,CA)|0)+Math.imul(C,DA)|0))<<13)|0;N=((e=e+Math.imul(C,CA)|0)+(I>>>13)|0)+(LA>>>26)|0,LA&=67108863,g=Math.imul(k,G),I=(I=Math.imul(k,J))+Math.imul(p,G)|0,e=Math.imul(p,J),g=g+Math.imul(F,X)|0,I=(I=I+Math.imul(F,v)|0)+Math.imul(m,X)|0,e=e+Math.imul(m,v)|0,g=g+Math.imul(U,W)|0,I=(I=I+Math.imul(U,P)|0)+Math.imul(f,W)|0,e=e+Math.imul(f,P)|0,g=g+Math.imul(S,K)|0,I=(I=I+Math.imul(S,q)|0)+Math.imul(l,K)|0,e=e+Math.imul(l,q)|0,g=g+Math.imul(x,$)|0,I=(I=I+Math.imul(x,AA)|0)+Math.imul(Y,$)|0,e=e+Math.imul(Y,AA)|0,g=g+Math.imul(j,tA)|0,I=(I=I+Math.imul(j,gA)|0)+Math.imul(L,tA)|0,e=e+Math.imul(L,gA)|0,g=g+Math.imul(s,eA)|0,I=(I=I+Math.imul(s,iA)|0)+Math.imul(y,eA)|0,e=e+Math.imul(y,iA)|0,g=g+Math.imul(Q,EA)|0,I=(I=I+Math.imul(Q,NA)|0)+Math.imul(a,EA)|0,e=e+Math.imul(a,NA)|0,g=g+Math.imul(c,DA)|0,I=(I=I+Math.imul(c,CA)|0)+Math.imul(o,DA)|0,e=e+Math.imul(o,CA)|0;var dA=(N+(g=g+Math.imul(D,cA)|0)|0)+((8191&(I=(I=I+Math.imul(D,oA)|0)+Math.imul(C,cA)|0))<<13)|0;N=((e=e+Math.imul(C,oA)|0)+(I>>>13)|0)+(dA>>>26)|0,dA&=67108863,g=Math.imul(k,X),I=(I=Math.imul(k,v))+Math.imul(p,X)|0,e=Math.imul(p,v),g=g+Math.imul(F,W)|0,I=(I=I+Math.imul(F,P)|0)+Math.imul(m,W)|0,e=e+Math.imul(m,P)|0,g=g+Math.imul(U,K)|0,I=(I=I+Math.imul(U,q)|0)+Math.imul(f,K)|0,e=e+Math.imul(f,q)|0,g=g+Math.imul(S,$)|0,I=(I=I+Math.imul(S,AA)|0)+Math.imul(l,$)|0,e=e+Math.imul(l,AA)|0,g=g+Math.imul(x,tA)|0,I=(I=I+Math.imul(x,gA)|0)+Math.imul(Y,tA)|0,e=e+Math.imul(Y,gA)|0,g=g+Math.imul(j,eA)|0,I=(I=I+Math.imul(j,iA)|0)+Math.imul(L,eA)|0,e=e+Math.imul(L,iA)|0,g=g+Math.imul(s,EA)|0,I=(I=I+Math.imul(s,NA)|0)+Math.imul(y,EA)|0,e=e+Math.imul(y,NA)|0,g=g+Math.imul(Q,DA)|0,I=(I=I+Math.imul(Q,CA)|0)+Math.imul(a,DA)|0,e=e+Math.imul(a,CA)|0;var xA=(N+(g=g+Math.imul(c,cA)|0)|0)+((8191&(I=(I=I+Math.imul(c,oA)|0)+Math.imul(o,cA)|0))<<13)|0;N=((e=e+Math.imul(o,oA)|0)+(I>>>13)|0)+(xA>>>26)|0,xA&=67108863,g=Math.imul(k,W),I=(I=Math.imul(k,P))+Math.imul(p,W)|0,e=Math.imul(p,P),g=g+Math.imul(F,K)|0,I=(I=I+Math.imul(F,q)|0)+Math.imul(m,K)|0,e=e+Math.imul(m,q)|0,g=g+Math.imul(U,$)|0,I=(I=I+Math.imul(U,AA)|0)+Math.imul(f,$)|0,e=e+Math.imul(f,AA)|0,g=g+Math.imul(S,tA)|0,I=(I=I+Math.imul(S,gA)|0)+Math.imul(l,tA)|0,e=e+Math.imul(l,gA)|0,g=g+Math.imul(x,eA)|0,I=(I=I+Math.imul(x,iA)|0)+Math.imul(Y,eA)|0,e=e+Math.imul(Y,iA)|0,g=g+Math.imul(j,EA)|0,I=(I=I+Math.imul(j,NA)|0)+Math.imul(L,EA)|0,e=e+Math.imul(L,NA)|0,g=g+Math.imul(s,DA)|0,I=(I=I+Math.imul(s,CA)|0)+Math.imul(y,DA)|0,e=e+Math.imul(y,CA)|0;var YA=(N+(g=g+Math.imul(Q,cA)|0)|0)+((8191&(I=(I=I+Math.imul(Q,oA)|0)+Math.imul(a,cA)|0))<<13)|0;N=((e=e+Math.imul(a,oA)|0)+(I>>>13)|0)+(YA>>>26)|0,YA&=67108863,g=Math.imul(k,K),I=(I=Math.imul(k,q))+Math.imul(p,K)|0,e=Math.imul(p,q),g=g+Math.imul(F,$)|0,I=(I=I+Math.imul(F,AA)|0)+Math.imul(m,$)|0,e=e+Math.imul(m,AA)|0,g=g+Math.imul(U,tA)|0,I=(I=I+Math.imul(U,gA)|0)+Math.imul(f,tA)|0,e=e+Math.imul(f,gA)|0,g=g+Math.imul(S,eA)|0,I=(I=I+Math.imul(S,iA)|0)+Math.imul(l,eA)|0,e=e+Math.imul(l,iA)|0,g=g+Math.imul(x,EA)|0,I=(I=I+Math.imul(x,NA)|0)+Math.imul(Y,EA)|0,e=e+Math.imul(Y,NA)|0,g=g+Math.imul(j,DA)|0,I=(I=I+Math.imul(j,CA)|0)+Math.imul(L,DA)|0,e=e+Math.imul(L,CA)|0;var uA=(N+(g=g+Math.imul(s,cA)|0)|0)+((8191&(I=(I=I+Math.imul(s,oA)|0)+Math.imul(y,cA)|0))<<13)|0;N=((e=e+Math.imul(y,oA)|0)+(I>>>13)|0)+(uA>>>26)|0,uA&=67108863,g=Math.imul(k,$),I=(I=Math.imul(k,AA))+Math.imul(p,$)|0,e=Math.imul(p,AA),g=g+Math.imul(F,tA)|0,I=(I=I+Math.imul(F,gA)|0)+Math.imul(m,tA)|0,e=e+Math.imul(m,gA)|0,g=g+Math.imul(U,eA)|0,I=(I=I+Math.imul(U,iA)|0)+Math.imul(f,eA)|0,e=e+Math.imul(f,iA)|0,g=g+Math.imul(S,EA)|0,I=(I=I+Math.imul(S,NA)|0)+Math.imul(l,EA)|0,e=e+Math.imul(l,NA)|0,g=g+Math.imul(x,DA)|0,I=(I=I+Math.imul(x,CA)|0)+Math.imul(Y,DA)|0,e=e+Math.imul(Y,CA)|0;var SA=(N+(g=g+Math.imul(j,cA)|0)|0)+((8191&(I=(I=I+Math.imul(j,oA)|0)+Math.imul(L,cA)|0))<<13)|0;N=((e=e+Math.imul(L,oA)|0)+(I>>>13)|0)+(SA>>>26)|0,SA&=67108863,g=Math.imul(k,tA),I=(I=Math.imul(k,gA))+Math.imul(p,tA)|0,e=Math.imul(p,gA),g=g+Math.imul(F,eA)|0,I=(I=I+Math.imul(F,iA)|0)+Math.imul(m,eA)|0,e=e+Math.imul(m,iA)|0,g=g+Math.imul(U,EA)|0,I=(I=I+Math.imul(U,NA)|0)+Math.imul(f,EA)|0,e=e+Math.imul(f,NA)|0,g=g+Math.imul(S,DA)|0,I=(I=I+Math.imul(S,CA)|0)+Math.imul(l,DA)|0,e=e+Math.imul(l,CA)|0;var lA=(N+(g=g+Math.imul(x,cA)|0)|0)+((8191&(I=(I=I+Math.imul(x,oA)|0)+Math.imul(Y,cA)|0))<<13)|0;N=((e=e+Math.imul(Y,oA)|0)+(I>>>13)|0)+(lA>>>26)|0,lA&=67108863,g=Math.imul(k,eA),I=(I=Math.imul(k,iA))+Math.imul(p,eA)|0,e=Math.imul(p,iA),g=g+Math.imul(F,EA)|0,I=(I=I+Math.imul(F,NA)|0)+Math.imul(m,EA)|0,e=e+Math.imul(m,NA)|0,g=g+Math.imul(U,DA)|0,I=(I=I+Math.imul(U,CA)|0)+Math.imul(f,DA)|0,e=e+Math.imul(f,CA)|0;var zA=(N+(g=g+Math.imul(S,cA)|0)|0)+((8191&(I=(I=I+Math.imul(S,oA)|0)+Math.imul(l,cA)|0))<<13)|0;N=((e=e+Math.imul(l,oA)|0)+(I>>>13)|0)+(zA>>>26)|0,zA&=67108863,g=Math.imul(k,EA),I=(I=Math.imul(k,NA))+Math.imul(p,EA)|0,e=Math.imul(p,NA),g=g+Math.imul(F,DA)|0,I=(I=I+Math.imul(F,CA)|0)+Math.imul(m,DA)|0,e=e+Math.imul(m,CA)|0;var UA=(N+(g=g+Math.imul(U,cA)|0)|0)+((8191&(I=(I=I+Math.imul(U,oA)|0)+Math.imul(f,cA)|0))<<13)|0;N=((e=e+Math.imul(f,oA)|0)+(I>>>13)|0)+(UA>>>26)|0,UA&=67108863,g=Math.imul(k,DA),I=(I=Math.imul(k,CA))+Math.imul(p,DA)|0,e=Math.imul(p,CA);var fA=(N+(g=g+Math.imul(F,cA)|0)|0)+((8191&(I=(I=I+Math.imul(F,oA)|0)+Math.imul(m,cA)|0))<<13)|0;N=((e=e+Math.imul(m,oA)|0)+(I>>>13)|0)+(fA>>>26)|0,fA&=67108863;var OA=(N+(g=Math.imul(k,cA))|0)+((8191&(I=(I=Math.imul(k,oA))+Math.imul(p,cA)|0))<<13)|0;return N=((e=Math.imul(p,oA))+(I>>>13)|0)+(OA>>>26)|0,OA&=67108863,E[0]=BA,E[1]=QA,E[2]=aA,E[3]=hA,E[4]=sA,E[5]=yA,E[6]=wA,E[7]=jA,E[8]=LA,E[9]=dA,E[10]=xA,E[11]=YA,E[12]=uA,E[13]=SA,E[14]=lA,E[15]=zA,E[16]=UA,E[17]=fA,E[18]=OA,0!==N&&(E[19]=N,t.length++),t};function c(A,M,t){return(new o).mulp(A,M,t)}function o(A,M){this.x=A,this.y=M}Math.imul||(r=C),e.prototype.mulTo=function(A,M){var t=this.length+A.length;return 10===this.length&&10===A.length?r(this,A,M):t<63?C(this,A,M):t<1024?function(A,M,t){t.negative=M.negative^A.negative,t.length=A.length+M.length;for(var g=0,I=0,e=0;e>>26)|0)>>>26,i&=67108863}t.words[e]=T,g=i,i=I}return 0!==g?t.words[e]=g:t.length--,t.strip()}(this,A,M):c(this,A,M)},o.prototype.makeRBT=function(A){for(var M=new Array(A),t=e.prototype._countBits(A)-1,g=0;g>=1;return g},o.prototype.permute=function(A,M,t,g,I,e){for(var i=0;i>>=1)I++;return 1<>>=13,t[2*i+1]=8191&e,e>>>=13;for(i=2*M;i>=26,M+=I/67108864|0,M+=e>>>26,this.words[t]=67108863&e}return 0!==M&&(this.words[t]=M,this.length++),this},e.prototype.muln=function(A){return this.clone().imuln(A)},e.prototype.sqr=function(){return this.mul(this)},e.prototype.isqr=function(){return this.imul(this.clone())},e.prototype.pow=function(A){var M=function(A){for(var M=new Array(A.bitLength()),t=0;t>>I}return M}(A);if(0===M.length)return new e(1);for(var t=this,g=0;g=0);var M,t=A%26,I=(A-t)/26,e=67108863>>>26-t<<26-t;if(0!==t){var i=0;for(M=0;M>>26-t}i&&(this.words[M]=i,this.length++)}if(0!==I){for(M=this.length-1;M>=0;M--)this.words[M+I]=this.words[M];for(M=0;M=0),I=M?(M-M%26)/26:0;var e=A%26,i=Math.min((A-e)/26,this.length),T=67108863^67108863>>>e<i)for(this.length-=i,N=0;N=0&&(0!==n||N>=I);N--){var D=0|this.words[N];this.words[N]=n<<26-e|D>>>e,n=D&T}return E&&0!==n&&(E.words[E.length++]=n),0===this.length&&(this.words[0]=0,this.length=1),this.strip()},e.prototype.ishrn=function(A,M,t){return g(0===this.negative),this.iushrn(A,M,t)},e.prototype.shln=function(A){return this.clone().ishln(A)},e.prototype.ushln=function(A){return this.clone().iushln(A)},e.prototype.shrn=function(A){return this.clone().ishrn(A)},e.prototype.ushrn=function(A){return this.clone().iushrn(A)},e.prototype.testn=function(A){g("number"==typeof A&&A>=0);var M=A%26,t=(A-M)/26,I=1<=0);var M=A%26,t=(A-M)/26;if(g(0===this.negative,"imaskn works only with positive numbers"),this.length<=t)return this;if(0!==M&&t++,this.length=Math.min(t,this.length),0!==M){var I=67108863^67108863>>>M<=67108864;M++)this.words[M]-=67108864,M===this.length-1?this.words[M+1]=1:this.words[M+1]++;return this.length=Math.max(this.length,M+1),this},e.prototype.isubn=function(A){if(g("number"==typeof A),g(A<67108864),A<0)return this.iaddn(-A);if(0!==this.negative)return this.negative=0,this.iaddn(A),this.negative=1,this;if(this.words[0]-=A,1===this.length&&this.words[0]<0)this.words[0]=-this.words[0],this.negative=1;else for(var M=0;M>26)-(E/67108864|0),this.words[I+t]=67108863&e}for(;I>26,this.words[I+t]=67108863&e;if(0===T)return this.strip();for(g(-1===T),T=0,I=0;I>26,this.words[I]=67108863&e;return this.negative=1,this.strip()},e.prototype._wordDiv=function(A,M){var t=(this.length,A.length),g=this.clone(),I=A,i=0|I.words[I.length-1];0!==(t=26-this._countBits(i))&&(I=I.ushln(t),g.iushln(t),i=0|I.words[I.length-1]);var T,E=g.length-I.length;if("mod"!==M){(T=new e(null)).length=E+1,T.words=new Array(T.length);for(var N=0;N=0;D--){var C=67108864*(0|g.words[I.length+D])+(0|g.words[I.length+D-1]);for(C=Math.min(C/i|0,67108863),g._ishlnsubmul(I,C,D);0!==g.negative;)C--,g.negative=0,g._ishlnsubmul(I,1,D),g.isZero()||(g.negative^=1);T&&(T.words[D]=C)}return T&&T.strip(),g.strip(),"div"!==M&&0!==t&&g.iushrn(t),{div:T||null,mod:g}},e.prototype.divmod=function(A,M,t){return g(!A.isZero()),this.isZero()?{div:new e(0),mod:new e(0)}:0!==this.negative&&0===A.negative?(T=this.neg().divmod(A,M),"mod"!==M&&(I=T.div.neg()),"div"!==M&&(i=T.mod.neg(),t&&0!==i.negative&&i.iadd(A)),{div:I,mod:i}):0===this.negative&&0!==A.negative?(T=this.divmod(A.neg(),M),"mod"!==M&&(I=T.div.neg()),{div:I,mod:T.mod}):0!=(this.negative&A.negative)?(T=this.neg().divmod(A.neg(),M),"div"!==M&&(i=T.mod.neg(),t&&0!==i.negative&&i.isub(A)),{div:T.div,mod:i}):A.length>this.length||this.cmp(A)<0?{div:new e(0),mod:this}:1===A.length?"div"===M?{div:this.divn(A.words[0]),mod:null}:"mod"===M?{div:null,mod:new e(this.modn(A.words[0]))}:{div:this.divn(A.words[0]),mod:new e(this.modn(A.words[0]))}:this._wordDiv(A,M);var I,i,T},e.prototype.div=function(A){return this.divmod(A,"div",!1).div},e.prototype.mod=function(A){return this.divmod(A,"mod",!1).mod},e.prototype.umod=function(A){return this.divmod(A,"mod",!0).mod},e.prototype.divRound=function(A){var M=this.divmod(A);if(M.mod.isZero())return M.div;var t=0!==M.div.negative?M.mod.isub(A):M.mod,g=A.ushrn(1),I=A.andln(1),e=t.cmp(g);return e<0||1===I&&0===e?M.div:0!==M.div.negative?M.div.isubn(1):M.div.iaddn(1)},e.prototype.modn=function(A){g(A<=67108863);for(var M=(1<<26)%A,t=0,I=this.length-1;I>=0;I--)t=(M*t+(0|this.words[I]))%A;return t},e.prototype.idivn=function(A){g(A<=67108863);for(var M=0,t=this.length-1;t>=0;t--){var I=(0|this.words[t])+67108864*M;this.words[t]=I/A|0,M=I%A}return this.strip()},e.prototype.divn=function(A){return this.clone().idivn(A)},e.prototype.egcd=function(A){g(0===A.negative),g(!A.isZero());var M=this,t=A.clone();M=0!==M.negative?M.umod(A):M.clone();for(var I=new e(1),i=new e(0),T=new e(0),E=new e(1),N=0;M.isEven()&&t.isEven();)M.iushrn(1),t.iushrn(1),++N;for(var n=t.clone(),D=M.clone();!M.isZero();){for(var C=0,r=1;0==(M.words[0]&r)&&C<26;++C,r<<=1);if(C>0)for(M.iushrn(C);C-- >0;)(I.isOdd()||i.isOdd())&&(I.iadd(n),i.isub(D)),I.iushrn(1),i.iushrn(1);for(var c=0,o=1;0==(t.words[0]&o)&&c<26;++c,o<<=1);if(c>0)for(t.iushrn(c);c-- >0;)(T.isOdd()||E.isOdd())&&(T.iadd(n),E.isub(D)),T.iushrn(1),E.iushrn(1);M.cmp(t)>=0?(M.isub(t),I.isub(T),i.isub(E)):(t.isub(M),T.isub(I),E.isub(i))}return{a:T,b:E,gcd:t.iushln(N)}},e.prototype._invmp=function(A){g(0===A.negative),g(!A.isZero());var M=this,t=A.clone();M=0!==M.negative?M.umod(A):M.clone();for(var I,i=new e(1),T=new e(0),E=t.clone();M.cmpn(1)>0&&t.cmpn(1)>0;){for(var N=0,n=1;0==(M.words[0]&n)&&N<26;++N,n<<=1);if(N>0)for(M.iushrn(N);N-- >0;)i.isOdd()&&i.iadd(E),i.iushrn(1);for(var D=0,C=1;0==(t.words[0]&C)&&D<26;++D,C<<=1);if(D>0)for(t.iushrn(D);D-- >0;)T.isOdd()&&T.iadd(E),T.iushrn(1);M.cmp(t)>=0?(M.isub(t),i.isub(T)):(t.isub(M),T.isub(i))}return(I=0===M.cmpn(1)?i:T).cmpn(0)<0&&I.iadd(A),I},e.prototype.gcd=function(A){if(this.isZero())return A.abs();if(A.isZero())return this.abs();var M=this.clone(),t=A.clone();M.negative=0,t.negative=0;for(var g=0;M.isEven()&&t.isEven();g++)M.iushrn(1),t.iushrn(1);for(;;){for(;M.isEven();)M.iushrn(1);for(;t.isEven();)t.iushrn(1);var I=M.cmp(t);if(I<0){var e=M;M=t,t=e}else if(0===I||0===t.cmpn(1))break;M.isub(t)}return t.iushln(g)},e.prototype.invm=function(A){return this.egcd(A).a.umod(A)},e.prototype.isEven=function(){return 0==(1&this.words[0])},e.prototype.isOdd=function(){return 1==(1&this.words[0])},e.prototype.andln=function(A){return this.words[0]&A},e.prototype.bincn=function(A){g("number"==typeof A);var M=A%26,t=(A-M)/26,I=1<>>26,T&=67108863,this.words[i]=T}return 0!==e&&(this.words[i]=e,this.length++),this},e.prototype.isZero=function(){return 1===this.length&&0===this.words[0]},e.prototype.cmpn=function(A){var M,t=A<0;if(0!==this.negative&&!t)return-1;if(0===this.negative&&t)return 1;if(this.strip(),this.length>1)M=1;else{t&&(A=-A),g(A<=67108863,"Number is too big");var I=0|this.words[0];M=I===A?0:IA.length)return 1;if(this.length=0;t--){var g=0|this.words[t],I=0|A.words[t];if(g!==I){gI&&(M=1);break}}return M},e.prototype.gtn=function(A){return 1===this.cmpn(A)},e.prototype.gt=function(A){return 1===this.cmp(A)},e.prototype.gten=function(A){return this.cmpn(A)>=0},e.prototype.gte=function(A){return this.cmp(A)>=0},e.prototype.ltn=function(A){return-1===this.cmpn(A)},e.prototype.lt=function(A){return-1===this.cmp(A)},e.prototype.lten=function(A){return this.cmpn(A)<=0},e.prototype.lte=function(A){return this.cmp(A)<=0},e.prototype.eqn=function(A){return 0===this.cmpn(A)},e.prototype.eq=function(A){return 0===this.cmp(A)},e.red=function(A){return new w(A)},e.prototype.toRed=function(A){return g(!this.red,"Already a number in reduction context"),g(0===this.negative,"red works only with positives"),A.convertTo(this)._forceRed(A)},e.prototype.fromRed=function(){return g(this.red,"fromRed works only with numbers in reduction context"),this.red.convertFrom(this)},e.prototype._forceRed=function(A){return this.red=A,this},e.prototype.forceRed=function(A){return g(!this.red,"Already a number in reduction context"),this._forceRed(A)},e.prototype.redAdd=function(A){return g(this.red,"redAdd works only with red numbers"),this.red.add(this,A)},e.prototype.redIAdd=function(A){return g(this.red,"redIAdd works only with red numbers"),this.red.iadd(this,A)},e.prototype.redSub=function(A){return g(this.red,"redSub works only with red numbers"),this.red.sub(this,A)},e.prototype.redISub=function(A){return g(this.red,"redISub works only with red numbers"),this.red.isub(this,A)},e.prototype.redShl=function(A){return g(this.red,"redShl works only with red numbers"),this.red.shl(this,A)},e.prototype.redMul=function(A){return g(this.red,"redMul works only with red numbers"),this.red._verify2(this,A),this.red.mul(this,A)},e.prototype.redIMul=function(A){return g(this.red,"redMul works only with red numbers"),this.red._verify2(this,A),this.red.imul(this,A)},e.prototype.redSqr=function(){return g(this.red,"redSqr works only with red numbers"),this.red._verify1(this),this.red.sqr(this)},e.prototype.redISqr=function(){return g(this.red,"redISqr works only with red numbers"),this.red._verify1(this),this.red.isqr(this)},e.prototype.redSqrt=function(){return g(this.red,"redSqrt works only with red numbers"),this.red._verify1(this),this.red.sqrt(this)},e.prototype.redInvm=function(){return g(this.red,"redInvm works only with red numbers"),this.red._verify1(this),this.red.invm(this)},e.prototype.redNeg=function(){return g(this.red,"redNeg works only with red numbers"),this.red._verify1(this),this.red.neg(this)},e.prototype.redPow=function(A){return g(this.red&&!A.red,"redPow(normalNum)"),this.red._verify1(this),this.red.pow(this,A)};var B={k256:null,p224:null,p192:null,p25519:null};function Q(A,M){this.name=A,this.p=new e(M,16),this.n=this.p.bitLength(),this.k=new e(1).iushln(this.n).isub(this.p),this.tmp=this._tmp()}function a(){Q.call(this,"k256","ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f")}function h(){Q.call(this,"p224","ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001")}function s(){Q.call(this,"p192","ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff")}function y(){Q.call(this,"25519","7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed")}function w(A){if("string"==typeof A){var M=e._prime(A);this.m=M.p,this.prime=M}else g(A.gtn(1),"modulus must be greater than 1"),this.m=A,this.prime=null}function j(A){w.call(this,A),this.shift=this.m.bitLength(),this.shift%26!=0&&(this.shift+=26-this.shift%26),this.r=new e(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)}Q.prototype._tmp=function(){var A=new e(null);return A.words=new Array(Math.ceil(this.n/13)),A},Q.prototype.ireduce=function(A){var M,t=A;do{this.split(t,this.tmp),M=(t=(t=this.imulK(t)).iadd(this.tmp)).bitLength()}while(M>this.n);var g=M0?t.isub(this.p):void 0!==t.strip?t.strip():t._strip(),t},Q.prototype.split=function(A,M){A.iushrn(this.n,0,M)},Q.prototype.imulK=function(A){return A.imul(this.k)},I(a,Q),a.prototype.split=function(A,M){for(var t=Math.min(A.length,9),g=0;g>>22,I=e}I>>>=22,A.words[g-10]=I,0===I&&A.length>10?A.length-=10:A.length-=9},a.prototype.imulK=function(A){A.words[A.length]=0,A.words[A.length+1]=0,A.length+=2;for(var M=0,t=0;t>>=26,A.words[t]=I,M=g}return 0!==M&&(A.words[A.length++]=M),A},e._prime=function(A){if(B[A])return B[A];var M;if("k256"===A)M=new a;else if("p224"===A)M=new h;else if("p192"===A)M=new s;else{if("p25519"!==A)throw new Error("Unknown prime "+A);M=new y}return B[A]=M,M},w.prototype._verify1=function(A){g(0===A.negative,"red works only with positives"),g(A.red,"red works only with red numbers")},w.prototype._verify2=function(A,M){g(0==(A.negative|M.negative),"red works only with positives"),g(A.red&&A.red===M.red,"red works only with red numbers")},w.prototype.imod=function(A){return this.prime?this.prime.ireduce(A)._forceRed(this):A.umod(this.m)._forceRed(this)},w.prototype.neg=function(A){return A.isZero()?A.clone():this.m.sub(A)._forceRed(this)},w.prototype.add=function(A,M){this._verify2(A,M);var t=A.add(M);return t.cmp(this.m)>=0&&t.isub(this.m),t._forceRed(this)},w.prototype.iadd=function(A,M){this._verify2(A,M);var t=A.iadd(M);return t.cmp(this.m)>=0&&t.isub(this.m),t},w.prototype.sub=function(A,M){this._verify2(A,M);var t=A.sub(M);return t.cmpn(0)<0&&t.iadd(this.m),t._forceRed(this)},w.prototype.isub=function(A,M){this._verify2(A,M);var t=A.isub(M);return t.cmpn(0)<0&&t.iadd(this.m),t},w.prototype.shl=function(A,M){return this._verify1(A),this.imod(A.ushln(M))},w.prototype.imul=function(A,M){return this._verify2(A,M),this.imod(A.imul(M))},w.prototype.mul=function(A,M){return this._verify2(A,M),this.imod(A.mul(M))},w.prototype.isqr=function(A){return this.imul(A,A.clone())},w.prototype.sqr=function(A){return this.mul(A,A)},w.prototype.sqrt=function(A){if(A.isZero())return A.clone();var M=this.m.andln(3);if(g(M%2==1),3===M){var t=this.m.add(new e(1)).iushrn(2);return this.pow(A,t)}for(var I=this.m.subn(1),i=0;!I.isZero()&&0===I.andln(1);)i++,I.iushrn(1);g(!I.isZero());var T=new e(1).toRed(this),E=T.redNeg(),N=this.m.subn(1).iushrn(1),n=this.m.bitLength();for(n=new e(2*n*n).toRed(this);0!==this.pow(n,N).cmp(E);)n.redIAdd(E);for(var D=this.pow(n,I),C=this.pow(A,I.addn(1).iushrn(1)),r=this.pow(A,I),c=i;0!==r.cmp(T);){for(var o=r,B=0;0!==o.cmp(T);B++)o=o.redSqr();g(B=0;g--){for(var N=M.words[g],n=E-1;n>=0;n--){var D=N>>n&1;I!==t[0]&&(I=this.sqr(I)),0!==D||0!==i?(i<<=1,i|=D,(4===++T||0===g&&0===n)&&(I=this.mul(I,t[i]),T=0,i=0)):T=0}E=26}return I},w.prototype.convertTo=function(A){var M=A.umod(this.m);return M===A?M.clone():M},w.prototype.convertFrom=function(A){var M=A.clone();return M.red=null,M},e.mont=function(A){return new j(A)},I(j,w),j.prototype.convertTo=function(A){return this.imod(A.ushln(this.shift))},j.prototype.convertFrom=function(A){var M=this.imod(A.mul(this.rinv));return M.red=null,M},j.prototype.imul=function(A,M){if(A.isZero()||M.isZero())return A.words[0]=0,A.length=1,A;var t=A.imul(M),g=t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),I=t.isub(g).iushrn(this.shift),e=I;return I.cmp(this.m)>=0?e=I.isub(this.m):I.cmpn(0)<0&&(e=I.iadd(this.m)),e._forceRed(this)},j.prototype.mul=function(A,M){if(A.isZero()||M.isZero())return new e(0)._forceRed(this);var t=A.mul(M),g=t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),I=t.isub(g).iushrn(this.shift),i=I;return I.cmp(this.m)>=0?i=I.isub(this.m):I.cmpn(0)<0&&(i=I.iadd(this.m)),i._forceRed(this)},j.prototype.invm=function(A){return this.imod(A._invmp(this.m).mul(this.r2))._forceRed(this)}}(A,this)}).call(this,t(92)(A))},function(A,M,t){"use strict";var g,I="object"==typeof Reflect?Reflect:null,e=I&&"function"==typeof I.apply?I.apply:function(A,M,t){return Function.prototype.apply.call(A,M,t)};g=I&&"function"==typeof I.ownKeys?I.ownKeys:Object.getOwnPropertySymbols?function(A){return Object.getOwnPropertyNames(A).concat(Object.getOwnPropertySymbols(A))}:function(A){return Object.getOwnPropertyNames(A)};var i=Number.isNaN||function(A){return A!=A};function T(){T.init.call(this)}A.exports=T,A.exports.once=function(A,M){return new Promise((function(t,g){function I(){void 0!==e&&A.removeListener("error",e),t([].slice.call(arguments))}var e;"error"!==M&&(e=function(t){A.removeListener(M,I),g(t)},A.once("error",e)),A.once(M,I)}))},T.EventEmitter=T,T.prototype._events=void 0,T.prototype._eventsCount=0,T.prototype._maxListeners=void 0;var E=10;function N(A){if("function"!=typeof A)throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof A)}function n(A){return void 0===A._maxListeners?T.defaultMaxListeners:A._maxListeners}function D(A,M,t,g){var I,e,i,T;if(N(t),void 0===(e=A._events)?(e=A._events=Object.create(null),A._eventsCount=0):(void 0!==e.newListener&&(A.emit("newListener",M,t.listener?t.listener:t),e=A._events),i=e[M]),void 0===i)i=e[M]=t,++A._eventsCount;else if("function"==typeof i?i=e[M]=g?[t,i]:[i,t]:g?i.unshift(t):i.push(t),(I=n(A))>0&&i.length>I&&!i.warned){i.warned=!0;var E=new Error("Possible EventEmitter memory leak detected. "+i.length+" "+String(M)+" listeners added. Use emitter.setMaxListeners() to increase limit");E.name="MaxListenersExceededWarning",E.emitter=A,E.type=M,E.count=i.length,T=E,console&&console.warn&&console.warn(T)}return A}function C(){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 r(A,M,t){var g={fired:!1,wrapFn:void 0,target:A,type:M,listener:t},I=C.bind(g);return I.listener=t,g.wrapFn=I,I}function c(A,M,t){var g=A._events;if(void 0===g)return[];var I=g[M];return void 0===I?[]:"function"==typeof I?t?[I.listener||I]:[I]:t?function(A){for(var M=new Array(A.length),t=0;t0&&(i=M[0]),i instanceof Error)throw i;var T=new Error("Unhandled error."+(i?" ("+i.message+")":""));throw T.context=i,T}var E=I[A];if(void 0===E)return!1;if("function"==typeof E)e(E,this,M);else{var N=E.length,n=B(E,N);for(t=0;t=0;e--)if(t[e]===M||t[e].listener===M){i=t[e].listener,I=e;break}if(I<0)return this;0===I?t.shift():function(A,M){for(;M+1=0;g--)this.removeListener(A,M[g]);return this},T.prototype.listeners=function(A){return c(this,A,!0)},T.prototype.rawListeners=function(A){return c(this,A,!1)},T.listenerCount=function(A,M){return"function"==typeof A.listenerCount?A.listenerCount(M):o.call(A,M)},T.prototype.listenerCount=o,T.prototype.eventNames=function(){return this._eventsCount>0?g(this._events):[]}},function(A,M){var t,g,I=A.exports={};function e(){throw new Error("setTimeout has not been defined")}function i(){throw new Error("clearTimeout has not been defined")}function T(A){if(t===setTimeout)return setTimeout(A,0);if((t===e||!t)&&setTimeout)return t=setTimeout,setTimeout(A,0);try{return t(A,0)}catch(M){try{return t.call(null,A,0)}catch(M){return t.call(this,A,0)}}}!function(){try{t="function"==typeof setTimeout?setTimeout:e}catch(A){t=e}try{g="function"==typeof clearTimeout?clearTimeout:i}catch(A){g=i}}();var E,N=[],n=!1,D=-1;function C(){n&&E&&(n=!1,E.length?N=E.concat(N):D=-1,N.length&&r())}function r(){if(!n){var A=T(C);n=!0;for(var M=N.length;M;){for(E=N,N=[];++D1)for(var t=1;t + * @license MIT + */ +var g=t(111),I=t(112),e=t(59);function i(){return E.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function T(A,M){if(i()=i())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+i().toString(16)+" bytes");return 0|A}function c(A,M){if(E.isBuffer(A))return A.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(A)||A instanceof ArrayBuffer))return A.byteLength;"string"!=typeof A&&(A=""+A);var t=A.length;if(0===t)return 0;for(var g=!1;;)switch(M){case"ascii":case"latin1":case"binary":return t;case"utf8":case"utf-8":case void 0:return b(A).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*t;case"hex":return t>>>1;case"base64":return G(A).length;default:if(g)return b(A).length;M=(""+M).toLowerCase(),g=!0}}function o(A,M,t){var g=!1;if((void 0===M||M<0)&&(M=0),M>this.length)return"";if((void 0===t||t>this.length)&&(t=this.length),t<=0)return"";if((t>>>=0)<=(M>>>=0))return"";for(A||(A="utf8");;)switch(A){case"hex":return S(this,M,t);case"utf8":case"utf-8":return x(this,M,t);case"ascii":return Y(this,M,t);case"latin1":case"binary":return u(this,M,t);case"base64":return d(this,M,t);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return l(this,M,t);default:if(g)throw new TypeError("Unknown encoding: "+A);A=(A+"").toLowerCase(),g=!0}}function B(A,M,t){var g=A[M];A[M]=A[t],A[t]=g}function Q(A,M,t,g,I){if(0===A.length)return-1;if("string"==typeof t?(g=t,t=0):t>2147483647?t=2147483647:t<-2147483648&&(t=-2147483648),t=+t,isNaN(t)&&(t=I?0:A.length-1),t<0&&(t=A.length+t),t>=A.length){if(I)return-1;t=A.length-1}else if(t<0){if(!I)return-1;t=0}if("string"==typeof M&&(M=E.from(M,g)),E.isBuffer(M))return 0===M.length?-1:a(A,M,t,g,I);if("number"==typeof M)return M&=255,E.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?I?Uint8Array.prototype.indexOf.call(A,M,t):Uint8Array.prototype.lastIndexOf.call(A,M,t):a(A,[M],t,g,I);throw new TypeError("val must be string, number or Buffer")}function a(A,M,t,g,I){var e,i=1,T=A.length,E=M.length;if(void 0!==g&&("ucs2"===(g=String(g).toLowerCase())||"ucs-2"===g||"utf16le"===g||"utf-16le"===g)){if(A.length<2||M.length<2)return-1;i=2,T/=2,E/=2,t/=2}function N(A,M){return 1===i?A[M]:A.readUInt16BE(M*i)}if(I){var n=-1;for(e=t;eT&&(t=T-E),e=t;e>=0;e--){for(var D=!0,C=0;CI&&(g=I):g=I;var e=M.length;if(e%2!=0)throw new TypeError("Invalid hex string");g>e/2&&(g=e/2);for(var i=0;i>8,I=t%256,e.push(I),e.push(g);return e}(M,A.length-t),A,t,g)}function d(A,M,t){return 0===M&&t===A.length?g.fromByteArray(A):g.fromByteArray(A.slice(M,t))}function x(A,M,t){t=Math.min(A.length,t);for(var g=[],I=M;I239?4:N>223?3:N>191?2:1;if(I+D<=t)switch(D){case 1:N<128&&(n=N);break;case 2:128==(192&(e=A[I+1]))&&(E=(31&N)<<6|63&e)>127&&(n=E);break;case 3:e=A[I+1],i=A[I+2],128==(192&e)&&128==(192&i)&&(E=(15&N)<<12|(63&e)<<6|63&i)>2047&&(E<55296||E>57343)&&(n=E);break;case 4:e=A[I+1],i=A[I+2],T=A[I+3],128==(192&e)&&128==(192&i)&&128==(192&T)&&(E=(15&N)<<18|(63&e)<<12|(63&i)<<6|63&T)>65535&&E<1114112&&(n=E)}null===n?(n=65533,D=1):n>65535&&(n-=65536,g.push(n>>>10&1023|55296),n=56320|1023&n),g.push(n),I+=D}return function(A){var M=A.length;if(M<=4096)return String.fromCharCode.apply(String,A);var t="",g=0;for(;g0&&(A=this.toString("hex",0,t).match(/.{2}/g).join(" "),this.length>t&&(A+=" ... ")),""},E.prototype.compare=function(A,M,t,g,I){if(!E.isBuffer(A))throw new TypeError("Argument must be a Buffer");if(void 0===M&&(M=0),void 0===t&&(t=A?A.length:0),void 0===g&&(g=0),void 0===I&&(I=this.length),M<0||t>A.length||g<0||I>this.length)throw new RangeError("out of range index");if(g>=I&&M>=t)return 0;if(g>=I)return-1;if(M>=t)return 1;if(this===A)return 0;for(var e=(I>>>=0)-(g>>>=0),i=(t>>>=0)-(M>>>=0),T=Math.min(e,i),N=this.slice(g,I),n=A.slice(M,t),D=0;DI)&&(t=I),A.length>0&&(t<0||M<0)||M>this.length)throw new RangeError("Attempt to write outside buffer bounds");g||(g="utf8");for(var e=!1;;)switch(g){case"hex":return h(this,A,M,t);case"utf8":case"utf-8":return s(this,A,M,t);case"ascii":return y(this,A,M,t);case"latin1":case"binary":return w(this,A,M,t);case"base64":return j(this,A,M,t);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return L(this,A,M,t);default:if(e)throw new TypeError("Unknown encoding: "+g);g=(""+g).toLowerCase(),e=!0}},E.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};function Y(A,M,t){var g="";t=Math.min(A.length,t);for(var I=M;Ig)&&(t=g);for(var I="",e=M;et)throw new RangeError("Trying to access beyond buffer length")}function U(A,M,t,g,I,e){if(!E.isBuffer(A))throw new TypeError('"buffer" argument must be a Buffer instance');if(M>I||MA.length)throw new RangeError("Index out of range")}function f(A,M,t,g){M<0&&(M=65535+M+1);for(var I=0,e=Math.min(A.length-t,2);I>>8*(g?I:1-I)}function O(A,M,t,g){M<0&&(M=4294967295+M+1);for(var I=0,e=Math.min(A.length-t,4);I>>8*(g?I:3-I)&255}function F(A,M,t,g,I,e){if(t+g>A.length)throw new RangeError("Index out of range");if(t<0)throw new RangeError("Index out of range")}function m(A,M,t,g,e){return e||F(A,0,t,4),I.write(A,M,t,g,23,4),t+4}function R(A,M,t,g,e){return e||F(A,0,t,8),I.write(A,M,t,g,52,8),t+8}E.prototype.slice=function(A,M){var t,g=this.length;if((A=~~A)<0?(A+=g)<0&&(A=0):A>g&&(A=g),(M=void 0===M?g:~~M)<0?(M+=g)<0&&(M=0):M>g&&(M=g),M0&&(I*=256);)g+=this[A+--M]*I;return g},E.prototype.readUInt8=function(A,M){return M||z(A,1,this.length),this[A]},E.prototype.readUInt16LE=function(A,M){return M||z(A,2,this.length),this[A]|this[A+1]<<8},E.prototype.readUInt16BE=function(A,M){return M||z(A,2,this.length),this[A]<<8|this[A+1]},E.prototype.readUInt32LE=function(A,M){return M||z(A,4,this.length),(this[A]|this[A+1]<<8|this[A+2]<<16)+16777216*this[A+3]},E.prototype.readUInt32BE=function(A,M){return M||z(A,4,this.length),16777216*this[A]+(this[A+1]<<16|this[A+2]<<8|this[A+3])},E.prototype.readIntLE=function(A,M,t){A|=0,M|=0,t||z(A,M,this.length);for(var g=this[A],I=1,e=0;++e=(I*=128)&&(g-=Math.pow(2,8*M)),g},E.prototype.readIntBE=function(A,M,t){A|=0,M|=0,t||z(A,M,this.length);for(var g=M,I=1,e=this[A+--g];g>0&&(I*=256);)e+=this[A+--g]*I;return e>=(I*=128)&&(e-=Math.pow(2,8*M)),e},E.prototype.readInt8=function(A,M){return M||z(A,1,this.length),128&this[A]?-1*(255-this[A]+1):this[A]},E.prototype.readInt16LE=function(A,M){M||z(A,2,this.length);var t=this[A]|this[A+1]<<8;return 32768&t?4294901760|t:t},E.prototype.readInt16BE=function(A,M){M||z(A,2,this.length);var t=this[A+1]|this[A]<<8;return 32768&t?4294901760|t:t},E.prototype.readInt32LE=function(A,M){return M||z(A,4,this.length),this[A]|this[A+1]<<8|this[A+2]<<16|this[A+3]<<24},E.prototype.readInt32BE=function(A,M){return M||z(A,4,this.length),this[A]<<24|this[A+1]<<16|this[A+2]<<8|this[A+3]},E.prototype.readFloatLE=function(A,M){return M||z(A,4,this.length),I.read(this,A,!0,23,4)},E.prototype.readFloatBE=function(A,M){return M||z(A,4,this.length),I.read(this,A,!1,23,4)},E.prototype.readDoubleLE=function(A,M){return M||z(A,8,this.length),I.read(this,A,!0,52,8)},E.prototype.readDoubleBE=function(A,M){return M||z(A,8,this.length),I.read(this,A,!1,52,8)},E.prototype.writeUIntLE=function(A,M,t,g){(A=+A,M|=0,t|=0,g)||U(this,A,M,t,Math.pow(2,8*t)-1,0);var I=1,e=0;for(this[M]=255&A;++e=0&&(e*=256);)this[M+I]=A/e&255;return M+t},E.prototype.writeUInt8=function(A,M,t){return A=+A,M|=0,t||U(this,A,M,1,255,0),E.TYPED_ARRAY_SUPPORT||(A=Math.floor(A)),this[M]=255&A,M+1},E.prototype.writeUInt16LE=function(A,M,t){return A=+A,M|=0,t||U(this,A,M,2,65535,0),E.TYPED_ARRAY_SUPPORT?(this[M]=255&A,this[M+1]=A>>>8):f(this,A,M,!0),M+2},E.prototype.writeUInt16BE=function(A,M,t){return A=+A,M|=0,t||U(this,A,M,2,65535,0),E.TYPED_ARRAY_SUPPORT?(this[M]=A>>>8,this[M+1]=255&A):f(this,A,M,!1),M+2},E.prototype.writeUInt32LE=function(A,M,t){return A=+A,M|=0,t||U(this,A,M,4,4294967295,0),E.TYPED_ARRAY_SUPPORT?(this[M+3]=A>>>24,this[M+2]=A>>>16,this[M+1]=A>>>8,this[M]=255&A):O(this,A,M,!0),M+4},E.prototype.writeUInt32BE=function(A,M,t){return A=+A,M|=0,t||U(this,A,M,4,4294967295,0),E.TYPED_ARRAY_SUPPORT?(this[M]=A>>>24,this[M+1]=A>>>16,this[M+2]=A>>>8,this[M+3]=255&A):O(this,A,M,!1),M+4},E.prototype.writeIntLE=function(A,M,t,g){if(A=+A,M|=0,!g){var I=Math.pow(2,8*t-1);U(this,A,M,t,I-1,-I)}var e=0,i=1,T=0;for(this[M]=255&A;++e>0)-T&255;return M+t},E.prototype.writeIntBE=function(A,M,t,g){if(A=+A,M|=0,!g){var I=Math.pow(2,8*t-1);U(this,A,M,t,I-1,-I)}var e=t-1,i=1,T=0;for(this[M+e]=255&A;--e>=0&&(i*=256);)A<0&&0===T&&0!==this[M+e+1]&&(T=1),this[M+e]=(A/i>>0)-T&255;return M+t},E.prototype.writeInt8=function(A,M,t){return A=+A,M|=0,t||U(this,A,M,1,127,-128),E.TYPED_ARRAY_SUPPORT||(A=Math.floor(A)),A<0&&(A=255+A+1),this[M]=255&A,M+1},E.prototype.writeInt16LE=function(A,M,t){return A=+A,M|=0,t||U(this,A,M,2,32767,-32768),E.TYPED_ARRAY_SUPPORT?(this[M]=255&A,this[M+1]=A>>>8):f(this,A,M,!0),M+2},E.prototype.writeInt16BE=function(A,M,t){return A=+A,M|=0,t||U(this,A,M,2,32767,-32768),E.TYPED_ARRAY_SUPPORT?(this[M]=A>>>8,this[M+1]=255&A):f(this,A,M,!1),M+2},E.prototype.writeInt32LE=function(A,M,t){return A=+A,M|=0,t||U(this,A,M,4,2147483647,-2147483648),E.TYPED_ARRAY_SUPPORT?(this[M]=255&A,this[M+1]=A>>>8,this[M+2]=A>>>16,this[M+3]=A>>>24):O(this,A,M,!0),M+4},E.prototype.writeInt32BE=function(A,M,t){return A=+A,M|=0,t||U(this,A,M,4,2147483647,-2147483648),A<0&&(A=4294967295+A+1),E.TYPED_ARRAY_SUPPORT?(this[M]=A>>>24,this[M+1]=A>>>16,this[M+2]=A>>>8,this[M+3]=255&A):O(this,A,M,!1),M+4},E.prototype.writeFloatLE=function(A,M,t){return m(this,A,M,!0,t)},E.prototype.writeFloatBE=function(A,M,t){return m(this,A,M,!1,t)},E.prototype.writeDoubleLE=function(A,M,t){return R(this,A,M,!0,t)},E.prototype.writeDoubleBE=function(A,M,t){return R(this,A,M,!1,t)},E.prototype.copy=function(A,M,t,g){if(t||(t=0),g||0===g||(g=this.length),M>=A.length&&(M=A.length),M||(M=0),g>0&&g=this.length)throw new RangeError("sourceStart out of bounds");if(g<0)throw new RangeError("sourceEnd out of bounds");g>this.length&&(g=this.length),A.length-M=0;--I)A[I+M]=this[I+t];else if(e<1e3||!E.TYPED_ARRAY_SUPPORT)for(I=0;I>>=0,t=void 0===t?this.length:t>>>0,A||(A=0),"number"==typeof A)for(e=M;e55295&&t<57344){if(!I){if(t>56319){(M-=3)>-1&&e.push(239,191,189);continue}if(i+1===g){(M-=3)>-1&&e.push(239,191,189);continue}I=t;continue}if(t<56320){(M-=3)>-1&&e.push(239,191,189),I=t;continue}t=65536+(I-55296<<10|t-56320)}else I&&(M-=3)>-1&&e.push(239,191,189);if(I=null,t<128){if((M-=1)<0)break;e.push(t)}else if(t<2048){if((M-=2)<0)break;e.push(t>>6|192,63&t|128)}else if(t<65536){if((M-=3)<0)break;e.push(t>>12|224,t>>6&63|128,63&t|128)}else{if(!(t<1114112))throw new Error("Invalid code point");if((M-=4)<0)break;e.push(t>>18|240,t>>12&63|128,t>>6&63|128,63&t|128)}}return e}function G(A){return g.toByteArray(function(A){if((A=function(A){return A.trim?A.trim():A.replace(/^\s+|\s+$/g,"")}(A).replace(k,"")).length<2)return"";for(;A.length%4!=0;)A+="=";return A}(A))}function J(A,M,t,g){for(var I=0;I=M.length||I>=A.length);++I)M[I+t]=A[I];return I}}).call(this,t(10))},function(A,M){var t;t=function(){return this}();try{t=t||new Function("return this")()}catch(A){"object"==typeof window&&(t=window)}A.exports=t},function(A,M){function t(A,M){if(!A)throw new Error(M||"Assertion failed")}A.exports=t,t.equal=function(A,M,t){if(A!=M)throw new Error(t||"Assertion failed: "+A+" != "+M)}},function(A,M,t){"use strict";var g=M,I=t(6),e=t(11),i=t(94);g.assert=e,g.toArray=i.toArray,g.zero2=i.zero2,g.toHex=i.toHex,g.encode=i.encode,g.getNAF=function(A,M,t){var g=new Array(Math.max(A.bitLength(),t)+1);g.fill(0);for(var I=1<(I>>1)-1?(I>>1)-E:E,e.isubn(T)):T=0,g[i]=T,e.iushrn(1)}return g},g.getJSF=function(A,M){var t=[[],[]];A=A.clone(),M=M.clone();for(var g=0,I=0;A.cmpn(-g)>0||M.cmpn(-I)>0;){var e,i,T,E=A.andln(3)+g&3,N=M.andln(3)+I&3;if(3===E&&(E=-1),3===N&&(N=-1),0==(1&E))e=0;else e=3!==(T=A.andln(7)+g&7)&&5!==T||2!==N?E:-E;if(t[0].push(e),0==(1&N))i=0;else i=3!==(T=M.andln(7)+I&7)&&5!==T||2!==E?N:-N;t[1].push(i),2*g===e+1&&(g=1-g),2*I===i+1&&(I=1-I),A.iushrn(1),M.iushrn(1)}return t},g.cachedProperty=function(A,M,t){var g="_"+M;A.prototype[M]=function(){return void 0!==this[g]?this[g]:this[g]=t.call(this)}},g.parseBytes=function(A){return"string"==typeof A?g.toArray(A,"hex"):A},g.intFromLE=function(A){return new I(A,"hex","le")}},function(A,M,t){"use strict";var g=function(){function A(){this.mimeType=null,this.duration=null,this.hasAudio=null,this.hasVideo=null,this.audioCodec=null,this.videoCodec=null,this.audioDataRate=null,this.videoDataRate=null,this.audioSampleRate=null,this.audioChannelCount=null,this.width=null,this.height=null,this.fps=null,this.profile=null,this.level=null,this.refFrames=null,this.chromaFormat=null,this.sarNum=null,this.sarDen=null,this.metadata=null,this.segments=null,this.segmentCount=null,this.hasKeyframesIndex=null,this.keyframesIndex=null}var M=A.prototype;return M.isComplete=function(){var A=!1===this.hasAudio||!0===this.hasAudio&&null!=this.audioCodec&&null!=this.audioSampleRate&&null!=this.audioChannelCount,M=!1===this.hasVideo||!0===this.hasVideo&&null!=this.videoCodec&&null!=this.width&&null!=this.height&&null!=this.fps&&null!=this.profile&&null!=this.level&&null!=this.refFrames&&null!=this.chromaFormat&&null!=this.sarNum&&null!=this.sarDen;return null!=this.mimeType&&null!=this.duration&&null!=this.metadata&&null!=this.hasKeyframesIndex&&A&&M},M.isSeekable=function(){return!0===this.hasKeyframesIndex},M.getNearestKeyframe=function(A){if(null==this.keyframesIndex)return null;var M=this.keyframesIndex,t=this._search(M.times,A);return{index:t,milliseconds:M.times[t],fileposition:M.filepositions[t]}},M._search=function(A,M){var t=0,g=A.length-1,I=0,e=0,i=g;for(M=A[I]&&M=A.length)&&56320==(64512&A.charCodeAt(M+1)))}function i(A){return(A>>>24|A>>>8&65280|A<<8&16711680|(255&A)<<24)>>>0}function T(A){return 1===A.length?"0"+A:A}function E(A){return 7===A.length?"0"+A:6===A.length?"00"+A:5===A.length?"000"+A:4===A.length?"0000"+A:3===A.length?"00000"+A:2===A.length?"000000"+A:1===A.length?"0000000"+A:A}M.inherits=I,M.toArray=function(A,M){if(Array.isArray(A))return A.slice();if(!A)return[];var t=[];if("string"==typeof A)if(M){if("hex"===M)for((A=A.replace(/[^a-z0-9]+/gi,"")).length%2!=0&&(A="0"+A),I=0;I>6|192,t[g++]=63&i|128):e(A,I)?(i=65536+((1023&i)<<10)+(1023&A.charCodeAt(++I)),t[g++]=i>>18|240,t[g++]=i>>12&63|128,t[g++]=i>>6&63|128,t[g++]=63&i|128):(t[g++]=i>>12|224,t[g++]=i>>6&63|128,t[g++]=63&i|128)}else for(I=0;I>>0}return i},M.split32=function(A,M){for(var t=new Array(4*A.length),g=0,I=0;g>>24,t[I+1]=e>>>16&255,t[I+2]=e>>>8&255,t[I+3]=255&e):(t[I+3]=e>>>24,t[I+2]=e>>>16&255,t[I+1]=e>>>8&255,t[I]=255&e)}return t},M.rotr32=function(A,M){return A>>>M|A<<32-M},M.rotl32=function(A,M){return A<>>32-M},M.sum32=function(A,M){return A+M>>>0},M.sum32_3=function(A,M,t){return A+M+t>>>0},M.sum32_4=function(A,M,t,g){return A+M+t+g>>>0},M.sum32_5=function(A,M,t,g,I){return A+M+t+g+I>>>0},M.sum64=function(A,M,t,g){var I=A[M],e=g+A[M+1]>>>0,i=(e>>0,A[M+1]=e},M.sum64_hi=function(A,M,t,g){return(M+g>>>0>>0},M.sum64_lo=function(A,M,t,g){return M+g>>>0},M.sum64_4_hi=function(A,M,t,g,I,e,i,T){var E=0,N=M;return E+=(N=N+g>>>0)>>0)>>0)>>0},M.sum64_4_lo=function(A,M,t,g,I,e,i,T){return M+g+e+T>>>0},M.sum64_5_hi=function(A,M,t,g,I,e,i,T,E,N){var n=0,D=M;return n+=(D=D+g>>>0)>>0)>>0)>>0)>>0},M.sum64_5_lo=function(A,M,t,g,I,e,i,T,E,N){return M+g+e+T+N>>>0},M.rotr64_hi=function(A,M,t){return(M<<32-t|A>>>t)>>>0},M.rotr64_lo=function(A,M,t){return(A<<32-t|M>>>t)>>>0},M.shr64_hi=function(A,M,t){return A>>>t},M.shr64_lo=function(A,M,t){return(A<<32-t|M>>>t)>>>0}},function(A,M,t){var g=t(4).Buffer,I=t(135).Transform,e=t(24).StringDecoder;function i(A){I.call(this),this.hashMode="string"==typeof A,this.hashMode?this[A]=this._finalOrDigest:this.final=this._finalOrDigest,this._final&&(this.__final=this._final,this._final=null),this._decoder=null,this._encoding=null}t(3)(i,I),i.prototype.update=function(A,M,t){"string"==typeof A&&(A=g.from(A,M));var I=this._update(A);return this.hashMode?this:(t&&(I=this._toString(I,t)),I)},i.prototype.setAutoPadding=function(){},i.prototype.getAuthTag=function(){throw new Error("trying to get auth tag in unsupported state")},i.prototype.setAuthTag=function(){throw new Error("trying to set auth tag in unsupported state")},i.prototype.setAAD=function(){throw new Error("trying to set aad in unsupported state")},i.prototype._transform=function(A,M,t){var g;try{this.hashMode?this._update(A):this.push(this._update(A))}catch(A){g=A}finally{t(g)}},i.prototype._flush=function(A){var M;try{this.push(this.__final())}catch(A){M=A}A(M)},i.prototype._finalOrDigest=function(A){var M=this.__final()||g.alloc(0);return A&&(M=this._toString(M,A,!0)),M},i.prototype._toString=function(A,M,t){if(this._decoder||(this._decoder=new e(M),this._encoding=M),this._encoding!==M)throw new Error("can't switch encodings");var g=this._decoder.write(A);return t&&(g+=this._decoder.end()),g},A.exports=i},function(A,M,t){"use strict";var g=t(31),I=Object.keys||function(A){var M=[];for(var t in A)M.push(t);return M};A.exports=D;var e=Object.create(t(25));e.inherits=t(3);var i=t(72),T=t(43);e.inherits(D,i);for(var E=I(T.prototype),N=0;N0}),!1)}A.exports=function(A,M){M=M||{};var I={main:t.m},T=M.all?{main:Object.keys(I.main)}:function(A,M){for(var t={main:[M]},g={main:[]},I={main:{}};i(t);)for(var T=Object.keys(t),E=0;E4294967295)throw new RangeError("requested too many random bytes");var t=I.allocUnsafe(A);if(A>0)if(A>65536)for(var i=0;i2?"one of ".concat(M," ").concat(A.slice(0,t-1).join(", "),", or ")+A[t-1]:2===t?"one of ".concat(M," ").concat(A[0]," or ").concat(A[1]):"of ".concat(M," ").concat(A[0])}return"of ".concat(M," ").concat(String(A))}I("ERR_INVALID_OPT_VALUE",(function(A,M){return'The value "'+M+'" is invalid for option "'+A+'"'}),TypeError),I("ERR_INVALID_ARG_TYPE",(function(A,M,t){var g,I,i,T;if("string"==typeof M&&(I="not ",M.substr(!i||i<0?0:+i,I.length)===I)?(g="must not be",M=M.replace(/^not /,"")):g="must be",function(A,M,t){return(void 0===t||t>A.length)&&(t=A.length),A.substring(t-M.length,t)===M}(A," argument"))T="The ".concat(A," ").concat(g," ").concat(e(M,"type"));else{var E=function(A,M,t){return"number"!=typeof t&&(t=0),!(t+M.length>A.length)&&-1!==A.indexOf(M,t)}(A,".")?"property":"argument";T='The "'.concat(A,'" ').concat(E," ").concat(g," ").concat(e(M,"type"))}return T+=". Received type ".concat(typeof t)}),TypeError),I("ERR_STREAM_PUSH_AFTER_EOF","stream.push() after EOF"),I("ERR_METHOD_NOT_IMPLEMENTED",(function(A){return"The "+A+" method is not implemented"})),I("ERR_STREAM_PREMATURE_CLOSE","Premature close"),I("ERR_STREAM_DESTROYED",(function(A){return"Cannot call "+A+" after a stream was destroyed"})),I("ERR_MULTIPLE_CALLBACK","Callback called multiple times"),I("ERR_STREAM_CANNOT_PIPE","Cannot pipe, not readable"),I("ERR_STREAM_WRITE_AFTER_END","write after end"),I("ERR_STREAM_NULL_VALUES","May not write null values to stream",TypeError),I("ERR_UNKNOWN_ENCODING",(function(A){return"Unknown encoding: "+A}),TypeError),I("ERR_STREAM_UNSHIFT_AFTER_END_EVENT","stream.unshift() after end event"),A.exports.codes=g},function(A,M,t){"use strict";(function(M){var g=Object.keys||function(A){var M=[];for(var t in A)M.push(t);return M};A.exports=N;var I=t(63),e=t(67);t(3)(N,I);for(var i=g(e.prototype),T=0;T=this._finalSize&&(this._update(this._block),this._block.fill(0));var t=8*this._len;if(t<=4294967295)this._block.writeUInt32BE(t,this._blockSize-4);else{var g=(4294967295&t)>>>0,I=(t-g)/4294967296;this._block.writeUInt32BE(I,this._blockSize-8),this._block.writeUInt32BE(g,this._blockSize-4)}this._update(this._block);var e=this._hash();return A?e.toString(A):e},I.prototype._update=function(){throw new Error("_update must be implemented by subclass")},A.exports=I},function(A,M,t){"use strict"; +/*! @name @videojs/vhs-utils @version 1.3.0 @license MIT */var g=function(){function A(){this.listeners={}}var M=A.prototype;return M.on=function(A,M){this.listeners[A]||(this.listeners[A]=[]),this.listeners[A].push(M)},M.off=function(A,M){if(!this.listeners[A])return!1;var t=this.listeners[A].indexOf(M);return this.listeners[A]=this.listeners[A].slice(0),this.listeners[A].splice(t,1),t>-1},M.trigger=function(A){var M=this.listeners[A];if(M)if(2===arguments.length)for(var t=M.length,g=0;g>5==6?2:A>>4==14?3:A>>3==30?4:A>>6==2?-1:-2}function T(A){var M=this.lastTotal-this.lastNeed,t=function(A,M,t){if(128!=(192&M[0]))return A.lastNeed=0,"�";if(A.lastNeed>1&&M.length>1){if(128!=(192&M[1]))return A.lastNeed=1,"�";if(A.lastNeed>2&&M.length>2&&128!=(192&M[2]))return A.lastNeed=2,"�"}}(this,A);return void 0!==t?t:this.lastNeed<=A.length?(A.copy(this.lastChar,M,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):(A.copy(this.lastChar,M,0,A.length),void(this.lastNeed-=A.length))}function E(A,M){if((A.length-M)%2==0){var t=A.toString("utf16le",M);if(t){var g=t.charCodeAt(t.length-1);if(g>=55296&&g<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=A[A.length-2],this.lastChar[1]=A[A.length-1],t.slice(0,-1)}return t}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=A[A.length-1],A.toString("utf16le",M,A.length-1)}function N(A){var M=A&&A.length?this.write(A):"";if(this.lastNeed){var t=this.lastTotal-this.lastNeed;return M+this.lastChar.toString("utf16le",0,t)}return M}function n(A,M){var t=(A.length-M)%3;return 0===t?A.toString("base64",M):(this.lastNeed=3-t,this.lastTotal=3,1===t?this.lastChar[0]=A[A.length-1]:(this.lastChar[0]=A[A.length-2],this.lastChar[1]=A[A.length-1]),A.toString("base64",M,A.length-t))}function D(A){var M=A&&A.length?this.write(A):"";return this.lastNeed?M+this.lastChar.toString("base64",0,3-this.lastNeed):M}function C(A){return A.toString(this.encoding)}function r(A){return A&&A.length?this.write(A):""}M.StringDecoder=e,e.prototype.write=function(A){if(0===A.length)return"";var M,t;if(this.lastNeed){if(void 0===(M=this.fillLast(A)))return"";t=this.lastNeed,this.lastNeed=0}else t=0;return t=0)return I>0&&(A.lastNeed=I-1),I;if(--g=0)return I>0&&(A.lastNeed=I-2),I;if(--g=0)return I>0&&(2===I?I=0:A.lastNeed=I-3),I;return 0}(this,A,M);if(!this.lastNeed)return A.toString("utf8",M);this.lastTotal=t;var g=A.length-(t-this.lastNeed);return A.copy(this.lastChar,0,g),A.toString("utf8",M,g)},e.prototype.fillLast=function(A){if(this.lastNeed<=A.length)return A.copy(this.lastChar,this.lastTotal-this.lastNeed,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal);A.copy(this.lastChar,this.lastTotal-this.lastNeed,0,A.length),this.lastNeed-=A.length}},function(A,M,t){(function(A){function t(A){return Object.prototype.toString.call(A)}M.isArray=function(A){return Array.isArray?Array.isArray(A):"[object Array]"===t(A)},M.isBoolean=function(A){return"boolean"==typeof A},M.isNull=function(A){return null===A},M.isNullOrUndefined=function(A){return null==A},M.isNumber=function(A){return"number"==typeof A},M.isString=function(A){return"string"==typeof A},M.isSymbol=function(A){return"symbol"==typeof A},M.isUndefined=function(A){return void 0===A},M.isRegExp=function(A){return"[object RegExp]"===t(A)},M.isObject=function(A){return"object"==typeof A&&null!==A},M.isDate=function(A){return"[object Date]"===t(A)},M.isError=function(A){return"[object Error]"===t(A)||A instanceof Error},M.isFunction=function(A){return"function"==typeof A},M.isPrimitive=function(A){return null===A||"boolean"==typeof A||"number"==typeof A||"string"==typeof A||"symbol"==typeof A||void 0===A},M.isBuffer=A.isBuffer}).call(this,t(9).Buffer)},function(A,M,t){(function(M){A.exports=function(A,t){for(var g=Math.min(A.length,t.length),I=new M(g),e=0;e=this._delta8){var t=(A=this.pending).length%this._delta8;this.pending=A.slice(A.length-t,A.length),0===this.pending.length&&(this.pending=null),A=g.join32(A,0,A.length-t,this.endian);for(var I=0;I>>24&255,g[I++]=A>>>16&255,g[I++]=A>>>8&255,g[I++]=255&A}else for(g[I++]=255&A,g[I++]=A>>>8&255,g[I++]=A>>>16&255,g[I++]=A>>>24&255,g[I++]=0,g[I++]=0,g[I++]=0,g[I++]=0,e=8;e=i-51&&(A[g]=A[g]*I--/50),t+=this.option.channels;this.startTime=E-51&&(t[e]=t[e]*i--/50),I+=this.option.channels;this.startTime32)throw new g.b("ExpGolomb: readBits() bits exceeded max 32bits!");if(A<=this._current_word_bits_left){var M=this._current_word>>>32-A;return this._current_word<<=A,this._current_word_bits_left-=A,M}var t=this._current_word_bits_left?this._current_word:0;t>>>=32-this._current_word_bits_left;var I=A-this._current_word_bits_left;this._fillCurrentWord();var e=Math.min(I,this._current_word_bits_left),i=this._current_word>>>32-e;return this._current_word<<=e,this._current_word_bits_left-=e,t=t<>>A))return this._current_word<<=A,this._current_word_bits_left-=A,A;return this._fillCurrentWord(),A+this._skipLeadingZero()},M.readUEG=function(){var A=this._skipLeadingZero();return this.readBits(A+1)-1},M.readSEG=function(){var A=this.readUEG();return 1&A?A+1>>>1:-1*(A>>>1)},A}(),e=function(){function A(){}return A._ebsp2rbsp=function(A){for(var M=A,t=M.byteLength,g=new Uint8Array(t),I=0,e=0;e=2&&3===M[e]&&0===M[e-1]&&0===M[e-2]||(g[I]=M[e],I++);return new Uint8Array(g.buffer,0,I)},A.parseSPS=function(M){var t=A._ebsp2rbsp(M),g=new I(t);g.readByte();var e=g.readByte();g.readByte();var i=g.readByte();g.readUEG();var T=A.getProfileString(e),E=A.getLevelString(i),N=1,n=420,D=8;if((100===e||110===e||122===e||244===e||44===e||83===e||86===e||118===e||128===e||138===e||144===e)&&(3===(N=g.readUEG())&&g.readBits(1),N<=3&&(n=[0,420,422,444][N]),D=g.readUEG()+8,g.readUEG(),g.readBits(1),g.readBool()))for(var C=3!==N?8:12,r=0;r0&&z<16?(d=[1,12,10,16,40,24,20,32,80,18,15,64,160,4,3,2][z-1],x=[1,11,11,11,33,11,11,11,33,11,11,33,99,3,2,1][z-1]):255===z&&(d=g.readByte()<<8|g.readByte(),x=g.readByte()<<8|g.readByte())}if(g.readBool()&&g.readBool(),g.readBool()&&(g.readBits(4),g.readBool()&&g.readBits(24)),g.readBool()&&(g.readUEG(),g.readUEG()),g.readBool()){var U=g.readBits(32),f=g.readBits(32);u=g.readBool(),Y=(S=f)/(l=2*U)}}var O=1;1===d&&1===x||(O=d/x);var F=0,m=0;0===N?(F=1,m=2-s):(F=3===N?1:2,m=(1===N?2:1)*(2-s));var R=16*(a+1),k=16*(h+1)*(2-s);R-=(y+w)*F,k-=(j+L)*m;var p=Math.ceil(R*O);return g.destroy(),g=null,{avc_profile:e,profile_string:T,avc_level:i,level_string:E,bit_depth:D,ref_frames:Q,chroma_format:n,chroma_format_string:A.getChromaFormatString(n),frame_rate:{fixed:u,fps:Y,fps_den:l,fps_num:S},sar_ratio:{width:d,height:x},codec_size:{width:R,height:k},present_size:{width:p,height:k}}},A._skipScalingList=function(A,M){for(var t=8,g=8,I=0;I>>24]^n[c>>>16&255]^D[o>>>8&255]^C[255&B]^M[Q++],i=N[c>>>24]^n[o>>>16&255]^D[B>>>8&255]^C[255&r]^M[Q++],T=N[o>>>24]^n[B>>>16&255]^D[r>>>8&255]^C[255&c]^M[Q++],E=N[B>>>24]^n[r>>>16&255]^D[c>>>8&255]^C[255&o]^M[Q++],r=e,c=i,o=T,B=E;return e=(g[r>>>24]<<24|g[c>>>16&255]<<16|g[o>>>8&255]<<8|g[255&B])^M[Q++],i=(g[c>>>24]<<24|g[o>>>16&255]<<16|g[B>>>8&255]<<8|g[255&r])^M[Q++],T=(g[o>>>24]<<24|g[B>>>16&255]<<16|g[r>>>8&255]<<8|g[255&c])^M[Q++],E=(g[B>>>24]<<24|g[r>>>16&255]<<16|g[c>>>8&255]<<8|g[255&o])^M[Q++],[e>>>=0,i>>>=0,T>>>=0,E>>>=0]}var T=[0,1,2,4,8,16,32,64,128,27,54],E=function(){for(var A=new Array(256),M=0;M<256;M++)A[M]=M<128?M<<1:M<<1^283;for(var t=[],g=[],I=[[],[],[],[]],e=[[],[],[],[]],i=0,T=0,E=0;E<256;++E){var N=T^T<<1^T<<2^T<<3^T<<4;N=N>>>8^255&N^99,t[i]=N,g[N]=i;var n=A[i],D=A[n],C=A[D],r=257*A[N]^16843008*N;I[0][i]=r<<24|r>>>8,I[1][i]=r<<16|r>>>16,I[2][i]=r<<8|r>>>24,I[3][i]=r,r=16843009*C^65537*D^257*n^16843008*i,e[0][N]=r<<24|r>>>8,e[1][N]=r<<16|r>>>16,e[2][N]=r<<8|r>>>24,e[3][N]=r,0===i?i=T=1:(i=n^A[A[A[C^n]]],T^=A[A[T]])}return{SBOX:t,INV_SBOX:g,SUB_MIX:I,INV_SUB_MIX:e}}();function N(A){this._key=I(A),this._reset()}N.blockSize=16,N.keySize=32,N.prototype.blockSize=N.blockSize,N.prototype.keySize=N.keySize,N.prototype._reset=function(){for(var A=this._key,M=A.length,t=M+6,g=4*(t+1),I=[],e=0;e>>24,i=E.SBOX[i>>>24]<<24|E.SBOX[i>>>16&255]<<16|E.SBOX[i>>>8&255]<<8|E.SBOX[255&i],i^=T[e/M|0]<<24):M>6&&e%M==4&&(i=E.SBOX[i>>>24]<<24|E.SBOX[i>>>16&255]<<16|E.SBOX[i>>>8&255]<<8|E.SBOX[255&i]),I[e]=I[e-M]^i}for(var N=[],n=0;n>>24]]^E.INV_SUB_MIX[1][E.SBOX[C>>>16&255]]^E.INV_SUB_MIX[2][E.SBOX[C>>>8&255]]^E.INV_SUB_MIX[3][E.SBOX[255&C]]}this._nRounds=t,this._keySchedule=I,this._invKeySchedule=N},N.prototype.encryptBlockRaw=function(A){return i(A=I(A),this._keySchedule,E.SUB_MIX,E.SBOX,this._nRounds)},N.prototype.encryptBlock=function(A){var M=this.encryptBlockRaw(A),t=g.allocUnsafe(16);return t.writeUInt32BE(M[0],0),t.writeUInt32BE(M[1],4),t.writeUInt32BE(M[2],8),t.writeUInt32BE(M[3],12),t},N.prototype.decryptBlock=function(A){var M=(A=I(A))[1];A[1]=A[3],A[3]=M;var t=i(A,this._invKeySchedule,E.INV_SUB_MIX,E.INV_SBOX,this._nRounds),e=g.allocUnsafe(16);return e.writeUInt32BE(t[0],0),e.writeUInt32BE(t[3],4),e.writeUInt32BE(t[2],8),e.writeUInt32BE(t[1],12),e},N.prototype.scrub=function(){e(this._keySchedule),e(this._invKeySchedule),e(this._key)},A.exports.AES=N},function(A,M,t){var g=t(4).Buffer,I=t(37);A.exports=function(A,M,t,e){if(g.isBuffer(A)||(A=g.from(A,"binary")),M&&(g.isBuffer(M)||(M=g.from(M,"binary")),8!==M.length))throw new RangeError("salt should be Buffer with 8 byte length");for(var i=t/8,T=g.alloc(i),E=g.alloc(e||0),N=g.alloc(0);i>0||e>0;){var n=new I;n.update(N),n.update(A),M&&n.update(M),N=n.digest();var D=0;if(i>0){var C=T.length-i;D=Math.min(i,N.length),N.copy(T,C,0,D),i-=D}if(D0){var r=E.length-e,c=Math.min(e,N.length-D);N.copy(E,r,D,D+c),e-=c}}return N.fill(0),{key:T,iv:E}}},function(A,M,t){"use strict";var g=t(6),I=t(12),e=I.getNAF,i=I.getJSF,T=I.assert;function E(A,M){this.type=A,this.p=new g(M.p,16),this.red=M.prime?g.red(M.prime):g.mont(this.p),this.zero=new g(0).toRed(this.red),this.one=new g(1).toRed(this.red),this.two=new g(2).toRed(this.red),this.n=M.n&&new g(M.n,16),this.g=M.g&&this.pointFromJSON(M.g,M.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 t=this.n&&this.p.div(this.n);!t||t.cmpn(100)>0?this.redN=null:(this._maxwellTrick=!0,this.redN=this.n.toRed(this.red))}function N(A,M){this.curve=A,this.type=M,this.precomputed=null}A.exports=E,E.prototype.point=function(){throw new Error("Not implemented")},E.prototype.validate=function(){throw new Error("Not implemented")},E.prototype._fixedNafMul=function(A,M){T(A.precomputed);var t=A._getDoubles(),g=e(M,1,this._bitLength),I=(1<=E;M--)N=(N<<1)+g[M];i.push(N)}for(var n=this.jpoint(null,null,null),D=this.jpoint(null,null,null),C=I;C>0;C--){for(E=0;E=0;N--){for(M=0;N>=0&&0===i[N];N--)M++;if(N>=0&&M++,E=E.dblp(M),N<0)break;var n=i[N];T(0!==n),E="affine"===A.type?n>0?E.mixedAdd(I[n-1>>1]):E.mixedAdd(I[-n-1>>1].neg()):n>0?E.add(I[n-1>>1]):E.add(I[-n-1>>1].neg())}return"affine"===A.type?E.toP():E},E.prototype._wnafMulAdd=function(A,M,t,g,I){for(var T=this._wnafT1,E=this._wnafT2,N=this._wnafT3,n=0,D=0;D=1;D-=2){var r=D-1,c=D;if(1===T[r]&&1===T[c]){var o=[M[r],null,null,M[c]];0===M[r].y.cmp(M[c].y)?(o[1]=M[r].add(M[c]),o[2]=M[r].toJ().mixedAdd(M[c].neg())):0===M[r].y.cmp(M[c].y.redNeg())?(o[1]=M[r].toJ().mixedAdd(M[c]),o[2]=M[r].add(M[c].neg())):(o[1]=M[r].toJ().mixedAdd(M[c]),o[2]=M[r].toJ().mixedAdd(M[c].neg()));var B=[-3,-1,-5,-7,0,7,5,1,3],Q=i(t[r],t[c]);n=Math.max(Q[0].length,n),N[r]=new Array(n),N[c]=new Array(n);for(var a=0;a=0;D--){for(var j=0;D>=0;){var L=!0;for(a=0;a=0&&j++,y=y.dblp(j),D<0)break;for(a=0;a0?d=E[a][x-1>>1]:x<0&&(d=E[a][-x-1>>1].neg()),y="affine"===d.type?y.mixedAdd(d):y.add(d))}}for(D=0;D=Math.ceil((A.bitLength()+1)/M.step)},N.prototype._getDoubles=function(A,M){if(this.precomputed&&this.precomputed.doubles)return this.precomputed.doubles;for(var t=[this],g=this,I=0;I=128){M.push(String.fromCharCode(65535&i)),g+=2;continue}}}else if(t[g]<240){if(I(t,g,2)){var T=(15&t[g])<<12|(63&t[g+1])<<6|63&t[g+2];if(T>=2048&&55296!=(63488&T)){M.push(String.fromCharCode(65535&T)),g+=3;continue}}}else if(t[g]<248&&I(t,g,3)){var E=(7&t[g])<<18|(63&t[g+1])<<12|(63&t[g+2])<<6|63&t[g+3];if(E>65536&&E<1114112){E-=65536,M.push(String.fromCharCode(E>>>10|55296)),M.push(String.fromCharCode(1023&E|56320)),g+=4;continue}}M.push(String.fromCharCode(65533)),++g}return M.join("")},T=t(2),E=(e=new ArrayBuffer(2),new DataView(e).setInt16(0,256,!0),256===new Int16Array(e)[0]),N=function(){function A(){}return A.parseScriptData=function(M,t,I){var e={};try{var i=A.parseValue(M,t,I);if(I-i.size<1)return e;var T=A.parseValue(M,t+i.size,I-i.size);e[i.data]=T.data}catch(A){g.a.e("AMF",A.toString())}return e},A.parseObject=function(M,t,g){if(g<3)throw new T.a("Data not enough when parse ScriptDataObject");var I=A.parseString(M,t,g),e=A.parseValue(M,t+I.size,g-I.size),i=e.objectEnd;return{data:{name:I.data,value:e.data},size:I.size+e.size,objectEnd:i}},A.parseVariable=function(M,t,g){return A.parseObject(M,t,g)},A.parseString=function(A,M,t){if(t<2)throw new T.a("Data not enough when parse String");var g=new DataView(A,M,t).getUint16(0,!E);return{data:g>0?i(new Uint8Array(A,M+2,g)):"",size:2+g}},A.parseLongString=function(A,M,t){if(t<4)throw new T.a("Data not enough when parse LongString");var g=new DataView(A,M,t).getUint32(0,!E);return{data:g>0?i(new Uint8Array(A,M+4,g)):"",size:4+g}},A.parseDate=function(A,M,t){if(t<10)throw new T.a("Data size invalid when parse Date");var g=new DataView(A,M,t),I=g.getFloat64(0,!E),e=g.getInt16(8,!E);return{data:new Date(I+=60*e*1e3),size:10}},A.parseValue=function(M,t,I){if(I<1)throw new T.a("Data not enough when parse Value");var e,i=new DataView(M,t,I),N=1,n=i.getUint8(0),D=!1;try{switch(n){case 0:e=i.getFloat64(1,!E),N+=8;break;case 1:e=!!i.getUint8(1),N+=1;break;case 2:var C=A.parseString(M,t+1,I-1);e=C.data,N+=C.size;break;case 3:e={};var r=0;for(9==(16777215&i.getUint32(I-4,!E))&&(r=3);N>>2!=0,i=0!=(1&M[4]),T=(g=M)[I=5]<<24|g[I+1]<<16|g[I+2]<<8|g[I+3];return T<9?t:{match:!0,consumed:T,dataOffset:T,hasAudioTrack:e,hasVideoTrack:i}},e.bindDataSource=function(A){return A.onDataArrival=this.parseChunks.bind(this),this},e.resetMediaInfo=function(){this._mediaInfo=new C.a},e.pause=function(){this._pause=!0},e.resume=function(){this._pause=!1},e._onDataAvailableToSoftwareDecodeVideo=function(A,M){var t=M.samples,g=-1;for(t.length>1&&(g=0);t.length;){for(var I=t.shift(),e=0,i=0;i=0&&g++,this._callbackMediaDataFunc(this._callbackMediaDataUserPtr,!0,1,A,I.isKeyframe,E,e,I.pts,0,0))}},e._onDataAvailableToSoftwareDecodeAudio=function(A,M){var t=M.samples,g=-1;for(t.length>1&&(g=0);t.length;){var I=t.shift();this._callbackMediaDataFunc&&(g>=0&&g++,this._callbackMediaDataFunc(this._callbackMediaDataUserPtr,!0,2,A,0,I.unit,I.length,this._SampleRate,this._ChannelNum,this._SampleRate>=44100?32:16))}},e._isInitialMetadataDispatched=function(){return this._hasAudio&&this._hasVideo?this._audioInitialMetadataDispatched&&this._videoInitialMetadataDispatched:this._hasAudio&&!this._hasVideo?this._audioInitialMetadataDispatched:!(this._hasAudio||!this._hasVideo)&&this._videoInitialMetadataDispatched},e.parseChunks=function(M,t){if(null!==M){if(!(this._onError&&this._onMediaInfo&&this._onTrackMetadata&&this._onDataAvailable))throw new T.a("Flv: onError & onMediaInfo & onTrackMetadata & onDataAvailable callback must be specified");var I=0,e=this._littleEndian;if(0===t){if(!(M.byteLength>13))return 0;I=A.probe(M).dataOffset}if(this._firstParse)this._firstParse=!1,t+I!==this._dataOffset&&g.a.w(this.TAG,"First time parsing but chunk byteStart invalid!"),0!==new DataView(M,I).getUint32(0,!e)&&g.a.w(this.TAG,"PrevTagSize0 !== 0 !!!"),I+=4;for(;IM.byteLength)break;var E=i.getUint8(0),N=16777215&i.getUint32(0,!e);if(I+11+N+4>M.byteLength)break;if(8===E||9===E||18===E){var n=i.getUint8(4),D=i.getUint8(5),C=i.getUint8(6)|D<<8|n<<16|i.getUint8(7)<<24;0!==(16777215&i.getUint32(7,!e))&&g.a.w(this.TAG,"Meet tag which has StreamID != 0!");var r=I+11;if(!this._pause)switch(E){case 8:this.audioCodecId=this._parseAudioData(M,r,N,C);break;case 9:this.videoCodecId=this._parseVideoData(M,r,N,C,t+I);break;case 18:this._parseScriptData(M,r,N)}var c=i.getUint32(11+N,!e);c!==11+N&&g.a.w(this.TAG,"Invalid PrevTagSize "+c),I+=11+N+4}else g.a.w(this.TAG,"Unsupported tag type "+E+", skipped"),I+=11+N+4}return this._isInitialMetadataDispatched()?this._dispatch&&(this._audioTrack.length||this._videoTrack.length)&&(7==this.videoCodecId?"auto"===this._config.decodeType||"hard"===this._config.decodeType?(this._onDataAvailable(this._audioTrack,this._videoTrack),this._callbackInitFlag||(this._callbackInitFlag=!0,this._callbackMediaDataFunc&&this._callbackMediaDataFunc(this._callbackMediaDataUserPtr,!1,1,0,0,null,0,0,0,0))):(this._videoTrack.samples.length>0&&this._onDataAvailableToSoftwareDecodeVideo(27,this._videoTrack),this._audioTrack.samples.length>0&&this._onDataAvailableToSoftwareDecodeAudio(10==this.audioCodecId?86018:0,this._audioTrack)):12==this.videoCodecId&&(this._videoTrack.samples.length>0&&this._onDataAvailableToSoftwareDecodeVideo(173,this._videoTrack),this._audioTrack.samples.length>0&&this._onDataAvailableToSoftwareDecodeAudio(10==this.audioCodecId?86018:0,this._audioTrack))):(this._debugProcCount++,this._debugProcCount>=60&&(console.log("InitialMetadataDispatched fail."),this._debugProcCount=0)),I}},e._parseScriptData=function(A,M,t){var I=N.parseScriptData(A,M,t);if(I.hasOwnProperty("onMetaData")){if(null==I.onMetaData||"object"!=typeof I.onMetaData)return void g.a.w(this.TAG,"Invalid onMetaData structure!");this._metadata&&g.a.w(this.TAG,"Found another onMetaData tag!"),this._metadata=I;var e=this._metadata.onMetaData;if(this._onMetaDataArrived&&this._onMetaDataArrived(Object.assign({},e)),"boolean"==typeof e.hasAudio&&!1===this._hasAudioFlagOverrided&&(this._hasAudio=e.hasAudio,this._mediaInfo.hasAudio=this._hasAudio),"boolean"==typeof e.hasVideo&&!1===this._hasVideoFlagOverrided&&(this._hasVideo=e.hasVideo,this._mediaInfo.hasVideo=this._hasVideo),"number"==typeof e.audiodatarate&&(this._mediaInfo.audioDataRate=e.audiodatarate),"number"==typeof e.videodatarate&&(this._mediaInfo.videoDataRate=e.videodatarate),"number"==typeof e.width&&(this._mediaInfo.width=e.width),"number"==typeof e.height&&(this._mediaInfo.height=e.height),"number"==typeof e.duration){if(!this._durationOverrided){var i=Math.floor(e.duration*this._timescale);this._duration=i,this._mediaInfo.duration=i}}else this._mediaInfo.duration=0;if("number"==typeof e.framerate){var T=Math.floor(1e3*e.framerate);if(T>0){var E=T/1e3;this._referenceFrameRate.fixed=!0,this._referenceFrameRate.fps=E,this._referenceFrameRate.fps_num=T,this._referenceFrameRate.fps_den=1e3,this._mediaInfo.fps=E}}if("object"==typeof e.keyframes){this._mediaInfo.hasKeyframesIndex=!0;var n=e.keyframes;this._mediaInfo.keyframesIndex=this._parseKeyframesIndex(n),e.keyframes=null}else this._mediaInfo.hasKeyframesIndex=!1;this._dispatch=!1,this._mediaInfo.metadata=e,g.a.v(this.TAG,"Parsed onMetaData"),this._mediaInfo.isComplete()&&this._onMediaInfo(this._mediaInfo)}Object.keys(I).length>0&&this._onScriptDataArrived&&this._onScriptDataArrived(Object.assign({},I))},e._parseKeyframesIndex=function(A){for(var M=[],t=[],g=1;g>>4;if(2!==i&&10!==i)return this._onError(D.a.CODEC_UNSUPPORTED,"Flv: Unsupported audio codec idx: "+i),0;var T=0,E=(12&e)>>>2;if(!(E>=0&&E<=4))return this._onError(D.a.FORMAT_ERROR,"Flv: Invalid audio sample rate idx: "+E),0;T=this._flvSoundRateTable[E];var N=1&e,n=this._audioMetadata,C=this._audioTrack;if(n||(!1===this._hasAudio&&!1===this._hasAudioFlagOverrided&&(this._hasAudio=!0,this._mediaInfo.hasAudio=!0),(n=this._audioMetadata={}).type="audio",n.id=C.id,n.timescale=this._timescale,n.duration=this._duration,n.audioSampleRate=T,n.channelCount=0===N?1:2),10===i){var r=this._parseAACAudioData(A,M+1,t-1);if(null==r)return 0;if(0===r.packetType){n.config&&g.a.w(this.TAG,"Found another AudioSpecificConfig!");var c=r.data;n.audioSampleRate=c.samplingRate,n.channelCount=c.channelCount,n.codec=c.codec,n.originalCodec=c.originalCodec,n.config=c.config,n.refSampleDuration=1024/n.audioSampleRate*n.timescale,g.a.v(this.TAG,"Parsed AudioSpecificConfig"),this._SampleRate=c.samplingRate,this._ChannelNum=c.channelCount,this._isInitialMetadataDispatched()?this._dispatch&&(this._audioTrack.length||this._videoTrack.length)&&this._onDataAvailable(this._audioTrack,this._videoTrack):this._audioInitialMetadataDispatched=!0,this._dispatch=!1,this._onTrackMetadata("audio",n);var o=this._mediaInfo;o.audioCodec=n.originalCodec,o.audioSampleRate=n.audioSampleRate,o.audioChannelCount=n.channelCount,o.hasVideo?null!=o.videoCodec&&(o.mimeType='video/x-flv; codecs="'+o.videoCodec+","+o.audioCodec+'"'):o.mimeType='video/x-flv; codecs="'+o.audioCodec+'"',o.isComplete()&&this._onMediaInfo(o)}else if(1===r.packetType){var B=this._timestampBase+I,Q={unit:r.data,length:r.data.byteLength,dts:B,pts:B};C.samples.push(Q),C.length+=r.data.length}else g.a.e(this.TAG,"Flv: Unsupported AAC data type "+r.packetType)}else if(2===i){if(!n.codec){var a=this._parseMP3AudioData(A,M+1,t-1,!0);if(null==a)return 0;n.audioSampleRate=a.samplingRate,n.channelCount=a.channelCount,n.codec=a.codec,n.originalCodec=a.originalCodec,n.refSampleDuration=1152/n.audioSampleRate*n.timescale,g.a.v(this.TAG,"Parsed MPEG Audio Frame Header"),this._audioInitialMetadataDispatched=!0,this._onTrackMetadata("audio",n);var h=this._mediaInfo;h.audioCodec=n.codec,h.audioSampleRate=n.audioSampleRate,h.audioChannelCount=n.channelCount,h.audioDataRate=a.bitRate,h.hasVideo?null!=h.videoCodec&&(h.mimeType='video/x-flv; codecs="'+h.videoCodec+","+h.audioCodec+'"'):h.mimeType='video/x-flv; codecs="'+h.audioCodec+'"',h.isComplete()&&this._onMediaInfo(h)}var s=this._parseMP3AudioData(A,M+1,t-1,!1);if(null==s)return 0;var y=this._timestampBase+I,w={unit:s,length:s.byteLength,dts:y,pts:y};C.samples.push(w),C.length+=s.length}return i},e._parseAACAudioData=function(A,M,t){if(!(t<=1)){var I={},e=new Uint8Array(A,M,t);return I.packetType=e[0],0===e[0]?I.data=this._parseAACAudioSpecificConfig(A,M+1,t-1):I.data=e.subarray(1),I}g.a.w(this.TAG,"Flv: Invalid AAC packet, missing AACPacketType or/and Data!")},e._parseAACAudioSpecificConfig=function(A,M,t){var g,I,e=new Uint8Array(A,M,t),i=null,T=0,E=null;if(T=g=e[0]>>>3,(I=(7&e[0])<<1|e[1]>>>7)<0||I>=this._mpegSamplingRates.length)this._onError(D.a.FORMAT_ERROR,"Flv: AAC invalid sampling frequency index!");else{var N=this._mpegSamplingRates[I],n=(120&e[1])>>>3;if(!(n<0||n>=8)){5===T&&(E=(7&e[1])<<1|e[2]>>>7,(124&e[2])>>>2);var C=self.navigator.userAgent.toLowerCase();return-1!==C.indexOf("firefox")?I>=6?(T=5,i=new Array(4),E=I-3):(T=2,i=new Array(2),E=I):-1!==C.indexOf("android")?(T=2,i=new Array(2),E=I):(T=5,E=I,i=new Array(4),I>=6?E=I-3:1===n&&(T=2,i=new Array(2),E=I)),i[0]=T<<3,i[0]|=(15&I)>>>1,i[1]=(15&I)<<7,i[1]|=(15&n)<<3,5===T&&(i[1]|=(15&E)>>>1,i[2]=(1&E)<<7,i[2]|=8,i[3]=0),{config:i,samplingRate:N,channelCount:n,codec:"mp4a.40."+T,originalCodec:"mp4a.40."+g}}this._onError(D.a.FORMAT_ERROR,"Flv: AAC invalid channel configuration")}},e._parseMP3AudioData=function(A,M,t,I){if(!(t<4)){this._littleEndian;var e=new Uint8Array(A,M,t),i=null;if(I){if(255!==e[0])return;var T=e[1]>>>3&3,E=(6&e[1])>>1,N=(240&e[2])>>>4,n=(12&e[2])>>>2,D=3!==(e[3]>>>6&3)?2:1,C=0,r=0;switch(T){case 0:C=this._mpegAudioV25SampleRateTable[n];break;case 2:C=this._mpegAudioV20SampleRateTable[n];break;case 3:C=this._mpegAudioV10SampleRateTable[n]}switch(E){case 1:34,N>>4,E=15&i;if(7===E||12===E)return this._parseAVCAndHEVCVideoPacket(E,A,M+1,t-1,I,e,T),E;this._onError(D.a.CODEC_UNSUPPORTED,"Flv: Unsupported codec in video frame: "+E)}},e._parseAVCAndHEVCVideoPacket=function(A,M,t,I,e,i,T){if(I<4)g.a.w(this.TAG,"Flv: Invalid AVC packet, missing AVCPacketType or/and CompositionTime");else{var E=this._littleEndian,N=new DataView(M,t,I),n=N.getUint8(0),C=(16777215&N.getUint32(0,!E))<<8>>8;if(0===n)7==A?(r.g("H264"),this._parseAVCDecoderConfigurationRecord(M,t+4,I-4)):(r.g("H265"),this._parseHEVCDecoderConfigurationRecord(M,t+4,I-4));else if(1===n)7==A?this._parseAVCVideoData(M,t+4,I-4,e,i,T,C):this._parseHEVCVideoData(M,t+4,I-4,e,i,T,C);else if(2!==n)return void this._onError(D.a.FORMAT_ERROR,"Flv: Invalid video packet type "+n)}},e._parseAVCDecoderConfigurationRecord=function(A,M,t){if(t<7)g.a.w(this.TAG,"Flv: Invalid AVCDecoderConfigurationRecord, lack of data!");else{var I=this._videoMetadata,e=this._videoTrack,i=this._littleEndian,T=new DataView(A,M,t);I?void 0!==I.avcc&&g.a.w(this.TAG,"Found another AVCDecoderConfigurationRecord!"):(!1===this._hasVideo&&!1===this._hasVideoFlagOverrided&&(this._hasVideo=!0,this._mediaInfo.hasVideo=!0),(I=this._videoMetadata={}).type="video",I.id=e.id,I.timescale=this._timescale,I.duration=this._duration);var E=T.getUint8(0),N=T.getUint8(1);T.getUint8(2),T.getUint8(3);if(1===E&&0!==N)if(this._naluLengthSize=1+(3&T.getUint8(4)),3===this._naluLengthSize||4===this._naluLengthSize){var C=31&T.getUint8(5);if(0!==C){C>1&&g.a.w(this.TAG,"Flv: Strange AVCDecoderConfigurationRecord: SPS Count = "+C);for(var r=6,c=0;c1&&g.a.w(this.TAG,"Flv: Strange AVCDecoderConfigurationRecord: PPS Count = "+d),r++;for(var x=0;x=t){g.a.w(this.TAG,"Malformed Nalu near timestamp "+c+", offset = "+C+", dataSize = "+t);break}var Q=N.getUint32(C,!E);if(3===r&&(Q>>>=8),Q>t-r)return void g.a.w(this.TAG,"Malformed Nalus near timestamp "+c+", NaluSize > DataSize!");var a=31&N.getUint8(C+r);if(5===a&&(o=!0,n.length<1&&this._AVC_SPS&&this._AVC_SPS.length>0))if(B){var h={type:7,data:this._AVC_SPS};n.push(h),D+=h.data.length;var s={type:8,data:this._AVC_PPS};n.push(s),D+=s.data.length}else{var y=this._AVC_SPS.subarray(0,this._AVC_SPS.length),w=y.length-4;y[0]=w>>24,y[1]=w>>16,y[2]=w>>8,y[3]=255&w;var j={type:7,data:y};n.push(j),D+=j.data.length;var L=this._AVC_PPS.subarray(0,this._AVC_PPS.length),d=L.length-4;L[0]=d>>24,L[1]=d>>16,L[2]=d>>8,L[3]=255&d;var x={type:8,data:L};n.push(x),D+=x.data.length}var Y=new Uint8Array(A,M+C,r+Q),u={type:a,data:Y};n.push(u),D+=Y.byteLength,C+=r+Q}if(n.length){var S=this._videoTrack,l={units:n,length:D,isKeyframe:o,dts:c,cts:T,pts:c+T};o&&(l.fileposition=e),S.samples.push(l),S.length+=D}},e._parseHEVCDecoderConfigurationRecord=function(A,M,t){if(t<7)g.a.w(this.TAG,"Flv: Invalid AVCDecoderConfigurationRecord, lack of data!");else{var I=this._videoMetadata,e=this._videoTrack,i=this._littleEndian,T=new DataView(A,M,t);I?void 0!==I.avcc&&g.a.w(this.TAG,"Found another AVCDecoderConfigurationRecord!"):(!1===this._hasVideo&&!1===this._hasVideoFlagOverrided&&(this._hasVideo=!0,this._mediaInfo.hasVideo=!0),(I=this._videoMetadata={}).type="video",I.id=e.id,I.timescale=this._timescale,I.duration=this._duration);var E=T.getUint8(0),N=T.getUint8(1),n=T.getUint8(2),D=T.getUint8(3);if(0===E&&0===N&&0===n&&1===D)console.log("H265 1");else{var C=22,r=T.getUint8(C);C+=1;for(var c=0;c=t){g.a.w(this.TAG,"Malformed Nalu near timestamp "+n+", offset = "+c+", dataSize = "+t);break}var o=N.getUint8(c),B=N.getUint8(c+1),Q=N.getUint8(c+2),a=N.getUint8(c+3);if(0===o&&0===B&&0===Q&&1===a)console.log("memcpy.....");else{var h=N.getUint32(c,!E);c+=4;N.getUint8(c);if(c+=1,h>t-4)return void g.a.w(this.TAG,"Malformed Nalus near timestamp "+n+", NaluSize > DataSize!");if(h+c-1>t)break;var s=N.getUint8(c-1),y=this._makeH265Header(s);if((32===s||34===s||36===s||38===s||40===s||42===s)&&C.length<1){if(this._HEVC_VPS){var w={type:5,data:this._HEVC_VPS};C.push(w),r+=this._HEVC_VPS.length}if(this._HEVC_SPS){var j={type:6,data:this._HEVC_SPS};C.push(j),r+=this._HEVC_SPS.length}if(this._HEVC_PPS){var L={type:7,data:this._HEVC_PPS};C.push(L),r+=this._HEVC_PPS.length}}if(1===y&&this._HEVC_VPS&&this._HEVC_VPS[0],M+c-1-4>=0&&h>0){var d=new Uint8Array(A,M+c-1-4,h+4);d[0]=0,d[1]=0,d[2]=0,d[3]=1;var x={type:5,data:d};C.push(x),r+=d.length}c+=h-1}}if(this._DebugBuf&&(this._DebugBufPos<1048576?(this._DebugBuf.set(data2,this._DebugBufPos),this._DebugBufPos+=data2.length):(console.log("DebugBuf: "+this._DebugBuf),this._DebugBufPos=0)),C.length){var Y=this._videoTrack,u={units:C,length:r,isKeyframe:D,dts:n,cts:T,pts:n+T};D&&(u.fileposition=e),Y.samples.push(u),Y.length+=r}},M=A,(t=[{key:"onTrackMetadata",get:function(){return this._onTrackMetadata},set:function(A){this._onTrackMetadata=A}},{key:"onMediaInfo",get:function(){return this._onMediaInfo},set:function(A){this._onMediaInfo=A}},{key:"onMetaDataArrived",get:function(){return this._onMetaDataArrived},set:function(A){this._onMetaDataArrived=A}},{key:"onScriptDataArrived",get:function(){return this._onScriptDataArrived},set:function(A){this._onScriptDataArrived=A}},{key:"onError",get:function(){return this._onError},set:function(A){this._onError=A}},{key:"onDataAvailable",get:function(){return this._onDataAvailable},set:function(A){this._onDataAvailable=A}},{key:"timestampBase",get:function(){return this._timestampBase},set:function(A){this._timestampBase=A}},{key:"overridedDuration",get:function(){return this._duration},set:function(A){this._durationOverrided=!0,this._duration=A,this._mediaInfo.duration=A}},{key:"overridedHasAudio",set:function(A){this._hasAudioFlagOverrided=!0,this._hasAudio=A,this._mediaInfo.hasAudio=A}},{key:"overridedHasVideo",set:function(A){this._hasVideoFlagOverrided=!0,this._hasVideo=A,this._mediaInfo.hasVideo=A}}])&&c(M.prototype,t),I&&c(M,I),A}();M.a=o},function(A,M,t){"use strict";var g=t(3),I=t(61),e=t(4).Buffer,i=new Array(16);function T(){I.call(this,64),this._a=1732584193,this._b=4023233417,this._c=2562383102,this._d=271733878}function E(A,M){return A<>>32-M}function N(A,M,t,g,I,e,i){return E(A+(M&t|~M&g)+I+e|0,i)+M|0}function n(A,M,t,g,I,e,i){return E(A+(M&g|t&~g)+I+e|0,i)+M|0}function D(A,M,t,g,I,e,i){return E(A+(M^t^g)+I+e|0,i)+M|0}function C(A,M,t,g,I,e,i){return E(A+(t^(M|~g))+I+e|0,i)+M|0}g(T,I),T.prototype._update=function(){for(var A=i,M=0;M<16;++M)A[M]=this._block.readInt32LE(4*M);var t=this._a,g=this._b,I=this._c,e=this._d;t=N(t,g,I,e,A[0],3614090360,7),e=N(e,t,g,I,A[1],3905402710,12),I=N(I,e,t,g,A[2],606105819,17),g=N(g,I,e,t,A[3],3250441966,22),t=N(t,g,I,e,A[4],4118548399,7),e=N(e,t,g,I,A[5],1200080426,12),I=N(I,e,t,g,A[6],2821735955,17),g=N(g,I,e,t,A[7],4249261313,22),t=N(t,g,I,e,A[8],1770035416,7),e=N(e,t,g,I,A[9],2336552879,12),I=N(I,e,t,g,A[10],4294925233,17),g=N(g,I,e,t,A[11],2304563134,22),t=N(t,g,I,e,A[12],1804603682,7),e=N(e,t,g,I,A[13],4254626195,12),I=N(I,e,t,g,A[14],2792965006,17),t=n(t,g=N(g,I,e,t,A[15],1236535329,22),I,e,A[1],4129170786,5),e=n(e,t,g,I,A[6],3225465664,9),I=n(I,e,t,g,A[11],643717713,14),g=n(g,I,e,t,A[0],3921069994,20),t=n(t,g,I,e,A[5],3593408605,5),e=n(e,t,g,I,A[10],38016083,9),I=n(I,e,t,g,A[15],3634488961,14),g=n(g,I,e,t,A[4],3889429448,20),t=n(t,g,I,e,A[9],568446438,5),e=n(e,t,g,I,A[14],3275163606,9),I=n(I,e,t,g,A[3],4107603335,14),g=n(g,I,e,t,A[8],1163531501,20),t=n(t,g,I,e,A[13],2850285829,5),e=n(e,t,g,I,A[2],4243563512,9),I=n(I,e,t,g,A[7],1735328473,14),t=D(t,g=n(g,I,e,t,A[12],2368359562,20),I,e,A[5],4294588738,4),e=D(e,t,g,I,A[8],2272392833,11),I=D(I,e,t,g,A[11],1839030562,16),g=D(g,I,e,t,A[14],4259657740,23),t=D(t,g,I,e,A[1],2763975236,4),e=D(e,t,g,I,A[4],1272893353,11),I=D(I,e,t,g,A[7],4139469664,16),g=D(g,I,e,t,A[10],3200236656,23),t=D(t,g,I,e,A[13],681279174,4),e=D(e,t,g,I,A[0],3936430074,11),I=D(I,e,t,g,A[3],3572445317,16),g=D(g,I,e,t,A[6],76029189,23),t=D(t,g,I,e,A[9],3654602809,4),e=D(e,t,g,I,A[12],3873151461,11),I=D(I,e,t,g,A[15],530742520,16),t=C(t,g=D(g,I,e,t,A[2],3299628645,23),I,e,A[0],4096336452,6),e=C(e,t,g,I,A[7],1126891415,10),I=C(I,e,t,g,A[14],2878612391,15),g=C(g,I,e,t,A[5],4237533241,21),t=C(t,g,I,e,A[12],1700485571,6),e=C(e,t,g,I,A[3],2399980690,10),I=C(I,e,t,g,A[10],4293915773,15),g=C(g,I,e,t,A[1],2240044497,21),t=C(t,g,I,e,A[8],1873313359,6),e=C(e,t,g,I,A[15],4264355552,10),I=C(I,e,t,g,A[6],2734768916,15),g=C(g,I,e,t,A[13],1309151649,21),t=C(t,g,I,e,A[4],4149444226,6),e=C(e,t,g,I,A[11],3174756917,10),I=C(I,e,t,g,A[2],718787259,15),g=C(g,I,e,t,A[9],3951481745,21),this._a=this._a+t|0,this._b=this._b+g|0,this._c=this._c+I|0,this._d=this._d+e|0},T.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 A=e.allocUnsafe(16);return A.writeInt32LE(this._a,0),A.writeInt32LE(this._b,4),A.writeInt32LE(this._c,8),A.writeInt32LE(this._d,12),A},A.exports=T},function(A,M,t){"use strict";var g=t(19).codes.ERR_STREAM_PREMATURE_CLOSE;function I(){}A.exports=function A(M,t,e){if("function"==typeof t)return A(M,null,t);t||(t={}),e=function(A){var M=!1;return function(){if(!M){M=!0;for(var t=arguments.length,g=new Array(t),I=0;I>>32-M}function o(A,M,t,g,I,e,i,T){return c(A+(M^t^g)+e+i|0,T)+I|0}function B(A,M,t,g,I,e,i,T){return c(A+(M&t|~M&g)+e+i|0,T)+I|0}function Q(A,M,t,g,I,e,i,T){return c(A+((M|~t)^g)+e+i|0,T)+I|0}function a(A,M,t,g,I,e,i,T){return c(A+(M&g|t&~g)+e+i|0,T)+I|0}function h(A,M,t,g,I,e,i,T){return c(A+(M^(t|~g))+e+i|0,T)+I|0}I(r,e),r.prototype._update=function(){for(var A=i,M=0;M<16;++M)A[M]=this._block.readInt32LE(4*M);for(var t=0|this._a,g=0|this._b,I=0|this._c,e=0|this._d,r=0|this._e,s=0|this._a,y=0|this._b,w=0|this._c,j=0|this._d,L=0|this._e,d=0;d<80;d+=1){var x,Y;d<16?(x=o(t,g,I,e,r,A[T[d]],D[0],N[d]),Y=h(s,y,w,j,L,A[E[d]],C[0],n[d])):d<32?(x=B(t,g,I,e,r,A[T[d]],D[1],N[d]),Y=a(s,y,w,j,L,A[E[d]],C[1],n[d])):d<48?(x=Q(t,g,I,e,r,A[T[d]],D[2],N[d]),Y=Q(s,y,w,j,L,A[E[d]],C[2],n[d])):d<64?(x=a(t,g,I,e,r,A[T[d]],D[3],N[d]),Y=B(s,y,w,j,L,A[E[d]],C[3],n[d])):(x=h(t,g,I,e,r,A[T[d]],D[4],N[d]),Y=o(s,y,w,j,L,A[E[d]],C[4],n[d])),t=r,r=e,e=c(I,10),I=g,g=x,s=L,L=j,j=c(w,10),w=y,y=Y}var u=this._b+I+j|0;this._b=this._c+e+L|0,this._c=this._d+r+s|0,this._d=this._e+t+y|0,this._e=this._a+g+w|0,this._a=u},r.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 A=g.alloc?g.alloc(20):new g(20);return A.writeInt32LE(this._a,0),A.writeInt32LE(this._b,4),A.writeInt32LE(this._c,8),A.writeInt32LE(this._d,12),A.writeInt32LE(this._e,16),A},A.exports=r},function(A,M,t){(M=A.exports=function(A){A=A.toLowerCase();var t=M[A];if(!t)throw new Error(A+" is not supported (we accept pull requests)");return new t}).sha=t(131),M.sha1=t(132),M.sha224=t(133),M.sha256=t(70),M.sha384=t(134),M.sha512=t(71)},function(A,M,t){(M=A.exports=t(72)).Stream=M,M.Readable=M,M.Writable=t(43),M.Duplex=t(16),M.Transform=t(75),M.PassThrough=t(141)},function(A,M,t){var g=t(9),I=g.Buffer;function e(A,M){for(var t in A)M[t]=A[t]}function i(A,M,t){return I(A,M,t)}I.from&&I.alloc&&I.allocUnsafe&&I.allocUnsafeSlow?A.exports=g:(e(g,M),M.Buffer=i),e(I,i),i.from=function(A,M,t){if("number"==typeof A)throw new TypeError("Argument must not be a number");return I(A,M,t)},i.alloc=function(A,M,t){if("number"!=typeof A)throw new TypeError("Argument must be a number");var g=I(A);return void 0!==M?"string"==typeof t?g.fill(M,t):g.fill(M):g.fill(0),g},i.allocUnsafe=function(A){if("number"!=typeof A)throw new TypeError("Argument must be a number");return I(A)},i.allocUnsafeSlow=function(A){if("number"!=typeof A)throw new TypeError("Argument must be a number");return g.SlowBuffer(A)}},function(A,M,t){"use strict";(function(M,g,I){var e=t(31);function i(A){var M=this;this.next=null,this.entry=null,this.finish=function(){!function(A,M,t){var g=A.entry;A.entry=null;for(;g;){var I=g.callback;M.pendingcb--,I(t),g=g.next}M.corkedRequestsFree?M.corkedRequestsFree.next=A:M.corkedRequestsFree=A}(M,A)}}A.exports=a;var T,E=!M.browser&&["v0.10","v0.9."].indexOf(M.version.slice(0,5))>-1?g:e.nextTick;a.WritableState=Q;var N=Object.create(t(25));N.inherits=t(3);var n={deprecate:t(68)},D=t(73),C=t(42).Buffer,r=I.Uint8Array||function(){};var c,o=t(74);function B(){}function Q(A,M){T=T||t(16),A=A||{};var g=M instanceof T;this.objectMode=!!A.objectMode,g&&(this.objectMode=this.objectMode||!!A.writableObjectMode);var I=A.highWaterMark,N=A.writableHighWaterMark,n=this.objectMode?16:16384;this.highWaterMark=I||0===I?I:g&&(N||0===N)?N:n,this.highWaterMark=Math.floor(this.highWaterMark),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;var D=!1===A.decodeStrings;this.decodeStrings=!D,this.defaultEncoding=A.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=function(A){!function(A,M){var t=A._writableState,g=t.sync,I=t.writecb;if(function(A){A.writing=!1,A.writecb=null,A.length-=A.writelen,A.writelen=0}(t),M)!function(A,M,t,g,I){--M.pendingcb,t?(e.nextTick(I,g),e.nextTick(L,A,M),A._writableState.errorEmitted=!0,A.emit("error",g)):(I(g),A._writableState.errorEmitted=!0,A.emit("error",g),L(A,M))}(A,t,g,M,I);else{var i=w(t);i||t.corked||t.bufferProcessing||!t.bufferedRequest||y(A,t),g?E(s,A,t,i,I):s(A,t,i,I)}}(M,A)},this.writecb=null,this.writelen=0,this.bufferedRequest=null,this.lastBufferedRequest=null,this.pendingcb=0,this.prefinished=!1,this.errorEmitted=!1,this.bufferedRequestCount=0,this.corkedRequestsFree=new i(this)}function a(A){if(T=T||t(16),!(c.call(a,this)||this instanceof T))return new a(A);this._writableState=new Q(A,this),this.writable=!0,A&&("function"==typeof A.write&&(this._write=A.write),"function"==typeof A.writev&&(this._writev=A.writev),"function"==typeof A.destroy&&(this._destroy=A.destroy),"function"==typeof A.final&&(this._final=A.final)),D.call(this)}function h(A,M,t,g,I,e,i){M.writelen=g,M.writecb=i,M.writing=!0,M.sync=!0,t?A._writev(I,M.onwrite):A._write(I,e,M.onwrite),M.sync=!1}function s(A,M,t,g){t||function(A,M){0===M.length&&M.needDrain&&(M.needDrain=!1,A.emit("drain"))}(A,M),M.pendingcb--,g(),L(A,M)}function y(A,M){M.bufferProcessing=!0;var t=M.bufferedRequest;if(A._writev&&t&&t.next){var g=M.bufferedRequestCount,I=new Array(g),e=M.corkedRequestsFree;e.entry=t;for(var T=0,E=!0;t;)I[T]=t,t.isBuf||(E=!1),t=t.next,T+=1;I.allBuffers=E,h(A,M,!0,M.length,I,"",e.finish),M.pendingcb++,M.lastBufferedRequest=null,e.next?(M.corkedRequestsFree=e.next,e.next=null):M.corkedRequestsFree=new i(M),M.bufferedRequestCount=0}else{for(;t;){var N=t.chunk,n=t.encoding,D=t.callback;if(h(A,M,!1,M.objectMode?1:N.length,N,n,D),t=t.next,M.bufferedRequestCount--,M.writing)break}null===t&&(M.lastBufferedRequest=null)}M.bufferedRequest=t,M.bufferProcessing=!1}function w(A){return A.ending&&0===A.length&&null===A.bufferedRequest&&!A.finished&&!A.writing}function j(A,M){A._final((function(t){M.pendingcb--,t&&A.emit("error",t),M.prefinished=!0,A.emit("prefinish"),L(A,M)}))}function L(A,M){var t=w(M);return t&&(!function(A,M){M.prefinished||M.finalCalled||("function"==typeof A._final?(M.pendingcb++,M.finalCalled=!0,e.nextTick(j,A,M)):(M.prefinished=!0,A.emit("prefinish")))}(A,M),0===M.pendingcb&&(M.finished=!0,A.emit("finish"))),t}N.inherits(a,D),Q.prototype.getBuffer=function(){for(var A=this.bufferedRequest,M=[];A;)M.push(A),A=A.next;return M},function(){try{Object.defineProperty(Q.prototype,"buffer",{get:n.deprecate((function(){return this.getBuffer()}),"_writableState.buffer is deprecated. Use _writableState.getBuffer instead.","DEP0003")})}catch(A){}}(),"function"==typeof Symbol&&Symbol.hasInstance&&"function"==typeof Function.prototype[Symbol.hasInstance]?(c=Function.prototype[Symbol.hasInstance],Object.defineProperty(a,Symbol.hasInstance,{value:function(A){return!!c.call(this,A)||this===a&&(A&&A._writableState instanceof Q)}})):c=function(A){return A instanceof this},a.prototype.pipe=function(){this.emit("error",new Error("Cannot pipe, not readable"))},a.prototype.write=function(A,M,t){var g,I=this._writableState,i=!1,T=!I.objectMode&&(g=A,C.isBuffer(g)||g instanceof r);return T&&!C.isBuffer(A)&&(A=function(A){return C.from(A)}(A)),"function"==typeof M&&(t=M,M=null),T?M="buffer":M||(M=I.defaultEncoding),"function"!=typeof t&&(t=B),I.ended?function(A,M){var t=new Error("write after end");A.emit("error",t),e.nextTick(M,t)}(this,t):(T||function(A,M,t,g){var I=!0,i=!1;return null===t?i=new TypeError("May not write null values to stream"):"string"==typeof t||void 0===t||M.objectMode||(i=new TypeError("Invalid non-string/buffer chunk")),i&&(A.emit("error",i),e.nextTick(g,i),I=!1),I}(this,I,A,t))&&(I.pendingcb++,i=function(A,M,t,g,I,e){if(!t){var i=function(A,M,t){A.objectMode||!1===A.decodeStrings||"string"!=typeof M||(M=C.from(M,t));return M}(M,g,I);g!==i&&(t=!0,I="buffer",g=i)}var T=M.objectMode?1:g.length;M.length+=T;var E=M.length-1))throw new TypeError("Unknown encoding: "+A);return this._writableState.defaultEncoding=A,this},Object.defineProperty(a.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),a.prototype._write=function(A,M,t){t(new Error("_write() is not implemented"))},a.prototype._writev=null,a.prototype.end=function(A,M,t){var g=this._writableState;"function"==typeof A?(t=A,A=null,M=null):"function"==typeof M&&(t=M,M=null),null!=A&&this.write(A,M),g.corked&&(g.corked=1,this.uncork()),g.ending||g.finished||function(A,M,t){M.ending=!0,L(A,M),t&&(M.finished?e.nextTick(t):A.once("finish",t));M.ended=!0,A.writable=!1}(this,g,t)},Object.defineProperty(a.prototype,"destroyed",{get:function(){return void 0!==this._writableState&&this._writableState.destroyed},set:function(A){this._writableState&&(this._writableState.destroyed=A)}}),a.prototype.destroy=o.destroy,a.prototype._undestroy=o.undestroy,a.prototype._destroy=function(A,M){this.end(),M(A)}}).call(this,t(8),t(139).setImmediate,t(10))},function(A,M,t){"use strict";var g=t(11);function I(A){this.options=A,this.type=this.options.type,this.blockSize=8,this._init(),this.buffer=new Array(this.blockSize),this.bufferOff=0}A.exports=I,I.prototype._init=function(){},I.prototype.update=function(A){return 0===A.length?[]:"decrypt"===this.type?this._updateDecrypt(A):this._updateEncrypt(A)},I.prototype._buffer=function(A,M){for(var t=Math.min(this.buffer.length-this.bufferOff,A.length-M),g=0;g0;g--)M+=this._buffer(A,M),t+=this._flushBuffer(I,t);return M+=this._buffer(A,M),I},I.prototype.final=function(A){var M,t;return A&&(M=this.update(A)),t="encrypt"===this.type?this._finalEncrypt():this._finalDecrypt(),M?M.concat(t):t},I.prototype._pad=function(A,M){if(0===M)return!1;for(;M=0||!t.umod(A.prime1)||!t.umod(A.prime2);)t=new g(I(M));return t}A.exports=e,e.getr=i}).call(this,t(9).Buffer)},function(A,M,t){"use strict";var g=M;g.version=t(171).version,g.utils=t(12),g.rand=t(47),g.curve=t(95),g.curves=t(50),g.ec=t(182),g.eddsa=t(186)},function(A,M,t){"use strict";var g,I=M,e=t(51),i=t(95),T=t(12).assert;function E(A){"short"===A.type?this.curve=new i.short(A):"edwards"===A.type?this.curve=new i.edwards(A):this.curve=new i.mont(A),this.g=this.curve.g,this.n=this.curve.n,this.hash=A.hash,T(this.g.validate(),"Invalid curve"),T(this.g.mul(this.n).isInfinity(),"Invalid curve, G*N != O")}function N(A,M){Object.defineProperty(I,A,{configurable:!0,enumerable:!0,get:function(){var t=new E(M);return Object.defineProperty(I,A,{configurable:!0,enumerable:!0,value:t}),t}})}I.PresetCurve=E,N("p192",{type:"short",prime:"p192",p:"ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff",a:"ffffffff ffffffff ffffffff fffffffe ffffffff fffffffc",b:"64210519 e59c80e7 0fa7e9ab 72243049 feb8deec c146b9b1",n:"ffffffff ffffffff ffffffff 99def836 146bc9b1 b4d22831",hash:e.sha256,gRed:!1,g:["188da80e b03090f6 7cbf20eb 43a18800 f4ff0afd 82ff1012","07192b95 ffc8da78 631011ed 6b24cdd5 73f977a1 1e794811"]}),N("p224",{type:"short",prime:"p224",p:"ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001",a:"ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff fffffffe",b:"b4050a85 0c04b3ab f5413256 5044b0b7 d7bfd8ba 270b3943 2355ffb4",n:"ffffffff ffffffff ffffffff ffff16a2 e0b8f03e 13dd2945 5c5c2a3d",hash:e.sha256,gRed:!1,g:["b70e0cbd 6bb4bf7f 321390b9 4a03c1d3 56c21122 343280d6 115c1d21","bd376388 b5f723fb 4c22dfe6 cd4375a0 5a074764 44d58199 85007e34"]}),N("p256",{type:"short",prime:null,p:"ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff ffffffff",a:"ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff fffffffc",b:"5ac635d8 aa3a93e7 b3ebbd55 769886bc 651d06b0 cc53b0f6 3bce3c3e 27d2604b",n:"ffffffff 00000000 ffffffff ffffffff bce6faad a7179e84 f3b9cac2 fc632551",hash:e.sha256,gRed:!1,g:["6b17d1f2 e12c4247 f8bce6e5 63a440f2 77037d81 2deb33a0 f4a13945 d898c296","4fe342e2 fe1a7f9b 8ee7eb4a 7c0f9e16 2bce3357 6b315ece cbb64068 37bf51f5"]}),N("p384",{type:"short",prime:null,p:"ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe ffffffff 00000000 00000000 ffffffff",a:"ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe ffffffff 00000000 00000000 fffffffc",b:"b3312fa7 e23ee7e4 988e056b e3f82d19 181d9c6e fe814112 0314088f 5013875a c656398d 8a2ed19d 2a85c8ed d3ec2aef",n:"ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff c7634d81 f4372ddf 581a0db2 48b0a77a ecec196a ccc52973",hash:e.sha384,gRed:!1,g:["aa87ca22 be8b0537 8eb1c71e f320ad74 6e1d3b62 8ba79b98 59f741e0 82542a38 5502f25d bf55296c 3a545e38 72760ab7","3617de4a 96262c6f 5d9e98bf 9292dc29 f8f41dbd 289a147c e9da3113 b5f0b8c0 0a60b1ce 1d7e819d 7a431d7c 90ea0e5f"]}),N("p521",{type:"short",prime:null,p:"000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff",a:"000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffc",b:"00000051 953eb961 8e1c9a1f 929a21a0 b68540ee a2da725b 99b315f3 b8b48991 8ef109e1 56193951 ec7e937b 1652c0bd 3bb1bf07 3573df88 3d2c34f1 ef451fd4 6b503f00",n:"000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffa 51868783 bf2f966b 7fcc0148 f709a5d0 3bb5c9b8 899c47ae bb6fb71e 91386409",hash:e.sha512,gRed:!1,g:["000000c6 858e06b7 0404e9cd 9e3ecb66 2395b442 9c648139 053fb521 f828af60 6b4d3dba a14b5e77 efe75928 fe1dc127 a2ffa8de 3348b3c1 856a429b f97e7e31 c2e5bd66","00000118 39296a78 9a3bc004 5c8a5fb4 2c7d1bd9 98f54449 579b4468 17afbd17 273e662c 97ee7299 5ef42640 c550b901 3fad0761 353c7086 a272c240 88be9476 9fd16650"]}),N("curve25519",{type:"mont",prime:"p25519",p:"7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed",a:"76d06",b:"1",n:"1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed",hash:e.sha256,gRed:!1,g:["9"]}),N("ed25519",{type:"edwards",prime:"p25519",p:"7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed",a:"-1",c:"1",d:"52036cee2b6ffe73 8cc740797779e898 00700a4d4141d8ab 75eb4dca135978a3",n:"1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed",hash:e.sha256,gRed:!1,g:["216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a","6666666666666666666666666666666666666666666666666666666666666658"]});try{g=t(181)}catch(A){g=void 0}N("secp256k1",{type:"short",prime:"k256",p:"ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f",a:"0",b:"7",n:"ffffffff ffffffff ffffffff fffffffe baaedce6 af48a03b bfd25e8c d0364141",h:"1",hash:e.sha256,beta:"7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee",lambda:"5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72",basis:[{a:"3086d221a7d46bcde86c90e49284eb15",b:"-e4437ed6010e88286f547fa90abfe4c3"},{a:"114ca50f7a8e2f3f657c1108d9d44cfd8",b:"3086d221a7d46bcde86c90e49284eb15"}],gRed:!1,g:["79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798","483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",g]})},function(A,M,t){var g=M;g.utils=t(14),g.common=t(27),g.sha=t(175),g.ripemd=t(179),g.hmac=t(180),g.sha1=g.sha.sha1,g.sha256=g.sha.sha256,g.sha224=g.sha.sha224,g.sha384=g.sha.sha384,g.sha512=g.sha.sha512,g.ripemd160=g.ripemd.ripemd160},function(A,M,t){"use strict";(function(M){var g,I=t(9),e=I.Buffer,i={};for(g in I)I.hasOwnProperty(g)&&"SlowBuffer"!==g&&"Buffer"!==g&&(i[g]=I[g]);var T=i.Buffer={};for(g in e)e.hasOwnProperty(g)&&"allocUnsafe"!==g&&"allocUnsafeSlow"!==g&&(T[g]=e[g]);if(i.Buffer.prototype=e.prototype,T.from&&T.from!==Uint8Array.from||(T.from=function(A,M,t){if("number"==typeof A)throw new TypeError('The "value" argument must not be of type number. Received type '+typeof A);if(A&&void 0===A.length)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof A);return e(A,M,t)}),T.alloc||(T.alloc=function(A,M,t){if("number"!=typeof A)throw new TypeError('The "size" argument must be of type number. Received type '+typeof A);if(A<0||A>=2*(1<<30))throw new RangeError('The value "'+A+'" is invalid for option "size"');var g=e(A);return M&&0!==M.length?"string"==typeof t?g.fill(M,t):g.fill(M):g.fill(0),g}),!i.kStringMaxLength)try{i.kStringMaxLength=M.binding("buffer").kStringMaxLength}catch(A){}i.constants||(i.constants={MAX_LENGTH:i.kMaxLength},i.kStringMaxLength&&(i.constants.MAX_STRING_LENGTH=i.kStringMaxLength)),A.exports=i}).call(this,t(8))},function(A,M,t){"use strict";var g=t(54).Reporter,I=t(28).EncoderBuffer,e=t(28).DecoderBuffer,i=t(11),T=["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"],E=["key","obj","use","optional","explicit","implicit","def","choice","any","contains"].concat(T);function N(A,M,t){var g={};this._baseState=g,g.name=t,g.enc=A,g.parent=M||null,g.children=null,g.tag=null,g.args=null,g.reverseArgs=null,g.choice=null,g.optional=!1,g.any=!1,g.obj=!1,g.use=null,g.useDecoder=null,g.key=null,g.default=null,g.explicit=null,g.implicit=null,g.contains=null,g.parent||(g.children=[],this._wrap())}A.exports=N;var n=["enc","parent","children","tag","args","reverseArgs","choice","optional","any","obj","use","alteredUse","key","default","explicit","implicit","contains"];N.prototype.clone=function(){var A=this._baseState,M={};n.forEach((function(t){M[t]=A[t]}));var t=new this.constructor(M.parent);return t._baseState=M,t},N.prototype._wrap=function(){var A=this._baseState;E.forEach((function(M){this[M]=function(){var t=new this.constructor(this);return A.children.push(t),t[M].apply(t,arguments)}}),this)},N.prototype._init=function(A){var M=this._baseState;i(null===M.parent),A.call(this),M.children=M.children.filter((function(A){return A._baseState.parent===this}),this),i.equal(M.children.length,1,"Root node can have only one child")},N.prototype._useArgs=function(A){var M=this._baseState,t=A.filter((function(A){return A instanceof this.constructor}),this);A=A.filter((function(A){return!(A instanceof this.constructor)}),this),0!==t.length&&(i(null===M.children),M.children=t,t.forEach((function(A){A._baseState.parent=this}),this)),0!==A.length&&(i(null===M.args),M.args=A,M.reverseArgs=A.map((function(A){if("object"!=typeof A||A.constructor!==Object)return A;var M={};return Object.keys(A).forEach((function(t){t==(0|t)&&(t|=0);var g=A[t];M[g]=t})),M})))},["_peekTag","_decodeTag","_use","_decodeStr","_decodeObjid","_decodeTime","_decodeNull","_decodeInt","_decodeBool","_decodeList","_encodeComposite","_encodeStr","_encodeObjid","_encodeTime","_encodeNull","_encodeInt","_encodeBool"].forEach((function(A){N.prototype[A]=function(){var M=this._baseState;throw new Error(A+" not implemented for encoding: "+M.enc)}})),T.forEach((function(A){N.prototype[A]=function(){var M=this._baseState,t=Array.prototype.slice.call(arguments);return i(null===M.tag),M.tag=A,this._useArgs(t),this}})),N.prototype.use=function(A){i(A);var M=this._baseState;return i(null===M.use),M.use=A,this},N.prototype.optional=function(){return this._baseState.optional=!0,this},N.prototype.def=function(A){var M=this._baseState;return i(null===M.default),M.default=A,M.optional=!0,this},N.prototype.explicit=function(A){var M=this._baseState;return i(null===M.explicit&&null===M.implicit),M.explicit=A,this},N.prototype.implicit=function(A){var M=this._baseState;return i(null===M.explicit&&null===M.implicit),M.implicit=A,this},N.prototype.obj=function(){var A=this._baseState,M=Array.prototype.slice.call(arguments);return A.obj=!0,0!==M.length&&this._useArgs(M),this},N.prototype.key=function(A){var M=this._baseState;return i(null===M.key),M.key=A,this},N.prototype.any=function(){return this._baseState.any=!0,this},N.prototype.choice=function(A){var M=this._baseState;return i(null===M.choice),M.choice=A,this._useArgs(Object.keys(A).map((function(M){return A[M]}))),this},N.prototype.contains=function(A){var M=this._baseState;return i(null===M.use),M.contains=A,this},N.prototype._decode=function(A,M){var t=this._baseState;if(null===t.parent)return A.wrapResult(t.children[0]._decode(A,M));var g,I=t.default,i=!0,T=null;if(null!==t.key&&(T=A.enterKey(t.key)),t.optional){var E=null;if(null!==t.explicit?E=t.explicit:null!==t.implicit?E=t.implicit:null!==t.tag&&(E=t.tag),null!==E||t.any){if(i=this._peekTag(A,E,t.any),A.isError(i))return i}else{var N=A.save();try{null===t.choice?this._decodeGeneric(t.tag,A,M):this._decodeChoice(A,M),i=!0}catch(A){i=!1}A.restore(N)}}if(t.obj&&i&&(g=A.enterObject()),i){if(null!==t.explicit){var n=this._decodeTag(A,t.explicit);if(A.isError(n))return n;A=n}var D=A.offset;if(null===t.use&&null===t.choice){var C;t.any&&(C=A.save());var r=this._decodeTag(A,null!==t.implicit?t.implicit:t.tag,t.any);if(A.isError(r))return r;t.any?I=A.raw(C):A=r}if(M&&M.track&&null!==t.tag&&M.track(A.path(),D,A.length,"tagged"),M&&M.track&&null!==t.tag&&M.track(A.path(),A.offset,A.length,"content"),t.any||(I=null===t.choice?this._decodeGeneric(t.tag,A,M):this._decodeChoice(A,M)),A.isError(I))return I;if(t.any||null!==t.choice||null===t.children||t.children.forEach((function(t){t._decode(A,M)})),t.contains&&("octstr"===t.tag||"bitstr"===t.tag)){var c=new e(I);I=this._getUse(t.contains,A._reporterState.obj)._decode(c,M)}}return t.obj&&i&&(I=A.leaveObject(g)),null===t.key||null===I&&!0!==i?null!==T&&A.exitKey(T):A.leaveKey(T,t.key,I),I},N.prototype._decodeGeneric=function(A,M,t){var g=this._baseState;return"seq"===A||"set"===A?null:"seqof"===A||"setof"===A?this._decodeList(M,A,g.args[0],t):/str$/.test(A)?this._decodeStr(M,A,t):"objid"===A&&g.args?this._decodeObjid(M,g.args[0],g.args[1],t):"objid"===A?this._decodeObjid(M,null,null,t):"gentime"===A||"utctime"===A?this._decodeTime(M,A,t):"null_"===A?this._decodeNull(M,t):"bool"===A?this._decodeBool(M,t):"objDesc"===A?this._decodeStr(M,A,t):"int"===A||"enum"===A?this._decodeInt(M,g.args&&g.args[0],t):null!==g.use?this._getUse(g.use,M._reporterState.obj)._decode(M,t):M.error("unknown tag: "+A)},N.prototype._getUse=function(A,M){var t=this._baseState;return t.useDecoder=this._use(A,M),i(null===t.useDecoder._baseState.parent),t.useDecoder=t.useDecoder._baseState.children[0],t.implicit!==t.useDecoder._baseState.implicit&&(t.useDecoder=t.useDecoder.clone(),t.useDecoder._baseState.implicit=t.implicit),t.useDecoder},N.prototype._decodeChoice=function(A,M){var t=this._baseState,g=null,I=!1;return Object.keys(t.choice).some((function(e){var i=A.save(),T=t.choice[e];try{var E=T._decode(A,M);if(A.isError(E))return!1;g={type:e,value:E},I=!0}catch(M){return A.restore(i),!1}return!0}),this),I?g:A.error("Choice not matched")},N.prototype._createEncoderBuffer=function(A){return new I(A,this.reporter)},N.prototype._encode=function(A,M,t){var g=this._baseState;if(null===g.default||g.default!==A){var I=this._encodeValue(A,M,t);if(void 0!==I&&!this._skipDefault(I,M,t))return I}},N.prototype._encodeValue=function(A,M,t){var I=this._baseState;if(null===I.parent)return I.children[0]._encode(A,M||new g);var e=null;if(this.reporter=M,I.optional&&void 0===A){if(null===I.default)return;A=I.default}var i=null,T=!1;if(I.any)e=this._createEncoderBuffer(A);else if(I.choice)e=this._encodeChoice(A,M);else if(I.contains)i=this._getUse(I.contains,t)._encode(A,M),T=!0;else if(I.children)i=I.children.map((function(t){if("null_"===t._baseState.tag)return t._encode(null,M,A);if(null===t._baseState.key)return M.error("Child should have a key");var g=M.enterKey(t._baseState.key);if("object"!=typeof A)return M.error("Child expected, but input is not object");var I=t._encode(A[t._baseState.key],M,A);return M.leaveKey(g),I}),this).filter((function(A){return A})),i=this._createEncoderBuffer(i);else if("seqof"===I.tag||"setof"===I.tag){if(!I.args||1!==I.args.length)return M.error("Too many args for : "+I.tag);if(!Array.isArray(A))return M.error("seqof/setof, but data is not Array");var E=this.clone();E._baseState.implicit=null,i=this._createEncoderBuffer(A.map((function(t){var g=this._baseState;return this._getUse(g.args[0],A)._encode(t,M)}),E))}else null!==I.use?e=this._getUse(I.use,t)._encode(A,M):(i=this._encodePrimitive(I.tag,A),T=!0);if(!I.any&&null===I.choice){var N=null!==I.implicit?I.implicit:I.tag,n=null===I.implicit?"universal":"context";null===N?null===I.use&&M.error("Tag could be omitted only for .use()"):null===I.use&&(e=this._encodeComposite(N,T,n,i))}return null!==I.explicit&&(e=this._encodeComposite(I.explicit,!1,"context",e)),e},N.prototype._encodeChoice=function(A,M){var t=this._baseState,g=t.choice[A.type];return g||i(!1,A.type+" not found in "+JSON.stringify(Object.keys(t.choice))),g._encode(A.value,M)},N.prototype._encodePrimitive=function(A,M){var t=this._baseState;if(/str$/.test(A))return this._encodeStr(M,A);if("objid"===A&&t.args)return this._encodeObjid(M,t.reverseArgs[0],t.args[1]);if("objid"===A)return this._encodeObjid(M,null,null);if("gentime"===A||"utctime"===A)return this._encodeTime(M,A);if("null_"===A)return this._encodeNull();if("int"===A||"enum"===A)return this._encodeInt(M,t.args&&t.reverseArgs[0]);if("bool"===A)return this._encodeBool(M);if("objDesc"===A)return this._encodeStr(M,A);throw new Error("Unsupported tag: "+A)},N.prototype._isNumstr=function(A){return/^[0-9 ]*$/.test(A)},N.prototype._isPrintstr=function(A){return/^[A-Za-z0-9 '()+,-./:=?]*$/.test(A)}},function(A,M,t){"use strict";var g=t(3);function I(A){this._reporterState={obj:null,path:[],options:A||{},errors:[]}}function e(A,M){this.path=A,this.rethrow(M)}M.Reporter=I,I.prototype.isError=function(A){return A instanceof e},I.prototype.save=function(){var A=this._reporterState;return{obj:A.obj,pathLen:A.path.length}},I.prototype.restore=function(A){var M=this._reporterState;M.obj=A.obj,M.path=M.path.slice(0,A.pathLen)},I.prototype.enterKey=function(A){return this._reporterState.path.push(A)},I.prototype.exitKey=function(A){var M=this._reporterState;M.path=M.path.slice(0,A-1)},I.prototype.leaveKey=function(A,M,t){var g=this._reporterState;this.exitKey(A),null!==g.obj&&(g.obj[M]=t)},I.prototype.path=function(){return this._reporterState.path.join("/")},I.prototype.enterObject=function(){var A=this._reporterState,M=A.obj;return A.obj={},M},I.prototype.leaveObject=function(A){var M=this._reporterState,t=M.obj;return M.obj=A,t},I.prototype.error=function(A){var M,t=this._reporterState,g=A instanceof e;if(M=g?A:new e(t.path.map((function(A){return"["+JSON.stringify(A)+"]"})).join(""),A.message||A,A.stack),!t.options.partial)throw M;return g||t.errors.push(M),M},I.prototype.wrapResult=function(A){var M=this._reporterState;return M.options.partial?{result:this.isError(A)?null:A,errors:M.errors}:A},g(e,Error),e.prototype.rethrow=function(A){if(this.message=A+" at: "+(this.path||"(shallow)"),Error.captureStackTrace&&Error.captureStackTrace(this,e),!this.stack)try{throw new Error(this.message)}catch(A){this.stack=A.stack}return this}},function(A,M,t){"use strict";function g(A){var M={};return Object.keys(A).forEach((function(t){(0|t)==t&&(t|=0);var g=A[t];M[g]=t})),M}M.tagClass={0:"universal",1:"application",2:"context",3:"private"},M.tagClassByName=g(M.tagClass),M.tag={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"},M.tagByName=g(M.tag)},function(A,M,t){"use strict";var g,I=function(){return void 0===g&&(g=Boolean(window&&document&&document.all&&!window.atob)),g},e=function(){var A={};return function(M){if(void 0===A[M]){var t=document.querySelector(M);if(window.HTMLIFrameElement&&t instanceof window.HTMLIFrameElement)try{t=t.contentDocument.head}catch(A){t=null}A[M]=t}return A[M]}}(),i=[];function T(A){for(var M=-1,t=0;t>>6),!((I=(60&A[M+2])>>>2)>n.length-1))return i=(1&A[M+2])<<2,i|=(192&A[M+3])>>>6,/firefox/i.test(E)?I>=6?(g=5,T=new Array(4),e=I-3):(g=2,T=new Array(2),e=I):-1!==E.indexOf("android")?(g=2,T=new Array(2),e=I):(g=5,T=new Array(4),t&&(-1!==t.indexOf("mp4a.40.29")||-1!==t.indexOf("mp4a.40.5"))||!t&&I>=6?e=I-3:((t&&-1!==t.indexOf("mp4a.40.2")&&(I>=6&&1===i||/vivaldi/i.test(E))||!t&&1===i)&&(g=2,T=new Array(2)),e=I)),T[0]=g<<3,T[0]|=(14&I)>>1,T[1]|=(1&I)<<7,T[1]|=i<<3,5===g&&(T[1]|=(14&e)>>1,T[2]=(1&e)<<7,T[2]|=8,T[3]=0),{config:T,samplerate:n[I],channelCount:i,codec:"mp4a.40."+g,manifestCodec:N};console.log("ADTS parse fail. reason: 'invalid ADTS sampling index:${adtsSampleingIndex}'")}function e(A,M){return 255===A[M]&&240==(246&A[M+1])}function T(A,M){return 1&A[M+1]?7:9}function E(A,M){return(3&A[M+3])<<11|A[M+4]<<3|(224&A[M+5])>>>5}function N(A,M){return!!(M+10&&M+e+i<=N)return{headerLength:e,frameLength:i,stamp:t+g*I}}(M,t,g,I,D(A));if(e){var i=e.stamp,N=e.headerLength,n=e.frameLength;return{sample:{unit:M.subarray(t+N,t+N+n),pts:i,dts:i},length:n+N}}}var r=function(){function A(A){this.data=A,this.bytesAvailable=A.byteLength,this.word=0,this.bitsAvailable=0}var M=A.prototype;return M.loadWord=function(){var A=this.data,M=this.bytesAvailable,t=A.byteLength-M,g=new Uint8Array(4),I=Math.min(4,M);if(0===I)throw new Error("no bytes available");g.set(A.subarray(t,t+I)),this.word=new DataView(g.buffer).getUint32(0),this.bitsAvailable=8*I,this.bytesAvailable-=I},M.skipBits=function(A){var M;this.bitsAvailable>A?(this.word<<=A,this.bitsAvailable-=A):(A-=this.bitsAvailable,A-=(M=A>>3)>>3,this.bytesAvailable-=M,this.loadWord(),this.word<<=A,this.bitsAvailable-=A)},M.readBits=function(A){var M=Math.min(this.bitsAvailable,A),t=this.word>>>32-M;return A>32&&logger.error("Cannot read more than 32 bits at a time"),this.bitsAvailable-=M,this.bitsAvailable>0?this.word<<=M:this.bytesAvailable>0&&this.loadWord(),(M=A-M)>0&&this.bitsAvailable?t<>>A))return this.word<<=A,this.bitsAvailable-=A,A;return this.loadWord(),A+this.skipLZ()},M.skipUEG=function(){this.skipBits(1+this.skipLZ())},M.skipEG=function(){this.skipBits(1+this.skipLZ())},M.readUEG=function(){var A=this.skipLZ();return this.readBits(A+1)-1},M.readEG=function(){var A=this.readUEG();return 1&A?1+A>>>1:-1*(A>>>1)},M.readBoolean=function(){return 1===this.readBits(1)},M.readUByte=function(){return this.readBits(8)},M.readUShort=function(){return this.readBits(16)},M.readUInt=function(){return this.readBits(32)},M.skipScalingList=function(A){var M,t=8,g=8;for(M=0;M0)console.log("EXCEPTION:: No used...");else{var g=-1;for(t.length>1&&(g=0);t.length;){for(var I=t.shift(),e=0,i=0;i=0&&g++,this._callbackMediaDataFunc(this._callbackMediaDataUserPtr,!0,1,A,I.isKeyframe,E,e,I.pts,0,0))}}},e._onDataAvailableToSoftwareDecodeAudio=function(A,M){var t=M.samples,g=-1;for(t.length>1&&(g=0);t.length;){var I=t.shift();this._callbackMediaDataFunc&&(g>=0&&g++,this._callbackMediaDataFunc(this._callbackMediaDataUserPtr,!0,2,A,0,I.unit,I.length,this._SampleRate,this._ChannelNum,this._SampleRate>=44100?32:16))}},A.createTrack=function(A,M){return{container:"video"===A||"audio"===A?"video/mp2t":void 0,type:A,id:RemuxerTrackIdConfig[A],pid:-1,inputTimeScale:9e4,sequenceNumber:0,samples:[],dropped:"video"===A?0:void 0,isAAC:"audio"===A||void 0,duration:"audio"===A?M:void 0,isHEVC:0}},A._syncOffset=function(A){for(var M=Math.min(1e3,A.length-564),t=0;t1;){var D=new Uint8Array(n[0].length+n[1].length);D.set(n[0]),D.set(n[1],n[0].length),n[0]=D,n.splice(1,1)}if(1===((M=n[0])[0]<<16)+(M[1]<<8)+M[2]){if((g=(M[4]<<8)+M[5])&&g>A.size-6)return null;if(192&(t=M[7])&&((i=536870912*(14&M[9])+4194304*(255&M[10])+16384*(254&M[11])+128*(255&M[12])+(254&M[13])/2)>4294967295&&(i-=4294967295),64&t?((T=536870912*(14&M[14])+4194304*(255&M[15])+16384*(254&M[16])+128*(255&M[17])+(254&M[18])/2)>4294967295&&(T-=4294967295),i-T>54e5&&(logger.warn(Math.round((i-T)/9e4)+"s delta between PTS and DTS, align them"),i=T)):T=i),E=(I=M[8])+9,A.size<=E)return null;A.size-=E,e=new Uint8Array(A.size);for(var C=0,r=n.length;Cc){E-=c;continue}M=M.subarray(E),c-=E,E=0}e.set(M,N),N+=c}return g&&(g-=I+3),{data:e,pts:i,dts:T,len:g}}return null}},e.pushAccesUnit=function(A,M){if(A.units.length&&A.frame){var t=M.samples,g=t.length;if(isNaN(A.pts)){if(!g)return void M.dropped++;var I=t[g-1];A.pts=I.pts,A.dts=I.dts}!this.config.forceKeyFrameOnDiscontinuity||!0===A.key||M.sps&&(g||this.contiguous)?(A.id=g,t.push(A)):M.dropped++}A.debug.length&&logger.log(A.pts+"/"+A.dts+":"+A.debug)},e.parseAVCPES=function(A,M,t){var g=this,I=0;(0===this.startVideoPts||A.pts>24&255,I[1]=t>>16&255,I[2]=t>>8&255,I[3]=255&t,I.set(M.data,4);var e={type:M.type,data:I};switch(E+=I.length,T.push(e),M.type){case 1:!0;var D=M.data;if(n&&D.length>4){var C=new r(D).readSliceType();2!==C&&4!==C&&7!==C&&9!==C||console.log("key = true")}break;case 5:!0,g.findKeyframe=!1,N=!0;break;case 6:!0;var o=new r(g.discardEPB(M.data));o.readUByte();for(var B=0,Q=0,a=!1,h=0;!a&&o.bytesAvailable>1;){B=0;do{B+=h=o.readUByte()}while(255===h);Q=0;do{Q+=h=o.readUByte()}while(255===h);if(4===B&&0!==o.bytesAvailable){if(a=!0,181===o.readUByte())if(49===o.readUShort())if(1195456820===o.readUInt())if(3===o.readUByte()){var s=o.readUByte(),y=31&s,w=[s,o.readUByte()];for(i=0;i16){var j=[];for(i=0;i<16;i++)j.push(o.readUByte().toString(16)),3!==i&&5!==i&&7!==i&&9!==i||j.push("-");var L=Q-16,d=new Uint8Array(L);for(i=0;i>8,m.avcc[R++]=g._AVC_SPS.length-4&255;var p=g._AVC_SPS.subarray(4,g._AVC_SPS.length);m.avcc.set(p,R),R+=p.length,m.avcc[R++]=1,m.avcc[R++]=g._AVC_PPS.length-4>>8,m.avcc[R++]=g._AVC_PPS.length-4&255;var b=g._AVC_PPS.subarray(4,g._AVC_PPS.length);m.avcc.set(b,R),R+=b.length,m.avcc[k]=1}g._onTrackMetadata("video",m)}break;case 9:case 12:default:!1}}));var D=(A.pts-this.startVideoPts)/90;N&&this.syncVideoPts<1&&(this.syncVideoPts=this.startVideoPts);var C=this._videoTrack,o={units:T,length:E,isKeyframe:N,dts:D,cts:0,pts:D};N&&(o.fileposition=this.tagPosition),0!==this.reset||this.findKeyframe||(C.samples.push(o),C.length+=E),this.filePosition+=E,this.accurateTimeOffset=o.pts,this.lastVideoPts=A.pts,1===I&&(this.seekVideoHistoryPts=D),M&&0===this.reset&&("auto"===this._config.decodeType||"hard"===this._config.decodeType?this._onDataAvailable(this._audioTrack,this._videoTrack):(this._videoTrack.samples.length>0&&this._onDataAvailableToSoftwareDecodeVideo(27,this._videoTrack),this._audioTrack.samples.length>0&&this._onDataAvailableToSoftwareDecodeAudio(86018,this._audioTrack)))},e.parseHEVCPES=function(A,M,t){(0===this.startVideoPts||A.pts32){var T=0;for(T=0;T<32;T++)if(0===A.data[T]&&0===A.data[T+1]&&0===A.data[T+2]&&1===A.data[T+3]&&64===A.data[T+4]){i=1,this.findKeyframe=!1;break}}g.push(e);var E=(A.pts-this.startVideoPts)/90;i&&this.syncVideoPts<1&&(this.syncVideoPts=this.startVideoPts);var N=this._videoTrack,n={units:g,length:I,isKeyframe:i,dts:E,cts:0,pts:E};i&&(n.fileposition=this.tagPosition),N.samples.push(n),N.length+=I,this._videoTrack.samples.length>0&&this._onDataAvailableToSoftwareDecodeVideo(173,this._videoTrack),this._audioTrack.samples.length>0&&this._onDataAvailableToSoftwareDecodeAudio(86018,this._audioTrack)},e._insertSampleInOrder=function(A,M){var t=A.length;if(t>0){if(M.pts>=A[t-1].pts)A.push(M);else for(var g=t-1;g>=0;g--)if(M.pts=0)9!=(t={data:A.subarray(E,I-i-1),type:g}).type&&T.push(t);else;I=0&&i>=0&&(t={data:A.subarray(E,e),type:g,state:i},T.push(t)),0===T.length){var N=this._getLastNalUnit();if(N){var n=new Uint8Array(N.data.byteLength+A.byteLength);n.set(N.data,0),n.set(A,N.data.byteLength),N.data=n}}return T},e.discardEPB=function(A){for(var M,t,g=A.byteLength,I=[],e=1;e1&&(console.log("AAC: align PTS for overlapping frames by "+Math.round((j-o)/90)),o=j)}for(;I0&&(this.syncAudioPts=this.syncVideoPts),this.syncAudioPts>0&&(r.samples.push(x),r.length+=L.length)),I+=L.length,e=L.sample.pts,g++}else I++;B=I>4>1){if((T=I+5+M[I+4])===I+188)continue}else T=I+4;switch(i){case C:e&&(o&&(E=this.parsePES(o))&&(1==this.isHEVC?this.parseHEVCPES(E,!1,g):this.parseAVCPES(E,!1,g)),o={data:[],size:0}),o&&(o.data.push(M.subarray(T,I+188)),o.size+=I+188-T);break;case r:e&&(B&&(E=this.parsePES(B))&&(this.isAAC?this.parseAACPES(E,!1):this.parseMPEGPES(E)),B={data:[],size:0}),B&&(B.data.push(M.subarray(T,I+188)),B.size+=I+188-T);break;case c:e&&(Q&&(E=this.parsePES(Q))&&parseID3PES(E),Q={data:[],size:0}),Q&&(Q.data.push(M.subarray(T,I+188)),Q.size+=I+188-T);break;case 0:e&&(T+=M[T]+1),N=this.parsePAT(M,T);break;case N:e&&(T+=M[T]+1);var s=this.parsePMT(M,T,!1,!1);C=s.avc,s.hevc>0&&(C=s.hevc,this.isHEVC=1),C>0&&(this.avcTrackPid=C),!this._callbackInitFlag&&this._callbackMediaDataFunc&&(this._callbackInitFlag=!0,1===this.isHEVC||"auto"!==this._config.decodeType&&"hard"!==this._config.decodeType||this._callbackMediaDataFunc(this._callbackMediaDataUserPtr,!1,1,0,0,null,0,0,0,0)),(r=s.audio)>0&&(this.audioTrackPid=r,this.isAAC=s.isAAC),(c=s.id3)>0&&(this.id3TrackPid=c),D&&!a&&(logger.log("reparse from beginning"),D=!1,I=h-188),a=!0;break;case 17:case 8191:break;default:D=!0}}else console.log("parse error... reason: 'TS packet did not start with 0x47'");o&&(E=this.parsePES(o))?(1==this.isHEVC?this.parseHEVCPES(E,!0,g):this.parseAVCPES(E,!0,g),this.avcTrackPesData=null):this.avcTrackPesData=o,B&&(E=this.parsePES(B))?(this.isAAC?this.parseAACPES(E,!0):this.parseMPEGPES(E),this.audioTrackPesData=null):(B&&B.size&&logger.log("last AAC PES packet truncated,might overlap between fragments"),this.audioTrackPesData=B),Q&&(E=this.parsePES(Q))?(parseID3PES(E),this.id3TrackPesData=null):this.id3TrackPesData=Q}},M=A,(t=[{key:"onTrackMetadata",get:function(){return this._onTrackMetadata},set:function(A){this._onTrackMetadata=A}},{key:"onMediaInfo",get:function(){return this._onMediaInfo},set:function(A){this._onMediaInfo=A}},{key:"onMetaDataArrived",get:function(){return this._onMetaDataArrived},set:function(A){this._onMetaDataArrived=A}},{key:"onScriptDataArrived",get:function(){return this._onScriptDataArrived},set:function(A){this._onScriptDataArrived=A}},{key:"onError",get:function(){return this._onError},set:function(A){this._onError=A}},{key:"onDataAvailable",get:function(){return this._onDataAvailable},set:function(A){this._onDataAvailable=A}},{key:"timestampBase",get:function(){return this._timestampBase},set:function(A){this._timestampBase=A}},{key:"overridedDuration",get:function(){return this._duration},set:function(A){this._durationOverrided=!0,this._duration=A,this._mediaInfo.duration=A}},{key:"overridedHasAudio",set:function(A){this._hasAudioFlagOverrided=!0,this._hasAudio=A,this._mediaInfo.hasAudio=A}},{key:"overridedHasVideo",set:function(A){this._hasVideoFlagOverrided=!0,this._hasVideo=A,this._mediaInfo.hasVideo=A}}])&&B(M.prototype,t),I&&B(M,I),A}();M.a=Q},function(A,M){var t={}.toString;A.exports=Array.isArray||function(A){return"[object Array]"==t.call(A)}},function(A,M,t){"use strict";t.r(M),function(A,g,I,e){var i=t(0),T=(t(117),""),E=void 0!==E?E:{};function N(){var M,i={};for(M in E)E.hasOwnProperty(M)&&(i[M]=E[M]);var N=[],n="./this.program",D=function(A,M){throw M},C=!1,r=!1,c=!1,o=!1,B=!1;if(C="object"==typeof window,r="function"==typeof importScripts,o="object"==typeof A&&"object"==typeof A.versions&&"string"==typeof A.versions.node,c=o&&!C&&!r,B=!C&&!c&&!r,E.ENVIRONMENT)throw new Error("Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -s ENVIRONMENT=web or -s ENVIRONMENT=node)");var Q,a,h,s,y="";function w(A){return E.locateFile?E.locateFile(A,y):y+A}if(c)y=g+"/",Q=function(A,M){var g;return h||(h=t(121)),s||(s=t(122)),A=s.normalize(A),g=h.readFileSync(A),M?g:g.toString()},a=function(A){var M=Q(A,!0);return M.buffer||(M=new Uint8Array(M)),F(M.buffer),M},A.argv.length>1&&(n=A.argv[1].replace(/\\/g,"/")),N=A.argv.slice(2),I.exports=E,A.on("uncaughtException",(function(A){if(!(A instanceof Wt))throw A})),A.on("unhandledRejection",mA),D=function(M){A.exit(M)},E.inspect=function(){return"[Emscripten Module object]"};else if(B)"undefined"!=typeof read&&(Q=function(A){return read(A)}),a=function(A){var M;return"function"==typeof readbuffer?new Uint8Array(readbuffer(A)):(F("object"==typeof(M=read(A,"binary"))),M)},"undefined"!=typeof scriptArgs?N=scriptArgs:void 0!==arguments&&(N=arguments),"function"==typeof quit&&(D=function(A){quit(A)}),"undefined"!=typeof print&&("undefined"==typeof console&&(console={}),console.log=print,console.warn=console.error="undefined"!=typeof printErr?printErr:print);else{if(!C&&!r)throw new Error("environment detection error");r?y=self.location.href:document.currentScript&&(y=document.currentScript.src),y=0!==y.indexOf("blob:")?y.substr(0,y.lastIndexOf("/")+1):"",Q=function(A){var M=new XMLHttpRequest;return M.open("GET",A,!1),M.send(null),M.responseText},r&&(a=function(A){var M=new XMLHttpRequest;return M.open("GET",A,!1),M.responseType="arraybuffer",M.send(null),new Uint8Array(M.response)}),function(A,M,t){var g=new XMLHttpRequest;g.open("GET",A,!0),g.responseType="arraybuffer",g.onload=function(){200==g.status||0==g.status&&g.response?M(g.response):t()},g.onerror=t,g.send(null)},function(A){document.title=A}}var j=E.print||console.log.bind(console),L=E.printErr||console.warn.bind(console);for(M in i)i.hasOwnProperty(M)&&(E[M]=i[M]);function d(A){d.shown||(d.shown={}),d.shown[A]||(d.shown[A]=1,L(A))}function x(A,M){var t=[1,0,1,96],g=M.slice(0,1),I=M.slice(1),e={i:127,j:126,f:125,d:124};t.push(I.length);for(var i=0;i=g);)++I;if(I-M>16&&A.subarray&&p)return p.decode(A.subarray(M,I));for(var e="";M>10,56320|1023&N)}}else e+=String.fromCharCode((31&i)<<6|T)}else e+=String.fromCharCode(i)}return e}function G(A,M){return A?b(Z,A,M):""}function J(A,M,t,g){if(!(g>0))return 0;for(var I=t,e=t+g-1,i=0;i=55296&&T<=57343)T=65536+((1023&T)<<10)|1023&A.charCodeAt(++i);if(T<=127){if(t>=e)break;M[t++]=T}else if(T<=2047){if(t+1>=e)break;M[t++]=192|T>>6,M[t++]=128|63&T}else if(T<=65535){if(t+2>=e)break;M[t++]=224|T>>12,M[t++]=128|T>>6&63,M[t++]=128|63&T}else{if(t+3>=e)break;T>=2097152&&d("Invalid Unicode code point 0x"+T.toString(16)+" encountered when serializing a JS string to an UTF-8 string on the asm.js/wasm heap! (Valid unicode code points should be in range 0-0x1FFFFF)."),M[t++]=240|T>>18,M[t++]=128|T>>12&63,M[t++]=128|T>>6&63,M[t++]=128|63&T}}return M[t]=0,t-I}function H(A,M,t){return F("number"==typeof t,"stringToUTF8(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!"),J(A,Z,M,t)}function X(A){for(var M=0,t=0;t=55296&&g<=57343&&(g=65536+((1023&g)<<10)|1023&A.charCodeAt(++t)),g<=127?++M:M+=g<=2047?2:g<=65535?3:4}return M}"undefined"!=typeof TextDecoder&&new TextDecoder("utf-16le");function v(A,M){F(A.length>=0,"writeArrayToMemory array must have a length (should be an array or typed array)"),P.set(A,M)}function V(A,M,t){for(var g=0;g>0]=A.charCodeAt(g);t||(P[M>>0]=0)}var W,P,Z,K,q,_,$=65536;function AA(A,M){return A%M>0&&(A+=M-A%M),A}function MA(A){W=A,E.HEAP8=P=new Int8Array(A),E.HEAP16=K=new Int16Array(A),E.HEAP32=q=new Int32Array(A),E.HEAPU8=Z=new Uint8Array(A),E.HEAPU16=new Uint16Array(A),E.HEAPU32=_=new Uint32Array(A),E.HEAPF32=new Float32Array(A),E.HEAPF64=new Float64Array(A)}var tA=6415696,gA=1172816,IA=6415696,eA=1172656;F(tA%16==0,"stack must start aligned"),F(IA%16==0,"heap must start aligned");var iA=5242880;E.TOTAL_STACK&&F(iA===E.TOTAL_STACK,"the stack size can no longer be determined at runtime");var TA=E.TOTAL_MEMORY||67108864;function EA(){F(0==(3&gA)),_[1+(gA>>2)]=34821223,_[2+(gA>>2)]=2310721022,q[0]=1668509029}function NA(){var A=_[1+(gA>>2)],M=_[2+(gA>>2)];34821223==A&&2310721022==M||mA("Stack overflow! Stack cookie has been overwritten, expected hex dwords 0x89BACDFE and 0x02135467, but received 0x"+M.toString(16)+" "+A.toString(16)),1668509029!==q[0]&&mA("Runtime error: The application has corrupted its heap memory area (address zero)!")}function nA(A){mA("Stack overflow! Attempted to allocate "+A+" bytes on the stack, but stack has only "+(gA-Xt()+A)+" bytes available!")}function DA(A){for(;A.length>0;){var M=A.shift();if("function"!=typeof M){var t=M.func;"number"==typeof t?void 0===M.arg?E.dynCall_v(t):E.dynCall_vi(t,M.arg):t(void 0===M.arg?null:M.arg)}else M()}}Object.getOwnPropertyDescriptor(E,"TOTAL_MEMORY")||Object.defineProperty(E,"TOTAL_MEMORY",{configurable:!0,get:function(){mA("Module.TOTAL_MEMORY has been replaced with plain INITIAL_TOTAL_MEMORY")}}),F(TA>=iA,"TOTAL_MEMORY should be larger than TOTAL_STACK, was "+TA+"! (TOTAL_STACK="+iA+")"),F("undefined"!=typeof Int32Array&&"undefined"!=typeof Float64Array&&void 0!==Int32Array.prototype.subarray&&void 0!==Int32Array.prototype.set,"JS engine does not provide full typed array support"),(z=E.wasmMemory?E.wasmMemory:new WebAssembly.Memory({initial:TA/$}))&&(W=z.buffer),F((TA=W.byteLength)%$==0),MA(W),q[eA>>2]=IA,function(){var A=new Int16Array(1),M=new Int8Array(A.buffer);if(A[0]=25459,115!==M[0]||99!==M[1])throw"Runtime error: expected the system to be little-endian!"}();var CA=[],rA=[],cA=[],oA=[],BA=!1,QA=!1;function aA(){if(E.preRun)for("function"==typeof E.preRun&&(E.preRun=[E.preRun]);E.preRun.length;)jA(E.preRun.shift());DA(CA)}function hA(){NA(),F(!BA),BA=!0,E.noFSInit||iM.init.initialized||iM.init(),tM.init(),DA(rA)}function sA(){NA(),iM.ignorePermissions=!1,DA(cA)}function yA(){NA(),QA=!0}function wA(){if(NA(),E.postRun)for("function"==typeof E.postRun&&(E.postRun=[E.postRun]);E.postRun.length;)LA(E.postRun.shift());DA(oA)}function jA(A){CA.unshift(A)}function LA(A){oA.unshift(A)}F(Math.imul,"This browser does not support Math.imul(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill"),F(Math.fround,"This browser does not support Math.fround(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill"),F(Math.clz32,"This browser does not support Math.clz32(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill"),F(Math.trunc,"This browser does not support Math.trunc(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill");var dA=Math.abs,xA=Math.ceil,YA=Math.floor,uA=Math.min,SA=0,lA=null,zA=null,UA={};function fA(A){for(var M=A;;){if(!UA[A])return A;A=M+Math.random()}return A}function OA(A){SA++,E.monitorRunDependencies&&E.monitorRunDependencies(SA),A?(F(!UA[A]),UA[A]=1,null===lA&&"undefined"!=typeof setInterval&&(lA=setInterval((function(){if(O)return clearInterval(lA),void(lA=null);var A=!1;for(var M in UA)A||(A=!0,L("still waiting on run dependencies:")),L("dependency: "+M);A&&L("(end of list)")}),1e4))):L("warning: run dependency added without ID")}function FA(A){if(SA--,E.monitorRunDependencies&&E.monitorRunDependencies(SA),A?(F(UA[A]),delete UA[A]):L("warning: run dependency removed without ID"),0==SA&&(null!==lA&&(clearInterval(lA),lA=null),zA)){var M=zA;zA=null,M()}}function mA(A){throw E.onAbort&&E.onAbort(A),j(A+=""),L(A),O=!0,1,A="abort("+A+") at "+PA(),new WebAssembly.RuntimeError(A)}E.preloadedImages={},E.preloadedAudios={};var RA="data:application/octet-stream;base64,";function kA(A){return String.prototype.startsWith?A.startsWith(RA):0===A.indexOf(RA)}var pA,bA,GA=T+"static/libDecoder.wasm";function JA(){try{if(S)return new Uint8Array(S);if(a)return a(GA);throw"both async and sync fetching of the wasm failed"}catch(A){mA(A)}}function HA(){return S||!C&&!r||"function"!=typeof fetch?new Promise((function(A,M){A(JA())})):fetch(GA,{credentials:"same-origin"}).then((function(A){if(!A.ok)throw"failed to load wasm binary file at '"+GA+"'";return A.arrayBuffer()})).catch((function(){return JA()}))}function XA(){var A={env:zM,wasi_unstable:zM};function M(A,M){var t=A.exports;E.asm=t,FA("wasm-instantiate")}OA("wasm-instantiate");var t=E;function g(A){F(E===t,"the Module object should not be replaced during async compilation - perhaps the order of HTML elements is wrong?"),t=null,M(A.instance)}function I(M){return HA().then((function(M){return WebAssembly.instantiate(M,A)})).then(M,(function(A){L("failed to asynchronously prepare wasm: "+A),mA(A)}))}if(E.instantiateWasm)try{return E.instantiateWasm(A,M)}catch(A){return L("Module.instantiateWasm callback failed with error: "+A),!1}return function(){if(S||"function"!=typeof WebAssembly.instantiateStreaming||kA(GA)||"function"!=typeof fetch)return I(g);fetch(GA,{credentials:"same-origin"}).then((function(M){return WebAssembly.instantiateStreaming(M,A).then(g,(function(A){L("wasm streaming compile failed: "+A),L("falling back to ArrayBuffer instantiation"),I(g)}))}))}(),{}}function vA(A){return d("warning: build with -s DEMANGLE_SUPPORT=1 to link in libcxxabi demangling"),A}function VA(A){return A.replace(/\b_Z[\w\d_]+/g,(function(A){var M=vA(A);return A===M?A:M+" ["+A+"]"}))}function WA(){var A=new Error;if(!A.stack){try{throw new Error(0)}catch(M){A=M}if(!A.stack)return"(no stack trace available)"}return A.stack.toString()}function PA(){var A=WA();return E.extraStackTrace&&(A+="\n"+E.extraStackTrace()),VA(A)}function ZA(A){return Jt(A)}kA(GA)||(GA=w(GA)),rA.push({func:function(){Gt()}});var KA={};function qA(A,M,t){throw KA[A]={ptr:A,adjusted:[A],type:M,destructor:t,refcount:0,caught:!1,rethrown:!1},A,"uncaught_exception"in Ht?Ht.uncaught_exceptions++:Ht.uncaught_exceptions=1,A+" - Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0 or DISABLE_EXCEPTION_CATCHING=2 to catch."}function _A(){}function $A(A){return E.___errno_location?q[E.___errno_location()>>2]=A:L("failed to set errno from JS"),A}var AM={splitPath:function(A){return/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(A).slice(1)},normalizeArray:function(A,M){for(var t=0,g=A.length-1;g>=0;g--){var I=A[g];"."===I?A.splice(g,1):".."===I?(A.splice(g,1),t++):t&&(A.splice(g,1),t--)}if(M)for(;t;t--)A.unshift("..");return A},normalize:function(A){var M="/"===A.charAt(0),t="/"===A.substr(-1);return(A=AM.normalizeArray(A.split("/").filter((function(A){return!!A})),!M).join("/"))||M||(A="."),A&&t&&(A+="/"),(M?"/":"")+A},dirname:function(A){var M=AM.splitPath(A),t=M[0],g=M[1];return t||g?(g&&(g=g.substr(0,g.length-1)),t+g):"."},basename:function(A){if("/"===A)return"/";var M=A.lastIndexOf("/");return-1===M?A:A.substr(M+1)},extname:function(A){return AM.splitPath(A)[3]},join:function(){var A=Array.prototype.slice.call(arguments,0);return AM.normalize(A.join("/"))},join2:function(A,M){return AM.normalize(A+"/"+M)}},MM={resolve:function(){for(var A="",M=!1,t=arguments.length-1;t>=-1&&!M;t--){var g=t>=0?arguments[t]:iM.cwd();if("string"!=typeof g)throw new TypeError("Arguments to path.resolve must be strings");if(!g)return"";A=g+"/"+A,M="/"===g.charAt(0)}return(M?"/":"")+(A=AM.normalizeArray(A.split("/").filter((function(A){return!!A})),!M).join("/"))||"."},relative:function(A,M){function t(A){for(var M=0;M=0&&""===A[t];t--);return M>t?[]:A.slice(M,t-M+1)}A=MM.resolve(A).substr(1),M=MM.resolve(M).substr(1);for(var g=t(A.split("/")),I=t(M.split("/")),e=Math.min(g.length,I.length),i=e,T=0;T0?g.slice(0,I).toString("utf-8"):null}else"undefined"!=typeof window&&"function"==typeof window.prompt?null!==(t=window.prompt("Input: "))&&(t+="\n"):"function"==typeof readline&&null!==(t=readline())&&(t+="\n");if(!t)return null;M.input=lM(t,!0)}return M.input.shift()},put_char:function(A,M){null===M||10===M?(j(b(A.output,0)),A.output=[]):0!=M&&A.output.push(M)},flush:function(A){A.output&&A.output.length>0&&(j(b(A.output,0)),A.output=[])}},default_tty1_ops:{put_char:function(A,M){null===M||10===M?(L(b(A.output,0)),A.output=[]):0!=M&&A.output.push(M)},flush:function(A){A.output&&A.output.length>0&&(L(b(A.output,0)),A.output=[])}}},gM={ops_table:null,mount:function(A){return gM.createNode(null,"/",16895,0)},createNode:function(A,M,t,g){if(iM.isBlkdev(t)||iM.isFIFO(t))throw new iM.ErrnoError(63);gM.ops_table||(gM.ops_table={dir:{node:{getattr:gM.node_ops.getattr,setattr:gM.node_ops.setattr,lookup:gM.node_ops.lookup,mknod:gM.node_ops.mknod,rename:gM.node_ops.rename,unlink:gM.node_ops.unlink,rmdir:gM.node_ops.rmdir,readdir:gM.node_ops.readdir,symlink:gM.node_ops.symlink},stream:{llseek:gM.stream_ops.llseek}},file:{node:{getattr:gM.node_ops.getattr,setattr:gM.node_ops.setattr},stream:{llseek:gM.stream_ops.llseek,read:gM.stream_ops.read,write:gM.stream_ops.write,allocate:gM.stream_ops.allocate,mmap:gM.stream_ops.mmap,msync:gM.stream_ops.msync}},link:{node:{getattr:gM.node_ops.getattr,setattr:gM.node_ops.setattr,readlink:gM.node_ops.readlink},stream:{}},chrdev:{node:{getattr:gM.node_ops.getattr,setattr:gM.node_ops.setattr},stream:iM.chrdev_stream_ops}});var I=iM.createNode(A,M,t,g);return iM.isDir(I.mode)?(I.node_ops=gM.ops_table.dir.node,I.stream_ops=gM.ops_table.dir.stream,I.contents={}):iM.isFile(I.mode)?(I.node_ops=gM.ops_table.file.node,I.stream_ops=gM.ops_table.file.stream,I.usedBytes=0,I.contents=null):iM.isLink(I.mode)?(I.node_ops=gM.ops_table.link.node,I.stream_ops=gM.ops_table.link.stream):iM.isChrdev(I.mode)&&(I.node_ops=gM.ops_table.chrdev.node,I.stream_ops=gM.ops_table.chrdev.stream),I.timestamp=Date.now(),A&&(A.contents[M]=I),I},getFileDataAsRegularArray:function(A){if(A.contents&&A.contents.subarray){for(var M=[],t=0;t=M)){M=Math.max(M,t*(t<1048576?2:1.125)|0),0!=t&&(M=Math.max(M,256));var g=A.contents;A.contents=new Uint8Array(M),A.usedBytes>0&&A.contents.set(g.subarray(0,A.usedBytes),0)}},resizeFileStorage:function(A,M){if(A.usedBytes!=M){if(0==M)return A.contents=null,void(A.usedBytes=0);if(!A.contents||A.contents.subarray){var t=A.contents;return A.contents=new Uint8Array(new ArrayBuffer(M)),t&&A.contents.set(t.subarray(0,Math.min(M,A.usedBytes))),void(A.usedBytes=M)}if(A.contents||(A.contents=[]),A.contents.length>M)A.contents.length=M;else for(;A.contents.length=A.node.usedBytes)return 0;var i=Math.min(A.node.usedBytes-I,g);if(F(i>=0),i>8&&e.subarray)M.set(e.subarray(I,I+i),t);else for(var T=0;T0||I+g8)throw new iM.ErrnoError(32);for(var I=AM.normalizeArray(A.split("/").filter((function(A){return!!A})),!1),e=iM.root,i="/",T=0;T40)throw new iM.ErrnoError(32)}}return{path:i,node:e}},getPath:function(A){for(var M;;){if(iM.isRoot(A)){var t=A.mount.mountpoint;return M?"/"!==t[t.length-1]?t+"/"+M:t+M:t}M=M?A.name+"/"+M:A.name,A=A.parent}},hashName:function(A,M){for(var t=0,g=0;g>>0)%iM.nameTable.length},hashAddNode:function(A){var M=iM.hashName(A.parent.id,A.name);A.name_next=iM.nameTable[M],iM.nameTable[M]=A},hashRemoveNode:function(A){var M=iM.hashName(A.parent.id,A.name);if(iM.nameTable[M]===A)iM.nameTable[M]=A.name_next;else for(var t=iM.nameTable[M];t;){if(t.name_next===A){t.name_next=A.name_next;break}t=t.name_next}},lookupNode:function(A,M){var t=iM.mayLookup(A);if(t)throw new iM.ErrnoError(t,A);for(var g=iM.hashName(A.id,M),I=iM.nameTable[g];I;I=I.name_next){var e=I.name;if(I.parent.id===A.id&&e===M)return I}return iM.lookup(A,M)},createNode:function(A,M,t,g){if(!iM.FSNode){iM.FSNode=function(A,M,t,g){A||(A=this),this.parent=A,this.mount=A.mount,this.mounted=null,this.id=iM.nextInode++,this.name=M,this.mode=t,this.node_ops={},this.stream_ops={},this.rdev=g},iM.FSNode.prototype={};Object.defineProperties(iM.FSNode.prototype,{read:{get:function(){return 365==(365&this.mode)},set:function(A){A?this.mode|=365:this.mode&=-366}},write:{get:function(){return 146==(146&this.mode)},set:function(A){A?this.mode|=146:this.mode&=-147}},isFolder:{get:function(){return iM.isDir(this.mode)}},isDevice:{get:function(){return iM.isChrdev(this.mode)}}})}var I=new iM.FSNode(A,M,t,g);return iM.hashAddNode(I),I},destroyNode:function(A){iM.hashRemoveNode(A)},isRoot:function(A){return A===A.parent},isMountpoint:function(A){return!!A.mounted},isFile:function(A){return 32768==(61440&A)},isDir:function(A){return 16384==(61440&A)},isLink:function(A){return 40960==(61440&A)},isChrdev:function(A){return 8192==(61440&A)},isBlkdev:function(A){return 24576==(61440&A)},isFIFO:function(A){return 4096==(61440&A)},isSocket:function(A){return 49152==(49152&A)},flagModes:{r:0,rs:1052672,"r+":2,w:577,wx:705,xw:705,"w+":578,"wx+":706,"xw+":706,a:1089,ax:1217,xa:1217,"a+":1090,"ax+":1218,"xa+":1218},modeStringToFlags:function(A){var M=iM.flagModes[A];if(void 0===M)throw new Error("Unknown file open mode: "+A);return M},flagsToPermissionString:function(A){var M=["r","w","rw"][3&A];return 512&A&&(M+="w"),M},nodePermissions:function(A,M){return iM.ignorePermissions||(-1===M.indexOf("r")||292&A.mode)&&(-1===M.indexOf("w")||146&A.mode)&&(-1===M.indexOf("x")||73&A.mode)?0:2},mayLookup:function(A){var M=iM.nodePermissions(A,"x");return M||(A.node_ops.lookup?0:2)},mayCreate:function(A,M){try{iM.lookupNode(A,M);return 20}catch(A){}return iM.nodePermissions(A,"wx")},mayDelete:function(A,M,t){var g;try{g=iM.lookupNode(A,M)}catch(A){return A.errno}var I=iM.nodePermissions(A,"wx");if(I)return I;if(t){if(!iM.isDir(g.mode))return 54;if(iM.isRoot(g)||iM.getPath(g)===iM.cwd())return 10}else if(iM.isDir(g.mode))return 31;return 0},mayOpen:function(A,M){return A?iM.isLink(A.mode)?32:iM.isDir(A.mode)&&("r"!==iM.flagsToPermissionString(M)||512&M)?31:iM.nodePermissions(A,iM.flagsToPermissionString(M)):44},MAX_OPEN_FDS:4096,nextfd:function(A,M){A=A||0,M=M||iM.MAX_OPEN_FDS;for(var t=A;t<=M;t++)if(!iM.streams[t])return t;throw new iM.ErrnoError(33)},getStream:function(A){return iM.streams[A]},createStream:function(A,M,t){iM.FSStream||(iM.FSStream=function(){},iM.FSStream.prototype={},Object.defineProperties(iM.FSStream.prototype,{object:{get:function(){return this.node},set:function(A){this.node=A}},isRead:{get:function(){return 1!=(2097155&this.flags)}},isWrite:{get:function(){return 0!=(2097155&this.flags)}},isAppend:{get:function(){return 1024&this.flags}}}));var g=new iM.FSStream;for(var I in A)g[I]=A[I];A=g;var e=iM.nextfd(M,t);return A.fd=e,iM.streams[e]=A,A},closeStream:function(A){iM.streams[A]=null},chrdev_stream_ops:{open:function(A){var M=iM.getDevice(A.node.rdev);A.stream_ops=M.stream_ops,A.stream_ops.open&&A.stream_ops.open(A)},llseek:function(){throw new iM.ErrnoError(70)}},major:function(A){return A>>8},minor:function(A){return 255&A},makedev:function(A,M){return A<<8|M},registerDevice:function(A,M){iM.devices[A]={stream_ops:M}},getDevice:function(A){return iM.devices[A]},getMounts:function(A){for(var M=[],t=[A];t.length;){var g=t.pop();M.push(g),t.push.apply(t,g.mounts)}return M},syncfs:function(A,M){"function"==typeof A&&(M=A,A=!1),iM.syncFSRequests++,iM.syncFSRequests>1&&console.log("warning: "+iM.syncFSRequests+" FS.syncfs operations in flight at once, probably just doing extra work");var t=iM.getMounts(iM.root.mount),g=0;function I(A){return F(iM.syncFSRequests>0),iM.syncFSRequests--,M(A)}function e(A){if(A)return e.errored?void 0:(e.errored=!0,I(A));++g>=t.length&&I(null)}t.forEach((function(M){if(!M.type.syncfs)return e(null);M.type.syncfs(M,A,e)}))},mount:function(A,M,t){if("string"==typeof A)throw A;var g,I="/"===t,e=!t;if(I&&iM.root)throw new iM.ErrnoError(10);if(!I&&!e){var i=iM.lookupPath(t,{follow_mount:!1});if(t=i.path,g=i.node,iM.isMountpoint(g))throw new iM.ErrnoError(10);if(!iM.isDir(g.mode))throw new iM.ErrnoError(54)}var T={type:A,opts:M,mountpoint:t,mounts:[]},E=A.mount(T);return E.mount=T,T.root=E,I?iM.root=E:g&&(g.mounted=T,g.mount&&g.mount.mounts.push(T)),E},unmount:function(A){var M=iM.lookupPath(A,{follow_mount:!1});if(!iM.isMountpoint(M.node))throw new iM.ErrnoError(28);var t=M.node,g=t.mounted,I=iM.getMounts(g);Object.keys(iM.nameTable).forEach((function(A){for(var M=iM.nameTable[A];M;){var t=M.name_next;-1!==I.indexOf(M.mount)&&iM.destroyNode(M),M=t}})),t.mounted=null;var e=t.mount.mounts.indexOf(g);F(-1!==e),t.mount.mounts.splice(e,1)},lookup:function(A,M){return A.node_ops.lookup(A,M)},mknod:function(A,M,t){var g=iM.lookupPath(A,{parent:!0}).node,I=AM.basename(A);if(!I||"."===I||".."===I)throw new iM.ErrnoError(28);var e=iM.mayCreate(g,I);if(e)throw new iM.ErrnoError(e);if(!g.node_ops.mknod)throw new iM.ErrnoError(63);return g.node_ops.mknod(g,I,M,t)},create:function(A,M){return M=void 0!==M?M:438,M&=4095,M|=32768,iM.mknod(A,M,0)},mkdir:function(A,M){return M=void 0!==M?M:511,M&=1023,M|=16384,iM.mknod(A,M,0)},mkdirTree:function(A,M){for(var t=A.split("/"),g="",I=0;Ithis.length-1||A<0)){var M=A%this.chunkSize,t=A/this.chunkSize|0;return this.getter(t)[M]}},e.prototype.setDataGetter=function(A){this.getter=A},e.prototype.cacheLength=function(){var A=new XMLHttpRequest;if(A.open("HEAD",t,!1),A.send(null),!(A.status>=200&&A.status<300||304===A.status))throw new Error("Couldn't load "+t+". Status: "+A.status);var M,g=Number(A.getResponseHeader("Content-length")),I=(M=A.getResponseHeader("Accept-Ranges"))&&"bytes"===M,e=(M=A.getResponseHeader("Content-Encoding"))&&"gzip"===M,i=1048576;I||(i=g);var T=this;T.setDataGetter((function(A){var M=A*i,I=(A+1)*i-1;if(I=Math.min(I,g-1),void 0===T.chunks[A]&&(T.chunks[A]=function(A,M){if(A>M)throw new Error("invalid range ("+A+", "+M+") or no bytes requested!");if(M>g-1)throw new Error("only "+g+" bytes available! programmer error!");var I=new XMLHttpRequest;if(I.open("GET",t,!1),g!==i&&I.setRequestHeader("Range","bytes="+A+"-"+M),"undefined"!=typeof Uint8Array&&(I.responseType="arraybuffer"),I.overrideMimeType&&I.overrideMimeType("text/plain; charset=x-user-defined"),I.send(null),!(I.status>=200&&I.status<300||304===I.status))throw new Error("Couldn't load "+t+". Status: "+I.status);return void 0!==I.response?new Uint8Array(I.response||[]):lM(I.responseText||"",!0)}(M,I)),void 0===T.chunks[A])throw new Error("doXHR failed!");return T.chunks[A]})),!e&&g||(i=g=1,g=this.getter(0).length,i=g,console.log("LazyFiles on gzip forces download of the whole file when length is accessed")),this._length=g,this._chunkSize=i,this.lengthKnown=!0},"undefined"!=typeof XMLHttpRequest){if(!r)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var i=new e;Object.defineProperties(i,{length:{get:function(){return this.lengthKnown||this.cacheLength(),this._length}},chunkSize:{get:function(){return this.lengthKnown||this.cacheLength(),this._chunkSize}}});var T={isDevice:!1,contents:i}}else T={isDevice:!1,url:t};var E=iM.createFile(A,M,T,g,I);T.contents?E.contents=T.contents:T.url&&(E.contents=null,E.url=T.url),Object.defineProperties(E,{usedBytes:{get:function(){return this.contents.length}}});var N={};return Object.keys(E.stream_ops).forEach((function(A){var M=E.stream_ops[A];N[A]=function(){if(!iM.forceLoadFile(E))throw new iM.ErrnoError(29);return M.apply(null,arguments)}})),N.read=function(A,M,t,g,I){if(!iM.forceLoadFile(E))throw new iM.ErrnoError(29);var e=A.node.contents;if(I>=e.length)return 0;var i=Math.min(e.length-I,g);if(F(i>=0),e.slice)for(var T=0;T>2]=g.dev,q[t+4>>2]=0,q[t+8>>2]=g.ino,q[t+12>>2]=g.mode,q[t+16>>2]=g.nlink,q[t+20>>2]=g.uid,q[t+24>>2]=g.gid,q[t+28>>2]=g.rdev,q[t+32>>2]=0,bA=[g.size>>>0,(pA=g.size,+dA(pA)>=1?pA>0?(0|uA(+YA(pA/4294967296),4294967295))>>>0:~~+xA((pA-+(~~pA>>>0))/4294967296)>>>0:0)],q[t+40>>2]=bA[0],q[t+44>>2]=bA[1],q[t+48>>2]=4096,q[t+52>>2]=g.blocks,q[t+56>>2]=g.atime.getTime()/1e3|0,q[t+60>>2]=0,q[t+64>>2]=g.mtime.getTime()/1e3|0,q[t+68>>2]=0,q[t+72>>2]=g.ctime.getTime()/1e3|0,q[t+76>>2]=0,bA=[g.ino>>>0,(pA=g.ino,+dA(pA)>=1?pA>0?(0|uA(+YA(pA/4294967296),4294967295))>>>0:~~+xA((pA-+(~~pA>>>0))/4294967296)>>>0:0)],q[t+80>>2]=bA[0],q[t+84>>2]=bA[1],0},doMsync:function(A,M,t,g){var I=new Uint8Array(Z.subarray(A,A+t));iM.msync(M,I,0,t,g)},doMkdir:function(A,M){return"/"===(A=AM.normalize(A))[A.length-1]&&(A=A.substr(0,A.length-1)),iM.mkdir(A,M,0),0},doMknod:function(A,M,t){switch(61440&M){case 32768:case 8192:case 24576:case 4096:case 49152:break;default:return-28}return iM.mknod(A,M,t),0},doReadlink:function(A,M,t){if(t<=0)return-28;var g=iM.readlink(A),I=Math.min(t,X(g)),e=P[M+I];return H(g,M,t+1),P[M+I]=e,I},doAccess:function(A,M){if(-8&M)return-28;var t;if(!(t=iM.lookupPath(A,{follow:!0}).node))return-44;var g="";return 4&M&&(g+="r"),2&M&&(g+="w"),1&M&&(g+="x"),g&&iM.nodePermissions(t,g)?-2:0},doDup:function(A,M,t){var g=iM.getStream(t);return g&&iM.close(g),iM.open(A,M,0,t,t).fd},doReadv:function(A,M,t,g){for(var I=0,e=0;e>2],T=q[M+(8*e+4)>>2],E=iM.read(A,P,i,T,g);if(E<0)return-1;if(I+=E,E>2],T=q[M+(8*e+4)>>2],E=iM.write(A,P,i,T,g);if(E<0)return-1;I+=E}return I},varargs:0,get:function(A){return TM.varargs+=4,q[TM.varargs-4>>2]},getStr:function(){return G(TM.get())},getStreamFromFD:function(A){void 0===A&&(A=TM.get());var M=iM.getStream(A);if(!M)throw new iM.ErrnoError(8);return M},get64:function(){var A=TM.get(),M=TM.get();return F(A>=0?0===M:-1===M),A},getZero:function(){F(0===TM.get())}};function EM(A,M){TM.varargs=M;try{var t=TM.getStreamFromFD();switch(TM.get()){case 0:return(g=TM.get())<0?-28:iM.open(t.path,t.flags,0,g).fd;case 1:case 2:return 0;case 3:return t.flags;case 4:var g=TM.get();return t.flags|=g,0;case 12:g=TM.get();return K[g+0>>1]=2,0;case 13:case 14:return 0;case 16:case 8:return-28;case 9:return $A(28),-1;default:return-28}}catch(A){return void 0!==iM&&A instanceof iM.ErrnoError||mA(A),-A.errno}}function NM(A,M){TM.varargs=M;try{var t=TM.getStreamFromFD(),g=TM.get(),I=TM.get();return iM.read(t,P,g,I)}catch(A){return void 0!==iM&&A instanceof iM.ErrnoError||mA(A),-A.errno}}function nM(A,M){TM.varargs=M;try{var t=TM.getStr(),g=TM.get(),I=TM.get();return iM.open(t,g,I).fd}catch(A){return void 0!==iM&&A instanceof iM.ErrnoError||mA(A),-A.errno}}function DM(){}function CM(){mA()}function rM(){return void 0===rM.start&&(rM.start=Date.now()),1e3*(Date.now()-rM.start)|0}function cM(){return P.length}function oM(A,M,t){Z.set(Z.subarray(M,M+t),A)}function BM(A){try{return z.grow(A-W.byteLength+65535>>16),MA(z.buffer),1}catch(M){console.error("emscripten_realloc_buffer: Attempted to grow heap from "+W.byteLength+" bytes to "+A+" bytes, but got error: "+M)}}function QM(A){var M=cM();F(A>M);if(A>2147418112)return L("Cannot enlarge memory, asked to go up to "+A+" bytes, but the limit is 2147418112 bytes!"),!1;for(var t=Math.max(M,16777216);t=0?+YA(A+.5):+xA(A-.5)}function uM(A){U(0|A)}function SM(A){var M=Date.now()/1e3|0;return A&&(q[A>>2]=M),M}function lM(A,M,t){var g=t>0?t:X(A)+1,I=new Array(g),e=J(A,I,0,I.length);return M&&(I.length=e),I}iM.staticInit();var zM={__cxa_allocate_exception:ZA,__cxa_throw:qA,__lock:_A,__syscall221:EM,__syscall3:NM,__syscall5:nM,__unlock:DM,abort:CM,clock:rM,emscripten_memcpy_big:oM,emscripten_resize_heap:QM,environ_get:sM,environ_sizes_get:yM,fd_close:wM,fd_fdstat_get:jM,fd_seek:LM,fd_write:dM,gettimeofday:xM,memory:z,round:YM,setTempRet0:uM,table:f,time:SM},UM=XA(),fM=UM.__wasm_call_ctors;UM.__wasm_call_ctors=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),fM.apply(null,arguments)};var OM=UM.WasmDecoder_Check;UM.WasmDecoder_Check=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),OM.apply(null,arguments)};var FM=UM.WasmDecoder_Init;UM.WasmDecoder_Init=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),FM.apply(null,arguments)};var mM=UM.WasmDecoder_Deinit;UM.WasmDecoder_Deinit=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),mM.apply(null,arguments)};var RM=UM.WasmDecoder_OpenChannel;UM.WasmDecoder_OpenChannel=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),RM.apply(null,arguments)};var kM=UM.WasmDecoder_InputFlvStream;UM.WasmDecoder_InputFlvStream=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),kM.apply(null,arguments)};var pM=UM.WasmDecoder_GetFlvDecodeData;UM.WasmDecoder_GetFlvDecodeData=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),pM.apply(null,arguments)};var bM=UM.WasmDecoder_InputFlvStreamAndDecode;UM.WasmDecoder_InputFlvStreamAndDecode=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),bM.apply(null,arguments)};var GM=UM.WasmDecoder_OpenVideoDecoder;UM.WasmDecoder_OpenVideoDecoder=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),GM.apply(null,arguments)};var JM=UM.WasmDecoder_OpenAudioDecoder;UM.WasmDecoder_OpenAudioDecoder=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),JM.apply(null,arguments)};var HM=UM.WasmDecoder_DecodeVideoFrame;UM.WasmDecoder_DecodeVideoFrame=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),HM.apply(null,arguments)};var XM=UM.WasmDecoder_DecodeAudioFrame;UM.WasmDecoder_DecodeAudioFrame=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),XM.apply(null,arguments)};var vM=UM.WasmDecoder_CloseChannel;UM.WasmDecoder_CloseChannel=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),vM.apply(null,arguments)};var VM=UM.main;UM.main=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),VM.apply(null,arguments)};var WM=UM.malloc;UM.malloc=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),WM.apply(null,arguments)};var PM=UM.free;UM.free=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),PM.apply(null,arguments)};var ZM=UM.__errno_location;UM.__errno_location=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),ZM.apply(null,arguments)};var KM=UM.fflush;UM.fflush=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),KM.apply(null,arguments)};var qM=UM._get_tzname;UM._get_tzname=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),qM.apply(null,arguments)};var _M=UM._get_daylight;UM._get_daylight=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),_M.apply(null,arguments)};var $M=UM._get_timezone;UM._get_timezone=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),$M.apply(null,arguments)};var At=UM.setThrew;UM.setThrew=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),At.apply(null,arguments)};var Mt=UM._ZSt18uncaught_exceptionv;UM._ZSt18uncaught_exceptionv=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),Mt.apply(null,arguments)};var tt=UM.stackSave;UM.stackSave=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),tt.apply(null,arguments)};var gt=UM.stackAlloc;UM.stackAlloc=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),gt.apply(null,arguments)};var It=UM.stackRestore;UM.stackRestore=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),It.apply(null,arguments)};var et=UM.__growWasmMemory;UM.__growWasmMemory=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),et.apply(null,arguments)};var it=UM.dynCall_ii;UM.dynCall_ii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),it.apply(null,arguments)};var Tt=UM.dynCall_viiiii;UM.dynCall_viiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),Tt.apply(null,arguments)};var Et=UM.dynCall_viiiiiifi;UM.dynCall_viiiiiifi=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),Et.apply(null,arguments)};var Nt=UM.dynCall_viiii;UM.dynCall_viiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),Nt.apply(null,arguments)};var nt=UM.dynCall_viii;UM.dynCall_viii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),nt.apply(null,arguments)};var Dt=UM.dynCall_iiiiiii;UM.dynCall_iiiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),Dt.apply(null,arguments)};var Ct=UM.dynCall_iiiiii;UM.dynCall_iiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),Ct.apply(null,arguments)};var rt=UM.dynCall_vii;UM.dynCall_vii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),rt.apply(null,arguments)};var ct=UM.dynCall_iii;UM.dynCall_iii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),ct.apply(null,arguments)};var ot=UM.dynCall_viiiiii;UM.dynCall_viiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),ot.apply(null,arguments)};var Bt=UM.dynCall_viiiiiiiii;UM.dynCall_viiiiiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),Bt.apply(null,arguments)};var Qt=UM.dynCall_viiiiiiii;UM.dynCall_viiiiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),Qt.apply(null,arguments)};var at=UM.dynCall_iiiii;UM.dynCall_iiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),at.apply(null,arguments)};var ht=UM.dynCall_viiiiiiiiiiiiii;UM.dynCall_viiiiiiiiiiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),ht.apply(null,arguments)};var st=UM.dynCall_viiiiiiiiiii;UM.dynCall_viiiiiiiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),st.apply(null,arguments)};var yt=UM.dynCall_viiiiiii;UM.dynCall_viiiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),yt.apply(null,arguments)};var wt=UM.dynCall_viiiiiiiiiiii;UM.dynCall_viiiiiiiiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),wt.apply(null,arguments)};var jt=UM.dynCall_vi;UM.dynCall_vi=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),jt.apply(null,arguments)};var Lt=UM.dynCall_iiii;UM.dynCall_iiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),Lt.apply(null,arguments)};var dt=UM.dynCall_viiiifii;UM.dynCall_viiiifii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),dt.apply(null,arguments)};var xt=UM.dynCall_fii;UM.dynCall_fii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),xt.apply(null,arguments)};var Yt=UM.dynCall_viiiiiiiiii;UM.dynCall_viiiiiiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),Yt.apply(null,arguments)};var ut=UM.dynCall_dd;UM.dynCall_dd=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),ut.apply(null,arguments)};var St=UM.dynCall_viifi;UM.dynCall_viifi=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),St.apply(null,arguments)};var lt=UM.dynCall_fiii;UM.dynCall_fiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),lt.apply(null,arguments)};var zt=UM.dynCall_viidi;UM.dynCall_viidi=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),zt.apply(null,arguments)};var Ut=UM.dynCall_iiiiiiii;UM.dynCall_iiiiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),Ut.apply(null,arguments)};var ft=UM.dynCall_viiijj;UM.dynCall_viiijj=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),ft.apply(null,arguments)};var Ot=UM.dynCall_iiiiiiidiiddii;UM.dynCall_iiiiiiidiiddii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),Ot.apply(null,arguments)};var Ft=UM.dynCall_jij;UM.dynCall_jij=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),Ft.apply(null,arguments)};var mt=UM.dynCall_jii;UM.dynCall_jii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),mt.apply(null,arguments)};var Rt=UM.dynCall_v;UM.dynCall_v=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),Rt.apply(null,arguments)};var kt=UM.dynCall_iidiiii;UM.dynCall_iidiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),kt.apply(null,arguments)};var pt=UM.dynCall_jiji;UM.dynCall_jiji=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),pt.apply(null,arguments)},E.asm=UM;var bt,Gt=E.___wasm_call_ctors=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.__wasm_call_ctors.apply(null,arguments)},Jt=(E._WasmDecoder_Check=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.WasmDecoder_Check.apply(null,arguments)},E._WasmDecoder_Init=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.WasmDecoder_Init.apply(null,arguments)},E._WasmDecoder_Deinit=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.WasmDecoder_Deinit.apply(null,arguments)},E._WasmDecoder_OpenChannel=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.WasmDecoder_OpenChannel.apply(null,arguments)},E._WasmDecoder_InputFlvStream=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.WasmDecoder_InputFlvStream.apply(null,arguments)},E._WasmDecoder_GetFlvDecodeData=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.WasmDecoder_GetFlvDecodeData.apply(null,arguments)},E._WasmDecoder_InputFlvStreamAndDecode=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.WasmDecoder_InputFlvStreamAndDecode.apply(null,arguments)},E._WasmDecoder_OpenVideoDecoder=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.WasmDecoder_OpenVideoDecoder.apply(null,arguments)},E._WasmDecoder_OpenAudioDecoder=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.WasmDecoder_OpenAudioDecoder.apply(null,arguments)},E._WasmDecoder_DecodeVideoFrame=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.WasmDecoder_DecodeVideoFrame.apply(null,arguments)},E._WasmDecoder_DecodeAudioFrame=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.WasmDecoder_DecodeAudioFrame.apply(null,arguments)},E._WasmDecoder_CloseChannel=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.WasmDecoder_CloseChannel.apply(null,arguments)},E._main=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.main.apply(null,arguments)},E._malloc=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.malloc.apply(null,arguments)}),Ht=(E._free=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.free.apply(null,arguments)},E.___errno_location=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.__errno_location.apply(null,arguments)},E._fflush=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.fflush.apply(null,arguments)},E.__get_tzname=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm._get_tzname.apply(null,arguments)},E.__get_daylight=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm._get_daylight.apply(null,arguments)},E.__get_timezone=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm._get_timezone.apply(null,arguments)},E._setThrew=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.setThrew.apply(null,arguments)},E.__ZSt18uncaught_exceptionv=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm._ZSt18uncaught_exceptionv.apply(null,arguments)}),Xt=E.stackSave=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.stackSave.apply(null,arguments)},vt=E.stackAlloc=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.stackAlloc.apply(null,arguments)},Vt=E.stackRestore=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.stackRestore.apply(null,arguments)};E.__growWasmMemory=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.__growWasmMemory.apply(null,arguments)},E.dynCall_ii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_ii.apply(null,arguments)},E.dynCall_viiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_viiiii.apply(null,arguments)},E.dynCall_viiiiiifi=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_viiiiiifi.apply(null,arguments)},E.dynCall_viiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_viiii.apply(null,arguments)},E.dynCall_viii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_viii.apply(null,arguments)},E.dynCall_iiiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_iiiiiii.apply(null,arguments)},E.dynCall_iiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_iiiiii.apply(null,arguments)},E.dynCall_vii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_vii.apply(null,arguments)},E.dynCall_iii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_iii.apply(null,arguments)},E.dynCall_viiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_viiiiii.apply(null,arguments)},E.dynCall_viiiiiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_viiiiiiiii.apply(null,arguments)},E.dynCall_viiiiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_viiiiiiii.apply(null,arguments)},E.dynCall_iiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_iiiii.apply(null,arguments)},E.dynCall_viiiiiiiiiiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_viiiiiiiiiiiiii.apply(null,arguments)},E.dynCall_viiiiiiiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_viiiiiiiiiii.apply(null,arguments)},E.dynCall_viiiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_viiiiiii.apply(null,arguments)},E.dynCall_viiiiiiiiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_viiiiiiiiiiii.apply(null,arguments)},E.dynCall_vi=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_vi.apply(null,arguments)},E.dynCall_iiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_iiii.apply(null,arguments)},E.dynCall_viiiifii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_viiiifii.apply(null,arguments)},E.dynCall_fii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_fii.apply(null,arguments)},E.dynCall_viiiiiiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_viiiiiiiiii.apply(null,arguments)},E.dynCall_dd=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_dd.apply(null,arguments)},E.dynCall_viifi=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_viifi.apply(null,arguments)},E.dynCall_fiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_fiii.apply(null,arguments)},E.dynCall_viidi=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_viidi.apply(null,arguments)},E.dynCall_iiiiiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_iiiiiiii.apply(null,arguments)},E.dynCall_viiijj=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_viiijj.apply(null,arguments)},E.dynCall_iiiiiiidiiddii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_iiiiiiidiiddii.apply(null,arguments)},E.dynCall_jij=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_jij.apply(null,arguments)},E.dynCall_jii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_jii.apply(null,arguments)},E.dynCall_v=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_v.apply(null,arguments)},E.dynCall_iidiiii=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_iidiiii.apply(null,arguments)},E.dynCall_jiji=function(){return F(BA,"you need to wait for the runtime to be ready (e.g. wait for main() to be called)"),F(!QA,"the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)"),E.asm.dynCall_jiji.apply(null,arguments)};function Wt(A){this.name="ExitStatus",this.message="Program terminated with exit("+A+")",this.status=A}E.asm=UM,Object.getOwnPropertyDescriptor(E,"intArrayFromString")||(E.intArrayFromString=function(){mA("'intArrayFromString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"intArrayToString")||(E.intArrayToString=function(){mA("'intArrayToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),E.ccall=R,E.cwrap=k,Object.getOwnPropertyDescriptor(E,"setValue")||(E.setValue=function(){mA("'setValue' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"getValue")||(E.getValue=function(){mA("'getValue' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"allocate")||(E.allocate=function(){mA("'allocate' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"getMemory")||(E.getMemory=function(){mA("'getMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you")}),Object.getOwnPropertyDescriptor(E,"AsciiToString")||(E.AsciiToString=function(){mA("'AsciiToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"stringToAscii")||(E.stringToAscii=function(){mA("'stringToAscii' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"UTF8ArrayToString")||(E.UTF8ArrayToString=function(){mA("'UTF8ArrayToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"UTF8ToString")||(E.UTF8ToString=function(){mA("'UTF8ToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"stringToUTF8Array")||(E.stringToUTF8Array=function(){mA("'stringToUTF8Array' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"stringToUTF8")||(E.stringToUTF8=function(){mA("'stringToUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"lengthBytesUTF8")||(E.lengthBytesUTF8=function(){mA("'lengthBytesUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"UTF16ToString")||(E.UTF16ToString=function(){mA("'UTF16ToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"stringToUTF16")||(E.stringToUTF16=function(){mA("'stringToUTF16' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"lengthBytesUTF16")||(E.lengthBytesUTF16=function(){mA("'lengthBytesUTF16' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"UTF32ToString")||(E.UTF32ToString=function(){mA("'UTF32ToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"stringToUTF32")||(E.stringToUTF32=function(){mA("'stringToUTF32' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"lengthBytesUTF32")||(E.lengthBytesUTF32=function(){mA("'lengthBytesUTF32' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"allocateUTF8")||(E.allocateUTF8=function(){mA("'allocateUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"stackTrace")||(E.stackTrace=function(){mA("'stackTrace' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"addOnPreRun")||(E.addOnPreRun=function(){mA("'addOnPreRun' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"addOnInit")||(E.addOnInit=function(){mA("'addOnInit' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"addOnPreMain")||(E.addOnPreMain=function(){mA("'addOnPreMain' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"addOnExit")||(E.addOnExit=function(){mA("'addOnExit' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"addOnPostRun")||(E.addOnPostRun=function(){mA("'addOnPostRun' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"writeStringToMemory")||(E.writeStringToMemory=function(){mA("'writeStringToMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"writeArrayToMemory")||(E.writeArrayToMemory=function(){mA("'writeArrayToMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"writeAsciiToMemory")||(E.writeAsciiToMemory=function(){mA("'writeAsciiToMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"addRunDependency")||(E.addRunDependency=function(){mA("'addRunDependency' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you")}),Object.getOwnPropertyDescriptor(E,"removeRunDependency")||(E.removeRunDependency=function(){mA("'removeRunDependency' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you")}),Object.getOwnPropertyDescriptor(E,"ENV")||(E.ENV=function(){mA("'ENV' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"FS")||(E.FS=function(){mA("'FS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"FS_createFolder")||(E.FS_createFolder=function(){mA("'FS_createFolder' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you")}),Object.getOwnPropertyDescriptor(E,"FS_createPath")||(E.FS_createPath=function(){mA("'FS_createPath' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you")}),Object.getOwnPropertyDescriptor(E,"FS_createDataFile")||(E.FS_createDataFile=function(){mA("'FS_createDataFile' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you")}),Object.getOwnPropertyDescriptor(E,"FS_createPreloadedFile")||(E.FS_createPreloadedFile=function(){mA("'FS_createPreloadedFile' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you")}),Object.getOwnPropertyDescriptor(E,"FS_createLazyFile")||(E.FS_createLazyFile=function(){mA("'FS_createLazyFile' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you")}),Object.getOwnPropertyDescriptor(E,"FS_createLink")||(E.FS_createLink=function(){mA("'FS_createLink' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you")}),Object.getOwnPropertyDescriptor(E,"FS_createDevice")||(E.FS_createDevice=function(){mA("'FS_createDevice' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you")}),Object.getOwnPropertyDescriptor(E,"FS_unlink")||(E.FS_unlink=function(){mA("'FS_unlink' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you")}),Object.getOwnPropertyDescriptor(E,"GL")||(E.GL=function(){mA("'GL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"dynamicAlloc")||(E.dynamicAlloc=function(){mA("'dynamicAlloc' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"loadDynamicLibrary")||(E.loadDynamicLibrary=function(){mA("'loadDynamicLibrary' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"loadWebAssemblyModule")||(E.loadWebAssemblyModule=function(){mA("'loadWebAssemblyModule' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"getLEB")||(E.getLEB=function(){mA("'getLEB' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"getFunctionTables")||(E.getFunctionTables=function(){mA("'getFunctionTables' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"alignFunctionTables")||(E.alignFunctionTables=function(){mA("'alignFunctionTables' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"registerFunctions")||(E.registerFunctions=function(){mA("'registerFunctions' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),E.addFunction=u,Object.getOwnPropertyDescriptor(E,"removeFunction")||(E.removeFunction=function(){mA("'removeFunction' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"getFuncWrapper")||(E.getFuncWrapper=function(){mA("'getFuncWrapper' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"prettyPrint")||(E.prettyPrint=function(){mA("'prettyPrint' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"makeBigInt")||(E.makeBigInt=function(){mA("'makeBigInt' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"dynCall")||(E.dynCall=function(){mA("'dynCall' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"getCompilerSetting")||(E.getCompilerSetting=function(){mA("'getCompilerSetting' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"stackSave")||(E.stackSave=function(){mA("'stackSave' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"stackRestore")||(E.stackRestore=function(){mA("'stackRestore' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"stackAlloc")||(E.stackAlloc=function(){mA("'stackAlloc' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"establishStackSpace")||(E.establishStackSpace=function(){mA("'establishStackSpace' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"print")||(E.print=function(){mA("'print' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"printErr")||(E.printErr=function(){mA("'printErr' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"getTempRet0")||(E.getTempRet0=function(){mA("'getTempRet0' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"setTempRet0")||(E.setTempRet0=function(){mA("'setTempRet0' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"callMain")||(E.callMain=function(){mA("'callMain' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"abort")||(E.abort=function(){mA("'abort' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"Pointer_stringify")||(E.Pointer_stringify=function(){mA("'Pointer_stringify' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),Object.getOwnPropertyDescriptor(E,"warnOnce")||(E.warnOnce=function(){mA("'warnOnce' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}),E.writeStackCookie=EA,E.checkStackCookie=NA,E.abortStackOverflow=nA,Object.getOwnPropertyDescriptor(E,"ALLOC_NORMAL")||Object.defineProperty(E,"ALLOC_NORMAL",{configurable:!0,get:function(){mA("'ALLOC_NORMAL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}}),Object.getOwnPropertyDescriptor(E,"ALLOC_STACK")||Object.defineProperty(E,"ALLOC_STACK",{configurable:!0,get:function(){mA("'ALLOC_STACK' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}}),Object.getOwnPropertyDescriptor(E,"ALLOC_DYNAMIC")||Object.defineProperty(E,"ALLOC_DYNAMIC",{configurable:!0,get:function(){mA("'ALLOC_DYNAMIC' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}}),Object.getOwnPropertyDescriptor(E,"ALLOC_NONE")||Object.defineProperty(E,"ALLOC_NONE",{configurable:!0,get:function(){mA("'ALLOC_NONE' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)")}}),Object.getOwnPropertyDescriptor(E,"calledRun")||Object.defineProperty(E,"calledRun",{configurable:!0,get:function(){mA("'calledRun' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you")}});function Pt(A){F(0==SA,'cannot call main when async dependencies remain! (listen on Module["onRuntimeInitialized"])'),F(0==CA.length,"cannot call main when preRun functions remain to be called");var M=E._main;try{qt(M(0,0),!0)}catch(A){if(A instanceof Wt)return;if("SimulateInfiniteLoop"==A)return void(l=!0);var t=A;A&&"object"==typeof A&&A.stack&&(t=[A,A.stack]),L("exception thrown: "+t),D(1,A)}finally{!0}}function Zt(A){function M(){bt||(bt=!0,O||(hA(),sA(),E.onRuntimeInitialized&&E.onRuntimeInitialized(),_t&&Pt(),wA()))}A=A||N,SA>0||(EA(),aA(),SA>0||(E.setStatus?(E.setStatus("Running..."),setTimeout((function(){setTimeout((function(){E.setStatus("")}),1),M()}),1)):M(),NA()))}function Kt(){var A=j,M=L,t=!1;j=L=function(A){t=!0};try{var g=E._fflush;g&&g(0),["stdout","stderr"].forEach((function(A){var M=iM.analyzePath("/dev/"+A);if(M){var g=M.object.rdev,I=tM.ttys[g];I&&I.output&&I.output.length&&(t=!0)}}))}catch(A){}j=A,L=M,t&&d("stdio streams had content in them that was not flushed. you should set EXIT_RUNTIME to 1 (see the FAQ), or make sure to emit a newline when you printf etc.")}function qt(A,M){Kt(),M&&l&&0===A||(l?M||L("program exited (with status: "+A+"), but EXIT_RUNTIME is not set, so halting execution but not exiting the runtime or preventing further async execution (build with EXIT_RUNTIME=1, if you want a true shutdown)"):(O=!0,A,yA(),E.onExit&&E.onExit(A)),D(A,new Wt(A)))}if(zA=function A(){bt||Zt(),bt||(zA=A)},E.run=Zt,E.preInit)for("function"==typeof E.preInit&&(E.preInit=[E.preInit]);E.preInit.length>0;)E.preInit.pop()();var _t=!0;E.noInitialRun&&(_t=!1),l=!0,Zt()}var n=!1;M.default=function(A){A.isFlvStream=!0,A.enableAudio=!1,A.pause=!1,A.exitFlag=!1,A.needMoreData=!1,A.wasmDecoderId=-1,A.wasmInitVideoDecoder=!1,A.wasmInitAudioDecoder=!1,A.videoQueue=[],A.audioQueue=[],A.videoFrameNum=0,A.videoRawData=null,A.audioRawData=null,A.videoPacketData=null,A.audioPacketData=null,A.findKeyFrame=!0,A.videoIntervalHandle=-1,A.videoIntervalValue=40,A.audioIntervalHandle=-1,A.audioIntervalValue=80,A.lastPTS=0,A.ptsCount=0,A.maxCacheFrameNum=0,A.calcDelayFlag=!0,A.procVideoFlag=!1,A.procAudioFlag=!1;var M=function(){null==A.videoRawData&&(A.videoRawData=E._malloc(8294400)),null==A.audioRawData&&(A.audioRawData=E._malloc(16384)),null==A.videoPacketData&&(A.videoPacketData=E._malloc(1048576)),null==A.audioPacketData&&(A.audioPacketData=E._malloc(2048)),A.wasmDecoderId<0&&(A.wasmDecoderId=E._WasmDecoder_OpenChannel())},t=function(M,t,g,I,e,T,N,n,D){if(A.procVideoFlag)return-1;A.procVideoFlag=!0,E.HEAPU8.set(n,A.videoPacketData);var C,r=i.D,c=0;if(r==i.D?c=0:r==i.B?c=2:r==i.C&&(c=26),1==M&&A.videoFrameNum>0&&(A.videoFrameNum-=1),1==M&&!A.wasmInitVideoDecoder&&g)0==E._WasmDecoder_OpenVideoDecoder(A.wasmDecoderId,t,0,0,c)&&(A.wasmInitVideoDecoder=!0);else if(2==M&&!A.wasmInitAudioDecoder){0==E._WasmDecoder_OpenAudioDecoder(A.wasmDecoderId,t,e,N,T)&&(A.wasmInitAudioDecoder=!0)}var o=null;if(1==M&&A.wasmInitVideoDecoder?o=E._WasmDecoder_DecodeVideoFrame(A.wasmDecoderId,A.videoPacketData,D,0,0,A.videoRawData,0):2==M&&A.wasmInitAudioDecoder&&A.enableAudio&&(o=E._WasmDecoder_DecodeAudioFrame(A.wasmDecoderId,A.videoPacketData,D,A.audioRawData,0)),0!=o&&null!=o){var B=E.HEAPU32[o/4],Q=(E.HEAPU32[o/4+1],E.HEAPU32[o/4+2]),a=(E.HEAPU32[o/4+3],E.HEAPU32[o/4+4]),h=E.HEAPU32[o/4+5],s=(E.HEAPU32[o/4+6],E.HEAPU32[o/4+7]),y=E.HEAPU32[o/4+8],w=E.HEAPU32[o/4+9],j=E.HEAPU32[o/4+10],L=E.HEAPU32[o/4+11],d=(E.HEAPU32[o/4+12],E.HEAPU32[o/4+13]);if(Q<1)return void(A.procVideoFlag=!1);if(1==B){0==c?C=a*h*3/2:2==c?C=a*h*3:(25==c||26==c)&&(C=a*h*4);var x=E.HEAPU8.subarray(A.videoRawData,A.videoRawData+C),Y=new Uint8Array(x),u={cmd:i.o,workerId:i.a,mediaType:1,ts:d,width:a,height:h,framesize:D,rawsize:C,fps:s,interval:y,playTimeSec:I,frameNum:A.videoFrameNum,dat:Y};if(A.postMessage(u,[u.dat.buffer]),a>=1920&&h>=720)return A.procVideoFlag=!1,1}else if(2==B){x=E.HEAPU8.subarray(A.audioRawData,A.audioRawData+Q),Y=new Uint8Array(x);var S=0;8==L?S=0:16==L?S=1:32==L&&(S=2);u={cmd:i.o,workerId:i.a,mediaType:2,fmt:S,ch:w,samplerate:j,ts:d,framesize:Q,dat:Y};A.postMessage(u,[u.dat.buffer])}}return A.procVideoFlag=!1,0},g=function(M,t,g,I,e,T){(E.HEAPU8.set(e,A.audioPacketData),A.wasmInitAudioDecoder)||0==E._WasmDecoder_OpenAudioDecoder(A.wasmDecoderId,M,t,I,g)&&(A.wasmInitAudioDecoder=!0);var N=null;if(A.wasmInitAudioDecoder&&A.enableAudio&&(N=E._WasmDecoder_DecodeAudioFrame(A.wasmDecoderId,A.audioPacketData,T,A.audioRawData,0)),0!=N&&null!=N){E.HEAPU32[N/4],E.HEAPU32[N/4+1];var n=E.HEAPU32[N/4+2],D=(E.HEAPU32[N/4+3],E.HEAPU32[N/4+4],E.HEAPU32[N/4+5],E.HEAPU32[N/4+6],E.HEAPU32[N/4+7],E.HEAPU32[N/4+8],E.HEAPU32[N/4+9]),C=E.HEAPU32[N/4+10],r=E.HEAPU32[N/4+11],c=(E.HEAPU32[N/4+12],E.HEAPU32[N/4+13]);if(n<1)return;var o=E.HEAPU8.subarray(A.audioRawData,A.audioRawData+n),B=new Uint8Array(o),Q=0;8==r?Q=0:16==r?Q=1:32==r&&(Q=2);var a={cmd:i.o,workerId:i.a,mediaType:2,fmt:Q,ch:D,samplerate:C,ts:c,framesize:n,dat:B};A.postMessage(a,[a.dat.buffer])}},I=function(){if(A.wasmDecoderId<0&&M(),A.wasmDecoderId>0&&A.videoQueue.length>0&&!A.pause){var g=null,I=-1;if(A.isFlvStream)g=A.videoQueue[0],I=t(g.mediaType,g.codecId,g.frameType,g.playTimeSec,g.samplerate,g.channelNum,g.bitPerSample,g.dat,g.dat.length);else{if(A.findKeyFrame)for(;A.videoQueue.length>0;){if((g=A.videoQueue[0]).frameType&&A.videoQueue.length<160){A.findKeyFrame=!1;break}A.videoQueue.shift()}else A.videoQueue.length>160&&(A.findKeyFrame=!0);A.videoQueue.length>0&&(null==g&&(g=A.videoQueue[0]),I=t(g.mediaType,g.codecId,g.frameType,g.playTimeSec,g.samplerate,g.channelNum,g.bitPerSample,g.dat,g.dat.length))}I>=0&&A.videoQueue.shift(),A.videoQueue.length>A.maxCacheFrameNum&&A.videoQueue.length}},e=function(){if(!(A.wasmDecoderId<0)&&A.wasmDecoderId>0)for(;A.audioQueue.length>0&&!A.pause;)if(!A.pause){var M=A.audioQueue[0];A.enableAudio&&g(M.codecId,M.samplerate,M.channelNum,M.bitPerSample,M.dat,M.dat.length),A.audioQueue.shift()}};E.onRuntimeInitialized=function(){A.videoIntervalHandle=setInterval(I,A.videoIntervalValue),A.isFlvStream&&(A.audioIntervalHandle=setInterval(e,A.audioIntervalValue));var M={cmd:i.m};A.postMessage(M),n=!0};var D=function(M){if(M>A.lastPTS)if(A.lastPTS>0){if(A.videoQueue.length%5==0){var t=A.ptsCount/5;A.videoQueue.length<=A.maxCacheFrameNum&&(!function(M){if(n){var t=M;(t<5||t>500)&&(t=40),t!=A.videoIntervalValue&&(A.videoIntervalHandle>=0&&(clearInterval(A.videoIntervalHandle),A.videoIntervalHandle=-1),A.videoIntervalValue=Math.floor(t),A.videoIntervalHandle=setInterval(I,A.videoIntervalValue),console.log("SetInterval: "+A.videoIntervalValue))}}(t),console.log("Update interval: "+t)),A.ptsCount=M-A.lastPTS}else A.ptsCount+=M-A.lastPTS;A.lastPTS=M}else A.lastPTS=M,A.ptsCount=40;else A.lastPTS=M,A.ptsCount=40};A.addEventListener("message",(function(t){var g=t.data;switch(g.cmd){case i.u:T=g.url,A.isFlvStream=g.isFlv,N();break;case i.k:A.exitFlag=!0,A.wasmDecoderId>0&&(E._WasmDecoder_CloseChannel(A.wasmDecoderId),A.wasmDecoderId=-1),null!=A.videoRawData&&(E._free(A.videoRawData),A.videoRawData=null),null!=A.audioRawData&&(E._free(A.audioRawData),A.audioRawData=null),null!=A.videoPacketData&&(E._free(A.videoPacketData),A.videoPacketData=null),null!=A.audioPacketData&&(E._free(A.audioPacketData),A.audioPacketData=null),A.videoIntervalHandle>=0&&(clearInterval(A.videoIntervalHandle),A.videoIntervalHandle=-1),A.audioIntervalHandle>=0&&(clearInterval(A.audioIntervalHandle),A.audioIntervalHandle=-1),A.videoQueue.clear,A.videoQueue.length=0,A.videoFrameNum=0,A.audioQueue.clear,A.audioQueue.length=0;var I={cmd:i.k,workerId:i.a};A.postMessage(I);break;case i.t:A.enableAudio=!0;break;case i.j:A.enableAudio=!1;break;case i.v:A.pause=!0;break;case i.y:A.pause=!1;break;case i.z:A.videoQueue.clear,A.videoQueue.length=0,A.videoFrameNum=0,A.audioQueue.clear,A.audioQueue.length=0;break;case i.A:A.updateIntervalTime(g.interval);break;case i.o:if(A.exitFlag){console.log("exit...");break}n?A.isFlvStream?(A.wasmDecoderId<0&&M(),A.wasmDecoderId>0?1==g.mediaType?A.videoIntervalHandle>=0&&(A.videoQueue.push(g),A.maxCacheFrameNum=5):2==g.mediaType&&A.audioIntervalHandle>=0&&!A.findKeyFrame&&A.audioQueue.push(g):A.videoQueue.length<100&&1==g.mediaType&&g.frameType&&A.videoIntervalHandle>=0&&(A.videoQueue.push(g),A.videoQueue.length>=5&&A.videoQueue.shift())):(1==g.mediaType?A.videoIntervalHandle>=0&&(A.videoQueue.push(g),D(g.samplerate),A.videoFrameNum+=1,A.calcDelayFlag&&A.maxCacheFrameNum=0&&A.audioQueue.push(g),A.needMoreData=!1):A.isFlvStream&&A.videoQueue.length<5?1==g.mediaType&&g.frameType&&A.videoIntervalHandle>=0&&(A.videoQueue.push(g),A.videoQueue.length>=5&&A.videoQueue.shift()):!A.isFlvStream&&A.videoQueue.length<1e3&&(1==g.mediaType?A.videoIntervalHandle>=0&&(A.videoQueue.push(g),D(g.samplerate),A.videoFrameNum+=1,A.calcDelayFlag&&A.maxCacheFrameNum=0&&A.audioQueue.push(g))}}))}}.call(this,t(8),"/",t(116)(A),t(9).Buffer)},function(A,M,t){"use strict";var g=t(4).Buffer,I=t(62).Transform;function e(A){I.call(this),this._block=g.allocUnsafe(A),this._blockSize=A,this._blockOffset=0,this._length=[0,0,0,0],this._finalized=!1}t(3)(e,I),e.prototype._transform=function(A,M,t){var g=null;try{this.update(A,M)}catch(A){g=A}t(g)},e.prototype._flush=function(A){var M=null;try{this.push(this.digest())}catch(A){M=A}A(M)},e.prototype.update=function(A,M){if(function(A,M){if(!g.isBuffer(A)&&"string"!=typeof A)throw new TypeError(M+" must be a string or a buffer")}(A,"Data"),this._finalized)throw new Error("Digest already called");g.isBuffer(A)||(A=g.from(A,M));for(var t=this._block,I=0;this._blockOffset+A.length-I>=this._blockSize;){for(var e=this._blockOffset;e0;++i)this._length[i]+=T,(T=this._length[i]/4294967296|0)>0&&(this._length[i]-=4294967296*T);return this},e.prototype._update=function(){throw new Error("_update is not implemented")},e.prototype.digest=function(A){if(this._finalized)throw new Error("Digest already called");this._finalized=!0;var M=this._digest();void 0!==A&&(M=M.toString(A)),this._block.fill(0),this._blockOffset=0;for(var t=0;t<4;++t)this._length[t]=0;return M},e.prototype._digest=function(){throw new Error("_digest is not implemented")},A.exports=e},function(A,M,t){(M=A.exports=t(63)).Stream=M,M.Readable=M,M.Writable=t(67),M.Duplex=t(20),M.Transform=t(69),M.PassThrough=t(129),M.finished=t(38),M.pipeline=t(130)},function(A,M,t){"use strict";(function(M,g){var I;A.exports=d,d.ReadableState=L;t(7).EventEmitter;var e=function(A,M){return A.listeners(M).length},i=t(64),T=t(9).Buffer,E=M.Uint8Array||function(){};var N,n=t(124);N=n&&n.debuglog?n.debuglog("stream"):function(){};var D,C,r,c=t(125),o=t(65),B=t(66).getHighWaterMark,Q=t(19).codes,a=Q.ERR_INVALID_ARG_TYPE,h=Q.ERR_STREAM_PUSH_AFTER_EOF,s=Q.ERR_METHOD_NOT_IMPLEMENTED,y=Q.ERR_STREAM_UNSHIFT_AFTER_END_EVENT;t(3)(d,i);var w=o.errorOrDestroy,j=["error","close","destroy","pause","resume"];function L(A,M,g){I=I||t(20),A=A||{},"boolean"!=typeof g&&(g=M instanceof I),this.objectMode=!!A.objectMode,g&&(this.objectMode=this.objectMode||!!A.readableObjectMode),this.highWaterMark=B(this,A,"readableHighWaterMark",g),this.buffer=new c,this.length=0,this.pipes=null,this.pipesCount=0,this.flowing=null,this.ended=!1,this.endEmitted=!1,this.reading=!1,this.sync=!0,this.needReadable=!1,this.emittedReadable=!1,this.readableListening=!1,this.resumeScheduled=!1,this.paused=!0,this.emitClose=!1!==A.emitClose,this.autoDestroy=!!A.autoDestroy,this.destroyed=!1,this.defaultEncoding=A.defaultEncoding||"utf8",this.awaitDrain=0,this.readingMore=!1,this.decoder=null,this.encoding=null,A.encoding&&(D||(D=t(24).StringDecoder),this.decoder=new D(A.encoding),this.encoding=A.encoding)}function d(A){if(I=I||t(20),!(this instanceof d))return new d(A);var M=this instanceof I;this._readableState=new L(A,this,M),this.readable=!0,A&&("function"==typeof A.read&&(this._read=A.read),"function"==typeof A.destroy&&(this._destroy=A.destroy)),i.call(this)}function x(A,M,t,g,I){N("readableAddChunk",M);var e,i=A._readableState;if(null===M)i.reading=!1,function(A,M){if(N("onEofChunk"),M.ended)return;if(M.decoder){var t=M.decoder.end();t&&t.length&&(M.buffer.push(t),M.length+=M.objectMode?1:t.length)}M.ended=!0,M.sync?S(A):(M.needReadable=!1,M.emittedReadable||(M.emittedReadable=!0,l(A)))}(A,i);else if(I||(e=function(A,M){var t;g=M,T.isBuffer(g)||g instanceof E||"string"==typeof M||void 0===M||A.objectMode||(t=new a("chunk",["string","Buffer","Uint8Array"],M));var g;return t}(i,M)),e)w(A,e);else if(i.objectMode||M&&M.length>0)if("string"==typeof M||i.objectMode||Object.getPrototypeOf(M)===T.prototype||(M=function(A){return T.from(A)}(M)),g)i.endEmitted?w(A,new y):Y(A,i,M,!0);else if(i.ended)w(A,new h);else{if(i.destroyed)return!1;i.reading=!1,i.decoder&&!t?(M=i.decoder.write(M),i.objectMode||0!==M.length?Y(A,i,M,!1):z(A,i)):Y(A,i,M,!1)}else g||(i.reading=!1,z(A,i));return!i.ended&&(i.lengthM.highWaterMark&&(M.highWaterMark=function(A){return A>=1073741824?A=1073741824:(A--,A|=A>>>1,A|=A>>>2,A|=A>>>4,A|=A>>>8,A|=A>>>16,A++),A}(A)),A<=M.length?A:M.ended?M.length:(M.needReadable=!0,0))}function S(A){var M=A._readableState;N("emitReadable",M.needReadable,M.emittedReadable),M.needReadable=!1,M.emittedReadable||(N("emitReadable",M.flowing),M.emittedReadable=!0,g.nextTick(l,A))}function l(A){var M=A._readableState;N("emitReadable_",M.destroyed,M.length,M.ended),M.destroyed||!M.length&&!M.ended||(A.emit("readable"),M.emittedReadable=!1),M.needReadable=!M.flowing&&!M.ended&&M.length<=M.highWaterMark,m(A)}function z(A,M){M.readingMore||(M.readingMore=!0,g.nextTick(U,A,M))}function U(A,M){for(;!M.reading&&!M.ended&&(M.length0,M.resumeScheduled&&!M.paused?M.flowing=!0:A.listenerCount("data")>0&&A.resume()}function O(A){N("readable nexttick read 0"),A.read(0)}function F(A,M){N("resume",M.reading),M.reading||A.read(0),M.resumeScheduled=!1,A.emit("resume"),m(A),M.flowing&&!M.reading&&A.read(0)}function m(A){var M=A._readableState;for(N("flow",M.flowing);M.flowing&&null!==A.read(););}function R(A,M){return 0===M.length?null:(M.objectMode?t=M.buffer.shift():!A||A>=M.length?(t=M.decoder?M.buffer.join(""):1===M.buffer.length?M.buffer.first():M.buffer.concat(M.length),M.buffer.clear()):t=M.buffer.consume(A,M.decoder),t);var t}function k(A){var M=A._readableState;N("endReadable",M.endEmitted),M.endEmitted||(M.ended=!0,g.nextTick(p,M,A))}function p(A,M){if(N("endReadableNT",A.endEmitted,A.length),!A.endEmitted&&0===A.length&&(A.endEmitted=!0,M.readable=!1,M.emit("end"),A.autoDestroy)){var t=M._writableState;(!t||t.autoDestroy&&t.finished)&&M.destroy()}}function b(A,M){for(var t=0,g=A.length;t=M.highWaterMark:M.length>0)||M.ended))return N("read: emitReadable",M.length,M.ended),0===M.length&&M.ended?k(this):S(this),null;if(0===(A=u(A,M))&&M.ended)return 0===M.length&&k(this),null;var g,I=M.needReadable;return N("need readable",I),(0===M.length||M.length-A0?R(A,M):null)?(M.needReadable=M.length<=M.highWaterMark,A=0):(M.length-=A,M.awaitDrain=0),0===M.length&&(M.ended||(M.needReadable=!0),t!==A&&M.ended&&k(this)),null!==g&&this.emit("data",g),g},d.prototype._read=function(A){w(this,new s("_read()"))},d.prototype.pipe=function(A,M){var t=this,I=this._readableState;switch(I.pipesCount){case 0:I.pipes=A;break;case 1:I.pipes=[I.pipes,A];break;default:I.pipes.push(A)}I.pipesCount+=1,N("pipe count=%d opts=%j",I.pipesCount,M);var i=(!M||!1!==M.end)&&A!==g.stdout&&A!==g.stderr?E:B;function T(M,g){N("onunpipe"),M===t&&g&&!1===g.hasUnpiped&&(g.hasUnpiped=!0,N("cleanup"),A.removeListener("close",c),A.removeListener("finish",o),A.removeListener("drain",n),A.removeListener("error",r),A.removeListener("unpipe",T),t.removeListener("end",E),t.removeListener("end",B),t.removeListener("data",C),D=!0,!I.awaitDrain||A._writableState&&!A._writableState.needDrain||n())}function E(){N("onend"),A.end()}I.endEmitted?g.nextTick(i):t.once("end",i),A.on("unpipe",T);var n=function(A){return function(){var M=A._readableState;N("pipeOnDrain",M.awaitDrain),M.awaitDrain&&M.awaitDrain--,0===M.awaitDrain&&e(A,"data")&&(M.flowing=!0,m(A))}}(t);A.on("drain",n);var D=!1;function C(M){N("ondata");var g=A.write(M);N("dest.write",g),!1===g&&((1===I.pipesCount&&I.pipes===A||I.pipesCount>1&&-1!==b(I.pipes,A))&&!D&&(N("false write response, pause",I.awaitDrain),I.awaitDrain++),t.pause())}function r(M){N("onerror",M),B(),A.removeListener("error",r),0===e(A,"error")&&w(A,M)}function c(){A.removeListener("finish",o),B()}function o(){N("onfinish"),A.removeListener("close",c),B()}function B(){N("unpipe"),t.unpipe(A)}return t.on("data",C),function(A,M,t){if("function"==typeof A.prependListener)return A.prependListener(M,t);A._events&&A._events[M]?Array.isArray(A._events[M])?A._events[M].unshift(t):A._events[M]=[t,A._events[M]]:A.on(M,t)}(A,"error",r),A.once("close",c),A.once("finish",o),A.emit("pipe",t),I.flowing||(N("pipe resume"),t.resume()),A},d.prototype.unpipe=function(A){var M=this._readableState,t={hasUnpiped:!1};if(0===M.pipesCount)return this;if(1===M.pipesCount)return A&&A!==M.pipes||(A||(A=M.pipes),M.pipes=null,M.pipesCount=0,M.flowing=!1,A&&A.emit("unpipe",this,t)),this;if(!A){var g=M.pipes,I=M.pipesCount;M.pipes=null,M.pipesCount=0,M.flowing=!1;for(var e=0;e0,!1!==I.flowing&&this.resume()):"readable"===A&&(I.endEmitted||I.readableListening||(I.readableListening=I.needReadable=!0,I.flowing=!1,I.emittedReadable=!1,N("on readable",I.length,I.reading),I.length?S(this):I.reading||g.nextTick(O,this))),t},d.prototype.addListener=d.prototype.on,d.prototype.removeListener=function(A,M){var t=i.prototype.removeListener.call(this,A,M);return"readable"===A&&g.nextTick(f,this),t},d.prototype.removeAllListeners=function(A){var M=i.prototype.removeAllListeners.apply(this,arguments);return"readable"!==A&&void 0!==A||g.nextTick(f,this),M},d.prototype.resume=function(){var A=this._readableState;return A.flowing||(N("resume"),A.flowing=!A.readableListening,function(A,M){M.resumeScheduled||(M.resumeScheduled=!0,g.nextTick(F,A,M))}(this,A)),A.paused=!1,this},d.prototype.pause=function(){return N("call pause flowing=%j",this._readableState.flowing),!1!==this._readableState.flowing&&(N("pause"),this._readableState.flowing=!1,this.emit("pause")),this._readableState.paused=!0,this},d.prototype.wrap=function(A){var M=this,t=this._readableState,g=!1;for(var I in A.on("end",(function(){if(N("wrapped end"),t.decoder&&!t.ended){var A=t.decoder.end();A&&A.length&&M.push(A)}M.push(null)})),A.on("data",(function(I){(N("wrapped data"),t.decoder&&(I=t.decoder.write(I)),t.objectMode&&null==I)||(t.objectMode||I&&I.length)&&(M.push(I)||(g=!0,A.pause()))})),A)void 0===this[I]&&"function"==typeof A[I]&&(this[I]=function(M){return function(){return A[M].apply(A,arguments)}}(I));for(var e=0;e-1))throw new y(A);return this._writableState.defaultEncoding=A,this},Object.defineProperty(d.prototype,"writableBuffer",{enumerable:!1,get:function(){return this._writableState&&this._writableState.getBuffer()}}),Object.defineProperty(d.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),d.prototype._write=function(A,M,t){t(new o("_write()"))},d.prototype._writev=null,d.prototype.end=function(A,M,t){var I=this._writableState;return"function"==typeof A?(t=A,A=null,M=null):"function"==typeof M&&(t=M,M=null),null!=A&&this.write(A,M),I.corked&&(I.corked=1,this.uncork()),I.ending||function(A,M,t){M.ending=!0,z(A,M),t&&(M.finished?g.nextTick(t):A.once("finish",t));M.ended=!0,A.writable=!1}(this,I,t),this},Object.defineProperty(d.prototype,"writableLength",{enumerable:!1,get:function(){return this._writableState.length}}),Object.defineProperty(d.prototype,"destroyed",{enumerable:!1,get:function(){return void 0!==this._writableState&&this._writableState.destroyed},set:function(A){this._writableState&&(this._writableState.destroyed=A)}}),d.prototype.destroy=D.destroy,d.prototype._undestroy=D.undestroy,d.prototype._destroy=function(A,M){M(A)}}).call(this,t(10),t(8))},function(A,M,t){(function(M){function t(A){try{if(!M.localStorage)return!1}catch(A){return!1}var t=M.localStorage[A];return null!=t&&"true"===String(t).toLowerCase()}A.exports=function(A,M){if(t("noDeprecation"))return A;var g=!1;return function(){if(!g){if(t("throwDeprecation"))throw new Error(M);t("traceDeprecation")?console.trace(M):console.warn(M),g=!0}return A.apply(this,arguments)}}}).call(this,t(10))},function(A,M,t){"use strict";A.exports=n;var g=t(19).codes,I=g.ERR_METHOD_NOT_IMPLEMENTED,e=g.ERR_MULTIPLE_CALLBACK,i=g.ERR_TRANSFORM_ALREADY_TRANSFORMING,T=g.ERR_TRANSFORM_WITH_LENGTH_0,E=t(20);function N(A,M){var t=this._transformState;t.transforming=!1;var g=t.writecb;if(null===g)return this.emit("error",new e);t.writechunk=null,t.writecb=null,null!=M&&this.push(M),g(A);var I=this._readableState;I.reading=!1,(I.needReadable||I.length>>2|A<<30)^(A>>>13|A<<19)^(A>>>22|A<<10)}function C(A){return(A>>>6|A<<26)^(A>>>11|A<<21)^(A>>>25|A<<7)}function r(A){return(A>>>7|A<<25)^(A>>>18|A<<14)^A>>>3}g(E,I),E.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},E.prototype._update=function(A){for(var M,t=this._w,g=0|this._a,I=0|this._b,e=0|this._c,T=0|this._d,E=0|this._e,c=0|this._f,o=0|this._g,B=0|this._h,Q=0;Q<16;++Q)t[Q]=A.readInt32BE(4*Q);for(;Q<64;++Q)t[Q]=0|(((M=t[Q-2])>>>17|M<<15)^(M>>>19|M<<13)^M>>>10)+t[Q-7]+r(t[Q-15])+t[Q-16];for(var a=0;a<64;++a){var h=B+C(E)+N(E,c,o)+i[a]+t[a]|0,s=D(g)+n(g,I,e)|0;B=o,o=c,c=E,E=T+h|0,T=e,e=I,I=g,g=h+s|0}this._a=g+this._a|0,this._b=I+this._b|0,this._c=e+this._c|0,this._d=T+this._d|0,this._e=E+this._e|0,this._f=c+this._f|0,this._g=o+this._g|0,this._h=B+this._h|0},E.prototype._hash=function(){var A=e.allocUnsafe(32);return A.writeInt32BE(this._a,0),A.writeInt32BE(this._b,4),A.writeInt32BE(this._c,8),A.writeInt32BE(this._d,12),A.writeInt32BE(this._e,16),A.writeInt32BE(this._f,20),A.writeInt32BE(this._g,24),A.writeInt32BE(this._h,28),A},A.exports=E},function(A,M,t){var g=t(3),I=t(21),e=t(4).Buffer,i=[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],T=new Array(160);function E(){this.init(),this._w=T,I.call(this,128,112)}function N(A,M,t){return t^A&(M^t)}function n(A,M,t){return A&M|t&(A|M)}function D(A,M){return(A>>>28|M<<4)^(M>>>2|A<<30)^(M>>>7|A<<25)}function C(A,M){return(A>>>14|M<<18)^(A>>>18|M<<14)^(M>>>9|A<<23)}function r(A,M){return(A>>>1|M<<31)^(A>>>8|M<<24)^A>>>7}function c(A,M){return(A>>>1|M<<31)^(A>>>8|M<<24)^(A>>>7|M<<25)}function o(A,M){return(A>>>19|M<<13)^(M>>>29|A<<3)^A>>>6}function B(A,M){return(A>>>19|M<<13)^(M>>>29|A<<3)^(A>>>6|M<<26)}function Q(A,M){return A>>>0>>0?1:0}g(E,I),E.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},E.prototype._update=function(A){for(var M=this._w,t=0|this._ah,g=0|this._bh,I=0|this._ch,e=0|this._dh,T=0|this._eh,E=0|this._fh,a=0|this._gh,h=0|this._hh,s=0|this._al,y=0|this._bl,w=0|this._cl,j=0|this._dl,L=0|this._el,d=0|this._fl,x=0|this._gl,Y=0|this._hl,u=0;u<32;u+=2)M[u]=A.readInt32BE(4*u),M[u+1]=A.readInt32BE(4*u+4);for(;u<160;u+=2){var S=M[u-30],l=M[u-30+1],z=r(S,l),U=c(l,S),f=o(S=M[u-4],l=M[u-4+1]),O=B(l,S),F=M[u-14],m=M[u-14+1],R=M[u-32],k=M[u-32+1],p=U+m|0,b=z+F+Q(p,U)|0;b=(b=b+f+Q(p=p+O|0,O)|0)+R+Q(p=p+k|0,k)|0,M[u]=b,M[u+1]=p}for(var G=0;G<160;G+=2){b=M[G],p=M[G+1];var J=n(t,g,I),H=n(s,y,w),X=D(t,s),v=D(s,t),V=C(T,L),W=C(L,T),P=i[G],Z=i[G+1],K=N(T,E,a),q=N(L,d,x),_=Y+W|0,$=h+V+Q(_,Y)|0;$=($=($=$+K+Q(_=_+q|0,q)|0)+P+Q(_=_+Z|0,Z)|0)+b+Q(_=_+p|0,p)|0;var AA=v+H|0,MA=X+J+Q(AA,v)|0;h=a,Y=x,a=E,x=d,E=T,d=L,T=e+$+Q(L=j+_|0,j)|0,e=I,j=w,I=g,w=y,g=t,y=s,t=$+MA+Q(s=_+AA|0,_)|0}this._al=this._al+s|0,this._bl=this._bl+y|0,this._cl=this._cl+w|0,this._dl=this._dl+j|0,this._el=this._el+L|0,this._fl=this._fl+d|0,this._gl=this._gl+x|0,this._hl=this._hl+Y|0,this._ah=this._ah+t+Q(this._al,s)|0,this._bh=this._bh+g+Q(this._bl,y)|0,this._ch=this._ch+I+Q(this._cl,w)|0,this._dh=this._dh+e+Q(this._dl,j)|0,this._eh=this._eh+T+Q(this._el,L)|0,this._fh=this._fh+E+Q(this._fl,d)|0,this._gh=this._gh+a+Q(this._gl,x)|0,this._hh=this._hh+h+Q(this._hl,Y)|0},E.prototype._hash=function(){var A=e.allocUnsafe(64);function M(M,t,g){A.writeInt32BE(M,g),A.writeInt32BE(t,g+4)}return M(this._ah,this._al,0),M(this._bh,this._bl,8),M(this._ch,this._cl,16),M(this._dh,this._dl,24),M(this._eh,this._el,32),M(this._fh,this._fl,40),M(this._gh,this._gl,48),M(this._hh,this._hl,56),A},A.exports=E},function(A,M,t){"use strict";(function(M,g){var I=t(31);A.exports=h;var e,i=t(59);h.ReadableState=a;t(7).EventEmitter;var T=function(A,M){return A.listeners(M).length},E=t(73),N=t(42).Buffer,n=M.Uint8Array||function(){};var D=Object.create(t(25));D.inherits=t(3);var C=t(136),r=void 0;r=C&&C.debuglog?C.debuglog("stream"):function(){};var c,o=t(137),B=t(74);D.inherits(h,E);var Q=["error","close","destroy","pause","resume"];function a(A,M){A=A||{};var g=M instanceof(e=e||t(16));this.objectMode=!!A.objectMode,g&&(this.objectMode=this.objectMode||!!A.readableObjectMode);var I=A.highWaterMark,i=A.readableHighWaterMark,T=this.objectMode?16:16384;this.highWaterMark=I||0===I?I:g&&(i||0===i)?i:T,this.highWaterMark=Math.floor(this.highWaterMark),this.buffer=new o,this.length=0,this.pipes=null,this.pipesCount=0,this.flowing=null,this.ended=!1,this.endEmitted=!1,this.reading=!1,this.sync=!0,this.needReadable=!1,this.emittedReadable=!1,this.readableListening=!1,this.resumeScheduled=!1,this.destroyed=!1,this.defaultEncoding=A.defaultEncoding||"utf8",this.awaitDrain=0,this.readingMore=!1,this.decoder=null,this.encoding=null,A.encoding&&(c||(c=t(24).StringDecoder),this.decoder=new c(A.encoding),this.encoding=A.encoding)}function h(A){if(e=e||t(16),!(this instanceof h))return new h(A);this._readableState=new a(A,this),this.readable=!0,A&&("function"==typeof A.read&&(this._read=A.read),"function"==typeof A.destroy&&(this._destroy=A.destroy)),E.call(this)}function s(A,M,t,g,I){var e,i=A._readableState;null===M?(i.reading=!1,function(A,M){if(M.ended)return;if(M.decoder){var t=M.decoder.end();t&&t.length&&(M.buffer.push(t),M.length+=M.objectMode?1:t.length)}M.ended=!0,j(A)}(A,i)):(I||(e=function(A,M){var t;g=M,N.isBuffer(g)||g instanceof n||"string"==typeof M||void 0===M||A.objectMode||(t=new TypeError("Invalid non-string/buffer chunk"));var g;return t}(i,M)),e?A.emit("error",e):i.objectMode||M&&M.length>0?("string"==typeof M||i.objectMode||Object.getPrototypeOf(M)===N.prototype||(M=function(A){return N.from(A)}(M)),g?i.endEmitted?A.emit("error",new Error("stream.unshift() after end event")):y(A,i,M,!0):i.ended?A.emit("error",new Error("stream.push() after EOF")):(i.reading=!1,i.decoder&&!t?(M=i.decoder.write(M),i.objectMode||0!==M.length?y(A,i,M,!1):d(A,i)):y(A,i,M,!1))):g||(i.reading=!1));return function(A){return!A.ended&&(A.needReadable||A.lengthM.highWaterMark&&(M.highWaterMark=function(A){return A>=8388608?A=8388608:(A--,A|=A>>>1,A|=A>>>2,A|=A>>>4,A|=A>>>8,A|=A>>>16,A++),A}(A)),A<=M.length?A:M.ended?M.length:(M.needReadable=!0,0))}function j(A){var M=A._readableState;M.needReadable=!1,M.emittedReadable||(r("emitReadable",M.flowing),M.emittedReadable=!0,M.sync?I.nextTick(L,A):L(A))}function L(A){r("emit readable"),A.emit("readable"),S(A)}function d(A,M){M.readingMore||(M.readingMore=!0,I.nextTick(x,A,M))}function x(A,M){for(var t=M.length;!M.reading&&!M.flowing&&!M.ended&&M.length=M.length?(t=M.decoder?M.buffer.join(""):1===M.buffer.length?M.buffer.head.data:M.buffer.concat(M.length),M.buffer.clear()):t=function(A,M,t){var g;Ae.length?e.length:A;if(i===e.length?I+=e:I+=e.slice(0,A),0===(A-=i)){i===e.length?(++g,t.next?M.head=t.next:M.head=M.tail=null):(M.head=t,t.data=e.slice(i));break}++g}return M.length-=g,I}(A,M):function(A,M){var t=N.allocUnsafe(A),g=M.head,I=1;g.data.copy(t),A-=g.data.length;for(;g=g.next;){var e=g.data,i=A>e.length?e.length:A;if(e.copy(t,t.length-A,0,i),0===(A-=i)){i===e.length?(++I,g.next?M.head=g.next:M.head=M.tail=null):(M.head=g,g.data=e.slice(i));break}++I}return M.length-=I,t}(A,M);return g}(A,M.buffer,M.decoder),t);var t}function z(A){var M=A._readableState;if(M.length>0)throw new Error('"endReadable()" called on non-empty stream');M.endEmitted||(M.ended=!0,I.nextTick(U,M,A))}function U(A,M){A.endEmitted||0!==A.length||(A.endEmitted=!0,M.readable=!1,M.emit("end"))}function f(A,M){for(var t=0,g=A.length;t=M.highWaterMark||M.ended))return r("read: emitReadable",M.length,M.ended),0===M.length&&M.ended?z(this):j(this),null;if(0===(A=w(A,M))&&M.ended)return 0===M.length&&z(this),null;var g,I=M.needReadable;return r("need readable",I),(0===M.length||M.length-A0?l(A,M):null)?(M.needReadable=!0,A=0):M.length-=A,0===M.length&&(M.ended||(M.needReadable=!0),t!==A&&M.ended&&z(this)),null!==g&&this.emit("data",g),g},h.prototype._read=function(A){this.emit("error",new Error("_read() is not implemented"))},h.prototype.pipe=function(A,M){var t=this,e=this._readableState;switch(e.pipesCount){case 0:e.pipes=A;break;case 1:e.pipes=[e.pipes,A];break;default:e.pipes.push(A)}e.pipesCount+=1,r("pipe count=%d opts=%j",e.pipesCount,M);var E=(!M||!1!==M.end)&&A!==g.stdout&&A!==g.stderr?n:h;function N(M,g){r("onunpipe"),M===t&&g&&!1===g.hasUnpiped&&(g.hasUnpiped=!0,r("cleanup"),A.removeListener("close",Q),A.removeListener("finish",a),A.removeListener("drain",D),A.removeListener("error",B),A.removeListener("unpipe",N),t.removeListener("end",n),t.removeListener("end",h),t.removeListener("data",o),C=!0,!e.awaitDrain||A._writableState&&!A._writableState.needDrain||D())}function n(){r("onend"),A.end()}e.endEmitted?I.nextTick(E):t.once("end",E),A.on("unpipe",N);var D=function(A){return function(){var M=A._readableState;r("pipeOnDrain",M.awaitDrain),M.awaitDrain&&M.awaitDrain--,0===M.awaitDrain&&T(A,"data")&&(M.flowing=!0,S(A))}}(t);A.on("drain",D);var C=!1;var c=!1;function o(M){r("ondata"),c=!1,!1!==A.write(M)||c||((1===e.pipesCount&&e.pipes===A||e.pipesCount>1&&-1!==f(e.pipes,A))&&!C&&(r("false write response, pause",t._readableState.awaitDrain),t._readableState.awaitDrain++,c=!0),t.pause())}function B(M){r("onerror",M),h(),A.removeListener("error",B),0===T(A,"error")&&A.emit("error",M)}function Q(){A.removeListener("finish",a),h()}function a(){r("onfinish"),A.removeListener("close",Q),h()}function h(){r("unpipe"),t.unpipe(A)}return t.on("data",o),function(A,M,t){if("function"==typeof A.prependListener)return A.prependListener(M,t);A._events&&A._events[M]?i(A._events[M])?A._events[M].unshift(t):A._events[M]=[t,A._events[M]]:A.on(M,t)}(A,"error",B),A.once("close",Q),A.once("finish",a),A.emit("pipe",t),e.flowing||(r("pipe resume"),t.resume()),A},h.prototype.unpipe=function(A){var M=this._readableState,t={hasUnpiped:!1};if(0===M.pipesCount)return this;if(1===M.pipesCount)return A&&A!==M.pipes||(A||(A=M.pipes),M.pipes=null,M.pipesCount=0,M.flowing=!1,A&&A.emit("unpipe",this,t)),this;if(!A){var g=M.pipes,I=M.pipesCount;M.pipes=null,M.pipesCount=0,M.flowing=!1;for(var e=0;et)?M=("rmd160"===A?new E:N(A)).update(M).digest():M.lengtht||M!=M)throw new TypeError("Bad key length")}},function(A,M,t){(function(M){var t;if(M.browser)t="utf-8";else if(M.version){t=parseInt(M.version.split(".")[0].slice(1),10)>=6?"utf-8":"binary"}else t="utf-8";A.exports=t}).call(this,t(8))},function(A,M,t){var g=t(77),I=t(39),e=t(40),i=t(4).Buffer,T=t(80),E=t(81),N=t(83),n=i.alloc(128),D={md5:16,sha1:20,sha224:28,sha256:32,sha384:48,sha512:64,rmd160:20,ripemd160:20};function C(A,M,t){var T=function(A){function M(M){return e(A).update(M).digest()}return"rmd160"===A||"ripemd160"===A?function(A){return(new I).update(A).digest()}:"md5"===A?g:M}(A),E="sha512"===A||"sha384"===A?128:64;M.length>E?M=T(M):M.length>>0},M.writeUInt32BE=function(A,M,t){A[0+t]=M>>>24,A[1+t]=M>>>16&255,A[2+t]=M>>>8&255,A[3+t]=255&M},M.ip=function(A,M,t,g){for(var I=0,e=0,i=6;i>=0;i-=2){for(var T=0;T<=24;T+=8)I<<=1,I|=M>>>T+i&1;for(T=0;T<=24;T+=8)I<<=1,I|=A>>>T+i&1}for(i=6;i>=0;i-=2){for(T=1;T<=25;T+=8)e<<=1,e|=M>>>T+i&1;for(T=1;T<=25;T+=8)e<<=1,e|=A>>>T+i&1}t[g+0]=I>>>0,t[g+1]=e>>>0},M.rip=function(A,M,t,g){for(var I=0,e=0,i=0;i<4;i++)for(var T=24;T>=0;T-=8)I<<=1,I|=M>>>T+i&1,I<<=1,I|=A>>>T+i&1;for(i=4;i<8;i++)for(T=24;T>=0;T-=8)e<<=1,e|=M>>>T+i&1,e<<=1,e|=A>>>T+i&1;t[g+0]=I>>>0,t[g+1]=e>>>0},M.pc1=function(A,M,t,g){for(var I=0,e=0,i=7;i>=5;i--){for(var T=0;T<=24;T+=8)I<<=1,I|=M>>T+i&1;for(T=0;T<=24;T+=8)I<<=1,I|=A>>T+i&1}for(T=0;T<=24;T+=8)I<<=1,I|=M>>T+i&1;for(i=1;i<=3;i++){for(T=0;T<=24;T+=8)e<<=1,e|=M>>T+i&1;for(T=0;T<=24;T+=8)e<<=1,e|=A>>T+i&1}for(T=0;T<=24;T+=8)e<<=1,e|=A>>T+i&1;t[g+0]=I>>>0,t[g+1]=e>>>0},M.r28shl=function(A,M){return A<>>28-M};var g=[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];M.pc2=function(A,M,t,I){for(var e=0,i=0,T=g.length>>>1,E=0;E>>g[E]&1;for(E=T;E>>g[E]&1;t[I+0]=e>>>0,t[I+1]=i>>>0},M.expand=function(A,M,t){var g=0,I=0;g=(1&A)<<5|A>>>27;for(var e=23;e>=15;e-=4)g<<=6,g|=A>>>e&63;for(e=11;e>=3;e-=4)I|=A>>>e&63,I<<=6;I|=(31&A)<<1|A>>>31,M[t+0]=g>>>0,M[t+1]=I>>>0};var I=[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];M.substitute=function(A,M){for(var t=0,g=0;g<4;g++){t<<=4,t|=I[64*g+(A>>>18-6*g&63)]}for(g=0;g<4;g++){t<<=4,t|=I[256+64*g+(M>>>18-6*g&63)]}return t>>>0};var e=[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];M.permute=function(A){for(var M=0,t=0;t>>e[t]&1;return M>>>0},M.padSplit=function(A,M,t){for(var g=A.toString(2);g.length>>1];t=e.r28shl(t,T),I=e.r28shl(I,T),e.pc2(t,I,A.keys,i)}},E.prototype._update=function(A,M,t,g){var I=this._desState,i=e.readUInt32BE(A,M),T=e.readUInt32BE(A,M+4);e.ip(i,T,I.tmp,0),i=I.tmp[0],T=I.tmp[1],"encrypt"===this.type?this._encrypt(I,i,T,I.tmp,0):this._decrypt(I,i,T,I.tmp,0),i=I.tmp[0],T=I.tmp[1],e.writeUInt32BE(t,i,g),e.writeUInt32BE(t,T,g+4)},E.prototype._pad=function(A,M){for(var t=A.length-M,g=M;g>>0,i=C}e.rip(T,i,g,I)},E.prototype._decrypt=function(A,M,t,g,I){for(var i=t,T=M,E=A.keys.length-2;E>=0;E-=2){var N=A.keys[E],n=A.keys[E+1];e.expand(i,A.tmp,0),N^=A.tmp[0],n^=A.tmp[1];var D=e.substitute(N,n),C=i;i=(T^e.permute(D))>>>0,T=C}e.rip(i,T,g,I)}},function(A,M,t){var g=t(26),I=t(4).Buffer,e=t(87);function i(A){var M=A._cipher.encryptBlockRaw(A._prev);return e(A._prev),M}M.encrypt=function(A,M){var t=Math.ceil(M.length/16),e=A._cache.length;A._cache=I.concat([A._cache,I.allocUnsafe(16*t)]);for(var T=0;TA;)t.ishrn(1);if(t.isEven()&&t.iadd(T),t.testn(1)||t.iadd(E),M.cmp(E)){if(!M.cmp(N))for(;t.mod(n).cmp(D);)t.iadd(r)}else for(;t.mod(e).cmp(C);)t.iadd(r);if(B(c=t.shrn(1))&&B(t)&&Q(c)&&Q(t)&&i.test(c)&&i.test(t))return t}}},function(A,M){A.exports=function(A){return A.webpackPolyfill||(A.deprecate=function(){},A.paths=[],A.children||(A.children=[]),Object.defineProperty(A,"loaded",{enumerable:!0,get:function(){return A.l}}),Object.defineProperty(A,"id",{enumerable:!0,get:function(){return A.i}}),A.webpackPolyfill=1),A}},function(A,M,t){var g=t(6),I=t(47);function e(A){this.rand=A||new I.Rand}A.exports=e,e.create=function(A){return new e(A)},e.prototype._randbelow=function(A){var M=A.bitLength(),t=Math.ceil(M/8);do{var I=new g(this.rand.generate(t))}while(I.cmp(A)>=0);return I},e.prototype._randrange=function(A,M){var t=M.sub(A);return A.add(this._randbelow(t))},e.prototype.test=function(A,M,t){var I=A.bitLength(),e=g.mont(A),i=new g(1).toRed(e);M||(M=Math.max(1,I/48|0));for(var T=A.subn(1),E=0;!T.testn(E);E++);for(var N=A.shrn(E),n=T.toRed(e);M>0;M--){var D=this._randrange(new g(2),T);t&&t(D);var C=D.toRed(e).redPow(N);if(0!==C.cmp(i)&&0!==C.cmp(n)){for(var r=1;r0;M--){var n=this._randrange(new g(2),i),D=A.gcd(n);if(0!==D.cmpn(1))return D;var C=n.toRed(I).redPow(E);if(0!==C.cmp(e)&&0!==C.cmp(N)){for(var r=1;r>8,i=255&I;e?t.push(e,i):t.push(i)}return t},g.zero2=I,g.toHex=e,g.encode=function(A,M){return"hex"===M?e(A):A}},function(A,M,t){"use strict";var g=M;g.base=t(34),g.short=t(172),g.mont=t(173),g.edwards=t(174)},function(A,M,t){"use strict";var g=t(14).rotr32;function I(A,M,t){return A&M^~A&t}function e(A,M,t){return A&M^A&t^M&t}function i(A,M,t){return A^M^t}M.ft_1=function(A,M,t,g){return 0===A?I(M,t,g):1===A||3===A?i(M,t,g):2===A?e(M,t,g):void 0},M.ch32=I,M.maj32=e,M.p32=i,M.s0_256=function(A){return g(A,2)^g(A,13)^g(A,22)},M.s1_256=function(A){return g(A,6)^g(A,11)^g(A,25)},M.g0_256=function(A){return g(A,7)^g(A,18)^A>>>3},M.g1_256=function(A){return g(A,17)^g(A,19)^A>>>10}},function(A,M,t){"use strict";var g=t(14),I=t(27),e=t(96),i=t(11),T=g.sum32,E=g.sum32_4,N=g.sum32_5,n=e.ch32,D=e.maj32,C=e.s0_256,r=e.s1_256,c=e.g0_256,o=e.g1_256,B=I.BlockHash,Q=[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];function a(){if(!(this instanceof a))return new a;B.call(this),this.h=[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225],this.k=Q,this.W=new Array(64)}g.inherits(a,B),A.exports=a,a.blockSize=512,a.outSize=256,a.hmacStrength=192,a.padLength=64,a.prototype._update=function(A,M){for(var t=this.W,g=0;g<16;g++)t[g]=A[M+g];for(;g=49&&N<=54?N-49+10:N>=17&&N<=22?N-17+10:N,i|=E}return g(!(240&i),"Invalid character in "+A),I}function E(A,M,t,I){for(var e=0,i=0,T=Math.min(A.length,t),E=M;E=49?N-49+10:N>=17?N-17+10:N,g(N>=0&&i0?A:M},e.min=function(A,M){return A.cmp(M)<0?A:M},e.prototype._init=function(A,M,t){if("number"==typeof A)return this._initNumber(A,M,t);if("object"==typeof A)return this._initArray(A,M,t);"hex"===M&&(M=16),g(M===(0|M)&&M>=2&&M<=36);var I=0;"-"===(A=A.toString().replace(/\s+/g,""))[0]&&I++,16===M?this._parseHex(A,I):this._parseBase(A,M,I),"-"===A[0]&&(this.negative=1),this._strip(),"le"===t&&this._initArray(this.toArray(),M,t)},e.prototype._initNumber=function(A,M,t){A<0&&(this.negative=1,A=-A),A<67108864?(this.words=[67108863&A],this.length=1):A<4503599627370496?(this.words=[67108863&A,A/67108864&67108863],this.length=2):(g(A<9007199254740992),this.words=[67108863&A,A/67108864&67108863,1],this.length=3),"le"===t&&this._initArray(this.toArray(),M,t)},e.prototype._initArray=function(A,M,t){if(g("number"==typeof A.length),A.length<=0)return this.words=[0],this.length=1,this;this.length=Math.ceil(A.length/3),this.words=new Array(this.length);for(var I=0;I=0;I-=3)i=A[I]|A[I-1]<<8|A[I-2]<<16,this.words[e]|=i<>>26-T&67108863,(T+=24)>=26&&(T-=26,e++);else if("le"===t)for(I=0,e=0;I>>26-T&67108863,(T+=24)>=26&&(T-=26,e++);return this._strip()},e.prototype._parseHex=function(A,M){this.length=Math.ceil((A.length-M)/6),this.words=new Array(this.length);for(var t=0;t=M;t-=6)I=T(A,t,t+6),this.words[g]|=I<>>26-e&4194303,(e+=24)>=26&&(e-=26,g++);t+6!==M&&(I=T(A,M,t+6),this.words[g]|=I<>>26-e&4194303),this._strip()},e.prototype._parseBase=function(A,M,t){this.words=[0],this.length=1;for(var g=0,I=1;I<=67108863;I*=M)g++;g--,I=I/M|0;for(var e=A.length-t,i=e%g,T=Math.min(e,e-i)+t,N=0,n=t;n1&&0===this.words[this.length-1];)this.length--;return this._normSign()},e.prototype._normSign=function(){return 1===this.length&&0===this.words[0]&&(this.negative=0),this},"undefined"!=typeof Symbol&&"function"==typeof Symbol.for)try{e.prototype[Symbol.for("nodejs.util.inspect.custom")]=n}catch(A){e.prototype.inspect=n}else e.prototype.inspect=n;function n(){return(this.red?""}var D=["","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"],C=[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],r=[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];e.prototype.toString=function(A,M){var t;if(M=0|M||1,16===(A=A||10)||"hex"===A){t="";for(var I=0,e=0,i=0;i>>24-I&16777215)||i!==this.length-1?D[6-E.length]+E+t:E+t,(I+=2)>=26&&(I-=26,i--)}for(0!==e&&(t=e.toString(16)+t);t.length%M!=0;)t="0"+t;return 0!==this.negative&&(t="-"+t),t}if(A===(0|A)&&A>=2&&A<=36){var N=C[A],n=r[A];t="";var c=this.clone();for(c.negative=0;!c.isZero();){var o=c.modrn(n).toString(A);t=(c=c.idivn(n)).isZero()?o+t:D[N-o.length]+o+t}for(this.isZero()&&(t="0"+t);t.length%M!=0;)t="0"+t;return 0!==this.negative&&(t="-"+t),t}g(!1,"Base should be between 2 and 36")},e.prototype.toNumber=function(){var A=this.words[0];return 2===this.length?A+=67108864*this.words[1]:3===this.length&&1===this.words[2]?A+=4503599627370496+67108864*this.words[1]:this.length>2&&g(!1,"Number can only safely store up to 53 bits"),0!==this.negative?-A:A},e.prototype.toJSON=function(){return this.toString(16,2)},i&&(e.prototype.toBuffer=function(A,M){return this.toArrayLike(i,A,M)}),e.prototype.toArray=function(A,M){return this.toArrayLike(Array,A,M)};function c(A,M,t){t.negative=M.negative^A.negative;var g=A.length+M.length|0;t.length=g,g=g-1|0;var I=0|A.words[0],e=0|M.words[0],i=I*e,T=67108863&i,E=i/67108864|0;t.words[0]=T;for(var N=1;N>>26,D=67108863&E,C=Math.min(N,M.length-1),r=Math.max(0,N-A.length+1);r<=C;r++){var c=N-r|0;n+=(i=(I=0|A.words[c])*(e=0|M.words[r])+D)/67108864|0,D=67108863&i}t.words[N]=0|D,E=0|n}return 0!==E?t.words[N]=0|E:t.length--,t._strip()}e.prototype.toArrayLike=function(A,M,t){this._strip();var I=this.byteLength(),e=t||Math.max(1,I);g(I<=e,"byte array longer than desired length"),g(e>0,"Requested array length <= 0");var i=function(A,M){return A.allocUnsafe?A.allocUnsafe(M):new A(M)}(A,e);return this["_toArrayLike"+("le"===M?"LE":"BE")](i,I),i},e.prototype._toArrayLikeLE=function(A,M){for(var t=0,g=0,I=0,e=0;I>8&255),t>16&255),6===e?(t>24&255),g=0,e=0):(g=i>>>24,e+=2)}if(t=0&&(A[t--]=i>>8&255),t>=0&&(A[t--]=i>>16&255),6===e?(t>=0&&(A[t--]=i>>24&255),g=0,e=0):(g=i>>>24,e+=2)}if(t>=0)for(A[t--]=g;t>=0;)A[t--]=0},Math.clz32?e.prototype._countBits=function(A){return 32-Math.clz32(A)}:e.prototype._countBits=function(A){var M=A,t=0;return M>=4096&&(t+=13,M>>>=13),M>=64&&(t+=7,M>>>=7),M>=8&&(t+=4,M>>>=4),M>=2&&(t+=2,M>>>=2),t+M},e.prototype._zeroBits=function(A){if(0===A)return 26;var M=A,t=0;return 0==(8191&M)&&(t+=13,M>>>=13),0==(127&M)&&(t+=7,M>>>=7),0==(15&M)&&(t+=4,M>>>=4),0==(3&M)&&(t+=2,M>>>=2),0==(1&M)&&t++,t},e.prototype.bitLength=function(){var A=this.words[this.length-1],M=this._countBits(A);return 26*(this.length-1)+M},e.prototype.zeroBits=function(){if(this.isZero())return 0;for(var A=0,M=0;MA.length?this.clone().ior(A):A.clone().ior(this)},e.prototype.uor=function(A){return this.length>A.length?this.clone().iuor(A):A.clone().iuor(this)},e.prototype.iuand=function(A){var M;M=this.length>A.length?A:this;for(var t=0;tA.length?this.clone().iand(A):A.clone().iand(this)},e.prototype.uand=function(A){return this.length>A.length?this.clone().iuand(A):A.clone().iuand(this)},e.prototype.iuxor=function(A){var M,t;this.length>A.length?(M=this,t=A):(M=A,t=this);for(var g=0;gA.length?this.clone().ixor(A):A.clone().ixor(this)},e.prototype.uxor=function(A){return this.length>A.length?this.clone().iuxor(A):A.clone().iuxor(this)},e.prototype.inotn=function(A){g("number"==typeof A&&A>=0);var M=0|Math.ceil(A/26),t=A%26;this._expand(M),t>0&&M--;for(var I=0;I0&&(this.words[I]=~this.words[I]&67108863>>26-t),this._strip()},e.prototype.notn=function(A){return this.clone().inotn(A)},e.prototype.setn=function(A,M){g("number"==typeof A&&A>=0);var t=A/26|0,I=A%26;return this._expand(t+1),this.words[t]=M?this.words[t]|1<A.length?(t=this,g=A):(t=A,g=this);for(var I=0,e=0;e>>26;for(;0!==I&&e>>26;if(this.length=t.length,0!==I)this.words[this.length]=I,this.length++;else if(t!==this)for(;eA.length?this.clone().iadd(A):A.clone().iadd(this)},e.prototype.isub=function(A){if(0!==A.negative){A.negative=0;var M=this.iadd(A);return A.negative=1,M._normSign()}if(0!==this.negative)return this.negative=0,this.iadd(A),this.negative=1,this._normSign();var t,g,I=this.cmp(A);if(0===I)return this.negative=0,this.length=1,this.words[0]=0,this;I>0?(t=this,g=A):(t=A,g=this);for(var e=0,i=0;i>26,this.words[i]=67108863&M;for(;0!==e&&i>26,this.words[i]=67108863&M;if(0===e&&i>>13,r=0|i[1],c=8191&r,o=r>>>13,B=0|i[2],Q=8191&B,a=B>>>13,h=0|i[3],s=8191&h,y=h>>>13,w=0|i[4],j=8191&w,L=w>>>13,d=0|i[5],x=8191&d,Y=d>>>13,u=0|i[6],S=8191&u,l=u>>>13,z=0|i[7],U=8191&z,f=z>>>13,O=0|i[8],F=8191&O,m=O>>>13,R=0|i[9],k=8191&R,p=R>>>13,b=0|T[0],G=8191&b,J=b>>>13,H=0|T[1],X=8191&H,v=H>>>13,V=0|T[2],W=8191&V,P=V>>>13,Z=0|T[3],K=8191&Z,q=Z>>>13,_=0|T[4],$=8191&_,AA=_>>>13,MA=0|T[5],tA=8191&MA,gA=MA>>>13,IA=0|T[6],eA=8191&IA,iA=IA>>>13,TA=0|T[7],EA=8191&TA,NA=TA>>>13,nA=0|T[8],DA=8191&nA,CA=nA>>>13,rA=0|T[9],cA=8191&rA,oA=rA>>>13;t.negative=A.negative^M.negative,t.length=19;var BA=(N+(g=Math.imul(D,G))|0)+((8191&(I=(I=Math.imul(D,J))+Math.imul(C,G)|0))<<13)|0;N=((e=Math.imul(C,J))+(I>>>13)|0)+(BA>>>26)|0,BA&=67108863,g=Math.imul(c,G),I=(I=Math.imul(c,J))+Math.imul(o,G)|0,e=Math.imul(o,J);var QA=(N+(g=g+Math.imul(D,X)|0)|0)+((8191&(I=(I=I+Math.imul(D,v)|0)+Math.imul(C,X)|0))<<13)|0;N=((e=e+Math.imul(C,v)|0)+(I>>>13)|0)+(QA>>>26)|0,QA&=67108863,g=Math.imul(Q,G),I=(I=Math.imul(Q,J))+Math.imul(a,G)|0,e=Math.imul(a,J),g=g+Math.imul(c,X)|0,I=(I=I+Math.imul(c,v)|0)+Math.imul(o,X)|0,e=e+Math.imul(o,v)|0;var aA=(N+(g=g+Math.imul(D,W)|0)|0)+((8191&(I=(I=I+Math.imul(D,P)|0)+Math.imul(C,W)|0))<<13)|0;N=((e=e+Math.imul(C,P)|0)+(I>>>13)|0)+(aA>>>26)|0,aA&=67108863,g=Math.imul(s,G),I=(I=Math.imul(s,J))+Math.imul(y,G)|0,e=Math.imul(y,J),g=g+Math.imul(Q,X)|0,I=(I=I+Math.imul(Q,v)|0)+Math.imul(a,X)|0,e=e+Math.imul(a,v)|0,g=g+Math.imul(c,W)|0,I=(I=I+Math.imul(c,P)|0)+Math.imul(o,W)|0,e=e+Math.imul(o,P)|0;var hA=(N+(g=g+Math.imul(D,K)|0)|0)+((8191&(I=(I=I+Math.imul(D,q)|0)+Math.imul(C,K)|0))<<13)|0;N=((e=e+Math.imul(C,q)|0)+(I>>>13)|0)+(hA>>>26)|0,hA&=67108863,g=Math.imul(j,G),I=(I=Math.imul(j,J))+Math.imul(L,G)|0,e=Math.imul(L,J),g=g+Math.imul(s,X)|0,I=(I=I+Math.imul(s,v)|0)+Math.imul(y,X)|0,e=e+Math.imul(y,v)|0,g=g+Math.imul(Q,W)|0,I=(I=I+Math.imul(Q,P)|0)+Math.imul(a,W)|0,e=e+Math.imul(a,P)|0,g=g+Math.imul(c,K)|0,I=(I=I+Math.imul(c,q)|0)+Math.imul(o,K)|0,e=e+Math.imul(o,q)|0;var sA=(N+(g=g+Math.imul(D,$)|0)|0)+((8191&(I=(I=I+Math.imul(D,AA)|0)+Math.imul(C,$)|0))<<13)|0;N=((e=e+Math.imul(C,AA)|0)+(I>>>13)|0)+(sA>>>26)|0,sA&=67108863,g=Math.imul(x,G),I=(I=Math.imul(x,J))+Math.imul(Y,G)|0,e=Math.imul(Y,J),g=g+Math.imul(j,X)|0,I=(I=I+Math.imul(j,v)|0)+Math.imul(L,X)|0,e=e+Math.imul(L,v)|0,g=g+Math.imul(s,W)|0,I=(I=I+Math.imul(s,P)|0)+Math.imul(y,W)|0,e=e+Math.imul(y,P)|0,g=g+Math.imul(Q,K)|0,I=(I=I+Math.imul(Q,q)|0)+Math.imul(a,K)|0,e=e+Math.imul(a,q)|0,g=g+Math.imul(c,$)|0,I=(I=I+Math.imul(c,AA)|0)+Math.imul(o,$)|0,e=e+Math.imul(o,AA)|0;var yA=(N+(g=g+Math.imul(D,tA)|0)|0)+((8191&(I=(I=I+Math.imul(D,gA)|0)+Math.imul(C,tA)|0))<<13)|0;N=((e=e+Math.imul(C,gA)|0)+(I>>>13)|0)+(yA>>>26)|0,yA&=67108863,g=Math.imul(S,G),I=(I=Math.imul(S,J))+Math.imul(l,G)|0,e=Math.imul(l,J),g=g+Math.imul(x,X)|0,I=(I=I+Math.imul(x,v)|0)+Math.imul(Y,X)|0,e=e+Math.imul(Y,v)|0,g=g+Math.imul(j,W)|0,I=(I=I+Math.imul(j,P)|0)+Math.imul(L,W)|0,e=e+Math.imul(L,P)|0,g=g+Math.imul(s,K)|0,I=(I=I+Math.imul(s,q)|0)+Math.imul(y,K)|0,e=e+Math.imul(y,q)|0,g=g+Math.imul(Q,$)|0,I=(I=I+Math.imul(Q,AA)|0)+Math.imul(a,$)|0,e=e+Math.imul(a,AA)|0,g=g+Math.imul(c,tA)|0,I=(I=I+Math.imul(c,gA)|0)+Math.imul(o,tA)|0,e=e+Math.imul(o,gA)|0;var wA=(N+(g=g+Math.imul(D,eA)|0)|0)+((8191&(I=(I=I+Math.imul(D,iA)|0)+Math.imul(C,eA)|0))<<13)|0;N=((e=e+Math.imul(C,iA)|0)+(I>>>13)|0)+(wA>>>26)|0,wA&=67108863,g=Math.imul(U,G),I=(I=Math.imul(U,J))+Math.imul(f,G)|0,e=Math.imul(f,J),g=g+Math.imul(S,X)|0,I=(I=I+Math.imul(S,v)|0)+Math.imul(l,X)|0,e=e+Math.imul(l,v)|0,g=g+Math.imul(x,W)|0,I=(I=I+Math.imul(x,P)|0)+Math.imul(Y,W)|0,e=e+Math.imul(Y,P)|0,g=g+Math.imul(j,K)|0,I=(I=I+Math.imul(j,q)|0)+Math.imul(L,K)|0,e=e+Math.imul(L,q)|0,g=g+Math.imul(s,$)|0,I=(I=I+Math.imul(s,AA)|0)+Math.imul(y,$)|0,e=e+Math.imul(y,AA)|0,g=g+Math.imul(Q,tA)|0,I=(I=I+Math.imul(Q,gA)|0)+Math.imul(a,tA)|0,e=e+Math.imul(a,gA)|0,g=g+Math.imul(c,eA)|0,I=(I=I+Math.imul(c,iA)|0)+Math.imul(o,eA)|0,e=e+Math.imul(o,iA)|0;var jA=(N+(g=g+Math.imul(D,EA)|0)|0)+((8191&(I=(I=I+Math.imul(D,NA)|0)+Math.imul(C,EA)|0))<<13)|0;N=((e=e+Math.imul(C,NA)|0)+(I>>>13)|0)+(jA>>>26)|0,jA&=67108863,g=Math.imul(F,G),I=(I=Math.imul(F,J))+Math.imul(m,G)|0,e=Math.imul(m,J),g=g+Math.imul(U,X)|0,I=(I=I+Math.imul(U,v)|0)+Math.imul(f,X)|0,e=e+Math.imul(f,v)|0,g=g+Math.imul(S,W)|0,I=(I=I+Math.imul(S,P)|0)+Math.imul(l,W)|0,e=e+Math.imul(l,P)|0,g=g+Math.imul(x,K)|0,I=(I=I+Math.imul(x,q)|0)+Math.imul(Y,K)|0,e=e+Math.imul(Y,q)|0,g=g+Math.imul(j,$)|0,I=(I=I+Math.imul(j,AA)|0)+Math.imul(L,$)|0,e=e+Math.imul(L,AA)|0,g=g+Math.imul(s,tA)|0,I=(I=I+Math.imul(s,gA)|0)+Math.imul(y,tA)|0,e=e+Math.imul(y,gA)|0,g=g+Math.imul(Q,eA)|0,I=(I=I+Math.imul(Q,iA)|0)+Math.imul(a,eA)|0,e=e+Math.imul(a,iA)|0,g=g+Math.imul(c,EA)|0,I=(I=I+Math.imul(c,NA)|0)+Math.imul(o,EA)|0,e=e+Math.imul(o,NA)|0;var LA=(N+(g=g+Math.imul(D,DA)|0)|0)+((8191&(I=(I=I+Math.imul(D,CA)|0)+Math.imul(C,DA)|0))<<13)|0;N=((e=e+Math.imul(C,CA)|0)+(I>>>13)|0)+(LA>>>26)|0,LA&=67108863,g=Math.imul(k,G),I=(I=Math.imul(k,J))+Math.imul(p,G)|0,e=Math.imul(p,J),g=g+Math.imul(F,X)|0,I=(I=I+Math.imul(F,v)|0)+Math.imul(m,X)|0,e=e+Math.imul(m,v)|0,g=g+Math.imul(U,W)|0,I=(I=I+Math.imul(U,P)|0)+Math.imul(f,W)|0,e=e+Math.imul(f,P)|0,g=g+Math.imul(S,K)|0,I=(I=I+Math.imul(S,q)|0)+Math.imul(l,K)|0,e=e+Math.imul(l,q)|0,g=g+Math.imul(x,$)|0,I=(I=I+Math.imul(x,AA)|0)+Math.imul(Y,$)|0,e=e+Math.imul(Y,AA)|0,g=g+Math.imul(j,tA)|0,I=(I=I+Math.imul(j,gA)|0)+Math.imul(L,tA)|0,e=e+Math.imul(L,gA)|0,g=g+Math.imul(s,eA)|0,I=(I=I+Math.imul(s,iA)|0)+Math.imul(y,eA)|0,e=e+Math.imul(y,iA)|0,g=g+Math.imul(Q,EA)|0,I=(I=I+Math.imul(Q,NA)|0)+Math.imul(a,EA)|0,e=e+Math.imul(a,NA)|0,g=g+Math.imul(c,DA)|0,I=(I=I+Math.imul(c,CA)|0)+Math.imul(o,DA)|0,e=e+Math.imul(o,CA)|0;var dA=(N+(g=g+Math.imul(D,cA)|0)|0)+((8191&(I=(I=I+Math.imul(D,oA)|0)+Math.imul(C,cA)|0))<<13)|0;N=((e=e+Math.imul(C,oA)|0)+(I>>>13)|0)+(dA>>>26)|0,dA&=67108863,g=Math.imul(k,X),I=(I=Math.imul(k,v))+Math.imul(p,X)|0,e=Math.imul(p,v),g=g+Math.imul(F,W)|0,I=(I=I+Math.imul(F,P)|0)+Math.imul(m,W)|0,e=e+Math.imul(m,P)|0,g=g+Math.imul(U,K)|0,I=(I=I+Math.imul(U,q)|0)+Math.imul(f,K)|0,e=e+Math.imul(f,q)|0,g=g+Math.imul(S,$)|0,I=(I=I+Math.imul(S,AA)|0)+Math.imul(l,$)|0,e=e+Math.imul(l,AA)|0,g=g+Math.imul(x,tA)|0,I=(I=I+Math.imul(x,gA)|0)+Math.imul(Y,tA)|0,e=e+Math.imul(Y,gA)|0,g=g+Math.imul(j,eA)|0,I=(I=I+Math.imul(j,iA)|0)+Math.imul(L,eA)|0,e=e+Math.imul(L,iA)|0,g=g+Math.imul(s,EA)|0,I=(I=I+Math.imul(s,NA)|0)+Math.imul(y,EA)|0,e=e+Math.imul(y,NA)|0,g=g+Math.imul(Q,DA)|0,I=(I=I+Math.imul(Q,CA)|0)+Math.imul(a,DA)|0,e=e+Math.imul(a,CA)|0;var xA=(N+(g=g+Math.imul(c,cA)|0)|0)+((8191&(I=(I=I+Math.imul(c,oA)|0)+Math.imul(o,cA)|0))<<13)|0;N=((e=e+Math.imul(o,oA)|0)+(I>>>13)|0)+(xA>>>26)|0,xA&=67108863,g=Math.imul(k,W),I=(I=Math.imul(k,P))+Math.imul(p,W)|0,e=Math.imul(p,P),g=g+Math.imul(F,K)|0,I=(I=I+Math.imul(F,q)|0)+Math.imul(m,K)|0,e=e+Math.imul(m,q)|0,g=g+Math.imul(U,$)|0,I=(I=I+Math.imul(U,AA)|0)+Math.imul(f,$)|0,e=e+Math.imul(f,AA)|0,g=g+Math.imul(S,tA)|0,I=(I=I+Math.imul(S,gA)|0)+Math.imul(l,tA)|0,e=e+Math.imul(l,gA)|0,g=g+Math.imul(x,eA)|0,I=(I=I+Math.imul(x,iA)|0)+Math.imul(Y,eA)|0,e=e+Math.imul(Y,iA)|0,g=g+Math.imul(j,EA)|0,I=(I=I+Math.imul(j,NA)|0)+Math.imul(L,EA)|0,e=e+Math.imul(L,NA)|0,g=g+Math.imul(s,DA)|0,I=(I=I+Math.imul(s,CA)|0)+Math.imul(y,DA)|0,e=e+Math.imul(y,CA)|0;var YA=(N+(g=g+Math.imul(Q,cA)|0)|0)+((8191&(I=(I=I+Math.imul(Q,oA)|0)+Math.imul(a,cA)|0))<<13)|0;N=((e=e+Math.imul(a,oA)|0)+(I>>>13)|0)+(YA>>>26)|0,YA&=67108863,g=Math.imul(k,K),I=(I=Math.imul(k,q))+Math.imul(p,K)|0,e=Math.imul(p,q),g=g+Math.imul(F,$)|0,I=(I=I+Math.imul(F,AA)|0)+Math.imul(m,$)|0,e=e+Math.imul(m,AA)|0,g=g+Math.imul(U,tA)|0,I=(I=I+Math.imul(U,gA)|0)+Math.imul(f,tA)|0,e=e+Math.imul(f,gA)|0,g=g+Math.imul(S,eA)|0,I=(I=I+Math.imul(S,iA)|0)+Math.imul(l,eA)|0,e=e+Math.imul(l,iA)|0,g=g+Math.imul(x,EA)|0,I=(I=I+Math.imul(x,NA)|0)+Math.imul(Y,EA)|0,e=e+Math.imul(Y,NA)|0,g=g+Math.imul(j,DA)|0,I=(I=I+Math.imul(j,CA)|0)+Math.imul(L,DA)|0,e=e+Math.imul(L,CA)|0;var uA=(N+(g=g+Math.imul(s,cA)|0)|0)+((8191&(I=(I=I+Math.imul(s,oA)|0)+Math.imul(y,cA)|0))<<13)|0;N=((e=e+Math.imul(y,oA)|0)+(I>>>13)|0)+(uA>>>26)|0,uA&=67108863,g=Math.imul(k,$),I=(I=Math.imul(k,AA))+Math.imul(p,$)|0,e=Math.imul(p,AA),g=g+Math.imul(F,tA)|0,I=(I=I+Math.imul(F,gA)|0)+Math.imul(m,tA)|0,e=e+Math.imul(m,gA)|0,g=g+Math.imul(U,eA)|0,I=(I=I+Math.imul(U,iA)|0)+Math.imul(f,eA)|0,e=e+Math.imul(f,iA)|0,g=g+Math.imul(S,EA)|0,I=(I=I+Math.imul(S,NA)|0)+Math.imul(l,EA)|0,e=e+Math.imul(l,NA)|0,g=g+Math.imul(x,DA)|0,I=(I=I+Math.imul(x,CA)|0)+Math.imul(Y,DA)|0,e=e+Math.imul(Y,CA)|0;var SA=(N+(g=g+Math.imul(j,cA)|0)|0)+((8191&(I=(I=I+Math.imul(j,oA)|0)+Math.imul(L,cA)|0))<<13)|0;N=((e=e+Math.imul(L,oA)|0)+(I>>>13)|0)+(SA>>>26)|0,SA&=67108863,g=Math.imul(k,tA),I=(I=Math.imul(k,gA))+Math.imul(p,tA)|0,e=Math.imul(p,gA),g=g+Math.imul(F,eA)|0,I=(I=I+Math.imul(F,iA)|0)+Math.imul(m,eA)|0,e=e+Math.imul(m,iA)|0,g=g+Math.imul(U,EA)|0,I=(I=I+Math.imul(U,NA)|0)+Math.imul(f,EA)|0,e=e+Math.imul(f,NA)|0,g=g+Math.imul(S,DA)|0,I=(I=I+Math.imul(S,CA)|0)+Math.imul(l,DA)|0,e=e+Math.imul(l,CA)|0;var lA=(N+(g=g+Math.imul(x,cA)|0)|0)+((8191&(I=(I=I+Math.imul(x,oA)|0)+Math.imul(Y,cA)|0))<<13)|0;N=((e=e+Math.imul(Y,oA)|0)+(I>>>13)|0)+(lA>>>26)|0,lA&=67108863,g=Math.imul(k,eA),I=(I=Math.imul(k,iA))+Math.imul(p,eA)|0,e=Math.imul(p,iA),g=g+Math.imul(F,EA)|0,I=(I=I+Math.imul(F,NA)|0)+Math.imul(m,EA)|0,e=e+Math.imul(m,NA)|0,g=g+Math.imul(U,DA)|0,I=(I=I+Math.imul(U,CA)|0)+Math.imul(f,DA)|0,e=e+Math.imul(f,CA)|0;var zA=(N+(g=g+Math.imul(S,cA)|0)|0)+((8191&(I=(I=I+Math.imul(S,oA)|0)+Math.imul(l,cA)|0))<<13)|0;N=((e=e+Math.imul(l,oA)|0)+(I>>>13)|0)+(zA>>>26)|0,zA&=67108863,g=Math.imul(k,EA),I=(I=Math.imul(k,NA))+Math.imul(p,EA)|0,e=Math.imul(p,NA),g=g+Math.imul(F,DA)|0,I=(I=I+Math.imul(F,CA)|0)+Math.imul(m,DA)|0,e=e+Math.imul(m,CA)|0;var UA=(N+(g=g+Math.imul(U,cA)|0)|0)+((8191&(I=(I=I+Math.imul(U,oA)|0)+Math.imul(f,cA)|0))<<13)|0;N=((e=e+Math.imul(f,oA)|0)+(I>>>13)|0)+(UA>>>26)|0,UA&=67108863,g=Math.imul(k,DA),I=(I=Math.imul(k,CA))+Math.imul(p,DA)|0,e=Math.imul(p,CA);var fA=(N+(g=g+Math.imul(F,cA)|0)|0)+((8191&(I=(I=I+Math.imul(F,oA)|0)+Math.imul(m,cA)|0))<<13)|0;N=((e=e+Math.imul(m,oA)|0)+(I>>>13)|0)+(fA>>>26)|0,fA&=67108863;var OA=(N+(g=Math.imul(k,cA))|0)+((8191&(I=(I=Math.imul(k,oA))+Math.imul(p,cA)|0))<<13)|0;return N=((e=Math.imul(p,oA))+(I>>>13)|0)+(OA>>>26)|0,OA&=67108863,E[0]=BA,E[1]=QA,E[2]=aA,E[3]=hA,E[4]=sA,E[5]=yA,E[6]=wA,E[7]=jA,E[8]=LA,E[9]=dA,E[10]=xA,E[11]=YA,E[12]=uA,E[13]=SA,E[14]=lA,E[15]=zA,E[16]=UA,E[17]=fA,E[18]=OA,0!==N&&(E[19]=N,t.length++),t};function B(A,M,t){t.negative=M.negative^A.negative,t.length=A.length+M.length;for(var g=0,I=0,e=0;e>>26)|0)>>>26,i&=67108863}t.words[e]=T,g=i,i=I}return 0!==g?t.words[e]=g:t.length--,t._strip()}function Q(A,M,t){return B(A,M,t)}function a(A,M){this.x=A,this.y=M}Math.imul||(o=c),e.prototype.mulTo=function(A,M){var t=this.length+A.length;return 10===this.length&&10===A.length?o(this,A,M):t<63?c(this,A,M):t<1024?B(this,A,M):Q(this,A,M)},a.prototype.makeRBT=function(A){for(var M=new Array(A),t=e.prototype._countBits(A)-1,g=0;g>=1;return g},a.prototype.permute=function(A,M,t,g,I,e){for(var i=0;i>>=1)I++;return 1<>>=13,t[2*i+1]=8191&e,e>>>=13;for(i=2*M;i>=26,t+=e/67108864|0,t+=i>>>26,this.words[I]=67108863&i}return 0!==t&&(this.words[I]=t,this.length++),M?this.ineg():this},e.prototype.muln=function(A){return this.clone().imuln(A)},e.prototype.sqr=function(){return this.mul(this)},e.prototype.isqr=function(){return this.imul(this.clone())},e.prototype.pow=function(A){var M=function(A){for(var M=new Array(A.bitLength()),t=0;t>>I&1}return M}(A);if(0===M.length)return new e(1);for(var t=this,g=0;g=0);var M,t=A%26,I=(A-t)/26,e=67108863>>>26-t<<26-t;if(0!==t){var i=0;for(M=0;M>>26-t}i&&(this.words[M]=i,this.length++)}if(0!==I){for(M=this.length-1;M>=0;M--)this.words[M+I]=this.words[M];for(M=0;M=0),I=M?(M-M%26)/26:0;var e=A%26,i=Math.min((A-e)/26,this.length),T=67108863^67108863>>>e<i)for(this.length-=i,N=0;N=0&&(0!==n||N>=I);N--){var D=0|this.words[N];this.words[N]=n<<26-e|D>>>e,n=D&T}return E&&0!==n&&(E.words[E.length++]=n),0===this.length&&(this.words[0]=0,this.length=1),this._strip()},e.prototype.ishrn=function(A,M,t){return g(0===this.negative),this.iushrn(A,M,t)},e.prototype.shln=function(A){return this.clone().ishln(A)},e.prototype.ushln=function(A){return this.clone().iushln(A)},e.prototype.shrn=function(A){return this.clone().ishrn(A)},e.prototype.ushrn=function(A){return this.clone().iushrn(A)},e.prototype.testn=function(A){g("number"==typeof A&&A>=0);var M=A%26,t=(A-M)/26,I=1<=0);var M=A%26,t=(A-M)/26;if(g(0===this.negative,"imaskn works only with positive numbers"),this.length<=t)return this;if(0!==M&&t++,this.length=Math.min(t,this.length),0!==M){var I=67108863^67108863>>>M<=67108864;M++)this.words[M]-=67108864,M===this.length-1?this.words[M+1]=1:this.words[M+1]++;return this.length=Math.max(this.length,M+1),this},e.prototype.isubn=function(A){if(g("number"==typeof A),g(A<67108864),A<0)return this.iaddn(-A);if(0!==this.negative)return this.negative=0,this.iaddn(A),this.negative=1,this;if(this.words[0]-=A,1===this.length&&this.words[0]<0)this.words[0]=-this.words[0],this.negative=1;else for(var M=0;M>26)-(E/67108864|0),this.words[I+t]=67108863&e}for(;I>26,this.words[I+t]=67108863&e;if(0===T)return this._strip();for(g(-1===T),T=0,I=0;I>26,this.words[I]=67108863&e;return this.negative=1,this._strip()},e.prototype._wordDiv=function(A,M){var t=(this.length,A.length),g=this.clone(),I=A,i=0|I.words[I.length-1];0!==(t=26-this._countBits(i))&&(I=I.ushln(t),g.iushln(t),i=0|I.words[I.length-1]);var T,E=g.length-I.length;if("mod"!==M){(T=new e(null)).length=E+1,T.words=new Array(T.length);for(var N=0;N=0;D--){var C=67108864*(0|g.words[I.length+D])+(0|g.words[I.length+D-1]);for(C=Math.min(C/i|0,67108863),g._ishlnsubmul(I,C,D);0!==g.negative;)C--,g.negative=0,g._ishlnsubmul(I,1,D),g.isZero()||(g.negative^=1);T&&(T.words[D]=C)}return T&&T._strip(),g._strip(),"div"!==M&&0!==t&&g.iushrn(t),{div:T||null,mod:g}},e.prototype.divmod=function(A,M,t){return g(!A.isZero()),this.isZero()?{div:new e(0),mod:new e(0)}:0!==this.negative&&0===A.negative?(T=this.neg().divmod(A,M),"mod"!==M&&(I=T.div.neg()),"div"!==M&&(i=T.mod.neg(),t&&0!==i.negative&&i.iadd(A)),{div:I,mod:i}):0===this.negative&&0!==A.negative?(T=this.divmod(A.neg(),M),"mod"!==M&&(I=T.div.neg()),{div:I,mod:T.mod}):0!=(this.negative&A.negative)?(T=this.neg().divmod(A.neg(),M),"div"!==M&&(i=T.mod.neg(),t&&0!==i.negative&&i.isub(A)),{div:T.div,mod:i}):A.length>this.length||this.cmp(A)<0?{div:new e(0),mod:this}:1===A.length?"div"===M?{div:this.divn(A.words[0]),mod:null}:"mod"===M?{div:null,mod:new e(this.modrn(A.words[0]))}:{div:this.divn(A.words[0]),mod:new e(this.modrn(A.words[0]))}:this._wordDiv(A,M);var I,i,T},e.prototype.div=function(A){return this.divmod(A,"div",!1).div},e.prototype.mod=function(A){return this.divmod(A,"mod",!1).mod},e.prototype.umod=function(A){return this.divmod(A,"mod",!0).mod},e.prototype.divRound=function(A){var M=this.divmod(A);if(M.mod.isZero())return M.div;var t=0!==M.div.negative?M.mod.isub(A):M.mod,g=A.ushrn(1),I=A.andln(1),e=t.cmp(g);return e<0||1===I&&0===e?M.div:0!==M.div.negative?M.div.isubn(1):M.div.iaddn(1)},e.prototype.modrn=function(A){var M=A<0;M&&(A=-A),g(A<=67108863);for(var t=(1<<26)%A,I=0,e=this.length-1;e>=0;e--)I=(t*I+(0|this.words[e]))%A;return M?-I:I},e.prototype.modn=function(A){return this.modrn(A)},e.prototype.idivn=function(A){var M=A<0;M&&(A=-A),g(A<=67108863);for(var t=0,I=this.length-1;I>=0;I--){var e=(0|this.words[I])+67108864*t;this.words[I]=e/A|0,t=e%A}return this._strip(),M?this.ineg():this},e.prototype.divn=function(A){return this.clone().idivn(A)},e.prototype.egcd=function(A){g(0===A.negative),g(!A.isZero());var M=this,t=A.clone();M=0!==M.negative?M.umod(A):M.clone();for(var I=new e(1),i=new e(0),T=new e(0),E=new e(1),N=0;M.isEven()&&t.isEven();)M.iushrn(1),t.iushrn(1),++N;for(var n=t.clone(),D=M.clone();!M.isZero();){for(var C=0,r=1;0==(M.words[0]&r)&&C<26;++C,r<<=1);if(C>0)for(M.iushrn(C);C-- >0;)(I.isOdd()||i.isOdd())&&(I.iadd(n),i.isub(D)),I.iushrn(1),i.iushrn(1);for(var c=0,o=1;0==(t.words[0]&o)&&c<26;++c,o<<=1);if(c>0)for(t.iushrn(c);c-- >0;)(T.isOdd()||E.isOdd())&&(T.iadd(n),E.isub(D)),T.iushrn(1),E.iushrn(1);M.cmp(t)>=0?(M.isub(t),I.isub(T),i.isub(E)):(t.isub(M),T.isub(I),E.isub(i))}return{a:T,b:E,gcd:t.iushln(N)}},e.prototype._invmp=function(A){g(0===A.negative),g(!A.isZero());var M=this,t=A.clone();M=0!==M.negative?M.umod(A):M.clone();for(var I,i=new e(1),T=new e(0),E=t.clone();M.cmpn(1)>0&&t.cmpn(1)>0;){for(var N=0,n=1;0==(M.words[0]&n)&&N<26;++N,n<<=1);if(N>0)for(M.iushrn(N);N-- >0;)i.isOdd()&&i.iadd(E),i.iushrn(1);for(var D=0,C=1;0==(t.words[0]&C)&&D<26;++D,C<<=1);if(D>0)for(t.iushrn(D);D-- >0;)T.isOdd()&&T.iadd(E),T.iushrn(1);M.cmp(t)>=0?(M.isub(t),i.isub(T)):(t.isub(M),T.isub(i))}return(I=0===M.cmpn(1)?i:T).cmpn(0)<0&&I.iadd(A),I},e.prototype.gcd=function(A){if(this.isZero())return A.abs();if(A.isZero())return this.abs();var M=this.clone(),t=A.clone();M.negative=0,t.negative=0;for(var g=0;M.isEven()&&t.isEven();g++)M.iushrn(1),t.iushrn(1);for(;;){for(;M.isEven();)M.iushrn(1);for(;t.isEven();)t.iushrn(1);var I=M.cmp(t);if(I<0){var e=M;M=t,t=e}else if(0===I||0===t.cmpn(1))break;M.isub(t)}return t.iushln(g)},e.prototype.invm=function(A){return this.egcd(A).a.umod(A)},e.prototype.isEven=function(){return 0==(1&this.words[0])},e.prototype.isOdd=function(){return 1==(1&this.words[0])},e.prototype.andln=function(A){return this.words[0]&A},e.prototype.bincn=function(A){g("number"==typeof A);var M=A%26,t=(A-M)/26,I=1<>>26,T&=67108863,this.words[i]=T}return 0!==e&&(this.words[i]=e,this.length++),this},e.prototype.isZero=function(){return 1===this.length&&0===this.words[0]},e.prototype.cmpn=function(A){var M,t=A<0;if(0!==this.negative&&!t)return-1;if(0===this.negative&&t)return 1;if(this._strip(),this.length>1)M=1;else{t&&(A=-A),g(A<=67108863,"Number is too big");var I=0|this.words[0];M=I===A?0:IA.length)return 1;if(this.length=0;t--){var g=0|this.words[t],I=0|A.words[t];if(g!==I){gI&&(M=1);break}}return M},e.prototype.gtn=function(A){return 1===this.cmpn(A)},e.prototype.gt=function(A){return 1===this.cmp(A)},e.prototype.gten=function(A){return this.cmpn(A)>=0},e.prototype.gte=function(A){return this.cmp(A)>=0},e.prototype.ltn=function(A){return-1===this.cmpn(A)},e.prototype.lt=function(A){return-1===this.cmp(A)},e.prototype.lten=function(A){return this.cmpn(A)<=0},e.prototype.lte=function(A){return this.cmp(A)<=0},e.prototype.eqn=function(A){return 0===this.cmpn(A)},e.prototype.eq=function(A){return 0===this.cmp(A)},e.red=function(A){return new d(A)},e.prototype.toRed=function(A){return g(!this.red,"Already a number in reduction context"),g(0===this.negative,"red works only with positives"),A.convertTo(this)._forceRed(A)},e.prototype.fromRed=function(){return g(this.red,"fromRed works only with numbers in reduction context"),this.red.convertFrom(this)},e.prototype._forceRed=function(A){return this.red=A,this},e.prototype.forceRed=function(A){return g(!this.red,"Already a number in reduction context"),this._forceRed(A)},e.prototype.redAdd=function(A){return g(this.red,"redAdd works only with red numbers"),this.red.add(this,A)},e.prototype.redIAdd=function(A){return g(this.red,"redIAdd works only with red numbers"),this.red.iadd(this,A)},e.prototype.redSub=function(A){return g(this.red,"redSub works only with red numbers"),this.red.sub(this,A)},e.prototype.redISub=function(A){return g(this.red,"redISub works only with red numbers"),this.red.isub(this,A)},e.prototype.redShl=function(A){return g(this.red,"redShl works only with red numbers"),this.red.shl(this,A)},e.prototype.redMul=function(A){return g(this.red,"redMul works only with red numbers"),this.red._verify2(this,A),this.red.mul(this,A)},e.prototype.redIMul=function(A){return g(this.red,"redMul works only with red numbers"),this.red._verify2(this,A),this.red.imul(this,A)},e.prototype.redSqr=function(){return g(this.red,"redSqr works only with red numbers"),this.red._verify1(this),this.red.sqr(this)},e.prototype.redISqr=function(){return g(this.red,"redISqr works only with red numbers"),this.red._verify1(this),this.red.isqr(this)},e.prototype.redSqrt=function(){return g(this.red,"redSqrt works only with red numbers"),this.red._verify1(this),this.red.sqrt(this)},e.prototype.redInvm=function(){return g(this.red,"redInvm works only with red numbers"),this.red._verify1(this),this.red.invm(this)},e.prototype.redNeg=function(){return g(this.red,"redNeg works only with red numbers"),this.red._verify1(this),this.red.neg(this)},e.prototype.redPow=function(A){return g(this.red&&!A.red,"redPow(normalNum)"),this.red._verify1(this),this.red.pow(this,A)};var h={k256:null,p224:null,p192:null,p25519:null};function s(A,M){this.name=A,this.p=new e(M,16),this.n=this.p.bitLength(),this.k=new e(1).iushln(this.n).isub(this.p),this.tmp=this._tmp()}function y(){s.call(this,"k256","ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f")}function w(){s.call(this,"p224","ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001")}function j(){s.call(this,"p192","ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff")}function L(){s.call(this,"25519","7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed")}function d(A){if("string"==typeof A){var M=e._prime(A);this.m=M.p,this.prime=M}else g(A.gtn(1),"modulus must be greater than 1"),this.m=A,this.prime=null}function x(A){d.call(this,A),this.shift=this.m.bitLength(),this.shift%26!=0&&(this.shift+=26-this.shift%26),this.r=new e(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)}s.prototype._tmp=function(){var A=new e(null);return A.words=new Array(Math.ceil(this.n/13)),A},s.prototype.ireduce=function(A){var M,t=A;do{this.split(t,this.tmp),M=(t=(t=this.imulK(t)).iadd(this.tmp)).bitLength()}while(M>this.n);var g=M0?t.isub(this.p):void 0!==t.strip?t.strip():t._strip(),t},s.prototype.split=function(A,M){A.iushrn(this.n,0,M)},s.prototype.imulK=function(A){return A.imul(this.k)},I(y,s),y.prototype.split=function(A,M){for(var t=Math.min(A.length,9),g=0;g>>22,I=e}I>>>=22,A.words[g-10]=I,0===I&&A.length>10?A.length-=10:A.length-=9},y.prototype.imulK=function(A){A.words[A.length]=0,A.words[A.length+1]=0,A.length+=2;for(var M=0,t=0;t>>=26,A.words[t]=I,M=g}return 0!==M&&(A.words[A.length++]=M),A},e._prime=function(A){if(h[A])return h[A];var M;if("k256"===A)M=new y;else if("p224"===A)M=new w;else if("p192"===A)M=new j;else{if("p25519"!==A)throw new Error("Unknown prime "+A);M=new L}return h[A]=M,M},d.prototype._verify1=function(A){g(0===A.negative,"red works only with positives"),g(A.red,"red works only with red numbers")},d.prototype._verify2=function(A,M){g(0==(A.negative|M.negative),"red works only with positives"),g(A.red&&A.red===M.red,"red works only with red numbers")},d.prototype.imod=function(A){return this.prime?this.prime.ireduce(A)._forceRed(this):(N(A,A.umod(this.m)._forceRed(this)),A)},d.prototype.neg=function(A){return A.isZero()?A.clone():this.m.sub(A)._forceRed(this)},d.prototype.add=function(A,M){this._verify2(A,M);var t=A.add(M);return t.cmp(this.m)>=0&&t.isub(this.m),t._forceRed(this)},d.prototype.iadd=function(A,M){this._verify2(A,M);var t=A.iadd(M);return t.cmp(this.m)>=0&&t.isub(this.m),t},d.prototype.sub=function(A,M){this._verify2(A,M);var t=A.sub(M);return t.cmpn(0)<0&&t.iadd(this.m),t._forceRed(this)},d.prototype.isub=function(A,M){this._verify2(A,M);var t=A.isub(M);return t.cmpn(0)<0&&t.iadd(this.m),t},d.prototype.shl=function(A,M){return this._verify1(A),this.imod(A.ushln(M))},d.prototype.imul=function(A,M){return this._verify2(A,M),this.imod(A.imul(M))},d.prototype.mul=function(A,M){return this._verify2(A,M),this.imod(A.mul(M))},d.prototype.isqr=function(A){return this.imul(A,A.clone())},d.prototype.sqr=function(A){return this.mul(A,A)},d.prototype.sqrt=function(A){if(A.isZero())return A.clone();var M=this.m.andln(3);if(g(M%2==1),3===M){var t=this.m.add(new e(1)).iushrn(2);return this.pow(A,t)}for(var I=this.m.subn(1),i=0;!I.isZero()&&0===I.andln(1);)i++,I.iushrn(1);g(!I.isZero());var T=new e(1).toRed(this),E=T.redNeg(),N=this.m.subn(1).iushrn(1),n=this.m.bitLength();for(n=new e(2*n*n).toRed(this);0!==this.pow(n,N).cmp(E);)n.redIAdd(E);for(var D=this.pow(n,I),C=this.pow(A,I.addn(1).iushrn(1)),r=this.pow(A,I),c=i;0!==r.cmp(T);){for(var o=r,B=0;0!==o.cmp(T);B++)o=o.redSqr();g(B=0;g--){for(var N=M.words[g],n=E-1;n>=0;n--){var D=N>>n&1;I!==t[0]&&(I=this.sqr(I)),0!==D||0!==i?(i<<=1,i|=D,(4===++T||0===g&&0===n)&&(I=this.mul(I,t[i]),T=0,i=0)):T=0}E=26}return I},d.prototype.convertTo=function(A){var M=A.umod(this.m);return M===A?M.clone():M},d.prototype.convertFrom=function(A){var M=A.clone();return M.red=null,M},e.mont=function(A){return new x(A)},I(x,d),x.prototype.convertTo=function(A){return this.imod(A.ushln(this.shift))},x.prototype.convertFrom=function(A){var M=this.imod(A.mul(this.rinv));return M.red=null,M},x.prototype.imul=function(A,M){if(A.isZero()||M.isZero())return A.words[0]=0,A.length=1,A;var t=A.imul(M),g=t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),I=t.isub(g).iushrn(this.shift),e=I;return I.cmp(this.m)>=0?e=I.isub(this.m):I.cmpn(0)<0&&(e=I.iadd(this.m)),e._forceRed(this)},x.prototype.mul=function(A,M){if(A.isZero()||M.isZero())return new e(0)._forceRed(this);var t=A.mul(M),g=t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),I=t.isub(g).iushrn(this.shift),i=I;return I.cmp(this.m)>=0?i=I.isub(this.m):I.cmpn(0)<0&&(i=I.iadd(this.m)),i._forceRed(this)},x.prototype.invm=function(A){return this.imod(A._invmp(this.m).mul(this.r2))._forceRed(this)}}(A,this)}).call(this,t(92)(A))},function(A,M,t){"use strict";var g=M;g.bignum=t(6),g.define=t(191).define,g.base=t(194),g.constants=t(195),g.decoders=t(103),g.encoders=t(101)},function(A,M,t){"use strict";var g=M;g.der=t(102),g.pem=t(192)},function(A,M,t){"use strict";var g=t(3),I=t(52).Buffer,e=t(53),i=t(55);function T(A){this.enc="der",this.name=A.name,this.entity=A,this.tree=new E,this.tree._init(A.body)}function E(A){e.call(this,"der",A)}function N(A){return A<10?"0"+A:A}A.exports=T,T.prototype.encode=function(A,M){return this.tree._encode(A,M).join()},g(E,e),E.prototype._encodeComposite=function(A,M,t,g){var e=function(A,M,t,g){var I;"seqof"===A?A="seq":"setof"===A&&(A="set");if(i.tagByName.hasOwnProperty(A))I=i.tagByName[A];else{if("number"!=typeof A||(0|A)!==A)return g.error("Unknown tag: "+A);I=A}if(I>=31)return g.error("Multi-octet tag encoding unsupported");M||(I|=32);return I|=i.tagClassByName[t||"universal"]<<6}(A,M,t,this.reporter);if(g.length<128){var T=I.alloc(2);return T[0]=e,T[1]=g.length,this._createEncoderBuffer([T,g])}for(var E=1,N=g.length;N>=256;N>>=8)E++;var n=I.alloc(2+E);n[0]=e,n[1]=128|E;for(var D=1+E,C=g.length;C>0;D--,C>>=8)n[D]=255&C;return this._createEncoderBuffer([n,g])},E.prototype._encodeStr=function(A,M){if("bitstr"===M)return this._createEncoderBuffer([0|A.unused,A.data]);if("bmpstr"===M){for(var t=I.alloc(2*A.length),g=0;g=40)return this.reporter.error("Second objid identifier OOB");A.splice(0,2,40*A[0]+A[1])}for(var i=0,T=0;T=128;E>>=7)i++}for(var N=I.alloc(i),n=N.length-1,D=A.length-1;D>=0;D--){var C=A[D];for(N[n--]=127&C;(C>>=7)>0;)N[n--]=128|127&C}return this._createEncoderBuffer(N)},E.prototype._encodeTime=function(A,M){var t,g=new Date(A);return"gentime"===M?t=[N(g.getUTCFullYear()),N(g.getUTCMonth()+1),N(g.getUTCDate()),N(g.getUTCHours()),N(g.getUTCMinutes()),N(g.getUTCSeconds()),"Z"].join(""):"utctime"===M?t=[N(g.getUTCFullYear()%100),N(g.getUTCMonth()+1),N(g.getUTCDate()),N(g.getUTCHours()),N(g.getUTCMinutes()),N(g.getUTCSeconds()),"Z"].join(""):this.reporter.error("Encoding "+M+" time is not supported yet"),this._encodeStr(t,"octstr")},E.prototype._encodeNull=function(){return this._createEncoderBuffer("")},E.prototype._encodeInt=function(A,M){if("string"==typeof A){if(!M)return this.reporter.error("String int or enum given, but no values map");if(!M.hasOwnProperty(A))return this.reporter.error("Values map doesn't contain: "+JSON.stringify(A));A=M[A]}if("number"!=typeof A&&!I.isBuffer(A)){var t=A.toArray();!A.sign&&128&t[0]&&t.unshift(0),A=I.from(t)}if(I.isBuffer(A)){var g=A.length;0===A.length&&g++;var e=I.alloc(g);return A.copy(e),0===A.length&&(e[0]=0),this._createEncoderBuffer(e)}if(A<128)return this._createEncoderBuffer(A);if(A<256)return this._createEncoderBuffer([0,A]);for(var i=1,T=A;T>=256;T>>=8)i++;for(var E=new Array(i),N=E.length-1;N>=0;N--)E[N]=255&A,A>>=8;return 128&E[0]&&E.unshift(0),this._createEncoderBuffer(I.from(E))},E.prototype._encodeBool=function(A){return this._createEncoderBuffer(A?255:0)},E.prototype._use=function(A,M){return"function"==typeof A&&(A=A(M)),A._getEncoder("der").tree},E.prototype._skipDefault=function(A,M,t){var g,I=this._baseState;if(null===I.default)return!1;var e=A.join();if(void 0===I.defaultBuffer&&(I.defaultBuffer=this._encodeValue(I.default,M,t).join()),e.length!==I.defaultBuffer.length)return!1;for(g=0;g>6],I=0==(32&t);if(31==(31&t)){var e=t;for(t=0;128==(128&e);){if(e=A.readUInt8(M),A.isError(e))return e;t<<=7,t|=127&e}}else t&=31;return{cls:g,primitive:I,tag:t,tagStr:T.tag[t]}}function D(A,M,t){var g=A.readUInt8(t);if(A.isError(g))return g;if(!M&&128===g)return null;if(0==(128&g))return g;var I=127&g;if(I>4)return A.error("length octect is too long");g=0;for(var e=0;e0?i-4:i;for(t=0;t>16&255,E[n++]=M>>8&255,E[n++]=255&M;2===T&&(M=I[A.charCodeAt(t)]<<2|I[A.charCodeAt(t+1)]>>4,E[n++]=255&M);1===T&&(M=I[A.charCodeAt(t)]<<10|I[A.charCodeAt(t+1)]<<4|I[A.charCodeAt(t+2)]>>2,E[n++]=M>>8&255,E[n++]=255&M);return E},M.fromByteArray=function(A){for(var M,t=A.length,I=t%3,e=[],i=0,T=t-I;iT?T:i+16383));1===I?(M=A[t-1],e.push(g[M>>2]+g[M<<4&63]+"==")):2===I&&(M=(A[t-2]<<8)+A[t-1],e.push(g[M>>10]+g[M>>4&63]+g[M<<2&63]+"="));return e.join("")};for(var g=[],I=[],e="undefined"!=typeof Uint8Array?Uint8Array:Array,i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",T=0,E=i.length;T0)throw new Error("Invalid string. Length must be a multiple of 4");var t=A.indexOf("=");return-1===t&&(t=M),[t,t===M?0:4-t%4]}function n(A,M,t){for(var I,e,i=[],T=M;T>18&63]+g[e>>12&63]+g[e>>6&63]+g[63&e]);return i.join("")}I["-".charCodeAt(0)]=62,I["_".charCodeAt(0)]=63},function(A,M){M.read=function(A,M,t,g,I){var e,i,T=8*I-g-1,E=(1<>1,n=-7,D=t?I-1:0,C=t?-1:1,r=A[M+D];for(D+=C,e=r&(1<<-n)-1,r>>=-n,n+=T;n>0;e=256*e+A[M+D],D+=C,n-=8);for(i=e&(1<<-n)-1,e>>=-n,n+=g;n>0;i=256*i+A[M+D],D+=C,n-=8);if(0===e)e=1-N;else{if(e===E)return i?NaN:1/0*(r?-1:1);i+=Math.pow(2,g),e-=N}return(r?-1:1)*i*Math.pow(2,e-g)},M.write=function(A,M,t,g,I,e){var i,T,E,N=8*e-I-1,n=(1<>1,C=23===I?Math.pow(2,-24)-Math.pow(2,-77):0,r=g?0:e-1,c=g?1:-1,o=M<0||0===M&&1/M<0?1:0;for(M=Math.abs(M),isNaN(M)||M===1/0?(T=isNaN(M)?1:0,i=n):(i=Math.floor(Math.log(M)/Math.LN2),M*(E=Math.pow(2,-i))<1&&(i--,E*=2),(M+=i+D>=1?C/E:C*Math.pow(2,1-D))*E>=2&&(i++,E/=2),i+D>=n?(T=0,i=n):i+D>=1?(T=(M*E-1)*Math.pow(2,I),i+=D):(T=M*Math.pow(2,D-1)*Math.pow(2,I),i=0));I>=8;A[t+r]=255&T,r+=c,T/=256,I-=8);for(i=i<0;A[t+r]=255&i,r+=c,i/=256,N-=8);A[t+r-c]|=128*o}},function(A,M,t){(function(M){var t;t="undefined"!=typeof window?window:void 0!==M?M:"undefined"!=typeof self?self:{},A.exports=t}).call(this,t(10))},function(A,M,t){(function(M,t){ +/*! + * @overview es6-promise - a tiny implementation of Promises/A+. + * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) + * @license Licensed under MIT license + * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE + * @version v4.2.8+1e68dce6 + */ +var g;g=function(){"use strict";function A(A){return"function"==typeof A}var g=Array.isArray?Array.isArray:function(A){return"[object Array]"===Object.prototype.toString.call(A)},I=0,e=void 0,i=void 0,T=function(A,M){c[I]=A,c[I+1]=M,2===(I+=2)&&(i?i(o):Q())},E="undefined"!=typeof window?window:void 0,N=E||{},n=N.MutationObserver||N.WebKitMutationObserver,D="undefined"==typeof self&&void 0!==M&&"[object process]"==={}.toString.call(M),C="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel;function r(){var A=setTimeout;return function(){return A(o,1)}}var c=new Array(1e3);function o(){for(var A=0;A1&&void 0!==arguments[1]?arguments[1]:{},t=0,T={},D=M.getImportObject,C=N(M,["getImportObject"]),r=new Worker("data:,ACTIONS="+JSON.stringify(I.default)+";getImportObject="+D+";importObject=undefined;wasmModule=null;moduleInstance=null;onmessage="+e.default,C);return r.onmessage=function(A){var M=A.data,e=M.id,i=M.result,N=M.action,D=M.payload;if(N===I.default.COMPILE_MODULE)if(0===i){var C=D.exports;T[e][0]({exports:C.reduce((function(A,M){return g({},A,E({},M,(function(){for(var A=arguments.length,g=Array(A),e=0;e=0||Object.prototype.hasOwnProperty.call(A,g)&&(t[g]=A[g]);return t}var n=function(){var A=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];return A.filter((function(A){return A instanceof ArrayBuffer||A instanceof MessagePort||A instanceof ImageBitmap}))}},function(A,M,t){"use strict";M.__esModule=!0;M.default={COMPILE_MODULE:0,CALL_FUNCTION_EXPORT:1,RUN_FUNCTION:2}},function(A,M,t){"use strict";M.__esModule=!0,M.default=function(A){var M=A.data,t=M.id,g=M.action,I=M.payload,e=function(A,M){self.postMessage({id:t,action:g,result:A,payload:M})},i=function(A){return e(1,""+A)},T=e.bind(null,0);if(g===ACTIONS.COMPILE_MODULE)Promise.resolve().then((function(){var A=void 0;if(void 0!==getImportObject&&(importObject=getImportObject()),"string"==typeof I){if(A=fetch(I),void 0!==WebAssembly.instantiateStreaming)return WebAssembly.instantiateStreaming(A,importObject);A=A.then((function(A){return A.arrayBuffer()}))}else A=Promise.resolve(I);return A.then((function(A){return WebAssembly.compile(A)})).then((function(A){return WebAssembly.instantiate(A,importObject).then((function(M){return{module:A,instance:M}}))}))})).then((function(A){var M=A.module,t=A.instance;moduleInstance=t,wasmModule=M,T({exports:WebAssembly.Module.exports(M).filter((function(A){return"function"===A.kind})).map((function(A){return A.name}))})})).catch(i);else if(g===ACTIONS.CALL_FUNCTION_EXPORT){var E=I.func,N=I.params;Promise.resolve().then((function(){var A=moduleInstance.exports;T(A[E].apply(A,N))})).catch(i)}else if(g===ACTIONS.RUN_FUNCTION){var n=I.func,D=I.params;Promise.resolve().then((function(){var A=new Function("return "+n)();T(A({module:wasmModule,instance:moduleInstance,importObject:importObject,params:D}))})).catch(i)}}},function(A,M,t){"use strict";M.__esModule=!0;M.getWasmSource=function(A){var M=A;return"string"==typeof M&&"undefined"!=typeof location&&(0===(M=M.trim()).indexOf("/")?M=location.origin+M:0!==M.indexOf("http")&&(M=location.href+("/"===location.href[location.href.length-1]?"":"/")+M)),M}},function(A,M){},function(A,M,t){(function(A){function t(A,M){for(var t=0,g=A.length-1;g>=0;g--){var I=A[g];"."===I?A.splice(g,1):".."===I?(A.splice(g,1),t++):t&&(A.splice(g,1),t--)}if(M)for(;t--;t)A.unshift("..");return A}function g(A,M){if(A.filter)return A.filter(M);for(var t=[],g=0;g=-1&&!I;e--){var i=e>=0?arguments[e]:A.cwd();if("string"!=typeof i)throw new TypeError("Arguments to path.resolve must be strings");i&&(M=i+"/"+M,I="/"===i.charAt(0))}return(I?"/":"")+(M=t(g(M.split("/"),(function(A){return!!A})),!I).join("/"))||"."},M.normalize=function(A){var e=M.isAbsolute(A),i="/"===I(A,-1);return(A=t(g(A.split("/"),(function(A){return!!A})),!e).join("/"))||e||(A="."),A&&i&&(A+="/"),(e?"/":"")+A},M.isAbsolute=function(A){return"/"===A.charAt(0)},M.join=function(){var A=Array.prototype.slice.call(arguments,0);return M.normalize(g(A,(function(A,M){if("string"!=typeof A)throw new TypeError("Arguments to path.join must be strings");return A})).join("/"))},M.relative=function(A,t){function g(A){for(var M=0;M=0&&""===A[t];t--);return M>t?[]:A.slice(M,t-M+1)}A=M.resolve(A).substr(1),t=M.resolve(t).substr(1);for(var I=g(A.split("/")),e=g(t.split("/")),i=Math.min(I.length,e.length),T=i,E=0;E=1;--e)if(47===(M=A.charCodeAt(e))){if(!I){g=e;break}}else I=!1;return-1===g?t?"/":".":t&&1===g?"/":A.slice(0,g)},M.basename=function(A,M){var t=function(A){"string"!=typeof A&&(A+="");var M,t=0,g=-1,I=!0;for(M=A.length-1;M>=0;--M)if(47===A.charCodeAt(M)){if(!I){t=M+1;break}}else-1===g&&(I=!1,g=M+1);return-1===g?"":A.slice(t,g)}(A);return M&&t.substr(-1*M.length)===M&&(t=t.substr(0,t.length-M.length)),t},M.extname=function(A){"string"!=typeof A&&(A+="");for(var M=-1,t=0,g=-1,I=!0,e=0,i=A.length-1;i>=0;--i){var T=A.charCodeAt(i);if(47!==T)-1===g&&(I=!1,g=i+1),46===T?-1===M?M=i:1!==e&&(e=1):-1!==M&&(e=-1);else if(!I){t=i+1;break}}return-1===M||-1===g||0===e||1===e&&M===g-1&&M===t+1?"":A.slice(M,g)};var I="b"==="ab".substr(-1)?function(A,M,t){return A.substr(M,t)}:function(A,M,t){return M<0&&(M=A.length+M),A.substr(M,t)}}).call(this,t(8))},function(A,M,t){"use strict";M.randomBytes=M.rng=M.pseudoRandomBytes=M.prng=t(18),M.createHash=M.Hash=t(23),M.createHmac=M.Hmac=t(76);var g=t(147),I=Object.keys(g),e=["sha1","sha224","sha256","sha384","sha512","md5","rmd160"].concat(I);M.getHashes=function(){return e};var i=t(79);M.pbkdf2=i.pbkdf2,M.pbkdf2Sync=i.pbkdf2Sync;var T=t(149);M.Cipher=T.Cipher,M.createCipher=T.createCipher,M.Cipheriv=T.Cipheriv,M.createCipheriv=T.createCipheriv,M.Decipher=T.Decipher,M.createDecipher=T.createDecipher,M.Decipheriv=T.Decipheriv,M.createDecipheriv=T.createDecipheriv,M.getCiphers=T.getCiphers,M.listCiphers=T.listCiphers;var E=t(164);M.DiffieHellmanGroup=E.DiffieHellmanGroup,M.createDiffieHellmanGroup=E.createDiffieHellmanGroup,M.getDiffieHellman=E.getDiffieHellman,M.createDiffieHellman=E.createDiffieHellman,M.DiffieHellman=E.DiffieHellman;var N=t(169);M.createSign=N.createSign,M.Sign=N.Sign,M.createVerify=N.createVerify,M.Verify=N.Verify,M.createECDH=t(200);var n=t(201);M.publicEncrypt=n.publicEncrypt,M.privateEncrypt=n.privateEncrypt,M.publicDecrypt=n.publicDecrypt,M.privateDecrypt=n.privateDecrypt;var D=t(204);M.randomFill=D.randomFill,M.randomFillSync=D.randomFillSync,M.createCredentials=function(){throw new Error(["sorry, createCredentials is not implemented yet","we accept pull requests","https://github.com/crypto-browserify/crypto-browserify"].join("\n"))},M.constants={DH_CHECK_P_NOT_SAFE_PRIME:2,DH_CHECK_P_NOT_PRIME:1,DH_UNABLE_TO_CHECK_GENERATOR:4,DH_NOT_SUITABLE_GENERATOR:8,NPN_ENABLED:1,ALPN_ENABLED:1,RSA_PKCS1_PADDING:1,RSA_SSLV23_PADDING:2,RSA_NO_PADDING:3,RSA_PKCS1_OAEP_PADDING:4,RSA_X931_PADDING:5,RSA_PKCS1_PSS_PADDING:6,POINT_CONVERSION_COMPRESSED:2,POINT_CONVERSION_UNCOMPRESSED:4,POINT_CONVERSION_HYBRID:6}},function(A,M){},function(A,M,t){"use strict";function g(A,M){var t=Object.keys(A);if(Object.getOwnPropertySymbols){var g=Object.getOwnPropertySymbols(A);M&&(g=g.filter((function(M){return Object.getOwnPropertyDescriptor(A,M).enumerable}))),t.push.apply(t,g)}return t}function I(A,M,t){return M in A?Object.defineProperty(A,M,{value:t,enumerable:!0,configurable:!0,writable:!0}):A[M]=t,A}function e(A,M){for(var t=0;t0?this.tail.next=M:this.head=M,this.tail=M,++this.length}},{key:"unshift",value:function(A){var M={data:A,next:this.head};0===this.length&&(this.tail=M),this.head=M,++this.length}},{key:"shift",value:function(){if(0!==this.length){var A=this.head.data;return 1===this.length?this.head=this.tail=null:this.head=this.head.next,--this.length,A}}},{key:"clear",value:function(){this.head=this.tail=null,this.length=0}},{key:"join",value:function(A){if(0===this.length)return"";for(var M=this.head,t=""+M.data;M=M.next;)t+=A+M.data;return t}},{key:"concat",value:function(A){if(0===this.length)return i.alloc(0);for(var M,t,g,I=i.allocUnsafe(A>>>0),e=this.head,T=0;e;)M=e.data,t=I,g=T,i.prototype.copy.call(M,t,g),T+=e.data.length,e=e.next;return I}},{key:"consume",value:function(A,M){var t;return AI.length?I.length:A;if(e===I.length?g+=I:g+=I.slice(0,A),0==(A-=e)){e===I.length?(++t,M.next?this.head=M.next:this.head=this.tail=null):(this.head=M,M.data=I.slice(e));break}++t}return this.length-=t,g}},{key:"_getBuffer",value:function(A){var M=i.allocUnsafe(A),t=this.head,g=1;for(t.data.copy(M),A-=t.data.length;t=t.next;){var I=t.data,e=A>I.length?I.length:A;if(I.copy(M,M.length-A,0,e),0==(A-=e)){e===I.length?(++g,t.next?this.head=t.next:this.head=this.tail=null):(this.head=t,t.data=I.slice(e));break}++g}return this.length-=g,M}},{key:E,value:function(A,M){return T(this,function(A){for(var M=1;M0,(function(A){g||(g=A),A&&i.forEach(N),e||(i.forEach(N),I(g))}))}));return M.reduce(n)}},function(A,M,t){var g=t(3),I=t(21),e=t(4).Buffer,i=[1518500249,1859775393,-1894007588,-899497514],T=new Array(80);function E(){this.init(),this._w=T,I.call(this,64,56)}function N(A){return A<<30|A>>>2}function n(A,M,t,g){return 0===A?M&t|~M&g:2===A?M&t|M&g|t&g:M^t^g}g(E,I),E.prototype.init=function(){return this._a=1732584193,this._b=4023233417,this._c=2562383102,this._d=271733878,this._e=3285377520,this},E.prototype._update=function(A){for(var M,t=this._w,g=0|this._a,I=0|this._b,e=0|this._c,T=0|this._d,E=0|this._e,D=0;D<16;++D)t[D]=A.readInt32BE(4*D);for(;D<80;++D)t[D]=t[D-3]^t[D-8]^t[D-14]^t[D-16];for(var C=0;C<80;++C){var r=~~(C/20),c=0|((M=g)<<5|M>>>27)+n(r,I,e,T)+E+t[C]+i[r];E=T,T=e,e=N(I),I=g,g=c}this._a=g+this._a|0,this._b=I+this._b|0,this._c=e+this._c|0,this._d=T+this._d|0,this._e=E+this._e|0},E.prototype._hash=function(){var A=e.allocUnsafe(20);return A.writeInt32BE(0|this._a,0),A.writeInt32BE(0|this._b,4),A.writeInt32BE(0|this._c,8),A.writeInt32BE(0|this._d,12),A.writeInt32BE(0|this._e,16),A},A.exports=E},function(A,M,t){var g=t(3),I=t(21),e=t(4).Buffer,i=[1518500249,1859775393,-1894007588,-899497514],T=new Array(80);function E(){this.init(),this._w=T,I.call(this,64,56)}function N(A){return A<<5|A>>>27}function n(A){return A<<30|A>>>2}function D(A,M,t,g){return 0===A?M&t|~M&g:2===A?M&t|M&g|t&g:M^t^g}g(E,I),E.prototype.init=function(){return this._a=1732584193,this._b=4023233417,this._c=2562383102,this._d=271733878,this._e=3285377520,this},E.prototype._update=function(A){for(var M,t=this._w,g=0|this._a,I=0|this._b,e=0|this._c,T=0|this._d,E=0|this._e,C=0;C<16;++C)t[C]=A.readInt32BE(4*C);for(;C<80;++C)t[C]=(M=t[C-3]^t[C-8]^t[C-14]^t[C-16])<<1|M>>>31;for(var r=0;r<80;++r){var c=~~(r/20),o=N(g)+D(c,I,e,T)+E+t[r]+i[c]|0;E=T,T=e,e=n(I),I=g,g=o}this._a=g+this._a|0,this._b=I+this._b|0,this._c=e+this._c|0,this._d=T+this._d|0,this._e=E+this._e|0},E.prototype._hash=function(){var A=e.allocUnsafe(20);return A.writeInt32BE(0|this._a,0),A.writeInt32BE(0|this._b,4),A.writeInt32BE(0|this._c,8),A.writeInt32BE(0|this._d,12),A.writeInt32BE(0|this._e,16),A},A.exports=E},function(A,M,t){var g=t(3),I=t(70),e=t(21),i=t(4).Buffer,T=new Array(64);function E(){this.init(),this._w=T,e.call(this,64,56)}g(E,I),E.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},E.prototype._hash=function(){var A=i.allocUnsafe(28);return A.writeInt32BE(this._a,0),A.writeInt32BE(this._b,4),A.writeInt32BE(this._c,8),A.writeInt32BE(this._d,12),A.writeInt32BE(this._e,16),A.writeInt32BE(this._f,20),A.writeInt32BE(this._g,24),A},A.exports=E},function(A,M,t){var g=t(3),I=t(71),e=t(21),i=t(4).Buffer,T=new Array(160);function E(){this.init(),this._w=T,e.call(this,128,112)}g(E,I),E.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},E.prototype._hash=function(){var A=i.allocUnsafe(48);function M(M,t,g){A.writeInt32BE(M,g),A.writeInt32BE(t,g+4)}return M(this._ah,this._al,0),M(this._bh,this._bl,8),M(this._ch,this._cl,16),M(this._dh,this._dl,24),M(this._eh,this._el,32),M(this._fh,this._fl,40),A},A.exports=E},function(A,M,t){A.exports=I;var g=t(7).EventEmitter;function I(){g.call(this)}t(3)(I,g),I.Readable=t(41),I.Writable=t(142),I.Duplex=t(143),I.Transform=t(144),I.PassThrough=t(145),I.Stream=I,I.prototype.pipe=function(A,M){var t=this;function I(M){A.writable&&!1===A.write(M)&&t.pause&&t.pause()}function e(){t.readable&&t.resume&&t.resume()}t.on("data",I),A.on("drain",e),A._isStdio||M&&!1===M.end||(t.on("end",T),t.on("close",E));var i=!1;function T(){i||(i=!0,A.end())}function E(){i||(i=!0,"function"==typeof A.destroy&&A.destroy())}function N(A){if(n(),0===g.listenerCount(this,"error"))throw A}function n(){t.removeListener("data",I),A.removeListener("drain",e),t.removeListener("end",T),t.removeListener("close",E),t.removeListener("error",N),A.removeListener("error",N),t.removeListener("end",n),t.removeListener("close",n),A.removeListener("close",n)}return t.on("error",N),A.on("error",N),t.on("end",n),t.on("close",n),A.on("close",n),A.emit("pipe",t),A}},function(A,M){},function(A,M,t){"use strict";var g=t(42).Buffer,I=t(138);A.exports=function(){function A(){!function(A,M){if(!(A instanceof M))throw new TypeError("Cannot call a class as a function")}(this,A),this.head=null,this.tail=null,this.length=0}return A.prototype.push=function(A){var M={data:A,next:null};this.length>0?this.tail.next=M:this.head=M,this.tail=M,++this.length},A.prototype.unshift=function(A){var M={data:A,next:this.head};0===this.length&&(this.tail=M),this.head=M,++this.length},A.prototype.shift=function(){if(0!==this.length){var A=this.head.data;return 1===this.length?this.head=this.tail=null:this.head=this.head.next,--this.length,A}},A.prototype.clear=function(){this.head=this.tail=null,this.length=0},A.prototype.join=function(A){if(0===this.length)return"";for(var M=this.head,t=""+M.data;M=M.next;)t+=A+M.data;return t},A.prototype.concat=function(A){if(0===this.length)return g.alloc(0);if(1===this.length)return this.head.data;for(var M,t,I,e=g.allocUnsafe(A>>>0),i=this.head,T=0;i;)M=i.data,t=e,I=T,M.copy(t,I),T+=i.data.length,i=i.next;return e},A}(),I&&I.inspect&&I.inspect.custom&&(A.exports.prototype[I.inspect.custom]=function(){var A=I.inspect({length:this.length});return this.constructor.name+" "+A})},function(A,M){},function(A,M,t){(function(A){var g=void 0!==A&&A||"undefined"!=typeof self&&self||window,I=Function.prototype.apply;function e(A,M){this._id=A,this._clearFn=M}M.setTimeout=function(){return new e(I.call(setTimeout,g,arguments),clearTimeout)},M.setInterval=function(){return new e(I.call(setInterval,g,arguments),clearInterval)},M.clearTimeout=M.clearInterval=function(A){A&&A.close()},e.prototype.unref=e.prototype.ref=function(){},e.prototype.close=function(){this._clearFn.call(g,this._id)},M.enroll=function(A,M){clearTimeout(A._idleTimeoutId),A._idleTimeout=M},M.unenroll=function(A){clearTimeout(A._idleTimeoutId),A._idleTimeout=-1},M._unrefActive=M.active=function(A){clearTimeout(A._idleTimeoutId);var M=A._idleTimeout;M>=0&&(A._idleTimeoutId=setTimeout((function(){A._onTimeout&&A._onTimeout()}),M))},t(140),M.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==A&&A.setImmediate||this&&this.setImmediate,M.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==A&&A.clearImmediate||this&&this.clearImmediate}).call(this,t(10))},function(A,M,t){(function(A,M){!function(A,t){"use strict";if(!A.setImmediate){var g,I,e,i,T,E=1,N={},n=!1,D=A.document,C=Object.getPrototypeOf&&Object.getPrototypeOf(A);C=C&&C.setTimeout?C:A,"[object process]"==={}.toString.call(A.process)?g=function(A){M.nextTick((function(){c(A)}))}:!function(){if(A.postMessage&&!A.importScripts){var M=!0,t=A.onmessage;return A.onmessage=function(){M=!1},A.postMessage("","*"),A.onmessage=t,M}}()?A.MessageChannel?((e=new MessageChannel).port1.onmessage=function(A){c(A.data)},g=function(A){e.port2.postMessage(A)}):D&&"onreadystatechange"in D.createElement("script")?(I=D.documentElement,g=function(A){var M=D.createElement("script");M.onreadystatechange=function(){c(A),M.onreadystatechange=null,I.removeChild(M),M=null},I.appendChild(M)}):g=function(A){setTimeout(c,0,A)}:(i="setImmediate$"+Math.random()+"$",T=function(M){M.source===A&&"string"==typeof M.data&&0===M.data.indexOf(i)&&c(+M.data.slice(i.length))},A.addEventListener?A.addEventListener("message",T,!1):A.attachEvent("onmessage",T),g=function(M){A.postMessage(i+M,"*")}),C.setImmediate=function(A){"function"!=typeof A&&(A=new Function(""+A));for(var M=new Array(arguments.length-1),t=0;t64?M=A(M):M.length<64&&(M=I.concat([M,i],64));for(var t=this._ipad=I.allocUnsafe(64),g=this._opad=I.allocUnsafe(64),T=0;T<64;T++)t[T]=54^M[T],g[T]=92^M[T];this._hash=[t]}g(T,e),T.prototype._update=function(A){this._hash.push(A)},T.prototype._final=function(){var A=this._alg(I.concat(this._hash));return this._alg(I.concat([this._opad,A]))},A.exports=T},function(A,M,t){A.exports=t(78)},function(A,M,t){(function(M,g){var I,e=t(4).Buffer,i=t(80),T=t(81),E=t(82),N=t(83),n=M.crypto&&M.crypto.subtle,D={sha:"SHA-1","sha-1":"SHA-1",sha1:"SHA-1",sha256:"SHA-256","sha-256":"SHA-256",sha384:"SHA-384","sha-384":"SHA-384","sha-512":"SHA-512",sha512:"SHA-512"},C=[];function r(A,M,t,g,I){return n.importKey("raw",A,{name:"PBKDF2"},!1,["deriveBits"]).then((function(A){return n.deriveBits({name:"PBKDF2",salt:M,iterations:t,hash:{name:I}},A,g<<3)})).then((function(A){return e.from(A)}))}A.exports=function(A,t,c,o,B,Q){"function"==typeof B&&(Q=B,B=void 0);var a=D[(B=B||"sha1").toLowerCase()];if(!a||"function"!=typeof M.Promise)return g.nextTick((function(){var M;try{M=E(A,t,c,o,B)}catch(A){return Q(A)}Q(null,M)}));if(i(c,o),A=N(A,T,"Password"),t=N(t,T,"Salt"),"function"!=typeof Q)throw new Error("No callback provided to pbkdf2");!function(A,M){A.then((function(A){g.nextTick((function(){M(null,A)}))}),(function(A){g.nextTick((function(){M(A)}))}))}(function(A){if(M.process&&!M.process.browser)return Promise.resolve(!1);if(!n||!n.importKey||!n.deriveBits)return Promise.resolve(!1);if(void 0!==C[A])return C[A];var t=r(I=I||e.alloc(8),I,10,128,A).then((function(){return!0})).catch((function(){return!1}));return C[A]=t,t}(a).then((function(M){return M?r(A,t,c,o,a):E(A,t,c,o,B)})),Q)}}).call(this,t(10),t(8))},function(A,M,t){var g=t(150),I=t(45),e=t(46),i=t(163),T=t(33);function E(A,M,t){if(A=A.toLowerCase(),e[A])return I.createCipheriv(A,M,t);if(i[A])return new g({key:M,iv:t,mode:A});throw new TypeError("invalid suite type")}function N(A,M,t){if(A=A.toLowerCase(),e[A])return I.createDecipheriv(A,M,t);if(i[A])return new g({key:M,iv:t,mode:A,decrypt:!0});throw new TypeError("invalid suite type")}M.createCipher=M.Cipher=function(A,M){var t,g;if(A=A.toLowerCase(),e[A])t=e[A].key,g=e[A].iv;else{if(!i[A])throw new TypeError("invalid suite type");t=8*i[A].key,g=i[A].iv}var I=T(M,!1,t,g);return E(A,I.key,I.iv)},M.createCipheriv=M.Cipheriv=E,M.createDecipher=M.Decipher=function(A,M){var t,g;if(A=A.toLowerCase(),e[A])t=e[A].key,g=e[A].iv;else{if(!i[A])throw new TypeError("invalid suite type");t=8*i[A].key,g=i[A].iv}var I=T(M,!1,t,g);return N(A,I.key,I.iv)},M.createDecipheriv=M.Decipheriv=N,M.listCiphers=M.getCiphers=function(){return Object.keys(i).concat(I.getCiphers())}},function(A,M,t){var g=t(15),I=t(151),e=t(3),i=t(4).Buffer,T={"des-ede3-cbc":I.CBC.instantiate(I.EDE),"des-ede3":I.EDE,"des-ede-cbc":I.CBC.instantiate(I.EDE),"des-ede":I.EDE,"des-cbc":I.CBC.instantiate(I.DES),"des-ecb":I.DES};function E(A){g.call(this);var M,t=A.mode.toLowerCase(),I=T[t];M=A.decrypt?"decrypt":"encrypt";var e=A.key;i.isBuffer(e)||(e=i.from(e)),"des-ede"!==t&&"des-ede-cbc"!==t||(e=i.concat([e,e.slice(0,8)]));var E=A.iv;i.isBuffer(E)||(E=i.from(E)),this._des=I.create({key:e,iv:E,type:M})}T.des=T["des-cbc"],T.des3=T["des-ede3-cbc"],A.exports=E,e(E,g),E.prototype._update=function(A){return i.from(this._des.update(A))},E.prototype._final=function(){return i.from(this._des.final())}},function(A,M,t){"use strict";M.utils=t(84),M.Cipher=t(44),M.DES=t(85),M.CBC=t(152),M.EDE=t(153)},function(A,M,t){"use strict";var g=t(11),I=t(3),e={};function i(A){g.equal(A.length,8,"Invalid IV length"),this.iv=new Array(8);for(var M=0;M15){var A=this.cache.slice(0,16);return this.cache=this.cache.slice(16),A}return null},C.prototype.flush=function(){for(var A=16-this.cache.length,M=e.allocUnsafe(A),t=-1;++t>i%8,A._prev=e(A._prev,t?g:I);return T}function e(A,M){var t=A.length,I=-1,e=g.allocUnsafe(A.length);for(A=g.concat([A,g.from([M])]);++I>7;return e}M.encrypt=function(A,M,t){for(var e=M.length,i=g.allocUnsafe(e),T=-1;++T>>0,0),M.writeUInt32BE(A[1]>>>0,4),M.writeUInt32BE(A[2]>>>0,8),M.writeUInt32BE(A[3]>>>0,12),M}function i(A){this.h=A,this.state=g.alloc(16,0),this.cache=g.allocUnsafe(0)}i.prototype.ghash=function(A){for(var M=-1;++M0;M--)g[M]=g[M]>>>1|(1&g[M-1])<<31;g[0]=g[0]>>>1,t&&(g[0]=g[0]^225<<24)}this.state=e(I)},i.prototype.update=function(A){var M;for(this.cache=g.concat([this.cache,A]);this.cache.length>=16;)M=this.cache.slice(0,16),this.cache=this.cache.slice(16),this.ghash(M)},i.prototype.final=function(A,M){return this.cache.length&&this.ghash(g.concat([this.cache,I],16)),this.ghash(e([0,A,0,M])),this.state},A.exports=i},function(A,M,t){var g=t(89),I=t(4).Buffer,e=t(46),i=t(90),T=t(15),E=t(32),N=t(33);function n(A,M,t){T.call(this),this._cache=new D,this._last=void 0,this._cipher=new E.AES(M),this._prev=I.from(t),this._mode=A,this._autopadding=!0}function D(){this.cache=I.allocUnsafe(0)}function C(A,M,t){var T=e[A.toLowerCase()];if(!T)throw new TypeError("invalid suite type");if("string"==typeof t&&(t=I.from(t)),"GCM"!==T.mode&&t.length!==T.iv)throw new TypeError("invalid iv length "+t.length);if("string"==typeof M&&(M=I.from(M)),M.length!==T.key/8)throw new TypeError("invalid key length "+M.length);return"stream"===T.type?new i(T.module,M,t,!0):"auth"===T.type?new g(T.module,M,t,!0):new n(T.module,M,t)}t(3)(n,T),n.prototype._update=function(A){var M,t;this._cache.add(A);for(var g=[];M=this._cache.get(this._autopadding);)t=this._mode.decrypt(this,M),g.push(t);return I.concat(g)},n.prototype._final=function(){var A=this._cache.flush();if(this._autopadding)return function(A){var M=A[15];if(M<1||M>16)throw new Error("unable to decrypt data");var t=-1;for(;++t16)return M=this.cache.slice(0,16),this.cache=this.cache.slice(16),M}else if(this.cache.length>=16)return M=this.cache.slice(0,16),this.cache=this.cache.slice(16),M;return null},D.prototype.flush=function(){if(this.cache.length)return this.cache},M.createDecipher=function(A,M){var t=e[A.toLowerCase()];if(!t)throw new TypeError("invalid suite type");var g=N(M,!1,t.key,t.iv);return C(A,g.key,g.iv)},M.createDecipheriv=C},function(A,M){M["des-ecb"]={key:8,iv:0},M["des-cbc"]=M.des={key:8,iv:8},M["des-ede3-cbc"]=M.des3={key:24,iv:8},M["des-ede3"]={key:24,iv:0},M["des-ede-cbc"]={key:16,iv:8},M["des-ede"]={key:16,iv:0}},function(A,M,t){(function(A){var g=t(91),I=t(167),e=t(168);var i={binary:!0,hex:!0,base64:!0};M.DiffieHellmanGroup=M.createDiffieHellmanGroup=M.getDiffieHellman=function(M){var t=new A(I[M].prime,"hex"),g=new A(I[M].gen,"hex");return new e(t,g)},M.createDiffieHellman=M.DiffieHellman=function M(t,I,T,E){return A.isBuffer(I)||void 0===i[I]?M(t,"binary",I,T):(I=I||"binary",E=E||"binary",T=T||new A([2]),A.isBuffer(T)||(T=new A(T,E)),"number"==typeof t?new e(g(t,T),T,!0):(A.isBuffer(t)||(t=new A(t,I)),new e(t,T,!0)))}}).call(this,t(9).Buffer)},function(A,M){},function(A,M){},function(A){A.exports=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"}}')},function(A,M,t){(function(M){var g=t(6),I=new(t(93)),e=new g(24),i=new g(11),T=new g(10),E=new g(3),N=new g(7),n=t(91),D=t(18);function C(A,t){return t=t||"utf8",M.isBuffer(A)||(A=new M(A,t)),this._pub=new g(A),this}function r(A,t){return t=t||"utf8",M.isBuffer(A)||(A=new M(A,t)),this._priv=new g(A),this}A.exports=o;var c={};function o(A,M,t){this.setGenerator(M),this.__prime=new g(A),this._prime=g.mont(this.__prime),this._primeLen=A.length,this._pub=void 0,this._priv=void 0,this._primeCode=void 0,t?(this.setPublicKey=C,this.setPrivateKey=r):this._primeCode=8}function B(A,t){var g=new M(A.toArray());return t?g.toString(t):g}Object.defineProperty(o.prototype,"verifyError",{enumerable:!0,get:function(){return"number"!=typeof this._primeCode&&(this._primeCode=function(A,M){var t=M.toString("hex"),g=[t,A.toString(16)].join("_");if(g in c)return c[g];var D,C=0;if(A.isEven()||!n.simpleSieve||!n.fermatTest(A)||!I.test(A))return C+=1,C+="02"===t||"05"===t?8:4,c[g]=C,C;switch(I.test(A.shrn(1))||(C+=2),t){case"02":A.mod(e).cmp(i)&&(C+=8);break;case"05":(D=A.mod(T)).cmp(E)&&D.cmp(N)&&(C+=8);break;default:C+=4}return c[g]=C,C}(this.__prime,this.__gen)),this._primeCode}}),o.prototype.generateKeys=function(){return this._priv||(this._priv=new g(D(this._primeLen))),this._pub=this._gen.toRed(this._prime).redPow(this._priv).fromRed(),this.getPublicKey()},o.prototype.computeSecret=function(A){var t=(A=(A=new g(A)).toRed(this._prime)).redPow(this._priv).fromRed(),I=new M(t.toArray()),e=this.getPrime();if(I.length0&&t.ishrn(g),t}function C(A,M,t){var e,i;do{for(e=g.alloc(0);8*e.length","license":"MIT","bugs":{"url":"https://github.com/indutny/elliptic/issues"},"homepage":"https://github.com/indutny/elliptic","devDependencies":{"brfs":"^1.4.3","coveralls":"^3.0.8","grunt":"^1.0.4","grunt-browserify":"^5.0.0","grunt-cli":"^1.2.0","grunt-contrib-connect":"^1.0.0","grunt-contrib-copy":"^1.0.0","grunt-contrib-uglify":"^1.0.1","grunt-mocha-istanbul":"^3.0.1","grunt-saucelabs":"^9.0.1","istanbul":"^0.4.2","jscs":"^3.0.7","jshint":"^2.10.3","mocha":"^6.2.2"},"dependencies":{"bn.js":"^4.4.0","brorand":"^1.0.1","hash.js":"^1.0.0","hmac-drbg":"^1.0.0","inherits":"^2.0.1","minimalistic-assert":"^1.0.0","minimalistic-crypto-utils":"^1.0.0"},"__npminstall_done":"Tue Sep 15 2020 09:43:51 GMT+0800 (GMT+08:00)","_from":"elliptic@6.5.3","_resolved":"https://registry.npm.taobao.org/elliptic/download/elliptic-6.5.3.tgz?cache=0&sync_timestamp=1592492864889&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Felliptic%2Fdownload%2Felliptic-6.5.3.tgz"}')},function(A,M,t){"use strict";var g=t(12),I=t(6),e=t(3),i=t(34),T=g.assert;function E(A){i.call(this,"short",A),this.a=new I(A.a,16).toRed(this.red),this.b=new I(A.b,16).toRed(this.red),this.tinv=this.two.redInvm(),this.zeroA=0===this.a.fromRed().cmpn(0),this.threeA=0===this.a.fromRed().sub(this.p).cmpn(-3),this.endo=this._getEndomorphism(A),this._endoWnafT1=new Array(4),this._endoWnafT2=new Array(4)}function N(A,M,t,g){i.BasePoint.call(this,A,"affine"),null===M&&null===t?(this.x=null,this.y=null,this.inf=!0):(this.x=new I(M,16),this.y=new I(t,16),g&&(this.x.forceRed(this.curve.red),this.y.forceRed(this.curve.red)),this.x.red||(this.x=this.x.toRed(this.curve.red)),this.y.red||(this.y=this.y.toRed(this.curve.red)),this.inf=!1)}function n(A,M,t,g){i.BasePoint.call(this,A,"jacobian"),null===M&&null===t&&null===g?(this.x=this.curve.one,this.y=this.curve.one,this.z=new I(0)):(this.x=new I(M,16),this.y=new I(t,16),this.z=new I(g,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.zOne=this.z===this.curve.one}e(E,i),A.exports=E,E.prototype._getEndomorphism=function(A){if(this.zeroA&&this.g&&this.n&&1===this.p.modn(3)){var M,t;if(A.beta)M=new I(A.beta,16).toRed(this.red);else{var g=this._getEndoRoots(this.p);M=(M=g[0].cmp(g[1])<0?g[0]:g[1]).toRed(this.red)}if(A.lambda)t=new I(A.lambda,16);else{var e=this._getEndoRoots(this.n);0===this.g.mul(e[0]).x.cmp(this.g.x.redMul(M))?t=e[0]:(t=e[1],T(0===this.g.mul(t).x.cmp(this.g.x.redMul(M))))}return{beta:M,lambda:t,basis:A.basis?A.basis.map((function(A){return{a:new I(A.a,16),b:new I(A.b,16)}})):this._getEndoBasis(t)}}},E.prototype._getEndoRoots=function(A){var M=A===this.p?this.red:I.mont(A),t=new I(2).toRed(M).redInvm(),g=t.redNeg(),e=new I(3).toRed(M).redNeg().redSqrt().redMul(t);return[g.redAdd(e).fromRed(),g.redSub(e).fromRed()]},E.prototype._getEndoBasis=function(A){for(var M,t,g,e,i,T,E,N,n,D=this.n.ushrn(Math.floor(this.n.bitLength()/2)),C=A,r=this.n.clone(),c=new I(1),o=new I(0),B=new I(0),Q=new I(1),a=0;0!==C.cmpn(0);){var h=r.div(C);N=r.sub(h.mul(C)),n=B.sub(h.mul(c));var s=Q.sub(h.mul(o));if(!g&&N.cmp(D)<0)M=E.neg(),t=c,g=N.neg(),e=n;else if(g&&2==++a)break;E=N,r=C,C=N,B=c,c=n,Q=o,o=s}i=N.neg(),T=n;var y=g.sqr().add(e.sqr());return i.sqr().add(T.sqr()).cmp(y)>=0&&(i=M,T=t),g.negative&&(g=g.neg(),e=e.neg()),i.negative&&(i=i.neg(),T=T.neg()),[{a:g,b:e},{a:i,b:T}]},E.prototype._endoSplit=function(A){var M=this.endo.basis,t=M[0],g=M[1],I=g.b.mul(A).divRound(this.n),e=t.b.neg().mul(A).divRound(this.n),i=I.mul(t.a),T=e.mul(g.a),E=I.mul(t.b),N=e.mul(g.b);return{k1:A.sub(i).sub(T),k2:E.add(N).neg()}},E.prototype.pointFromX=function(A,M){(A=new I(A,16)).red||(A=A.toRed(this.red));var t=A.redSqr().redMul(A).redIAdd(A.redMul(this.a)).redIAdd(this.b),g=t.redSqrt();if(0!==g.redSqr().redSub(t).cmp(this.zero))throw new Error("invalid point");var e=g.fromRed().isOdd();return(M&&!e||!M&&e)&&(g=g.redNeg()),this.point(A,g)},E.prototype.validate=function(A){if(A.inf)return!0;var M=A.x,t=A.y,g=this.a.redMul(M),I=M.redSqr().redMul(M).redIAdd(g).redIAdd(this.b);return 0===t.redSqr().redISub(I).cmpn(0)},E.prototype._endoWnafMulAdd=function(A,M,t){for(var g=this._endoWnafT1,I=this._endoWnafT2,e=0;e":""},N.prototype.isInfinity=function(){return this.inf},N.prototype.add=function(A){if(this.inf)return A;if(A.inf)return this;if(this.eq(A))return this.dbl();if(this.neg().eq(A))return this.curve.point(null,null);if(0===this.x.cmp(A.x))return this.curve.point(null,null);var M=this.y.redSub(A.y);0!==M.cmpn(0)&&(M=M.redMul(this.x.redSub(A.x).redInvm()));var t=M.redSqr().redISub(this.x).redISub(A.x),g=M.redMul(this.x.redSub(t)).redISub(this.y);return this.curve.point(t,g)},N.prototype.dbl=function(){if(this.inf)return this;var A=this.y.redAdd(this.y);if(0===A.cmpn(0))return this.curve.point(null,null);var M=this.curve.a,t=this.x.redSqr(),g=A.redInvm(),I=t.redAdd(t).redIAdd(t).redIAdd(M).redMul(g),e=I.redSqr().redISub(this.x.redAdd(this.x)),i=I.redMul(this.x.redSub(e)).redISub(this.y);return this.curve.point(e,i)},N.prototype.getX=function(){return this.x.fromRed()},N.prototype.getY=function(){return this.y.fromRed()},N.prototype.mul=function(A){return A=new I(A,16),this.isInfinity()?this:this._hasDoubles(A)?this.curve._fixedNafMul(this,A):this.curve.endo?this.curve._endoWnafMulAdd([this],[A]):this.curve._wnafMul(this,A)},N.prototype.mulAdd=function(A,M,t){var g=[this,M],I=[A,t];return this.curve.endo?this.curve._endoWnafMulAdd(g,I):this.curve._wnafMulAdd(1,g,I,2)},N.prototype.jmulAdd=function(A,M,t){var g=[this,M],I=[A,t];return this.curve.endo?this.curve._endoWnafMulAdd(g,I,!0):this.curve._wnafMulAdd(1,g,I,2,!0)},N.prototype.eq=function(A){return this===A||this.inf===A.inf&&(this.inf||0===this.x.cmp(A.x)&&0===this.y.cmp(A.y))},N.prototype.neg=function(A){if(this.inf)return this;var M=this.curve.point(this.x,this.y.redNeg());if(A&&this.precomputed){var t=this.precomputed,g=function(A){return A.neg()};M.precomputed={naf:t.naf&&{wnd:t.naf.wnd,points:t.naf.points.map(g)},doubles:t.doubles&&{step:t.doubles.step,points:t.doubles.points.map(g)}}}return M},N.prototype.toJ=function(){return this.inf?this.curve.jpoint(null,null,null):this.curve.jpoint(this.x,this.y,this.curve.one)},e(n,i.BasePoint),E.prototype.jpoint=function(A,M,t){return new n(this,A,M,t)},n.prototype.toP=function(){if(this.isInfinity())return this.curve.point(null,null);var A=this.z.redInvm(),M=A.redSqr(),t=this.x.redMul(M),g=this.y.redMul(M).redMul(A);return this.curve.point(t,g)},n.prototype.neg=function(){return this.curve.jpoint(this.x,this.y.redNeg(),this.z)},n.prototype.add=function(A){if(this.isInfinity())return A;if(A.isInfinity())return this;var M=A.z.redSqr(),t=this.z.redSqr(),g=this.x.redMul(M),I=A.x.redMul(t),e=this.y.redMul(M.redMul(A.z)),i=A.y.redMul(t.redMul(this.z)),T=g.redSub(I),E=e.redSub(i);if(0===T.cmpn(0))return 0!==E.cmpn(0)?this.curve.jpoint(null,null,null):this.dbl();var N=T.redSqr(),n=N.redMul(T),D=g.redMul(N),C=E.redSqr().redIAdd(n).redISub(D).redISub(D),r=E.redMul(D.redISub(C)).redISub(e.redMul(n)),c=this.z.redMul(A.z).redMul(T);return this.curve.jpoint(C,r,c)},n.prototype.mixedAdd=function(A){if(this.isInfinity())return A.toJ();if(A.isInfinity())return this;var M=this.z.redSqr(),t=this.x,g=A.x.redMul(M),I=this.y,e=A.y.redMul(M).redMul(this.z),i=t.redSub(g),T=I.redSub(e);if(0===i.cmpn(0))return 0!==T.cmpn(0)?this.curve.jpoint(null,null,null):this.dbl();var E=i.redSqr(),N=E.redMul(i),n=t.redMul(E),D=T.redSqr().redIAdd(N).redISub(n).redISub(n),C=T.redMul(n.redISub(D)).redISub(I.redMul(N)),r=this.z.redMul(i);return this.curve.jpoint(D,C,r)},n.prototype.dblp=function(A){if(0===A)return this;if(this.isInfinity())return this;if(!A)return this.dbl();if(this.curve.zeroA||this.curve.threeA){for(var M=this,t=0;t=0)return!1;if(t.redIAdd(I),0===this.x.cmp(t))return!0}},n.prototype.inspect=function(){return this.isInfinity()?"":""},n.prototype.isInfinity=function(){return 0===this.z.cmpn(0)}},function(A,M,t){"use strict";var g=t(6),I=t(3),e=t(34),i=t(12);function T(A){e.call(this,"mont",A),this.a=new g(A.a,16).toRed(this.red),this.b=new g(A.b,16).toRed(this.red),this.i4=new g(4).toRed(this.red).redInvm(),this.two=new g(2).toRed(this.red),this.a24=this.i4.redMul(this.a.redAdd(this.two))}function E(A,M,t){e.BasePoint.call(this,A,"projective"),null===M&&null===t?(this.x=this.curve.one,this.z=this.curve.zero):(this.x=new g(M,16),this.z=new g(t,16),this.x.red||(this.x=this.x.toRed(this.curve.red)),this.z.red||(this.z=this.z.toRed(this.curve.red)))}I(T,e),A.exports=T,T.prototype.validate=function(A){var M=A.normalize().x,t=M.redSqr(),g=t.redMul(M).redAdd(t.redMul(this.a)).redAdd(M);return 0===g.redSqrt().redSqr().cmp(g)},I(E,e.BasePoint),T.prototype.decodePoint=function(A,M){return this.point(i.toArray(A,M),1)},T.prototype.point=function(A,M){return new E(this,A,M)},T.prototype.pointFromJSON=function(A){return E.fromJSON(this,A)},E.prototype.precompute=function(){},E.prototype._encode=function(){return this.getX().toArray("be",this.curve.p.byteLength())},E.fromJSON=function(A,M){return new E(A,M[0],M[1]||A.one)},E.prototype.inspect=function(){return this.isInfinity()?"":""},E.prototype.isInfinity=function(){return 0===this.z.cmpn(0)},E.prototype.dbl=function(){var A=this.x.redAdd(this.z).redSqr(),M=this.x.redSub(this.z).redSqr(),t=A.redSub(M),g=A.redMul(M),I=t.redMul(M.redAdd(this.curve.a24.redMul(t)));return this.curve.point(g,I)},E.prototype.add=function(){throw new Error("Not supported on Montgomery curve")},E.prototype.diffAdd=function(A,M){var t=this.x.redAdd(this.z),g=this.x.redSub(this.z),I=A.x.redAdd(A.z),e=A.x.redSub(A.z).redMul(t),i=I.redMul(g),T=M.z.redMul(e.redAdd(i).redSqr()),E=M.x.redMul(e.redISub(i).redSqr());return this.curve.point(T,E)},E.prototype.mul=function(A){for(var M=A.clone(),t=this,g=this.curve.point(null,null),I=[];0!==M.cmpn(0);M.iushrn(1))I.push(M.andln(1));for(var e=I.length-1;e>=0;e--)0===I[e]?(t=t.diffAdd(g,this),g=g.dbl()):(g=t.diffAdd(g,this),t=t.dbl());return g},E.prototype.mulAdd=function(){throw new Error("Not supported on Montgomery curve")},E.prototype.jumlAdd=function(){throw new Error("Not supported on Montgomery curve")},E.prototype.eq=function(A){return 0===this.getX().cmp(A.getX())},E.prototype.normalize=function(){return this.x=this.x.redMul(this.z.redInvm()),this.z=this.curve.one,this},E.prototype.getX=function(){return this.normalize(),this.x.fromRed()}},function(A,M,t){"use strict";var g=t(12),I=t(6),e=t(3),i=t(34),T=g.assert;function E(A){this.twisted=1!=(0|A.a),this.mOneA=this.twisted&&-1==(0|A.a),this.extended=this.mOneA,i.call(this,"edwards",A),this.a=new I(A.a,16).umod(this.red.m),this.a=this.a.toRed(this.red),this.c=new I(A.c,16).toRed(this.red),this.c2=this.c.redSqr(),this.d=new I(A.d,16).toRed(this.red),this.dd=this.d.redAdd(this.d),T(!this.twisted||0===this.c.fromRed().cmpn(1)),this.oneC=1==(0|A.c)}function N(A,M,t,g,e){i.BasePoint.call(this,A,"projective"),null===M&&null===t&&null===g?(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 I(M,16),this.y=new I(t,16),this.z=g?new I(g,16):this.curve.one,this.t=e&&new I(e,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()))))}e(E,i),A.exports=E,E.prototype._mulA=function(A){return this.mOneA?A.redNeg():this.a.redMul(A)},E.prototype._mulC=function(A){return this.oneC?A:this.c.redMul(A)},E.prototype.jpoint=function(A,M,t,g){return this.point(A,M,t,g)},E.prototype.pointFromX=function(A,M){(A=new I(A,16)).red||(A=A.toRed(this.red));var t=A.redSqr(),g=this.c2.redSub(this.a.redMul(t)),e=this.one.redSub(this.c2.redMul(this.d).redMul(t)),i=g.redMul(e.redInvm()),T=i.redSqrt();if(0!==T.redSqr().redSub(i).cmp(this.zero))throw new Error("invalid point");var E=T.fromRed().isOdd();return(M&&!E||!M&&E)&&(T=T.redNeg()),this.point(A,T)},E.prototype.pointFromY=function(A,M){(A=new I(A,16)).red||(A=A.toRed(this.red));var t=A.redSqr(),g=t.redSub(this.c2),e=t.redMul(this.d).redMul(this.c2).redSub(this.a),i=g.redMul(e.redInvm());if(0===i.cmp(this.zero)){if(M)throw new Error("invalid point");return this.point(this.zero,A)}var T=i.redSqrt();if(0!==T.redSqr().redSub(i).cmp(this.zero))throw new Error("invalid point");return T.fromRed().isOdd()!==M&&(T=T.redNeg()),this.point(T,A)},E.prototype.validate=function(A){if(A.isInfinity())return!0;A.normalize();var M=A.x.redSqr(),t=A.y.redSqr(),g=M.redMul(this.a).redAdd(t),I=this.c2.redMul(this.one.redAdd(this.d.redMul(M).redMul(t)));return 0===g.cmp(I)},e(N,i.BasePoint),E.prototype.pointFromJSON=function(A){return N.fromJSON(this,A)},E.prototype.point=function(A,M,t,g){return new N(this,A,M,t,g)},N.fromJSON=function(A,M){return new N(A,M[0],M[1],M[2])},N.prototype.inspect=function(){return this.isInfinity()?"":""},N.prototype.isInfinity=function(){return 0===this.x.cmpn(0)&&(0===this.y.cmp(this.z)||this.zOne&&0===this.y.cmp(this.curve.c))},N.prototype._extDbl=function(){var A=this.x.redSqr(),M=this.y.redSqr(),t=this.z.redSqr();t=t.redIAdd(t);var g=this.curve._mulA(A),I=this.x.redAdd(this.y).redSqr().redISub(A).redISub(M),e=g.redAdd(M),i=e.redSub(t),T=g.redSub(M),E=I.redMul(i),N=e.redMul(T),n=I.redMul(T),D=i.redMul(e);return this.curve.point(E,N,D,n)},N.prototype._projDbl=function(){var A,M,t,g=this.x.redAdd(this.y).redSqr(),I=this.x.redSqr(),e=this.y.redSqr();if(this.curve.twisted){var i=(N=this.curve._mulA(I)).redAdd(e);if(this.zOne)A=g.redSub(I).redSub(e).redMul(i.redSub(this.curve.two)),M=i.redMul(N.redSub(e)),t=i.redSqr().redSub(i).redSub(i);else{var T=this.z.redSqr(),E=i.redSub(T).redISub(T);A=g.redSub(I).redISub(e).redMul(E),M=i.redMul(N.redSub(e)),t=i.redMul(E)}}else{var N=I.redAdd(e);T=this.curve._mulC(this.z).redSqr(),E=N.redSub(T).redSub(T);A=this.curve._mulC(g.redISub(N)).redMul(E),M=this.curve._mulC(N).redMul(I.redISub(e)),t=N.redMul(E)}return this.curve.point(A,M,t)},N.prototype.dbl=function(){return this.isInfinity()?this:this.curve.extended?this._extDbl():this._projDbl()},N.prototype._extAdd=function(A){var M=this.y.redSub(this.x).redMul(A.y.redSub(A.x)),t=this.y.redAdd(this.x).redMul(A.y.redAdd(A.x)),g=this.t.redMul(this.curve.dd).redMul(A.t),I=this.z.redMul(A.z.redAdd(A.z)),e=t.redSub(M),i=I.redSub(g),T=I.redAdd(g),E=t.redAdd(M),N=e.redMul(i),n=T.redMul(E),D=e.redMul(E),C=i.redMul(T);return this.curve.point(N,n,C,D)},N.prototype._projAdd=function(A){var M,t,g=this.z.redMul(A.z),I=g.redSqr(),e=this.x.redMul(A.x),i=this.y.redMul(A.y),T=this.curve.d.redMul(e).redMul(i),E=I.redSub(T),N=I.redAdd(T),n=this.x.redAdd(this.y).redMul(A.x.redAdd(A.y)).redISub(e).redISub(i),D=g.redMul(E).redMul(n);return this.curve.twisted?(M=g.redMul(N).redMul(i.redSub(this.curve._mulA(e))),t=E.redMul(N)):(M=g.redMul(N).redMul(i.redSub(e)),t=this.curve._mulC(E).redMul(N)),this.curve.point(D,M,t)},N.prototype.add=function(A){return this.isInfinity()?A:A.isInfinity()?this:this.curve.extended?this._extAdd(A):this._projAdd(A)},N.prototype.mul=function(A){return this._hasDoubles(A)?this.curve._fixedNafMul(this,A):this.curve._wnafMul(this,A)},N.prototype.mulAdd=function(A,M,t){return this.curve._wnafMulAdd(1,[this,M],[A,t],2,!1)},N.prototype.jmulAdd=function(A,M,t){return this.curve._wnafMulAdd(1,[this,M],[A,t],2,!0)},N.prototype.normalize=function(){if(this.zOne)return this;var A=this.z.redInvm();return this.x=this.x.redMul(A),this.y=this.y.redMul(A),this.t&&(this.t=this.t.redMul(A)),this.z=this.curve.one,this.zOne=!0,this},N.prototype.neg=function(){return this.curve.point(this.x.redNeg(),this.y,this.z,this.t&&this.t.redNeg())},N.prototype.getX=function(){return this.normalize(),this.x.fromRed()},N.prototype.getY=function(){return this.normalize(),this.y.fromRed()},N.prototype.eq=function(A){return this===A||0===this.getX().cmp(A.getX())&&0===this.getY().cmp(A.getY())},N.prototype.eqXToP=function(A){var M=A.toRed(this.curve.red).redMul(this.z);if(0===this.x.cmp(M))return!0;for(var t=A.clone(),g=this.curve.redN.redMul(this.z);;){if(t.iadd(this.curve.n),t.cmp(this.curve.p)>=0)return!1;if(M.redIAdd(g),0===this.x.cmp(M))return!0}},N.prototype.toP=N.prototype.normalize,N.prototype.mixedAdd=N.prototype.add},function(A,M,t){"use strict";M.sha1=t(176),M.sha224=t(177),M.sha256=t(97),M.sha384=t(178),M.sha512=t(98)},function(A,M,t){"use strict";var g=t(14),I=t(27),e=t(96),i=g.rotl32,T=g.sum32,E=g.sum32_5,N=e.ft_1,n=I.BlockHash,D=[1518500249,1859775393,2400959708,3395469782];function C(){if(!(this instanceof C))return new C;n.call(this),this.h=[1732584193,4023233417,2562383102,271733878,3285377520],this.W=new Array(80)}g.inherits(C,n),A.exports=C,C.blockSize=512,C.outSize=160,C.hmacStrength=80,C.padLength=64,C.prototype._update=function(A,M){for(var t=this.W,g=0;g<16;g++)t[g]=A[M+g];for(;gthis.blockSize&&(A=(new this.Hash).update(A).digest()),I(A.length<=this.blockSize);for(var M=A.length;M0))return i.iaddn(1),this.keyFromPrivate(i)}},D.prototype._truncateToN=function(A,M){var t=8*A.byteLength()-this.n.bitLength();return t>0&&(A=A.ushrn(t)),!M&&A.cmp(this.n)>=0?A.sub(this.n):A},D.prototype.sign=function(A,M,t,e){"object"==typeof t&&(e=t,t=null),e||(e={}),M=this.keyFromPrivate(M,t),A=this._truncateToN(new g(A,16));for(var i=this.n.byteLength(),T=M.getPrivate().toArray("be",i),E=A.toArray("be",i),N=new I({hash:this.hash,entropy:T,nonce:E,pers:e.pers,persEnc:e.persEnc||"utf8"}),D=this.n.sub(new g(1)),C=0;;C++){var r=e.k?e.k(C):new g(N.generate(this.n.byteLength()));if(!((r=this._truncateToN(r,!0)).cmpn(1)<=0||r.cmp(D)>=0)){var c=this.g.mul(r);if(!c.isInfinity()){var o=c.getX(),B=o.umod(this.n);if(0!==B.cmpn(0)){var Q=r.invm(this.n).mul(B.mul(M.getPrivate()).iadd(A));if(0!==(Q=Q.umod(this.n)).cmpn(0)){var a=(c.getY().isOdd()?1:0)|(0!==o.cmp(B)?2:0);return e.canonical&&Q.cmp(this.nh)>0&&(Q=this.n.sub(Q),a^=1),new n({r:B,s:Q,recoveryParam:a})}}}}}},D.prototype.verify=function(A,M,t,I){A=this._truncateToN(new g(A,16)),t=this.keyFromPublic(t,I);var e=(M=new n(M,"hex")).r,i=M.s;if(e.cmpn(1)<0||e.cmp(this.n)>=0)return!1;if(i.cmpn(1)<0||i.cmp(this.n)>=0)return!1;var T,E=i.invm(this.n),N=E.mul(A).umod(this.n),D=E.mul(e).umod(this.n);return this.curve._maxwellTrick?!(T=this.g.jmulAdd(N,t.getPublic(),D)).isInfinity()&&T.eqXToP(e):!(T=this.g.mulAdd(N,t.getPublic(),D)).isInfinity()&&0===T.getX().umod(this.n).cmp(e)},D.prototype.recoverPubKey=function(A,M,t,I){E((3&t)===t,"The recovery param is more than two bits"),M=new n(M,I);var e=this.n,i=new g(A),T=M.r,N=M.s,D=1&t,C=t>>1;if(T.cmp(this.curve.p.umod(this.curve.n))>=0&&C)throw new Error("Unable to find sencond key candinate");T=C?this.curve.pointFromX(T.add(this.curve.n),D):this.curve.pointFromX(T,D);var r=M.r.invm(e),c=e.sub(i).mul(r).umod(e),o=N.mul(r).umod(e);return this.g.mulAdd(c,T,o)},D.prototype.getKeyRecoveryParam=function(A,M,t,g){if(null!==(M=new n(M,g)).recoveryParam)return M.recoveryParam;for(var I=0;I<4;I++){var e;try{e=this.recoverPubKey(A,M,I)}catch(A){continue}if(e.eq(t))return I}throw new Error("Unable to find valid recovery factor")}},function(A,M,t){"use strict";var g=t(51),I=t(94),e=t(11);function i(A){if(!(this instanceof i))return new i(A);this.hash=A.hash,this.predResist=!!A.predResist,this.outLen=this.hash.outSize,this.minEntropy=A.minEntropy||this.hash.hmacStrength,this._reseed=null,this.reseedInterval=null,this.K=null,this.V=null;var M=I.toArray(A.entropy,A.entropyEnc||"hex"),t=I.toArray(A.nonce,A.nonceEnc||"hex"),g=I.toArray(A.pers,A.persEnc||"hex");e(M.length>=this.minEntropy/8,"Not enough entropy. Minimum is: "+this.minEntropy+" bits"),this._init(M,t,g)}A.exports=i,i.prototype._init=function(A,M,t){var g=A.concat(M).concat(t);this.K=new Array(this.outLen/8),this.V=new Array(this.outLen/8);for(var I=0;I=this.minEntropy/8,"Not enough entropy. Minimum is: "+this.minEntropy+" bits"),this._update(A.concat(t||[])),this._reseed=1},i.prototype.generate=function(A,M,t,g){if(this._reseed>this.reseedInterval)throw new Error("Reseed is required");"string"!=typeof M&&(g=t,t=M,M=null),t&&(t=I.toArray(t,g||"hex"),this._update(t));for(var e=[];e.length"}},function(A,M,t){"use strict";var g=t(6),I=t(12),e=I.assert;function i(A,M){if(A instanceof i)return A;this._importDER(A,M)||(e(A.r&&A.s,"Signature without r or s"),this.r=new g(A.r,16),this.s=new g(A.s,16),void 0===A.recoveryParam?this.recoveryParam=null:this.recoveryParam=A.recoveryParam)}function T(){this.place=0}function E(A,M){var t=A[M.place++];if(!(128&t))return t;var g=15&t;if(0===g||g>4)return!1;for(var I=0,e=0,i=M.place;e>>=0;return!(I<=127)&&(M.place=i,I)}function N(A){for(var M=0,t=A.length-1;!A[M]&&!(128&A[M+1])&&M>>3);for(A.push(128|t);--t;)A.push(M>>>(t<<3)&255);A.push(M)}}A.exports=i,i.prototype._importDER=function(A,M){A=I.toArray(A,M);var t=new T;if(48!==A[t.place++])return!1;var e=E(A,t);if(!1===e)return!1;if(e+t.place!==A.length)return!1;if(2!==A[t.place++])return!1;var i=E(A,t);if(!1===i)return!1;var N=A.slice(t.place,i+t.place);if(t.place+=i,2!==A[t.place++])return!1;var n=E(A,t);if(!1===n)return!1;if(A.length!==n+t.place)return!1;var D=A.slice(t.place,n+t.place);if(0===N[0]){if(!(128&N[1]))return!1;N=N.slice(1)}if(0===D[0]){if(!(128&D[1]))return!1;D=D.slice(1)}return this.r=new g(N),this.s=new g(D),this.recoveryParam=null,!0},i.prototype.toDER=function(A){var M=this.r.toArray(),t=this.s.toArray();for(128&M[0]&&(M=[0].concat(M)),128&t[0]&&(t=[0].concat(t)),M=N(M),t=N(t);!(t[0]||128&t[1]);)t=t.slice(1);var g=[2];n(g,M.length),(g=g.concat(M)).push(2),n(g,t.length);var e=g.concat(t),i=[48];return n(i,e.length),i=i.concat(e),I.encode(i,A)}},function(A,M,t){"use strict";var g=t(51),I=t(50),e=t(12),i=e.assert,T=e.parseBytes,E=t(187),N=t(188);function n(A){if(i("ed25519"===A,"only tested with ed25519 so far"),!(this instanceof n))return new n(A);A=I[A].curve;this.curve=A,this.g=A.g,this.g.precompute(A.n.bitLength()+1),this.pointClass=A.point().constructor,this.encodingLength=Math.ceil(A.n.bitLength()/8),this.hash=g.sha512}A.exports=n,n.prototype.sign=function(A,M){A=T(A);var t=this.keyFromSecret(M),g=this.hashInt(t.messagePrefix(),A),I=this.g.mul(g),e=this.encodePoint(I),i=this.hashInt(e,t.pubBytes(),A).mul(t.priv()),E=g.add(i).umod(this.curve.n);return this.makeSignature({R:I,S:E,Rencoded:e})},n.prototype.verify=function(A,M,t){A=T(A),M=this.makeSignature(M);var g=this.keyFromPublic(t),I=this.hashInt(M.Rencoded(),g.pubBytes(),A),e=this.g.mul(M.S());return M.R().add(g.pub().mul(I)).eq(e)},n.prototype.hashInt=function(){for(var A=this.hash(),M=0;M=M)throw new Error("invalid sig")}A.exports=function(A,M,t,N,n){var D=i(t);if("ec"===D.type){if("ecdsa"!==N&&"ecdsa/rsa"!==N)throw new Error("wrong public key type");return function(A,M,t){var g=T[t.data.algorithm.curve.join(".")];if(!g)throw new Error("unknown curve "+t.data.algorithm.curve.join("."));var I=new e(g),i=t.data.subjectPrivateKey.data;return I.verify(M,A,i)}(A,M,D)}if("dsa"===D.type){if("dsa"!==N)throw new Error("wrong public key type");return function(A,M,t){var g=t.data.p,e=t.data.q,T=t.data.g,N=t.data.pub_key,n=i.signature.decode(A,"der"),D=n.s,C=n.r;E(D,e),E(C,e);var r=I.mont(g),c=D.invm(e);return 0===T.toRed(r).redPow(new I(M).mul(c).mod(e)).fromRed().mul(N.toRed(r).redPow(C.mul(c).mod(e)).fromRed()).mod(g).mod(e).cmp(C)}(A,M,D)}if("rsa"!==N&&"ecdsa/rsa"!==N)throw new Error("wrong public key type");M=g.concat([n,M]);for(var C=D.modulus.byteLength(),r=[1],c=0;M.length+r.length+2t-C-2)throw new Error("message too long");var r=D.alloc(t-g-C-2),c=t-n-1,o=I(n),B=T(D.concat([N,r,D.alloc(1,1),M],c),i(o,c)),Q=T(o,i(B,n));return new E(D.concat([D.alloc(1),Q,B],t))}(c,M);else if(1===C)r=function(A,M,t){var g,e=M.length,i=A.modulus.byteLength();if(e>i-11)throw new Error("message too long");g=t?D.alloc(i-e-3,255):function(A){var M,t=D.allocUnsafe(A),g=0,e=I(2*A),i=0;for(;g=0)throw new Error("data too long for modulus")}return t?n(r,c):N(r,c)}},function(A,M,t){var g=t(35),I=t(106),e=t(107),i=t(6),T=t(48),E=t(23),N=t(108),n=t(4).Buffer;A.exports=function(A,M,t){var D;D=A.padding?A.padding:t?1:4;var C,r=g(A),c=r.modulus.byteLength();if(M.length>c||new i(M).cmp(r.modulus)>=0)throw new Error("decryption error");C=t?N(new i(M),r):T(M,r);var o=n.alloc(c-C.length);if(C=n.concat([o,C],c),4===D)return function(A,M){var t=A.modulus.byteLength(),g=E("sha1").update(n.alloc(0)).digest(),i=g.length;if(0!==M[0])throw new Error("decryption error");var T=M.slice(1,i+1),N=M.slice(i+1),D=e(T,I(N,i)),C=e(N,I(D,t-i-1));if(function(A,M){A=n.from(A),M=n.from(M);var t=0,g=A.length;A.length!==M.length&&(t++,g=Math.min(A.length,M.length));var I=-1;for(;++I=M.length){e++;break}var i=M.slice(2,I-1);("0002"!==g.toString("hex")&&!t||"0001"!==g.toString("hex")&&t)&&e++;i.length<8&&e++;if(e)throw new Error("decryption error");return M.slice(I)}(0,C,t);if(3===D)return C;throw new Error("unknown padding")}},function(A,M,t){"use strict";(function(A,g){function I(){throw new Error("secure random number generation not supported by this browser\nuse chrome, FireFox or Internet Explorer 11")}var e=t(4),i=t(18),T=e.Buffer,E=e.kMaxLength,N=A.crypto||A.msCrypto,n=Math.pow(2,32)-1;function D(A,M){if("number"!=typeof A||A!=A)throw new TypeError("offset must be a number");if(A>n||A<0)throw new TypeError("offset must be a uint32");if(A>E||A>M)throw new RangeError("offset out of range")}function C(A,M,t){if("number"!=typeof A||A!=A)throw new TypeError("size must be a number");if(A>n||A<0)throw new TypeError("size must be a uint32");if(A+M>t||A>E)throw new RangeError("buffer too small")}function r(A,M,t,I){if(g.browser){var e=A.buffer,T=new Uint8Array(e,M,t);return N.getRandomValues(T),I?void g.nextTick((function(){I(null,A)})):A}if(!I)return i(t).copy(A,M),A;i(t,(function(t,g){if(t)return I(t);g.copy(A,M),I(null,A)}))}N&&N.getRandomValues||!g.browser?(M.randomFill=function(M,t,g,I){if(!(T.isBuffer(M)||M instanceof A.Uint8Array))throw new TypeError('"buf" argument must be a Buffer or Uint8Array');if("function"==typeof t)I=t,t=0,g=M.length;else if("function"==typeof g)I=g,g=M.length-t;else if("function"!=typeof I)throw new TypeError('"cb" argument must be a function');return D(t,M.length),C(g,t,M.length),r(M,t,g,I)},M.randomFillSync=function(M,t,g){void 0===t&&(t=0);if(!(T.isBuffer(M)||M instanceof A.Uint8Array))throw new TypeError('"buf" argument must be a Buffer or Uint8Array');D(t,M.length),void 0===g&&(g=M.length-t);return C(g,t,M.length),r(M,t,g)}):(M.randomFill=I,M.randomFillSync=I)}).call(this,t(10),t(8))},function(A,M,t){"use strict";t.r(M);var g=t(0),I=t(29);M.default=function(A){console.log("new AudioWorker.js"),A._requestAbort=!1,A.soundPlayer=null,A.audioSampleQueue=[];A.addEventListener("message",(function(M){var t=M.data;switch(console.log("AudioWorker: recv data."),t.cmd){case g.u:console.log("init audio player."),function(M,t,g){if(null==A.soundPlayer){var e="16bitInt";switch(M){case 0:e="8bitInt";break;case 1:e="16bitInt";break;case 2:e="32bitInt";break;case 3:e="32bitFloat";break;default:this.logger.logError("Unsupported audio sampleFmt "+M+"!")}A.soundPlayer=new I.a,A.soundPlayer.init({encoding:e,channels:t,sampleRate:g,flushingTime:5e3})}}(1,2,8e3);break;case g.o:console.log("recv audio data...");break;case g.k:A._requestAbort=!0}}))}},function(A,M,t){var g=t(56),I=t(207);"string"==typeof(I=I.__esModule?I.default:I)&&(I=[[A.i,I,""]]);var e={insert:"head",singleton:!1};g(I,e);A.exports=I.locals||{}},function(A,M,t){var g=t(57),I=t(109),e=t(208),i=t(209),T=t(210),E=t(211),N=t(212),n=t(213);M=g(!1);var D=I(e),C=I(i,{hash:"?#iefix&v=4.7.0"}),r=I(T),c=I(E),o=I(N),B=I(n,{hash:"#fontawesomeregular"});M.push([A.i,"/*!\r\n * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome\r\n * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)\r\n */\r\n/* FONT PATH\r\n * -------------------------- */\r\n@font-face {\r\n font-family: 'FontAwesome';\r\n src: url("+D+");\r\n src: url("+C+") format('embedded-opentype'), \r\n url("+r+") format('woff2'), \r\n url("+c+") format('woff'), url("+o+") format('truetype'), url("+B+') format(\'svg\');\r\n font-weight: normal;\r\n font-style: normal;\r\n}\r\n.fa {\r\n display: inline-block;\r\n font: normal normal normal 14px/1 FontAwesome;\r\n font-size: inherit;\r\n text-rendering: auto;\r\n -webkit-font-smoothing: antialiased;\r\n -moz-osx-font-smoothing: grayscale;\r\n}\r\n/* makes the font 33% larger relative to the icon container */\r\n.fa-lg {\r\n font-size: 1.33333333em;\r\n line-height: 0.75em;\r\n vertical-align: -15%;\r\n}\r\n.fa-2x {\r\n font-size: 2em;\r\n}\r\n.fa-3x {\r\n font-size: 3em;\r\n}\r\n.fa-4x {\r\n font-size: 4em;\r\n}\r\n.fa-5x {\r\n font-size: 5em;\r\n}\r\n.fa-fw {\r\n width: 1.28571429em;\r\n text-align: center;\r\n}\r\n.fa-ul {\r\n padding-left: 0;\r\n margin-left: 2.14285714em;\r\n list-style-type: none;\r\n}\r\n.fa-ul > li {\r\n position: relative;\r\n}\r\n.fa-li {\r\n position: absolute;\r\n left: -2.14285714em;\r\n width: 2.14285714em;\r\n top: 0.14285714em;\r\n text-align: center;\r\n}\r\n.fa-li.fa-lg {\r\n left: -1.85714286em;\r\n}\r\n.fa-border {\r\n padding: .2em .25em .15em;\r\n border: solid 0.08em #eee;\r\n border-radius: .1em;\r\n}\r\n.fa-pull-left {\r\n float: left;\r\n}\r\n.fa-pull-right {\r\n float: right;\r\n}\r\n.fa.fa-pull-left {\r\n margin-right: .3em;\r\n}\r\n.fa.fa-pull-right {\r\n margin-left: .3em;\r\n}\r\n/* Deprecated as of 4.4.0 */\r\n.pull-right {\r\n float: right;\r\n}\r\n.pull-left {\r\n float: left;\r\n}\r\n.fa.pull-left {\r\n margin-right: .3em;\r\n}\r\n.fa.pull-right {\r\n margin-left: .3em;\r\n}\r\n.fa-spin {\r\n -webkit-animation: fa-spin 2s infinite linear;\r\n animation: fa-spin 2s infinite linear;\r\n}\r\n.fa-pulse {\r\n -webkit-animation: fa-spin 1s infinite steps(8);\r\n animation: fa-spin 1s infinite steps(8);\r\n}\r\n@-webkit-keyframes fa-spin {\r\n 0% {\r\n -webkit-transform: rotate(0deg);\r\n transform: rotate(0deg);\r\n }\r\n 100% {\r\n -webkit-transform: rotate(359deg);\r\n transform: rotate(359deg);\r\n }\r\n}\r\n@keyframes fa-spin {\r\n 0% {\r\n -webkit-transform: rotate(0deg);\r\n transform: rotate(0deg);\r\n }\r\n 100% {\r\n -webkit-transform: rotate(359deg);\r\n transform: rotate(359deg);\r\n }\r\n}\r\n.fa-rotate-90 {\r\n -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";\r\n -webkit-transform: rotate(90deg);\r\n -ms-transform: rotate(90deg);\r\n transform: rotate(90deg);\r\n}\r\n.fa-rotate-180 {\r\n -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";\r\n -webkit-transform: rotate(180deg);\r\n -ms-transform: rotate(180deg);\r\n transform: rotate(180deg);\r\n}\r\n.fa-rotate-270 {\r\n -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";\r\n -webkit-transform: rotate(270deg);\r\n -ms-transform: rotate(270deg);\r\n transform: rotate(270deg);\r\n}\r\n.fa-flip-horizontal {\r\n -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";\r\n -webkit-transform: scale(-1, 1);\r\n -ms-transform: scale(-1, 1);\r\n transform: scale(-1, 1);\r\n}\r\n.fa-flip-vertical {\r\n -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";\r\n -webkit-transform: scale(1, -1);\r\n -ms-transform: scale(1, -1);\r\n transform: scale(1, -1);\r\n}\r\n:root .fa-rotate-90,\r\n:root .fa-rotate-180,\r\n:root .fa-rotate-270,\r\n:root .fa-flip-horizontal,\r\n:root .fa-flip-vertical {\r\n filter: none;\r\n}\r\n.fa-stack {\r\n position: relative;\r\n display: inline-block;\r\n width: 2em;\r\n height: 2em;\r\n line-height: 2em;\r\n vertical-align: middle;\r\n}\r\n.fa-stack-1x,\r\n.fa-stack-2x {\r\n position: absolute;\r\n left: 0;\r\n width: 100%;\r\n text-align: center;\r\n}\r\n.fa-stack-1x {\r\n line-height: inherit;\r\n}\r\n.fa-stack-2x {\r\n font-size: 2em;\r\n}\r\n.fa-inverse {\r\n color: #fff;\r\n}\r\n/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen\r\n readers do not read off random characters that represent icons */\r\n.fa-glass:before {\r\n content: "\\f000";\r\n}\r\n.fa-music:before {\r\n content: "\\f001";\r\n}\r\n.fa-search:before {\r\n content: "\\f002";\r\n}\r\n.fa-envelope-o:before {\r\n content: "\\f003";\r\n}\r\n.fa-heart:before {\r\n content: "\\f004";\r\n}\r\n.fa-star:before {\r\n content: "\\f005";\r\n}\r\n.fa-star-o:before {\r\n content: "\\f006";\r\n}\r\n.fa-user:before {\r\n content: "\\f007";\r\n}\r\n.fa-film:before {\r\n content: "\\f008";\r\n}\r\n.fa-th-large:before {\r\n content: "\\f009";\r\n}\r\n.fa-th:before {\r\n content: "\\f00a";\r\n}\r\n.fa-th-list:before {\r\n content: "\\f00b";\r\n}\r\n.fa-check:before {\r\n content: "\\f00c";\r\n}\r\n.fa-remove:before,\r\n.fa-close:before,\r\n.fa-times:before {\r\n content: "\\f00d";\r\n}\r\n.fa-search-plus:before {\r\n content: "\\f00e";\r\n}\r\n.fa-search-minus:before {\r\n content: "\\f010";\r\n}\r\n.fa-power-off:before {\r\n content: "\\f011";\r\n}\r\n.fa-signal:before {\r\n content: "\\f012";\r\n}\r\n.fa-gear:before,\r\n.fa-cog:before {\r\n content: "\\f013";\r\n}\r\n.fa-trash-o:before {\r\n content: "\\f014";\r\n}\r\n.fa-home:before {\r\n content: "\\f015";\r\n}\r\n.fa-file-o:before {\r\n content: "\\f016";\r\n}\r\n.fa-clock-o:before {\r\n content: "\\f017";\r\n}\r\n.fa-road:before {\r\n content: "\\f018";\r\n}\r\n.fa-download:before {\r\n content: "\\f019";\r\n}\r\n.fa-arrow-circle-o-down:before {\r\n content: "\\f01a";\r\n}\r\n.fa-arrow-circle-o-up:before {\r\n content: "\\f01b";\r\n}\r\n.fa-inbox:before {\r\n content: "\\f01c";\r\n}\r\n.fa-play-circle-o:before {\r\n content: "\\f01d";\r\n}\r\n.fa-rotate-right:before,\r\n.fa-repeat:before {\r\n content: "\\f01e";\r\n}\r\n.fa-refresh:before {\r\n content: "\\f021";\r\n}\r\n.fa-list-alt:before {\r\n content: "\\f022";\r\n}\r\n.fa-lock:before {\r\n content: "\\f023";\r\n}\r\n.fa-flag:before {\r\n content: "\\f024";\r\n}\r\n.fa-headphones:before {\r\n content: "\\f025";\r\n}\r\n.fa-volume-off:before {\r\n content: "\\f026";\r\n}\r\n.fa-volume-down:before {\r\n content: "\\f027";\r\n}\r\n.fa-volume-up:before {\r\n content: "\\f028";\r\n}\r\n.fa-qrcode:before {\r\n content: "\\f029";\r\n}\r\n.fa-barcode:before {\r\n content: "\\f02a";\r\n}\r\n.fa-tag:before {\r\n content: "\\f02b";\r\n}\r\n.fa-tags:before {\r\n content: "\\f02c";\r\n}\r\n.fa-book:before {\r\n content: "\\f02d";\r\n}\r\n.fa-bookmark:before {\r\n content: "\\f02e";\r\n}\r\n.fa-print:before {\r\n content: "\\f02f";\r\n}\r\n.fa-camera:before {\r\n content: "\\f030";\r\n}\r\n.fa-font:before {\r\n content: "\\f031";\r\n}\r\n.fa-bold:before {\r\n content: "\\f032";\r\n}\r\n.fa-italic:before {\r\n content: "\\f033";\r\n}\r\n.fa-text-height:before {\r\n content: "\\f034";\r\n}\r\n.fa-text-width:before {\r\n content: "\\f035";\r\n}\r\n.fa-align-left:before {\r\n content: "\\f036";\r\n}\r\n.fa-align-center:before {\r\n content: "\\f037";\r\n}\r\n.fa-align-right:before {\r\n content: "\\f038";\r\n}\r\n.fa-align-justify:before {\r\n content: "\\f039";\r\n}\r\n.fa-list:before {\r\n content: "\\f03a";\r\n}\r\n.fa-dedent:before,\r\n.fa-outdent:before {\r\n content: "\\f03b";\r\n}\r\n.fa-indent:before {\r\n content: "\\f03c";\r\n}\r\n.fa-video-camera:before {\r\n content: "\\f03d";\r\n}\r\n.fa-photo:before,\r\n.fa-image:before,\r\n.fa-picture-o:before {\r\n content: "\\f03e";\r\n}\r\n.fa-pencil:before {\r\n content: "\\f040";\r\n}\r\n.fa-map-marker:before {\r\n content: "\\f041";\r\n}\r\n.fa-adjust:before {\r\n content: "\\f042";\r\n}\r\n.fa-tint:before {\r\n content: "\\f043";\r\n}\r\n.fa-edit:before,\r\n.fa-pencil-square-o:before {\r\n content: "\\f044";\r\n}\r\n.fa-share-square-o:before {\r\n content: "\\f045";\r\n}\r\n.fa-check-square-o:before {\r\n content: "\\f046";\r\n}\r\n.fa-arrows:before {\r\n content: "\\f047";\r\n}\r\n.fa-step-backward:before {\r\n content: "\\f048";\r\n}\r\n.fa-fast-backward:before {\r\n content: "\\f049";\r\n}\r\n.fa-backward:before {\r\n content: "\\f04a";\r\n}\r\n.fa-play:before {\r\n content: "\\f04b";\r\n}\r\n.fa-pause:before {\r\n content: "\\f04c";\r\n}\r\n.fa-stop:before {\r\n content: "\\f04d";\r\n}\r\n.fa-forward:before {\r\n content: "\\f04e";\r\n}\r\n.fa-fast-forward:before {\r\n content: "\\f050";\r\n}\r\n.fa-step-forward:before {\r\n content: "\\f051";\r\n}\r\n.fa-eject:before {\r\n content: "\\f052";\r\n}\r\n.fa-chevron-left:before {\r\n content: "\\f053";\r\n}\r\n.fa-chevron-right:before {\r\n content: "\\f054";\r\n}\r\n.fa-plus-circle:before {\r\n content: "\\f055";\r\n}\r\n.fa-minus-circle:before {\r\n content: "\\f056";\r\n}\r\n.fa-times-circle:before {\r\n content: "\\f057";\r\n}\r\n.fa-check-circle:before {\r\n content: "\\f058";\r\n}\r\n.fa-question-circle:before {\r\n content: "\\f059";\r\n}\r\n.fa-info-circle:before {\r\n content: "\\f05a";\r\n}\r\n.fa-crosshairs:before {\r\n content: "\\f05b";\r\n}\r\n.fa-times-circle-o:before {\r\n content: "\\f05c";\r\n}\r\n.fa-check-circle-o:before {\r\n content: "\\f05d";\r\n}\r\n.fa-ban:before {\r\n content: "\\f05e";\r\n}\r\n.fa-arrow-left:before {\r\n content: "\\f060";\r\n}\r\n.fa-arrow-right:before {\r\n content: "\\f061";\r\n}\r\n.fa-arrow-up:before {\r\n content: "\\f062";\r\n}\r\n.fa-arrow-down:before {\r\n content: "\\f063";\r\n}\r\n.fa-mail-forward:before,\r\n.fa-share:before {\r\n content: "\\f064";\r\n}\r\n.fa-expand:before {\r\n content: "\\f065";\r\n}\r\n.fa-compress:before {\r\n content: "\\f066";\r\n}\r\n.fa-plus:before {\r\n content: "\\f067";\r\n}\r\n.fa-minus:before {\r\n content: "\\f068";\r\n}\r\n.fa-asterisk:before {\r\n content: "\\f069";\r\n}\r\n.fa-exclamation-circle:before {\r\n content: "\\f06a";\r\n}\r\n.fa-gift:before {\r\n content: "\\f06b";\r\n}\r\n.fa-leaf:before {\r\n content: "\\f06c";\r\n}\r\n.fa-fire:before {\r\n content: "\\f06d";\r\n}\r\n.fa-eye:before {\r\n content: "\\f06e";\r\n}\r\n.fa-eye-slash:before {\r\n content: "\\f070";\r\n}\r\n.fa-warning:before,\r\n.fa-exclamation-triangle:before {\r\n content: "\\f071";\r\n}\r\n.fa-plane:before {\r\n content: "\\f072";\r\n}\r\n.fa-calendar:before {\r\n content: "\\f073";\r\n}\r\n.fa-random:before {\r\n content: "\\f074";\r\n}\r\n.fa-comment:before {\r\n content: "\\f075";\r\n}\r\n.fa-magnet:before {\r\n content: "\\f076";\r\n}\r\n.fa-chevron-up:before {\r\n content: "\\f077";\r\n}\r\n.fa-chevron-down:before {\r\n content: "\\f078";\r\n}\r\n.fa-retweet:before {\r\n content: "\\f079";\r\n}\r\n.fa-shopping-cart:before {\r\n content: "\\f07a";\r\n}\r\n.fa-folder:before {\r\n content: "\\f07b";\r\n}\r\n.fa-folder-open:before {\r\n content: "\\f07c";\r\n}\r\n.fa-arrows-v:before {\r\n content: "\\f07d";\r\n}\r\n.fa-arrows-h:before {\r\n content: "\\f07e";\r\n}\r\n.fa-bar-chart-o:before,\r\n.fa-bar-chart:before {\r\n content: "\\f080";\r\n}\r\n.fa-twitter-square:before {\r\n content: "\\f081";\r\n}\r\n.fa-facebook-square:before {\r\n content: "\\f082";\r\n}\r\n.fa-camera-retro:before {\r\n content: "\\f083";\r\n}\r\n.fa-key:before {\r\n content: "\\f084";\r\n}\r\n.fa-gears:before,\r\n.fa-cogs:before {\r\n content: "\\f085";\r\n}\r\n.fa-comments:before {\r\n content: "\\f086";\r\n}\r\n.fa-thumbs-o-up:before {\r\n content: "\\f087";\r\n}\r\n.fa-thumbs-o-down:before {\r\n content: "\\f088";\r\n}\r\n.fa-star-half:before {\r\n content: "\\f089";\r\n}\r\n.fa-heart-o:before {\r\n content: "\\f08a";\r\n}\r\n.fa-sign-out:before {\r\n content: "\\f08b";\r\n}\r\n.fa-linkedin-square:before {\r\n content: "\\f08c";\r\n}\r\n.fa-thumb-tack:before {\r\n content: "\\f08d";\r\n}\r\n.fa-external-link:before {\r\n content: "\\f08e";\r\n}\r\n.fa-sign-in:before {\r\n content: "\\f090";\r\n}\r\n.fa-trophy:before {\r\n content: "\\f091";\r\n}\r\n.fa-github-square:before {\r\n content: "\\f092";\r\n}\r\n.fa-upload:before {\r\n content: "\\f093";\r\n}\r\n.fa-lemon-o:before {\r\n content: "\\f094";\r\n}\r\n.fa-phone:before {\r\n content: "\\f095";\r\n}\r\n.fa-square-o:before {\r\n content: "\\f096";\r\n}\r\n.fa-bookmark-o:before {\r\n content: "\\f097";\r\n}\r\n.fa-phone-square:before {\r\n content: "\\f098";\r\n}\r\n.fa-twitter:before {\r\n content: "\\f099";\r\n}\r\n.fa-facebook-f:before,\r\n.fa-facebook:before {\r\n content: "\\f09a";\r\n}\r\n.fa-github:before {\r\n content: "\\f09b";\r\n}\r\n.fa-unlock:before {\r\n content: "\\f09c";\r\n}\r\n.fa-credit-card:before {\r\n content: "\\f09d";\r\n}\r\n.fa-feed:before,\r\n.fa-rss:before {\r\n content: "\\f09e";\r\n}\r\n.fa-hdd-o:before {\r\n content: "\\f0a0";\r\n}\r\n.fa-bullhorn:before {\r\n content: "\\f0a1";\r\n}\r\n.fa-bell:before {\r\n content: "\\f0f3";\r\n}\r\n.fa-certificate:before {\r\n content: "\\f0a3";\r\n}\r\n.fa-hand-o-right:before {\r\n content: "\\f0a4";\r\n}\r\n.fa-hand-o-left:before {\r\n content: "\\f0a5";\r\n}\r\n.fa-hand-o-up:before {\r\n content: "\\f0a6";\r\n}\r\n.fa-hand-o-down:before {\r\n content: "\\f0a7";\r\n}\r\n.fa-arrow-circle-left:before {\r\n content: "\\f0a8";\r\n}\r\n.fa-arrow-circle-right:before {\r\n content: "\\f0a9";\r\n}\r\n.fa-arrow-circle-up:before {\r\n content: "\\f0aa";\r\n}\r\n.fa-arrow-circle-down:before {\r\n content: "\\f0ab";\r\n}\r\n.fa-globe:before {\r\n content: "\\f0ac";\r\n}\r\n.fa-wrench:before {\r\n content: "\\f0ad";\r\n}\r\n.fa-tasks:before {\r\n content: "\\f0ae";\r\n}\r\n.fa-filter:before {\r\n content: "\\f0b0";\r\n}\r\n.fa-briefcase:before {\r\n content: "\\f0b1";\r\n}\r\n.fa-arrows-alt:before {\r\n content: "\\f0b2";\r\n}\r\n.fa-group:before,\r\n.fa-users:before {\r\n content: "\\f0c0";\r\n}\r\n.fa-chain:before,\r\n.fa-link:before {\r\n content: "\\f0c1";\r\n}\r\n.fa-cloud:before {\r\n content: "\\f0c2";\r\n}\r\n.fa-flask:before {\r\n content: "\\f0c3";\r\n}\r\n.fa-cut:before,\r\n.fa-scissors:before {\r\n content: "\\f0c4";\r\n}\r\n.fa-copy:before,\r\n.fa-files-o:before {\r\n content: "\\f0c5";\r\n}\r\n.fa-paperclip:before {\r\n content: "\\f0c6";\r\n}\r\n.fa-save:before,\r\n.fa-floppy-o:before {\r\n content: "\\f0c7";\r\n}\r\n.fa-square:before {\r\n content: "\\f0c8";\r\n}\r\n.fa-navicon:before,\r\n.fa-reorder:before,\r\n.fa-bars:before {\r\n content: "\\f0c9";\r\n}\r\n.fa-list-ul:before {\r\n content: "\\f0ca";\r\n}\r\n.fa-list-ol:before {\r\n content: "\\f0cb";\r\n}\r\n.fa-strikethrough:before {\r\n content: "\\f0cc";\r\n}\r\n.fa-underline:before {\r\n content: "\\f0cd";\r\n}\r\n.fa-table:before {\r\n content: "\\f0ce";\r\n}\r\n.fa-magic:before {\r\n content: "\\f0d0";\r\n}\r\n.fa-truck:before {\r\n content: "\\f0d1";\r\n}\r\n.fa-pinterest:before {\r\n content: "\\f0d2";\r\n}\r\n.fa-pinterest-square:before {\r\n content: "\\f0d3";\r\n}\r\n.fa-google-plus-square:before {\r\n content: "\\f0d4";\r\n}\r\n.fa-google-plus:before {\r\n content: "\\f0d5";\r\n}\r\n.fa-money:before {\r\n content: "\\f0d6";\r\n}\r\n.fa-caret-down:before {\r\n content: "\\f0d7";\r\n}\r\n.fa-caret-up:before {\r\n content: "\\f0d8";\r\n}\r\n.fa-caret-left:before {\r\n content: "\\f0d9";\r\n}\r\n.fa-caret-right:before {\r\n content: "\\f0da";\r\n}\r\n.fa-columns:before {\r\n content: "\\f0db";\r\n}\r\n.fa-unsorted:before,\r\n.fa-sort:before {\r\n content: "\\f0dc";\r\n}\r\n.fa-sort-down:before,\r\n.fa-sort-desc:before {\r\n content: "\\f0dd";\r\n}\r\n.fa-sort-up:before,\r\n.fa-sort-asc:before {\r\n content: "\\f0de";\r\n}\r\n.fa-envelope:before {\r\n content: "\\f0e0";\r\n}\r\n.fa-linkedin:before {\r\n content: "\\f0e1";\r\n}\r\n.fa-rotate-left:before,\r\n.fa-undo:before {\r\n content: "\\f0e2";\r\n}\r\n.fa-legal:before,\r\n.fa-gavel:before {\r\n content: "\\f0e3";\r\n}\r\n.fa-dashboard:before,\r\n.fa-tachometer:before {\r\n content: "\\f0e4";\r\n}\r\n.fa-comment-o:before {\r\n content: "\\f0e5";\r\n}\r\n.fa-comments-o:before {\r\n content: "\\f0e6";\r\n}\r\n.fa-flash:before,\r\n.fa-bolt:before {\r\n content: "\\f0e7";\r\n}\r\n.fa-sitemap:before {\r\n content: "\\f0e8";\r\n}\r\n.fa-umbrella:before {\r\n content: "\\f0e9";\r\n}\r\n.fa-paste:before,\r\n.fa-clipboard:before {\r\n content: "\\f0ea";\r\n}\r\n.fa-lightbulb-o:before {\r\n content: "\\f0eb";\r\n}\r\n.fa-exchange:before {\r\n content: "\\f0ec";\r\n}\r\n.fa-cloud-download:before {\r\n content: "\\f0ed";\r\n}\r\n.fa-cloud-upload:before {\r\n content: "\\f0ee";\r\n}\r\n.fa-user-md:before {\r\n content: "\\f0f0";\r\n}\r\n.fa-stethoscope:before {\r\n content: "\\f0f1";\r\n}\r\n.fa-suitcase:before {\r\n content: "\\f0f2";\r\n}\r\n.fa-bell-o:before {\r\n content: "\\f0a2";\r\n}\r\n.fa-coffee:before {\r\n content: "\\f0f4";\r\n}\r\n.fa-cutlery:before {\r\n content: "\\f0f5";\r\n}\r\n.fa-file-text-o:before {\r\n content: "\\f0f6";\r\n}\r\n.fa-building-o:before {\r\n content: "\\f0f7";\r\n}\r\n.fa-hospital-o:before {\r\n content: "\\f0f8";\r\n}\r\n.fa-ambulance:before {\r\n content: "\\f0f9";\r\n}\r\n.fa-medkit:before {\r\n content: "\\f0fa";\r\n}\r\n.fa-fighter-jet:before {\r\n content: "\\f0fb";\r\n}\r\n.fa-beer:before {\r\n content: "\\f0fc";\r\n}\r\n.fa-h-square:before {\r\n content: "\\f0fd";\r\n}\r\n.fa-plus-square:before {\r\n content: "\\f0fe";\r\n}\r\n.fa-angle-double-left:before {\r\n content: "\\f100";\r\n}\r\n.fa-angle-double-right:before {\r\n content: "\\f101";\r\n}\r\n.fa-angle-double-up:before {\r\n content: "\\f102";\r\n}\r\n.fa-angle-double-down:before {\r\n content: "\\f103";\r\n}\r\n.fa-angle-left:before {\r\n content: "\\f104";\r\n}\r\n.fa-angle-right:before {\r\n content: "\\f105";\r\n}\r\n.fa-angle-up:before {\r\n content: "\\f106";\r\n}\r\n.fa-angle-down:before {\r\n content: "\\f107";\r\n}\r\n.fa-desktop:before {\r\n content: "\\f108";\r\n}\r\n.fa-laptop:before {\r\n content: "\\f109";\r\n}\r\n.fa-tablet:before {\r\n content: "\\f10a";\r\n}\r\n.fa-mobile-phone:before,\r\n.fa-mobile:before {\r\n content: "\\f10b";\r\n}\r\n.fa-circle-o:before {\r\n content: "\\f10c";\r\n}\r\n.fa-quote-left:before {\r\n content: "\\f10d";\r\n}\r\n.fa-quote-right:before {\r\n content: "\\f10e";\r\n}\r\n.fa-spinner:before {\r\n content: "\\f110";\r\n}\r\n.fa-circle:before {\r\n content: "\\f111";\r\n}\r\n.fa-mail-reply:before,\r\n.fa-reply:before {\r\n content: "\\f112";\r\n}\r\n.fa-github-alt:before {\r\n content: "\\f113";\r\n}\r\n.fa-folder-o:before {\r\n content: "\\f114";\r\n}\r\n.fa-folder-open-o:before {\r\n content: "\\f115";\r\n}\r\n.fa-smile-o:before {\r\n content: "\\f118";\r\n}\r\n.fa-frown-o:before {\r\n content: "\\f119";\r\n}\r\n.fa-meh-o:before {\r\n content: "\\f11a";\r\n}\r\n.fa-gamepad:before {\r\n content: "\\f11b";\r\n}\r\n.fa-keyboard-o:before {\r\n content: "\\f11c";\r\n}\r\n.fa-flag-o:before {\r\n content: "\\f11d";\r\n}\r\n.fa-flag-checkered:before {\r\n content: "\\f11e";\r\n}\r\n.fa-terminal:before {\r\n content: "\\f120";\r\n}\r\n.fa-code:before {\r\n content: "\\f121";\r\n}\r\n.fa-mail-reply-all:before,\r\n.fa-reply-all:before {\r\n content: "\\f122";\r\n}\r\n.fa-star-half-empty:before,\r\n.fa-star-half-full:before,\r\n.fa-star-half-o:before {\r\n content: "\\f123";\r\n}\r\n.fa-location-arrow:before {\r\n content: "\\f124";\r\n}\r\n.fa-crop:before {\r\n content: "\\f125";\r\n}\r\n.fa-code-fork:before {\r\n content: "\\f126";\r\n}\r\n.fa-unlink:before,\r\n.fa-chain-broken:before {\r\n content: "\\f127";\r\n}\r\n.fa-question:before {\r\n content: "\\f128";\r\n}\r\n.fa-info:before {\r\n content: "\\f129";\r\n}\r\n.fa-exclamation:before {\r\n content: "\\f12a";\r\n}\r\n.fa-superscript:before {\r\n content: "\\f12b";\r\n}\r\n.fa-subscript:before {\r\n content: "\\f12c";\r\n}\r\n.fa-eraser:before {\r\n content: "\\f12d";\r\n}\r\n.fa-puzzle-piece:before {\r\n content: "\\f12e";\r\n}\r\n.fa-microphone:before {\r\n content: "\\f130";\r\n}\r\n.fa-microphone-slash:before {\r\n content: "\\f131";\r\n}\r\n.fa-shield:before {\r\n content: "\\f132";\r\n}\r\n.fa-calendar-o:before {\r\n content: "\\f133";\r\n}\r\n.fa-fire-extinguisher:before {\r\n content: "\\f134";\r\n}\r\n.fa-rocket:before {\r\n content: "\\f135";\r\n}\r\n.fa-maxcdn:before {\r\n content: "\\f136";\r\n}\r\n.fa-chevron-circle-left:before {\r\n content: "\\f137";\r\n}\r\n.fa-chevron-circle-right:before {\r\n content: "\\f138";\r\n}\r\n.fa-chevron-circle-up:before {\r\n content: "\\f139";\r\n}\r\n.fa-chevron-circle-down:before {\r\n content: "\\f13a";\r\n}\r\n.fa-html5:before {\r\n content: "\\f13b";\r\n}\r\n.fa-css3:before {\r\n content: "\\f13c";\r\n}\r\n.fa-anchor:before {\r\n content: "\\f13d";\r\n}\r\n.fa-unlock-alt:before {\r\n content: "\\f13e";\r\n}\r\n.fa-bullseye:before {\r\n content: "\\f140";\r\n}\r\n.fa-ellipsis-h:before {\r\n content: "\\f141";\r\n}\r\n.fa-ellipsis-v:before {\r\n content: "\\f142";\r\n}\r\n.fa-rss-square:before {\r\n content: "\\f143";\r\n}\r\n.fa-play-circle:before {\r\n content: "\\f144";\r\n}\r\n.fa-ticket:before {\r\n content: "\\f145";\r\n}\r\n.fa-minus-square:before {\r\n content: "\\f146";\r\n}\r\n.fa-minus-square-o:before {\r\n content: "\\f147";\r\n}\r\n.fa-level-up:before {\r\n content: "\\f148";\r\n}\r\n.fa-level-down:before {\r\n content: "\\f149";\r\n}\r\n.fa-check-square:before {\r\n content: "\\f14a";\r\n}\r\n.fa-pencil-square:before {\r\n content: "\\f14b";\r\n}\r\n.fa-external-link-square:before {\r\n content: "\\f14c";\r\n}\r\n.fa-share-square:before {\r\n content: "\\f14d";\r\n}\r\n.fa-compass:before {\r\n content: "\\f14e";\r\n}\r\n.fa-toggle-down:before,\r\n.fa-caret-square-o-down:before {\r\n content: "\\f150";\r\n}\r\n.fa-toggle-up:before,\r\n.fa-caret-square-o-up:before {\r\n content: "\\f151";\r\n}\r\n.fa-toggle-right:before,\r\n.fa-caret-square-o-right:before {\r\n content: "\\f152";\r\n}\r\n.fa-euro:before,\r\n.fa-eur:before {\r\n content: "\\f153";\r\n}\r\n.fa-gbp:before {\r\n content: "\\f154";\r\n}\r\n.fa-dollar:before,\r\n.fa-usd:before {\r\n content: "\\f155";\r\n}\r\n.fa-rupee:before,\r\n.fa-inr:before {\r\n content: "\\f156";\r\n}\r\n.fa-cny:before,\r\n.fa-rmb:before,\r\n.fa-yen:before,\r\n.fa-jpy:before {\r\n content: "\\f157";\r\n}\r\n.fa-ruble:before,\r\n.fa-rouble:before,\r\n.fa-rub:before {\r\n content: "\\f158";\r\n}\r\n.fa-won:before,\r\n.fa-krw:before {\r\n content: "\\f159";\r\n}\r\n.fa-bitcoin:before,\r\n.fa-btc:before {\r\n content: "\\f15a";\r\n}\r\n.fa-file:before {\r\n content: "\\f15b";\r\n}\r\n.fa-file-text:before {\r\n content: "\\f15c";\r\n}\r\n.fa-sort-alpha-asc:before {\r\n content: "\\f15d";\r\n}\r\n.fa-sort-alpha-desc:before {\r\n content: "\\f15e";\r\n}\r\n.fa-sort-amount-asc:before {\r\n content: "\\f160";\r\n}\r\n.fa-sort-amount-desc:before {\r\n content: "\\f161";\r\n}\r\n.fa-sort-numeric-asc:before {\r\n content: "\\f162";\r\n}\r\n.fa-sort-numeric-desc:before {\r\n content: "\\f163";\r\n}\r\n.fa-thumbs-up:before {\r\n content: "\\f164";\r\n}\r\n.fa-thumbs-down:before {\r\n content: "\\f165";\r\n}\r\n.fa-youtube-square:before {\r\n content: "\\f166";\r\n}\r\n.fa-youtube:before {\r\n content: "\\f167";\r\n}\r\n.fa-xing:before {\r\n content: "\\f168";\r\n}\r\n.fa-xing-square:before {\r\n content: "\\f169";\r\n}\r\n.fa-youtube-play:before {\r\n content: "\\f16a";\r\n}\r\n.fa-dropbox:before {\r\n content: "\\f16b";\r\n}\r\n.fa-stack-overflow:before {\r\n content: "\\f16c";\r\n}\r\n.fa-instagram:before {\r\n content: "\\f16d";\r\n}\r\n.fa-flickr:before {\r\n content: "\\f16e";\r\n}\r\n.fa-adn:before {\r\n content: "\\f170";\r\n}\r\n.fa-bitbucket:before {\r\n content: "\\f171";\r\n}\r\n.fa-bitbucket-square:before {\r\n content: "\\f172";\r\n}\r\n.fa-tumblr:before {\r\n content: "\\f173";\r\n}\r\n.fa-tumblr-square:before {\r\n content: "\\f174";\r\n}\r\n.fa-long-arrow-down:before {\r\n content: "\\f175";\r\n}\r\n.fa-long-arrow-up:before {\r\n content: "\\f176";\r\n}\r\n.fa-long-arrow-left:before {\r\n content: "\\f177";\r\n}\r\n.fa-long-arrow-right:before {\r\n content: "\\f178";\r\n}\r\n.fa-apple:before {\r\n content: "\\f179";\r\n}\r\n.fa-windows:before {\r\n content: "\\f17a";\r\n}\r\n.fa-android:before {\r\n content: "\\f17b";\r\n}\r\n.fa-linux:before {\r\n content: "\\f17c";\r\n}\r\n.fa-dribbble:before {\r\n content: "\\f17d";\r\n}\r\n.fa-skype:before {\r\n content: "\\f17e";\r\n}\r\n.fa-foursquare:before {\r\n content: "\\f180";\r\n}\r\n.fa-trello:before {\r\n content: "\\f181";\r\n}\r\n.fa-female:before {\r\n content: "\\f182";\r\n}\r\n.fa-male:before {\r\n content: "\\f183";\r\n}\r\n.fa-gittip:before,\r\n.fa-gratipay:before {\r\n content: "\\f184";\r\n}\r\n.fa-sun-o:before {\r\n content: "\\f185";\r\n}\r\n.fa-moon-o:before {\r\n content: "\\f186";\r\n}\r\n.fa-archive:before {\r\n content: "\\f187";\r\n}\r\n.fa-bug:before {\r\n content: "\\f188";\r\n}\r\n.fa-vk:before {\r\n content: "\\f189";\r\n}\r\n.fa-weibo:before {\r\n content: "\\f18a";\r\n}\r\n.fa-renren:before {\r\n content: "\\f18b";\r\n}\r\n.fa-pagelines:before {\r\n content: "\\f18c";\r\n}\r\n.fa-stack-exchange:before {\r\n content: "\\f18d";\r\n}\r\n.fa-arrow-circle-o-right:before {\r\n content: "\\f18e";\r\n}\r\n.fa-arrow-circle-o-left:before {\r\n content: "\\f190";\r\n}\r\n.fa-toggle-left:before,\r\n.fa-caret-square-o-left:before {\r\n content: "\\f191";\r\n}\r\n.fa-dot-circle-o:before {\r\n content: "\\f192";\r\n}\r\n.fa-wheelchair:before {\r\n content: "\\f193";\r\n}\r\n.fa-vimeo-square:before {\r\n content: "\\f194";\r\n}\r\n.fa-turkish-lira:before,\r\n.fa-try:before {\r\n content: "\\f195";\r\n}\r\n.fa-plus-square-o:before {\r\n content: "\\f196";\r\n}\r\n.fa-space-shuttle:before {\r\n content: "\\f197";\r\n}\r\n.fa-slack:before {\r\n content: "\\f198";\r\n}\r\n.fa-envelope-square:before {\r\n content: "\\f199";\r\n}\r\n.fa-wordpress:before {\r\n content: "\\f19a";\r\n}\r\n.fa-openid:before {\r\n content: "\\f19b";\r\n}\r\n.fa-institution:before,\r\n.fa-bank:before,\r\n.fa-university:before {\r\n content: "\\f19c";\r\n}\r\n.fa-mortar-board:before,\r\n.fa-graduation-cap:before {\r\n content: "\\f19d";\r\n}\r\n.fa-yahoo:before {\r\n content: "\\f19e";\r\n}\r\n.fa-google:before {\r\n content: "\\f1a0";\r\n}\r\n.fa-reddit:before {\r\n content: "\\f1a1";\r\n}\r\n.fa-reddit-square:before {\r\n content: "\\f1a2";\r\n}\r\n.fa-stumbleupon-circle:before {\r\n content: "\\f1a3";\r\n}\r\n.fa-stumbleupon:before {\r\n content: "\\f1a4";\r\n}\r\n.fa-delicious:before {\r\n content: "\\f1a5";\r\n}\r\n.fa-digg:before {\r\n content: "\\f1a6";\r\n}\r\n.fa-pied-piper-pp:before {\r\n content: "\\f1a7";\r\n}\r\n.fa-pied-piper-alt:before {\r\n content: "\\f1a8";\r\n}\r\n.fa-drupal:before {\r\n content: "\\f1a9";\r\n}\r\n.fa-joomla:before {\r\n content: "\\f1aa";\r\n}\r\n.fa-language:before {\r\n content: "\\f1ab";\r\n}\r\n.fa-fax:before {\r\n content: "\\f1ac";\r\n}\r\n.fa-building:before {\r\n content: "\\f1ad";\r\n}\r\n.fa-child:before {\r\n content: "\\f1ae";\r\n}\r\n.fa-paw:before {\r\n content: "\\f1b0";\r\n}\r\n.fa-spoon:before {\r\n content: "\\f1b1";\r\n}\r\n.fa-cube:before {\r\n content: "\\f1b2";\r\n}\r\n.fa-cubes:before {\r\n content: "\\f1b3";\r\n}\r\n.fa-behance:before {\r\n content: "\\f1b4";\r\n}\r\n.fa-behance-square:before {\r\n content: "\\f1b5";\r\n}\r\n.fa-steam:before {\r\n content: "\\f1b6";\r\n}\r\n.fa-steam-square:before {\r\n content: "\\f1b7";\r\n}\r\n.fa-recycle:before {\r\n content: "\\f1b8";\r\n}\r\n.fa-automobile:before,\r\n.fa-car:before {\r\n content: "\\f1b9";\r\n}\r\n.fa-cab:before,\r\n.fa-taxi:before {\r\n content: "\\f1ba";\r\n}\r\n.fa-tree:before {\r\n content: "\\f1bb";\r\n}\r\n.fa-spotify:before {\r\n content: "\\f1bc";\r\n}\r\n.fa-deviantart:before {\r\n content: "\\f1bd";\r\n}\r\n.fa-soundcloud:before {\r\n content: "\\f1be";\r\n}\r\n.fa-database:before {\r\n content: "\\f1c0";\r\n}\r\n.fa-file-pdf-o:before {\r\n content: "\\f1c1";\r\n}\r\n.fa-file-word-o:before {\r\n content: "\\f1c2";\r\n}\r\n.fa-file-excel-o:before {\r\n content: "\\f1c3";\r\n}\r\n.fa-file-powerpoint-o:before {\r\n content: "\\f1c4";\r\n}\r\n.fa-file-photo-o:before,\r\n.fa-file-picture-o:before,\r\n.fa-file-image-o:before {\r\n content: "\\f1c5";\r\n}\r\n.fa-file-zip-o:before,\r\n.fa-file-archive-o:before {\r\n content: "\\f1c6";\r\n}\r\n.fa-file-sound-o:before,\r\n.fa-file-audio-o:before {\r\n content: "\\f1c7";\r\n}\r\n.fa-file-movie-o:before,\r\n.fa-file-video-o:before {\r\n content: "\\f1c8";\r\n}\r\n.fa-file-code-o:before {\r\n content: "\\f1c9";\r\n}\r\n.fa-vine:before {\r\n content: "\\f1ca";\r\n}\r\n.fa-codepen:before {\r\n content: "\\f1cb";\r\n}\r\n.fa-jsfiddle:before {\r\n content: "\\f1cc";\r\n}\r\n.fa-life-bouy:before,\r\n.fa-life-buoy:before,\r\n.fa-life-saver:before,\r\n.fa-support:before,\r\n.fa-life-ring:before {\r\n content: "\\f1cd";\r\n}\r\n.fa-circle-o-notch:before {\r\n content: "\\f1ce";\r\n}\r\n.fa-ra:before,\r\n.fa-resistance:before,\r\n.fa-rebel:before {\r\n content: "\\f1d0";\r\n}\r\n.fa-ge:before,\r\n.fa-empire:before {\r\n content: "\\f1d1";\r\n}\r\n.fa-git-square:before {\r\n content: "\\f1d2";\r\n}\r\n.fa-git:before {\r\n content: "\\f1d3";\r\n}\r\n.fa-y-combinator-square:before,\r\n.fa-yc-square:before,\r\n.fa-hacker-news:before {\r\n content: "\\f1d4";\r\n}\r\n.fa-tencent-weibo:before {\r\n content: "\\f1d5";\r\n}\r\n.fa-qq:before {\r\n content: "\\f1d6";\r\n}\r\n.fa-wechat:before,\r\n.fa-weixin:before {\r\n content: "\\f1d7";\r\n}\r\n.fa-send:before,\r\n.fa-paper-plane:before {\r\n content: "\\f1d8";\r\n}\r\n.fa-send-o:before,\r\n.fa-paper-plane-o:before {\r\n content: "\\f1d9";\r\n}\r\n.fa-history:before {\r\n content: "\\f1da";\r\n}\r\n.fa-circle-thin:before {\r\n content: "\\f1db";\r\n}\r\n.fa-header:before {\r\n content: "\\f1dc";\r\n}\r\n.fa-paragraph:before {\r\n content: "\\f1dd";\r\n}\r\n.fa-sliders:before {\r\n content: "\\f1de";\r\n}\r\n.fa-share-alt:before {\r\n content: "\\f1e0";\r\n}\r\n.fa-share-alt-square:before {\r\n content: "\\f1e1";\r\n}\r\n.fa-bomb:before {\r\n content: "\\f1e2";\r\n}\r\n.fa-soccer-ball-o:before,\r\n.fa-futbol-o:before {\r\n content: "\\f1e3";\r\n}\r\n.fa-tty:before {\r\n content: "\\f1e4";\r\n}\r\n.fa-binoculars:before {\r\n content: "\\f1e5";\r\n}\r\n.fa-plug:before {\r\n content: "\\f1e6";\r\n}\r\n.fa-slideshare:before {\r\n content: "\\f1e7";\r\n}\r\n.fa-twitch:before {\r\n content: "\\f1e8";\r\n}\r\n.fa-yelp:before {\r\n content: "\\f1e9";\r\n}\r\n.fa-newspaper-o:before {\r\n content: "\\f1ea";\r\n}\r\n.fa-wifi:before {\r\n content: "\\f1eb";\r\n}\r\n.fa-calculator:before {\r\n content: "\\f1ec";\r\n}\r\n.fa-paypal:before {\r\n content: "\\f1ed";\r\n}\r\n.fa-google-wallet:before {\r\n content: "\\f1ee";\r\n}\r\n.fa-cc-visa:before {\r\n content: "\\f1f0";\r\n}\r\n.fa-cc-mastercard:before {\r\n content: "\\f1f1";\r\n}\r\n.fa-cc-discover:before {\r\n content: "\\f1f2";\r\n}\r\n.fa-cc-amex:before {\r\n content: "\\f1f3";\r\n}\r\n.fa-cc-paypal:before {\r\n content: "\\f1f4";\r\n}\r\n.fa-cc-stripe:before {\r\n content: "\\f1f5";\r\n}\r\n.fa-bell-slash:before {\r\n content: "\\f1f6";\r\n}\r\n.fa-bell-slash-o:before {\r\n content: "\\f1f7";\r\n}\r\n.fa-trash:before {\r\n content: "\\f1f8";\r\n}\r\n.fa-copyright:before {\r\n content: "\\f1f9";\r\n}\r\n.fa-at:before {\r\n content: "\\f1fa";\r\n}\r\n.fa-eyedropper:before {\r\n content: "\\f1fb";\r\n}\r\n.fa-paint-brush:before {\r\n content: "\\f1fc";\r\n}\r\n.fa-birthday-cake:before {\r\n content: "\\f1fd";\r\n}\r\n.fa-area-chart:before {\r\n content: "\\f1fe";\r\n}\r\n.fa-pie-chart:before {\r\n content: "\\f200";\r\n}\r\n.fa-line-chart:before {\r\n content: "\\f201";\r\n}\r\n.fa-lastfm:before {\r\n content: "\\f202";\r\n}\r\n.fa-lastfm-square:before {\r\n content: "\\f203";\r\n}\r\n.fa-toggle-off:before {\r\n content: "\\f204";\r\n}\r\n.fa-toggle-on:before {\r\n content: "\\f205";\r\n}\r\n.fa-bicycle:before {\r\n content: "\\f206";\r\n}\r\n.fa-bus:before {\r\n content: "\\f207";\r\n}\r\n.fa-ioxhost:before {\r\n content: "\\f208";\r\n}\r\n.fa-angellist:before {\r\n content: "\\f209";\r\n}\r\n.fa-cc:before {\r\n content: "\\f20a";\r\n}\r\n.fa-shekel:before,\r\n.fa-sheqel:before,\r\n.fa-ils:before {\r\n content: "\\f20b";\r\n}\r\n.fa-meanpath:before {\r\n content: "\\f20c";\r\n}\r\n.fa-buysellads:before {\r\n content: "\\f20d";\r\n}\r\n.fa-connectdevelop:before {\r\n content: "\\f20e";\r\n}\r\n.fa-dashcube:before {\r\n content: "\\f210";\r\n}\r\n.fa-forumbee:before {\r\n content: "\\f211";\r\n}\r\n.fa-leanpub:before {\r\n content: "\\f212";\r\n}\r\n.fa-sellsy:before {\r\n content: "\\f213";\r\n}\r\n.fa-shirtsinbulk:before {\r\n content: "\\f214";\r\n}\r\n.fa-simplybuilt:before {\r\n content: "\\f215";\r\n}\r\n.fa-skyatlas:before {\r\n content: "\\f216";\r\n}\r\n.fa-cart-plus:before {\r\n content: "\\f217";\r\n}\r\n.fa-cart-arrow-down:before {\r\n content: "\\f218";\r\n}\r\n.fa-diamond:before {\r\n content: "\\f219";\r\n}\r\n.fa-ship:before {\r\n content: "\\f21a";\r\n}\r\n.fa-user-secret:before {\r\n content: "\\f21b";\r\n}\r\n.fa-motorcycle:before {\r\n content: "\\f21c";\r\n}\r\n.fa-street-view:before {\r\n content: "\\f21d";\r\n}\r\n.fa-heartbeat:before {\r\n content: "\\f21e";\r\n}\r\n.fa-venus:before {\r\n content: "\\f221";\r\n}\r\n.fa-mars:before {\r\n content: "\\f222";\r\n}\r\n.fa-mercury:before {\r\n content: "\\f223";\r\n}\r\n.fa-intersex:before,\r\n.fa-transgender:before {\r\n content: "\\f224";\r\n}\r\n.fa-transgender-alt:before {\r\n content: "\\f225";\r\n}\r\n.fa-venus-double:before {\r\n content: "\\f226";\r\n}\r\n.fa-mars-double:before {\r\n content: "\\f227";\r\n}\r\n.fa-venus-mars:before {\r\n content: "\\f228";\r\n}\r\n.fa-mars-stroke:before {\r\n content: "\\f229";\r\n}\r\n.fa-mars-stroke-v:before {\r\n content: "\\f22a";\r\n}\r\n.fa-mars-stroke-h:before {\r\n content: "\\f22b";\r\n}\r\n.fa-neuter:before {\r\n content: "\\f22c";\r\n}\r\n.fa-genderless:before {\r\n content: "\\f22d";\r\n}\r\n.fa-facebook-official:before {\r\n content: "\\f230";\r\n}\r\n.fa-pinterest-p:before {\r\n content: "\\f231";\r\n}\r\n.fa-whatsapp:before {\r\n content: "\\f232";\r\n}\r\n.fa-server:before {\r\n content: "\\f233";\r\n}\r\n.fa-user-plus:before {\r\n content: "\\f234";\r\n}\r\n.fa-user-times:before {\r\n content: "\\f235";\r\n}\r\n.fa-hotel:before,\r\n.fa-bed:before {\r\n content: "\\f236";\r\n}\r\n.fa-viacoin:before {\r\n content: "\\f237";\r\n}\r\n.fa-train:before {\r\n content: "\\f238";\r\n}\r\n.fa-subway:before {\r\n content: "\\f239";\r\n}\r\n.fa-medium:before {\r\n content: "\\f23a";\r\n}\r\n.fa-yc:before,\r\n.fa-y-combinator:before {\r\n content: "\\f23b";\r\n}\r\n.fa-optin-monster:before {\r\n content: "\\f23c";\r\n}\r\n.fa-opencart:before {\r\n content: "\\f23d";\r\n}\r\n.fa-expeditedssl:before {\r\n content: "\\f23e";\r\n}\r\n.fa-battery-4:before,\r\n.fa-battery:before,\r\n.fa-battery-full:before {\r\n content: "\\f240";\r\n}\r\n.fa-battery-3:before,\r\n.fa-battery-three-quarters:before {\r\n content: "\\f241";\r\n}\r\n.fa-battery-2:before,\r\n.fa-battery-half:before {\r\n content: "\\f242";\r\n}\r\n.fa-battery-1:before,\r\n.fa-battery-quarter:before {\r\n content: "\\f243";\r\n}\r\n.fa-battery-0:before,\r\n.fa-battery-empty:before {\r\n content: "\\f244";\r\n}\r\n.fa-mouse-pointer:before {\r\n content: "\\f245";\r\n}\r\n.fa-i-cursor:before {\r\n content: "\\f246";\r\n}\r\n.fa-object-group:before {\r\n content: "\\f247";\r\n}\r\n.fa-object-ungroup:before {\r\n content: "\\f248";\r\n}\r\n.fa-sticky-note:before {\r\n content: "\\f249";\r\n}\r\n.fa-sticky-note-o:before {\r\n content: "\\f24a";\r\n}\r\n.fa-cc-jcb:before {\r\n content: "\\f24b";\r\n}\r\n.fa-cc-diners-club:before {\r\n content: "\\f24c";\r\n}\r\n.fa-clone:before {\r\n content: "\\f24d";\r\n}\r\n.fa-balance-scale:before {\r\n content: "\\f24e";\r\n}\r\n.fa-hourglass-o:before {\r\n content: "\\f250";\r\n}\r\n.fa-hourglass-1:before,\r\n.fa-hourglass-start:before {\r\n content: "\\f251";\r\n}\r\n.fa-hourglass-2:before,\r\n.fa-hourglass-half:before {\r\n content: "\\f252";\r\n}\r\n.fa-hourglass-3:before,\r\n.fa-hourglass-end:before {\r\n content: "\\f253";\r\n}\r\n.fa-hourglass:before {\r\n content: "\\f254";\r\n}\r\n.fa-hand-grab-o:before,\r\n.fa-hand-rock-o:before {\r\n content: "\\f255";\r\n}\r\n.fa-hand-stop-o:before,\r\n.fa-hand-paper-o:before {\r\n content: "\\f256";\r\n}\r\n.fa-hand-scissors-o:before {\r\n content: "\\f257";\r\n}\r\n.fa-hand-lizard-o:before {\r\n content: "\\f258";\r\n}\r\n.fa-hand-spock-o:before {\r\n content: "\\f259";\r\n}\r\n.fa-hand-pointer-o:before {\r\n content: "\\f25a";\r\n}\r\n.fa-hand-peace-o:before {\r\n content: "\\f25b";\r\n}\r\n.fa-trademark:before {\r\n content: "\\f25c";\r\n}\r\n.fa-registered:before {\r\n content: "\\f25d";\r\n}\r\n.fa-creative-commons:before {\r\n content: "\\f25e";\r\n}\r\n.fa-gg:before {\r\n content: "\\f260";\r\n}\r\n.fa-gg-circle:before {\r\n content: "\\f261";\r\n}\r\n.fa-tripadvisor:before {\r\n content: "\\f262";\r\n}\r\n.fa-odnoklassniki:before {\r\n content: "\\f263";\r\n}\r\n.fa-odnoklassniki-square:before {\r\n content: "\\f264";\r\n}\r\n.fa-get-pocket:before {\r\n content: "\\f265";\r\n}\r\n.fa-wikipedia-w:before {\r\n content: "\\f266";\r\n}\r\n.fa-safari:before {\r\n content: "\\f267";\r\n}\r\n.fa-chrome:before {\r\n content: "\\f268";\r\n}\r\n.fa-firefox:before {\r\n content: "\\f269";\r\n}\r\n.fa-opera:before {\r\n content: "\\f26a";\r\n}\r\n.fa-internet-explorer:before {\r\n content: "\\f26b";\r\n}\r\n.fa-tv:before,\r\n.fa-television:before {\r\n content: "\\f26c";\r\n}\r\n.fa-contao:before {\r\n content: "\\f26d";\r\n}\r\n.fa-500px:before {\r\n content: "\\f26e";\r\n}\r\n.fa-amazon:before {\r\n content: "\\f270";\r\n}\r\n.fa-calendar-plus-o:before {\r\n content: "\\f271";\r\n}\r\n.fa-calendar-minus-o:before {\r\n content: "\\f272";\r\n}\r\n.fa-calendar-times-o:before {\r\n content: "\\f273";\r\n}\r\n.fa-calendar-check-o:before {\r\n content: "\\f274";\r\n}\r\n.fa-industry:before {\r\n content: "\\f275";\r\n}\r\n.fa-map-pin:before {\r\n content: "\\f276";\r\n}\r\n.fa-map-signs:before {\r\n content: "\\f277";\r\n}\r\n.fa-map-o:before {\r\n content: "\\f278";\r\n}\r\n.fa-map:before {\r\n content: "\\f279";\r\n}\r\n.fa-commenting:before {\r\n content: "\\f27a";\r\n}\r\n.fa-commenting-o:before {\r\n content: "\\f27b";\r\n}\r\n.fa-houzz:before {\r\n content: "\\f27c";\r\n}\r\n.fa-vimeo:before {\r\n content: "\\f27d";\r\n}\r\n.fa-black-tie:before {\r\n content: "\\f27e";\r\n}\r\n.fa-fonticons:before {\r\n content: "\\f280";\r\n}\r\n.fa-reddit-alien:before {\r\n content: "\\f281";\r\n}\r\n.fa-edge:before {\r\n content: "\\f282";\r\n}\r\n.fa-credit-card-alt:before {\r\n content: "\\f283";\r\n}\r\n.fa-codiepie:before {\r\n content: "\\f284";\r\n}\r\n.fa-modx:before {\r\n content: "\\f285";\r\n}\r\n.fa-fort-awesome:before {\r\n content: "\\f286";\r\n}\r\n.fa-usb:before {\r\n content: "\\f287";\r\n}\r\n.fa-product-hunt:before {\r\n content: "\\f288";\r\n}\r\n.fa-mixcloud:before {\r\n content: "\\f289";\r\n}\r\n.fa-scribd:before {\r\n content: "\\f28a";\r\n}\r\n.fa-pause-circle:before {\r\n content: "\\f28b";\r\n}\r\n.fa-pause-circle-o:before {\r\n content: "\\f28c";\r\n}\r\n.fa-stop-circle:before {\r\n content: "\\f28d";\r\n}\r\n.fa-stop-circle-o:before {\r\n content: "\\f28e";\r\n}\r\n.fa-shopping-bag:before {\r\n content: "\\f290";\r\n}\r\n.fa-shopping-basket:before {\r\n content: "\\f291";\r\n}\r\n.fa-hashtag:before {\r\n content: "\\f292";\r\n}\r\n.fa-bluetooth:before {\r\n content: "\\f293";\r\n}\r\n.fa-bluetooth-b:before {\r\n content: "\\f294";\r\n}\r\n.fa-percent:before {\r\n content: "\\f295";\r\n}\r\n.fa-gitlab:before {\r\n content: "\\f296";\r\n}\r\n.fa-wpbeginner:before {\r\n content: "\\f297";\r\n}\r\n.fa-wpforms:before {\r\n content: "\\f298";\r\n}\r\n.fa-envira:before {\r\n content: "\\f299";\r\n}\r\n.fa-universal-access:before {\r\n content: "\\f29a";\r\n}\r\n.fa-wheelchair-alt:before {\r\n content: "\\f29b";\r\n}\r\n.fa-question-circle-o:before {\r\n content: "\\f29c";\r\n}\r\n.fa-blind:before {\r\n content: "\\f29d";\r\n}\r\n.fa-audio-description:before {\r\n content: "\\f29e";\r\n}\r\n.fa-volume-control-phone:before {\r\n content: "\\f2a0";\r\n}\r\n.fa-braille:before {\r\n content: "\\f2a1";\r\n}\r\n.fa-assistive-listening-systems:before {\r\n content: "\\f2a2";\r\n}\r\n.fa-asl-interpreting:before,\r\n.fa-american-sign-language-interpreting:before {\r\n content: "\\f2a3";\r\n}\r\n.fa-deafness:before,\r\n.fa-hard-of-hearing:before,\r\n.fa-deaf:before {\r\n content: "\\f2a4";\r\n}\r\n.fa-glide:before {\r\n content: "\\f2a5";\r\n}\r\n.fa-glide-g:before {\r\n content: "\\f2a6";\r\n}\r\n.fa-signing:before,\r\n.fa-sign-language:before {\r\n content: "\\f2a7";\r\n}\r\n.fa-low-vision:before {\r\n content: "\\f2a8";\r\n}\r\n.fa-viadeo:before {\r\n content: "\\f2a9";\r\n}\r\n.fa-viadeo-square:before {\r\n content: "\\f2aa";\r\n}\r\n.fa-snapchat:before {\r\n content: "\\f2ab";\r\n}\r\n.fa-snapchat-ghost:before {\r\n content: "\\f2ac";\r\n}\r\n.fa-snapchat-square:before {\r\n content: "\\f2ad";\r\n}\r\n.fa-pied-piper:before {\r\n content: "\\f2ae";\r\n}\r\n.fa-first-order:before {\r\n content: "\\f2b0";\r\n}\r\n.fa-yoast:before {\r\n content: "\\f2b1";\r\n}\r\n.fa-themeisle:before {\r\n content: "\\f2b2";\r\n}\r\n.fa-google-plus-circle:before,\r\n.fa-google-plus-official:before {\r\n content: "\\f2b3";\r\n}\r\n.fa-fa:before,\r\n.fa-font-awesome:before {\r\n content: "\\f2b4";\r\n}\r\n.fa-handshake-o:before {\r\n content: "\\f2b5";\r\n}\r\n.fa-envelope-open:before {\r\n content: "\\f2b6";\r\n}\r\n.fa-envelope-open-o:before {\r\n content: "\\f2b7";\r\n}\r\n.fa-linode:before {\r\n content: "\\f2b8";\r\n}\r\n.fa-address-book:before {\r\n content: "\\f2b9";\r\n}\r\n.fa-address-book-o:before {\r\n content: "\\f2ba";\r\n}\r\n.fa-vcard:before,\r\n.fa-address-card:before {\r\n content: "\\f2bb";\r\n}\r\n.fa-vcard-o:before,\r\n.fa-address-card-o:before {\r\n content: "\\f2bc";\r\n}\r\n.fa-user-circle:before {\r\n content: "\\f2bd";\r\n}\r\n.fa-user-circle-o:before {\r\n content: "\\f2be";\r\n}\r\n.fa-user-o:before {\r\n content: "\\f2c0";\r\n}\r\n.fa-id-badge:before {\r\n content: "\\f2c1";\r\n}\r\n.fa-drivers-license:before,\r\n.fa-id-card:before {\r\n content: "\\f2c2";\r\n}\r\n.fa-drivers-license-o:before,\r\n.fa-id-card-o:before {\r\n content: "\\f2c3";\r\n}\r\n.fa-quora:before {\r\n content: "\\f2c4";\r\n}\r\n.fa-free-code-camp:before {\r\n content: "\\f2c5";\r\n}\r\n.fa-telegram:before {\r\n content: "\\f2c6";\r\n}\r\n.fa-thermometer-4:before,\r\n.fa-thermometer:before,\r\n.fa-thermometer-full:before {\r\n content: "\\f2c7";\r\n}\r\n.fa-thermometer-3:before,\r\n.fa-thermometer-three-quarters:before {\r\n content: "\\f2c8";\r\n}\r\n.fa-thermometer-2:before,\r\n.fa-thermometer-half:before {\r\n content: "\\f2c9";\r\n}\r\n.fa-thermometer-1:before,\r\n.fa-thermometer-quarter:before {\r\n content: "\\f2ca";\r\n}\r\n.fa-thermometer-0:before,\r\n.fa-thermometer-empty:before {\r\n content: "\\f2cb";\r\n}\r\n.fa-shower:before {\r\n content: "\\f2cc";\r\n}\r\n.fa-bathtub:before,\r\n.fa-s15:before,\r\n.fa-bath:before {\r\n content: "\\f2cd";\r\n}\r\n.fa-podcast:before {\r\n content: "\\f2ce";\r\n}\r\n.fa-window-maximize:before {\r\n content: "\\f2d0";\r\n}\r\n.fa-window-minimize:before {\r\n content: "\\f2d1";\r\n}\r\n.fa-window-restore:before {\r\n content: "\\f2d2";\r\n}\r\n.fa-times-rectangle:before,\r\n.fa-window-close:before {\r\n content: "\\f2d3";\r\n}\r\n.fa-times-rectangle-o:before,\r\n.fa-window-close-o:before {\r\n content: "\\f2d4";\r\n}\r\n.fa-bandcamp:before {\r\n content: "\\f2d5";\r\n}\r\n.fa-grav:before {\r\n content: "\\f2d6";\r\n}\r\n.fa-etsy:before {\r\n content: "\\f2d7";\r\n}\r\n.fa-imdb:before {\r\n content: "\\f2d8";\r\n}\r\n.fa-ravelry:before {\r\n content: "\\f2d9";\r\n}\r\n.fa-eercast:before {\r\n content: "\\f2da";\r\n}\r\n.fa-microchip:before {\r\n content: "\\f2db";\r\n}\r\n.fa-snowflake-o:before {\r\n content: "\\f2dc";\r\n}\r\n.fa-superpowers:before {\r\n content: "\\f2dd";\r\n}\r\n.fa-wpexplorer:before {\r\n content: "\\f2de";\r\n}\r\n.fa-meetup:before {\r\n content: "\\f2e0";\r\n}\r\n.sr-only {\r\n position: absolute;\r\n width: 1px;\r\n height: 1px;\r\n padding: 0;\r\n margin: -1px;\r\n overflow: hidden;\r\n clip: rect(0, 0, 0, 0);\r\n border: 0;\r\n}\r\n.sr-only-focusable:active,\r\n.sr-only-focusable:focus {\r\n position: static;\r\n width: auto;\r\n height: auto;\r\n margin: 0;\r\n overflow: visible;\r\n clip: auto;\r\n}\r\n',""]),A.exports=M},function(A,M,t){"use strict";t.r(M),M.default="data:application/vnd.ms-fontobject;base64,bocCAKyGAgABAAIAAAAAAAAAAAAAAAAAAAABAJABAAAAAExQAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAWXjPkAAAAAAAAAAAAAAAAAAAAAAAABYARgBvAG4AdABBAHcAZQBzAG8AbQBlAAAADgBSAGUAZwB1AGwAYQByAAAAJABWAGUAcgBzAGkAbwBuACAANAAuADcALgAwACAAMgAwADEANgAAABYARgBvAG4AdABBAHcAZQBzAG8AbQBlAAAAAAAAAQAAAA0AgAADAFBGRlRNa75HuQAChpAAAAAcR0RFRgLwAAQAAoZwAAAAIE9TLzKIMnpAAAABWAAAAGBjbWFwCr86fwAADKgAAALyZ2FzcP//AAMAAoZoAAAACGdseWaP965NAAAarAACTLxoZWFkEInlLQAAANwAAAA2aGhlYQ8DCrUAAAEUAAAAJGhtdHhFeRiFAAABuAAACvBsb2NhAvWiXAAAD5wAAAsQbWF4cAMsAhwAAAE4AAAAIG5hbWXjl4usAAJnaAAABIZwb3N0r4+boQACa/AAABp1AAEAAAAEAcuQz3hZXw889QALBwAAAAAA1DPNMgAAAADUM80y////AAkBBgAAAAAIAAIAAQAAAAAAAQAABgD/AAAACQD/////CQEAAQAAAAAAAAAAAAAAAAAAArUAAQAAAsMCGQAnAAAAAAACAAAAAQABAAAAQAAAAAAAAAADBmkBkAAFAAAEjAQzAAAAhgSMBDMAAAJzAAABigAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABweXJzAEAAIPUABgD/AAAABgABAAAAAAEAAAAAAAAAAAAAACAAAQOAAHAAAAAAAlUAAAHAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAABdBgAAAAaAAAAHAAAABwAAAAaAAAAGgAAABQAAAAeAAAAGgAAABwAAAAcAAAAHAAB5BYAAbgaAAAAGgAAABgAAAAcAAAAGAAAABYAAAAaAABoGAAAABgAAAAeAADIGgAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABwAAAASAAAAHAABABoAAAAMAAAAEgAAABoAAAAWAAAAHAAAABgAAAAeAAAAGgAAKBQAAAAaAAAAHgAAABoAAAAWAAAAEAAAABwAAAAYAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAeAAAAGAAAABAAAAAYAAAAEAAAABwAAAAaAAAAGgAAABwAAAAQAAAAHAAAABoAAegWAAAAGAAAABgAAAAaAAAAHAAAABAAAAAYCAAEFAACaBQAAWgYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAABABgAAAAaAADUGgAA1BwAAAAYAAAAGAAANBYAAAAWAAAAGgAB6BgAAAAYAAAAHAAAABYAAAAcAAAAHAAAABwAAEAWAAAAGgAAABwAAAAcAAAAGAAAABwAAWgcAAFoHgAAABoAAAAaAAAAHgAAAAwAAQAcAAAAIAAAABgAAAAYAAAAHAAAABwAAAAeAAAAHAAAABgAAAAYAAAADgAAABwAAAAaAAAAGAAAABIAAAAcAAAAGAAAABoAAAAYAAAAGgAAABgAAAAWAAAAFgAAABQAAAAYAAAAGgAAsBAAAXwYAAAAGgAAAB4AAAAWAAAAGAAAABwAAAAcAAEAGAAACBwAAAAcAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABoAAFQcAAAAFgAAFBwAAAAYAAAAHgAAABoAAEAeAAAAGgABzBwAAAQcAAAAFgAAEBgAAAAYAAAAGAAAABwAAAAcAAA8HAAAABgAAAAaAAAAGgAAbBwAAQAYAAAAGAAAABgAAAAkAAAAHgAAABAAAAAQAAAACgABAAoAAAAaAAAAEAAAABAAAAAQAAAAHAAAABgAAAAYAAAAHAAAoBwAAAAcAAAAHAAAAA4AAAQcAAAAGgAAABwAAAAQAAAAHAAAAB4AAAAeAAAAFgAAABYAAAAcAAAAHAABAB4AAAAWAAAAGAAAABYAAAAWAAAAHgABABwAAAAeAAAAGgABABgAAAAYAAAAEAAAtBAAADQSAAE0EgABNAoAALQKAAA0EgABNBIAATQeAAAAHgAAABIAAAAMAAAAGAAAABoAAAAaAAAAHAABABgAAAAcAAAAGgAAABoAAAAeAAAAHAAAABwAAAAYAAAAGAAAABgAAAAeAAAAHgAAABwAAQAcAAEAGgAANB4AALQcAAAAGgAACBYAAAgaAAAAEAAAABoAAAAQAAGACgAAAAoAAYgYAAAUGAAAFB4AAAQaAAAAEgAAABYAADQUAAAAGgAAABYAAAwaAACQHAAAABgAAAAYAAAAGAAAABgAAAAWAAAAHAAAMBwAAAASAAAAGAAAABYAAAAGAAAAGAAAABgAAAAcAADYGAAAABYAAAAQAAAMEAAADBgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAEAAAABAAAAAQAADQDggAABAMABAUAAAAHAAAABQAAOAYAAAAGAAAABoAAIgaAACIHAAAiBwAAIgYAACIGAAAiBoAAAAaAAAAGAAAABgAAGwWAAAUGAAAABwAAAAcAAEAGAAALBgAAAAYAAAAGAAAABYAAAAYAAAAEAABEBgAAAAMAAAMDAAADBwAAQAcAAAAFgAAABoAAAAWAAAAGAAALBgAAAAYAAAAFAAAsBgAAAAUAAAAEAAAABgAAAAcAACwGAAAABwAAQAaAACAHgP//BwAAAAYAAAAFgAAABQAAFQYAAAAGAAAABgAAAAYAAAAGgAAABgAAAASAAAAFgAAACIAAAAaAAAAGAAAABwAAAAcAAAAIAAAACQAAAAYAAG0GAAAABwAAAAYAAAAGAAAAB4AAAAYAAAAIAAAABgAAAAf2ACkGAAAABgAAAAYAAAAHAAAABgAAAAUAAEAGgAAAAwAAQAcAAAAJAAAACAAAAAYAAAAHAAAABgAAAAcAABAIAAAACAAAAAYAACAGAAAABAAAAAkAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAJwcAAAAIAAAABwAAAAcAACAHAAATBwAAAAYAAAAHAABEBgAAAAUAADkHAAASCAAAAAcAAAAHAAAABgAAAAYAAAAHAAA+BQAAGAYAAAAGAAAABgAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAGQcAAGQGAABZCAAAAAgAACoHAAAABgAACQcAACcJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAIAAAOCAAADgWAAAAGAAAABgAAAAcAAAAHAAAABwAAAAgAAAAHAAAACAAAAAcAAAAGAAAACAAAAAgAAAAJAAAABgAAAAgAAAAFAAALCAAAAAYAAAAGAAAABgAAAAgAAAAGAAAABgAAAAgAAAAIAAAABgAAAAgAAAAIAAAABoAAAAaAAAAIAAAACAAAEwYAAAAJAAAABgAAAAcAAAAFAAACBgAAAAUAAAAGAAACBwAAAAcA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AaoBqwGsAa0BrgGvAbABsQGyAbMBtAG1AbYBtwG4AbkBugG7AbwBvQG+Ab8BwAHBAcIBwwHEAcUBxgHHAcgByQHKAcsBzAHNAc4BzwHQAdEB0gHTAdQB1QHWAdcB2AHZAdoB2wHcAd0B3gHfAeAB4QHiAeMB5AHlAeYB5wHoAekB6gHrAewB7QHuAe8B8AHxAfIB8wH0AfUB9gH3AfgB+QH6AfsB/AH9Af4B/wIAAgECAgIDAgQCBQIGAgcCCAAiAgkCCgILAgwCDQIOAg8CEAIRAhICEwIUAhUCFgIXAhgCGQIaAhsCHAIdAh4CHwIgAiECIgIjAiQCJQImAicCKAIpAioCKwIsAi0CLgIvAjACMQIyAjMCNAI1AjYCNwI4AjkCOgI7AjwCPQI+Aj8CQAJBAkICQwJEAkUCRgJHAkgCSQJKAksCTAJNAk4CTwJQAlECUgJTANICVAJVAlYCVwJYAlkCWgJbAlwCXQJeAl8CYAJhAmICYwJkAmUCZgJnAmgCaQJqAmsCbAJtAm4CbwJwAnECcgJzAnQCdQJ2AncCeAJ5AnoCewJ8An0CfgJ/AoACgQKCAoMChAKFAoYChwKIAokCigKLAowCjQKOAo8CkAKRApICkwKUApUClgKXApgCmQKaApsCnAKdAp4CnwKgAqECogKjAqQCpQKmAqcCqAKpAqoCqwKsAq0CrgKvArACsQKyArMCtAK1ArYCtwK4ArkCugK7ArwCvQK+Ar8CwALBAsICwwLEAsUCxgLHAsgCyQLKAssCzALNAs4CzwLQAtEC0gLTAtQC1QLWAtcC2ALZAtoC2wLcAt0C3gLfAuAC4QLiAuMC5ALlAuYC5wLoAukC6gLrAuwC7QLuAu8C8ALxAvIC8wL0AvUC9gL3AvgC+QL6AvsC/AL9Av4C/wMAAwEDAgMDAwQDBQMGAwcDCAMJAwoDCwMMAw0DDgMPAxADEQMSAxMDFAMVAxYDFwMYAxkDGgMbAxwDHQMeAx8DIAMhAyIDIwMkAyUDJgMnAygDKQMqAysDLAMtAy4DLwMwAzEDMgMzAzQDNQM2AzcDOAM5AzoDOwM8Az0DPgM/A0ADQQNCA0MDRANFA0YDRwNIA0kDSgNLA0wDTQNOA08DUANRA1IDUwNUA1UDVgNXA1gDWQNaA1sDXANdA14DXwNgA2EDYgNjA2QDZQNmA2cDaANpA2oDawNsA20DbgNvA3ADcQNyA3MDdAN1A3YDdwN4A3kDegN7A3wDfQN+A38DgAOBA4IDgwOEA4UDhgOHA4gDiQOKA4sDjAONA44DjwOQA5EDkgOTA5QDlQOWA5cDmAOZA5oDmwOcA50DngOfA6ADoQOiA6MDpAOlA6YDpwOoA6kDqgOrA6wDrQOuA68DsAOxAJQFZ2xhc3MFbXVzaWMGc2VhcmNoCGVudmVsb3BlBWhlYXJ0BHN0YXIKc3Rhcl9lbXB0eQR1c2VyBGZpbG0IdGhfbGFyZ2UCdGgHdGhfbGlzdAJvawZyZW1vdmUHem9vbV9pbgh6b29tX291dANvZmYGc2lnbmFsA2NvZwV0cmFzaARob21lCGZpbGVfYWx0BHRpbWUEcm9hZAxkb3dubG9hZF9hbHQIZG93bmxvYWQGdXBsb2FkBWluYm94C3BsYXlfY2lyY2xlBnJlcGVhdAdyZWZyZXNoCGxpc3RfYWx0BGxvY2sEZmxhZwpoZWFkcGhvbmVzCnZvbHVtZV9vZmYLdm9sdW1lX2Rvd24Jdm9sdW1lX3VwBnFyY29kZQdiYXJjb2RlA3RhZwR0YWdzBGJvb2sIYm9va21hcmsFcHJpbnQGY2FtZXJhBGZvbnQEYm9sZAZpdGFsaWMLdGV4dF9oZWlnaHQKdGV4dF93aWR0aAphbGlnbl9sZWZ0DGFsaWduX2NlbnRlcgthbGlnbl9yaWdodA1hbGlnbl9qdXN0aWZ5BGxpc3QLaW5kZW50X2xlZnQMaW5kZW50X3JpZ2h0DmZhY2V0aW1lX3ZpZGVvB3BpY3R1cmUGcGVuY2lsCm1hcF9tYXJrZXIGYWRqdXN0BHRpbnQEZWRpdAVzaGFyZQVjaGVjawRtb3ZlDXN0ZXBfYmFja3dhcmQNZmFzdF9iYWNrd2FyZAhiYWNrd2FyZARwbGF5BXBhdXNlBHN0b3AHZm9yd2FyZAxmYXN0X2ZvcndhcmQMc3RlcF9mb3J3YXJkBWVqZWN0DGNoZXZyb25fbGVmdA1jaGV2cm9uX3JpZ2h0CXBsdXNfc2lnbgptaW51c19zaWduC3JlbW92ZV9zaWduB29rX3NpZ24NcXVlc3Rpb25fc2lnbglpbmZvX3NpZ24Kc2NyZWVuc2hvdA1yZW1vdmVfY2lyY2xlCW9rX2NpcmNsZQpiYW5fY2lyY2xlCmFycm93X2xlZnQLYXJyb3dfcmlnaHQIYXJyb3dfdXAKYXJyb3dfZG93bglzaGFyZV9hbHQLcmVzaXplX2Z1bGwMcmVzaXplX3NtYWxsEGV4Y2xhbWF0aW9uX3NpZ24EZ2lmdARsZWFmBGZpcmUIZXllX29wZW4JZXllX2Nsb3NlDHdhcm5pbmdfc2lnbgVwbGFuZQhjYWxlbmRhcgZyYW5kb20HY29tbWVudAZtYWduZXQKY2hldnJvbl91cAxjaGV2cm9uX2Rvd24HcmV0d2VldA1zaG9wcGluZ19jYXJ0DGZvbGRlcl9jbG9zZQtmb2xkZXJfb3Blbg9yZXNpemVfdmVydGljYWwRcmVzaXplX2hvcml6b250YWwJYmFyX2NoYXJ0DHR3aXR0ZXJfc2lnbg1mYWNlYm9va19zaWduDGNhbWVyYV9yZXRybwNrZXkEY29ncwhjb21tZW50cw10aHVtYnNfdXBfYWx0D3RodW1ic19kb3duX2FsdAlzdGFyX2hhbGYLaGVhcnRfZW1wdHkHc2lnbm91dA1saW5rZWRpbl9zaWduB3B1c2hwaW4NZXh0ZXJuYWxfbGluawZzaWduaW4GdHJvcGh5C2dpdGh1Yl9zaWduCnVwbG9hZF9hbHQFbGVtb24FcGhvbmULY2hlY2tfZW1wdHkOYm9va21hcmtfZW1wdHkKcGhvbmVfc2lnbgd0d2l0dGVyCGZhY2Vib29rBmdpdGh1YgZ1bmxvY2sLY3JlZGl0X2NhcmQDcnNzA2hkZAhidWxsaG9ybgRiZWxsC2NlcnRpZmljYXRlCmhhbmRfcmlnaHQJaGFuZF9sZWZ0B2hhbmRfdXAJaGFuZF9kb3duEWNpcmNsZV9hcnJvd19sZWZ0EmNpcmNsZV9hcnJvd19yaWdodA9jaXJjbGVfYXJyb3dfdXARY2lyY2xlX2Fycm93X2Rvd24FZ2xvYmUGd3JlbmNoBXRhc2tzBmZpbHRlcglicmllZmNhc2UKZnVsbHNjcmVlbgVncm91cARsaW5rBWNsb3VkBmJlYWtlcgNjdXQEY29weQpwYXBlcl9jbGlwBHNhdmUKc2lnbl9ibGFuawdyZW9yZGVyAnVsAm9sDXN0cmlrZXRocm91Z2gJdW5kZXJsaW5lBXRhYmxlBW1hZ2ljBXRydWNrCXBpbnRlcmVzdA5waW50ZXJlc3Rfc2lnbhBnb29nbGVfcGx1c19zaWduC2dvb2dsZV9wbHVzBW1vbmV5CmNhcmV0X2Rvd24IY2FyZXRfdXAKY2FyZXRfbGVmdAtjYXJldF9yaWdodAdjb2x1bW5zBHNvcnQJc29ydF9kb3duB3NvcnRfdXAMZW52ZWxvcGVfYWx0CGxpbmtlZGluBHVuZG8FbGVnYWwJZGFzaGJvYXJkC2NvbW1lbnRfYWx0DGNvbW1lbnRzX2FsdARib2x0B3NpdGVtYXAIdW1icmVsbGEFcGFzdGUKbGlnaHRfYnVsYghleGNoYW5nZQ5jbG91ZF9kb3dubG9hZAxjbG91ZF91cGxvYWQHdXNlcl9tZAtzdGV0aG9zY29wZQhzdWl0Y2FzZQhiZWxsX2FsdAZjb2ZmZWUEZm9vZA1maWxlX3RleHRfYWx0CGJ1aWxkaW5nCGhvc3BpdGFsCWFtYnVsYW5jZQZtZWRraXQLZmlnaHRlcl9qZXQEYmVlcgZoX3NpZ24EZjBmZRFkb3VibGVfYW5nbGVfbGVmdBJkb3VibGVfYW5nbGVfcmlnaHQPZG91YmxlX2FuZ2xlX3VwEWRvdWJsZV9hbmdsZV9kb3duCmFuZ2xlX2xlZnQLYW5nbGVfcmlnaHQIYW5nbGVfdXAKYW5nbGVfZG93bgdkZXNrdG9wBmxhcHRvcAZ0YWJsZXQMbW9iaWxlX3Bob25lDGNpcmNsZV9ibGFuawpxdW90ZV9sZWZ0C3F1b3RlX3JpZ2h0B3NwaW5uZXIGY2lyY2xlBXJlcGx5CmdpdGh1Yl9hbHQQZm9sZGVyX2Nsb3NlX2FsdA9mb2xkZXJfb3Blbl9hbHQKZXhwYW5kX2FsdAxjb2xsYXBzZV9hbHQFc21pbGUFZnJvd24DbWVoB2dhbWVwYWQIa2V5Ym9hcmQIZmxhZ19hbHQOZmxhZ19jaGVja2VyZWQIdGVybWluYWwEY29kZQlyZXBseV9hbGwPc3Rhcl9oYWxmX2VtcHR5DmxvY2F0aW9uX2Fycm93BGNyb3AJY29kZV9mb3JrBnVubGluawRfMjc5C2V4Y2xhbWF0aW9uC3N1cGVyc2NyaXB0CXN1YnNjcmlwdARfMjgzDHB1enpsZV9waWVjZQptaWNyb3Bob25lDm1pY3JvcGhvbmVfb2ZmBnNoaWVsZA5jYWxlbmRhcl9lbXB0eRFmaXJlX2V4dGluZ3Vpc2hlcgZyb2NrZXQGbWF4Y2RuEWNoZXZyb25fc2lnbl9sZWZ0EmNoZXZyb25fc2lnbl9yaWdodA9jaGV2cm9uX3NpZ25fdXARY2hldnJvbl9zaWduX2Rvd24FaHRtbDUEY3NzMwZhbmNob3IKdW5sb2NrX2FsdAhidWxsc2V5ZRNlbGxpcHNpc19ob3Jpem9udGFsEWVsbGlwc2lzX3ZlcnRpY2FsBF8zMDMJcGxheV9zaWduBnRpY2tldA5taW51c19zaWduX2FsdAtjaGVja19taW51cwhsZXZlbF91cApsZXZlbF9kb3duCmNoZWNrX3NpZ24JZWRpdF9zaWduBF8zMTIKc2hhcmVfc2lnbgdjb21wYXNzCGNvbGxhcHNlDGNvbGxhcHNlX3RvcARfMzE3A2V1cgNnYnADdXNkA2lucgNqcHkDcnViA2tydwNidGMEZmlsZQlmaWxlX3RleHQQc29ydF9ieV9hbHBoYWJldARfMzI5EnNvcnRfYnlfYXR0cmlidXRlcxZzb3J0X2J5X2F0dHJpYnV0ZXNfYWx0DXNvcnRfYnlfb3JkZXIRc29ydF9ieV9vcmRlcl9hbHQEXzMzNARfMzM1DHlvdXR1YmVfc2lnbgd5b3V0dWJlBHhpbmcJeGluZ19zaWduDHlvdXR1YmVfcGxheQdkcm9wYm94DXN0YWNrZXhjaGFuZ2UJaW5zdGFncmFtBmZsaWNrcgNhZG4EZjE3MQ5iaXRidWNrZXRfc2lnbgZ0dW1ibHILdHVtYmxyX3NpZ24PbG9uZ19hcnJvd19kb3duDWxvbmdfYXJyb3dfdXAPbG9uZ19hcnJvd19sZWZ0EGxvbmdfYXJyb3dfcmlnaHQHd2luZG93cwdhbmRyb2lkBWxpbnV4B2RyaWJibGUFc2t5cGUKZm91cnNxdWFyZQZ0cmVsbG8GZmVtYWxlBG1hbGUGZ2l0dGlwA3N1bgRfMzY2B2FyY2hpdmUDYnVnAnZrBXdlaWJvBnJlbnJlbgRfMzcyDnN0YWNrX2V4Y2hhbmdlBF8zNzQVYXJyb3dfY2lyY2xlX2FsdF9sZWZ0BF8zNzYOZG90X2NpcmNsZV9hbHQEXzM3OAx2aW1lb19zcXVhcmUEXzM4MA1wbHVzX3NxdWFyZV9vBF8zODIEXzM4MwRfMzg0BF8zODUEXzM4NgRfMzg3BF8zODgEXzM4OQd1bmlGMUEwBGYxYTEEXzM5MgRfMzkzBGYxYTQEXzM5NQRfMzk2BF8zOTcEXzM5OARfMzk5BF80MDAEZjFhYgRfNDAyBF80MDMEXzQwNAd1bmlGMUIxBF80MDYEXzQwNwRfNDA4BF80MDkEXzQxMARfNDExBF80MTIEXzQxMwRfNDE0BF80MTUEXzQxNgRfNDE3BF80MTgEXzQxOQd1bmlGMUMwB3VuaUYxQzEEXzQyMgRfNDIzBF80MjQEXzQyNQRfNDI2BF80MjcEXzQyOARfNDI5BF80MzAEXzQzMQRfNDMyBF80MzMEXzQzNAd1bmlGMUQwB3VuaUYxRDEHdW5pRjFEMgRfNDM4BF80MzkHdW5pRjFENQd1bmlGMUQ2B3VuaUYxRDcEXzQ0MwRfNDQ0BF80NDUEXzQ0NgRfNDQ3BF80NDgEXzQ0OQd1bmlGMUUwBF80NTEEXzQ1MgRfNDUzBF80NTQEXzQ1NQRfNDU2BF80NTcEXzQ1OARfNDU5BF80NjAEXzQ2MQRfNDYyBF80NjMEXzQ2NAd1bmlGMUYwBF80NjYEXzQ2NwRmMWYzBF80NjkEXzQ3MARfNDcxBF80NzIEXzQ3MwRfNDc0BF80NzUEXzQ3NgRmMWZjBF80NzgEXzQ3OQRfNDgwBF80ODEEXzQ4MgRfNDgzBF80ODQEXzQ4NQRfNDg2BF80ODcEXzQ4OARfNDg5BF80OTAEXzQ5MQRfNDkyBF80OTMEXzQ5NARmMjEwBF80OTYEZjIxMgRfNDk4BF80OTkEXzUwMARfNTAxBF81MDIEXzUwMwRfNTA0BF81MDUEXzUwNgRfNTA3BF81MDgEXzUwOQV2ZW51cwRfNTExBF81MTIEXzUxMwRfNTE0BF81MTUEXzUxNgRfNTE3BF81MTgEXzUxOQRfNTIwBF81MjEEXzUyMgRfNTIzBF81MjQEXzUyNQRfNTI2BF81MjcEXzUyOARfNTI5BF81MzAEXzUzMQRfNTMyBF81MzMEXzUzNARfNTM1BF81MzYEXzUzNwRfNTM4BF81MzkEXzU0MARfNTQxBF81NDIEXzU0MwRfNTQ0BF81NDUEXzU0NgRfNTQ3BF81NDgEXzU0OQRfNTUwBF81NTEEXzU1MgRfNTUzBF81NTQEXzU1NQRfNTU2BF81NTcEXzU1OARfNTU5BF81NjAEXzU2MQRfNTYyBF81NjMEXzU2NARfNTY1BF81NjYEXzU2NwRfNTY4BF81NjkEZjI2MARmMjYxBF81NzIEZjI2MwRfNTc0BF81NzUEXzU3NgRfNTc3BF81NzgEXzU3OQRfNTgwBF81ODEEXzU4MgRfNTgzBF81ODQEXzU4NQRfNTg2BF81ODcEXzU4OARfNTg5BF81OTAEXzU5MQRfNTkyBF81OTMEXzU5NARfNTk1BF81OTYEXzU5NwRfNTk4BGYyN2UHdW5pRjI4MAd1bmlGMjgxBF82MDIEXzYwMwRfNjA0B3VuaUYyODUHdW5pRjI4NgRfNjA3BF82MDgEXzYwOQRfNjEwBF82MTEEXzYxMgRfNjEzBF82MTQEXzYxNQRfNjE2BF82MTcEXzYxOARfNjE5BF82MjAEXzYyMQRfNjIyBF82MjMEXzYyNARfNjI1BF82MjYEXzYyNwRfNjI4BF82MjkHdW5pRjJBMAd1bmlGMkExB3VuaUYyQTIHdW5pRjJBMwd1bmlGMkE0B3VuaUYyQTUHdW5pRjJBNgd1bmlGMkE3B3VuaUYyQTgHdW5pRjJBOQd1bmlGMkFBB3VuaUYyQUIHdW5pRjJBQwd1bmlGMkFEB3VuaUYyQUUHdW5pRjJCMAd1bmlGMkIxB3VuaUYyQjIHdW5pRjJCMwd1bmlGMkI0B3VuaUYyQjUHdW5pRjJCNgd1bmlGMkI3B3VuaUYyQjgHdW5pRjJCOQd1bmlGMkJBB3VuaUYyQkIHdW5pRjJCQwd1bmlGMkJEB3VuaUYyQkUHdW5pRjJDMAd1bmlGMkMxB3VuaUYyQzIHdW5pRjJDMwd1bmlGMkM0B3VuaUYyQzUHdW5pRjJDNgd1bmlGMkM3B3VuaUYyQzgHdW5pRjJDOQd1bmlGMkNBB3VuaUYyQ0IHdW5pRjJDQwd1bmlGMkNEB3VuaUYyQ0UHdW5pRjJEMAd1bmlGMkQxB3VuaUYyRDIHdW5pRjJEMwd1bmlGMkQ0B3VuaUYyRDUHdW5pRjJENgd1bmlGMkQ3B3VuaUYyRDgHdW5pRjJEOQd1bmlGMkRBB3VuaUYyREIHdW5pRjJEQwd1bmlGMkREB3VuaUYyREUHdW5pRjJFMAd1bmlGMkUxB3VuaUYyRTIHdW5pRjJFMwd1bmlGMkU0B3VuaUYyRTUHdW5pRjJFNgd1bmlGMkU3BF82OTgHdW5pRjJFOQd1bmlGMkVBB3VuaUYyRUIHdW5pRjJFQwd1bmlGMkVEB3VuaUYyRUUAAAAAAAAB//8AAgABAAAADgAAABgAAAAAAAIAAQABAsIAAQAEAAAAAgAAAAAAAQAAAADMPaLPAAAAAMtPPDAAAAAA1DFouQ=="},function(A,M,t){"use strict";t.r(M),M.default="data:application/vnd.ms-fontobject;base64,bocCAKyGAgABAAIAAAAAAAAAAAAAAAAAAAABAJABAAAAAExQAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAWXjPkAAAAAAAAAAAAAAAAAAAAAAAABYARgBvAG4AdABBAHcAZQBzAG8AbQBlAAAADgBSAGUAZwB1AGwAYQByAAAAJABWAGUAcgBzAGkAbwBuACAANAAuADcALgAwACAAMgAwADEANgAAABYARgBvAG4AdABBAHcAZQBzAG8AbQBlAAAAAAAAAQAAAA0AgAADAFBGRlRNa75HuQAChpAAAAAcR0RFRgLwAAQAAoZwAAAAIE9TLzKIMnpAAAABWAAAAGBjbWFwCr86fwAADKgAAALyZ2FzcP//AAMAAoZoAAAACGdseWaP965NAAAarAACTLxoZWFkEInlLQAAANwAAAA2aGhlYQ8DCrUAAAEUAAAAJGhtdHhFeRiFAAABuAAACvBsb2NhAvWiXAAAD5wAAAsQbWF4cAMsAhwAAAE4AAAAIG5hbWXjl4usAAJnaAAABIZwb3N0r4+boQACa/AAABp1AAEAAAAEAcuQz3hZXw889QALBwAAAAAA1DPNMgAAAADUM80y////AAkBBgAAAAAIAAIAAQAAAAAAAQAABgD/AAAACQD/////CQEAAQAAAAAAAAAAAAAAAAAAArUAAQAAAsMCGQAnAAAAAAACAAAAAQABAAAAQAAAAAAAAAADBmkBkAAFAAAEjAQzAAAAhgSMBDMAAAJzAAABigAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABweXJzAEAAIPUABgD/AAAABgABAAAAAAEAAAAAAAAAAAAAACAAAQOAAHAAAAAAAlUAAAHAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAABdBgAAAAaAAAAHAAAABwAAAAaAAAAGgAAABQAAAAeAAAAGgAAABwAAAAcAAAAHAAB5BYAAbgaAAAAGgAAABgAAAAcAAAAGAAAABYAAAAaAABoGAAAABgAAAAeAADIGgAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABwAAAASAAAAHAABABoAAAAMAAAAEgAAABoAAAAWAAAAHAAAABgAAAAeAAAAGgAAKBQAAAAaAAAAHgAAABoAAAAWAAAAEAAAABwAAAAYAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAeAAAAGAAAABAAAAAYAAAAEAAAABwAAAAaAAAAGgAAABwAAAAQAAAAHAAAABoAAegWAAAAGAAAABgAAAAaAAAAHAAAABAAAAAYCAAEFAACaBQAAWgYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAABABgAAAAaAADUGgAA1BwAAAAYAAAAGAAANBYAAAAWAAAAGgAB6BgAAAAYAAAAHAAAABYAAAAcAAAAHAAAABwAAEAWAAAAGgAAABwAAAAcAAAAGAAAABwAAWgcAAFoHgAAABoAAAAaAAAAHgAAAAwAAQAcAAAAIAAAABgAAAAYAAAAHAAAABwAAAAeAAAAHAAAABgAAAAYAAAADgAAABwAAAAaAAAAGAAAABIAAAAcAAAAGAAAABoAAAAYAAAAGgAAABgAAAAWAAAAFgAAABQAAAAYAAAAGgAAsBAAAXwYAAAAGgAAAB4AAAAWAAAAGAAAABwAAAAcAAEAGAAACBwAAAAcAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABoAAFQcAAAAFgAAFBwAAAAYAAAAHgAAABoAAEAeAAAAGgABzBwAAAQcAAAAFgAAEBgAAAAYAAAAGAAAABwAAAAcAAA8HAAAABgAAAAaAAAAGgAAbBwAAQAYAAAAGAAAABgAAAAkAAAAHgAAABAAAAAQAAAACgABAAoAAAAaAAAAEAAAABAAAAAQAAAAHAAAABgAAAAYAAAAHAAAoBwAAAAcAAAAHAAAAA4AAAQcAAAAGgAAABwAAAAQAAAAHAAAAB4AAAAeAAAAFgAAABYAAAAcAAAAHAABAB4AAAAWAAAAGAAAABYAAAAWAAAAHgABABwAAAAeAAAAGgABABgAAAAYAAAAEAAAtBAAADQSAAE0EgABNAoAALQKAAA0EgABNBIAATQeAAAAHgAAABIAAAAMAAAAGAAAABoAAAAaAAAAHAABABgAAAAcAAAAGgAAABoAAAAeAAAAHAAAABwAAAAYAAAAGAAAABgAAAAeAAAAHgAAABwAAQAcAAEAGgAANB4AALQcAAAAGgAACBYAAAgaAAAAEAAAABoAAAAQAAGACgAAAAoAAYgYAAAUGAAAFB4AAAQaAAAAEgAAABYAADQUAAAAGgAAABYAAAwaAACQHAAAABgAAAAYAAAAGAAAABgAAAAWAAAAHAAAMBwAAAASAAAAGAAAABYAAAAGAAAAGAAAABgAAAAcAADYGAAAABYAAAAQAAAMEAAADBgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAEAAAABAAAAAQAADQDggAABAMABAUAAAAHAAAABQAAOAYAAAAGAAAABoAAIgaAACIHAAAiBwAAIgYAACIGAAAiBoAAAAaAAAAGAAAABgAAGwWAAAUGAAAABwAAAAcAAEAGAAALBgAAAAYAAAAGAAAABYAAAAYAAAAEAABEBgAAAAMAAAMDAAADBwAAQAcAAAAFgAAABoAAAAWAAAAGAAALBgAAAAYAAAAFAAAsBgAAAAUAAAAEAAAABgAAAAcAACwGAAAABwAAQAaAACAHgP//BwAAAAYAAAAFgAAABQAAFQYAAAAGAAAABgAAAAYAAAAGgAAABgAAAASAAAAFgAAACIAAAAaAAAAGAAAABwAAAAcAAAAIAAAACQAAAAYAAG0GAAAABwAAAAYAAAAGAAAAB4AAAAYAAAAIAAAABgAAAAf2ACkGAAAABgAAAAYAAAAHAAAABgAAAAUAAEAGgAAAAwAAQAcAAAAJAAAACAAAAAYAAAAHAAAABgAAAAcAABAIAAAACAAAAAYAACAGAAAABAAAAAkAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAJwcAAAAIAAAABwAAAAcAACAHAAATBwAAAAYAAAAHAABEBgAAAAUAADkHAAASCAAAAAcAAAAHAAAABgAAAAYAAAAHAAA+BQAAGAYAAAAGAAAABgAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAGQcAAGQGAABZCAAAAAgAACoHAAAABgAACQcAACcJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAIAAAOCAAADgWAAAAGAAAABgAAAAcAAAAHAAAABwAAAAgAAAAHAAAACAAAAAcAAAAGAAAACAAAAAgAAAAJAAAABgAAAAgAAAAFAAALCAAAAAYAAAAGAAAABgAAAAgAAAAGAAAABgAAAAgAAAAIAAAABgAAAAgAAAAIAAAABoAAAAaAAAAIAAAACAAAEwYAAAAJAAAABgAAAAcAAAAFAAACBgAAAAUAAAAGAAACBwAAAAcA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AaoBqwGsAa0BrgGvAbABsQGyAbMBtAG1AbYBtwG4AbkBugG7AbwBvQG+Ab8BwAHBAcIBwwHEAcUBxgHHAcgByQHKAcsBzAHNAc4BzwHQAdEB0gHTAdQB1QHWAdcB2AHZAdoB2wHcAd0B3gHfAeAB4QHiAeMB5AHlAeYB5wHoAekB6gHrAewB7QHuAe8B8AHxAfIB8wH0AfUB9gH3AfgB+QH6AfsB/AH9Af4B/wIAAgECAgIDAgQCBQIGAgcCCAAiAgkCCgILAgwCDQIOAg8CEAIRAhICEwIUAhUCFgIXAhgCGQIaAhsCHAIdAh4CHwIgAiECIgIjAiQCJQImAicCKAIpAioCKwIsAi0CLgIvAjACMQIyAjMCNAI1AjYCNwI4AjkCOgI7AjwCPQI+Aj8CQAJBAkICQwJEAkUCRgJHAkgCSQJKAksCTAJNAk4CTwJQAlECUgJTANICVAJVAlYCVwJYAlkCWgJbAlwCXQJeAl8CYAJhAmICYwJkAmUCZgJnAmgCaQJqAmsCbAJtAm4CbwJwAnECcgJzAnQCdQJ2AncCeAJ5AnoCewJ8An0CfgJ/AoACgQKCAoMChAKFAoYChwKIAokCigKLAowCjQKOAo8CkAKRApICkwKUApUClgKXApgCmQKaApsCnAKdAp4CnwKgAqECogKjAqQCpQKmAqcCqAKpAqoCqwKsAq0CrgKvArACsQKyArMCtAK1ArYCtwK4ArkCugK7ArwCvQK+Ar8CwALBAsICwwLEAsUCxgLHAsgCyQLKAssCzALNAs4CzwLQAtEC0gLTAtQC1QLWAtcC2ALZAtoC2wLcAt0C3gLfAuAC4QLiAuMC5ALlAuYC5wLoAukC6gLrAuwC7QLuAu8C8ALxAvIC8wL0AvUC9gL3AvgC+QL6AvsC/AL9Av4C/wMAAwEDAgMDAwQDBQMGAwcDCAMJAwoDCwMMAw0DDgMPAxADEQMSAxMDFAMVAxYDFwMYAxkDGgMbAxwDHQMeAx8DIAMhAyIDIwMkAyUDJgMnAygDKQMqAysDLAMtAy4DLwMwAzEDMgMzAzQDNQM2AzcDOAM5AzoDOwM8Az0DPgM/A0ADQQNCA0MDRANFA0YDRwNIA0kDSgNLA0wDTQNOA08DUANRA1IDUwNUA1UDVgNXA1gDWQNaA1sDXANdA14DXwNgA2EDYgNjA2QDZQNmA2cDaANpA2oDawNsA20DbgNvA3ADcQNyA3MDdAN1A3YDdwN4A3kDegN7A3wDfQN+A38DgAOBA4IDgwOEA4UDhgOHA4gDiQOKA4sDjAONA44DjwOQA5EDkgOTA5QDlQOWA5cDmAOZA5oDmwOcA50DngOfA6ADoQOiA6MDpAOlA6YDpwOoA6kDqgOrA6wDrQOuA68DsAOxAJQFZ2xhc3MFbXVzaWMGc2VhcmNoCGVudmVsb3BlBWhlYXJ0BHN0YXIKc3Rhcl9lbXB0eQR1c2VyBGZpbG0IdGhfbGFyZ2UCdGgHdGhfbGlzdAJvawZyZW1vdmUHem9vbV9pbgh6b29tX291dANvZmYGc2lnbmFsA2NvZwV0cmFzaARob21lCGZpbGVfYWx0BHRpbWUEcm9hZAxkb3dubG9hZF9hbHQIZG93bmxvYWQGdXBsb2FkBWluYm94C3BsYXlfY2lyY2xlBnJlcGVhdAdyZWZyZXNoCGxpc3RfYWx0BGxvY2sEZmxhZwpoZWFkcGhvbmVzCnZvbHVtZV9vZmYLdm9sdW1lX2Rvd24Jdm9sdW1lX3VwBnFyY29kZQdiYXJjb2RlA3RhZwR0YWdzBGJvb2sIYm9va21hcmsFcHJpbnQGY2FtZXJhBGZvbnQEYm9sZAZpdGFsaWMLdGV4dF9oZWlnaHQKdGV4dF93aWR0aAphbGlnbl9sZWZ0DGFsaWduX2NlbnRlcgthbGlnbl9yaWdodA1hbGlnbl9qdXN0aWZ5BGxpc3QLaW5kZW50X2xlZnQMaW5kZW50X3JpZ2h0DmZhY2V0aW1lX3ZpZGVvB3BpY3R1cmUGcGVuY2lsCm1hcF9tYXJrZXIGYWRqdXN0BHRpbnQEZWRpdAVzaGFyZQVjaGVjawRtb3ZlDXN0ZXBfYmFja3dhcmQNZmFzdF9iYWNrd2FyZAhiYWNrd2FyZARwbGF5BXBhdXNlBHN0b3AHZm9yd2FyZAxmYXN0X2ZvcndhcmQMc3RlcF9mb3J3YXJkBWVqZWN0DGNoZXZyb25fbGVmdA1jaGV2cm9uX3JpZ2h0CXBsdXNfc2lnbgptaW51c19zaWduC3JlbW92ZV9zaWduB29rX3NpZ24NcXVlc3Rpb25fc2lnbglpbmZvX3NpZ24Kc2NyZWVuc2hvdA1yZW1vdmVfY2lyY2xlCW9rX2NpcmNsZQpiYW5fY2lyY2xlCmFycm93X2xlZnQLYXJyb3dfcmlnaHQIYXJyb3dfdXAKYXJyb3dfZG93bglzaGFyZV9hbHQLcmVzaXplX2Z1bGwMcmVzaXplX3NtYWxsEGV4Y2xhbWF0aW9uX3NpZ24EZ2lmdARsZWFmBGZpcmUIZXllX29wZW4JZXllX2Nsb3NlDHdhcm5pbmdfc2lnbgVwbGFuZQhjYWxlbmRhcgZyYW5kb20HY29tbWVudAZtYWduZXQKY2hldnJvbl91cAxjaGV2cm9uX2Rvd24HcmV0d2VldA1zaG9wcGluZ19jYXJ0DGZvbGRlcl9jbG9zZQtmb2xkZXJfb3Blbg9yZXNpemVfdmVydGljYWwRcmVzaXplX2hvcml6b250YWwJYmFyX2NoYXJ0DHR3aXR0ZXJfc2lnbg1mYWNlYm9va19zaWduDGNhbWVyYV9yZXRybwNrZXkEY29ncwhjb21tZW50cw10aHVtYnNfdXBfYWx0D3RodW1ic19kb3duX2FsdAlzdGFyX2hhbGYLaGVhcnRfZW1wdHkHc2lnbm91dA1saW5rZWRpbl9zaWduB3B1c2hwaW4NZXh0ZXJuYWxfbGluawZzaWduaW4GdHJvcGh5C2dpdGh1Yl9zaWduCnVwbG9hZF9hbHQFbGVtb24FcGhvbmULY2hlY2tfZW1wdHkOYm9va21hcmtfZW1wdHkKcGhvbmVfc2lnbgd0d2l0dGVyCGZhY2Vib29rBmdpdGh1YgZ1bmxvY2sLY3JlZGl0X2NhcmQDcnNzA2hkZAhidWxsaG9ybgRiZWxsC2NlcnRpZmljYXRlCmhhbmRfcmlnaHQJaGFuZF9sZWZ0B2hhbmRfdXAJaGFuZF9kb3duEWNpcmNsZV9hcnJvd19sZWZ0EmNpcmNsZV9hcnJvd19yaWdodA9jaXJjbGVfYXJyb3dfdXARY2lyY2xlX2Fycm93X2Rvd24FZ2xvYmUGd3JlbmNoBXRhc2tzBmZpbHRlcglicmllZmNhc2UKZnVsbHNjcmVlbgVncm91cARsaW5rBWNsb3VkBmJlYWtlcgNjdXQEY29weQpwYXBlcl9jbGlwBHNhdmUKc2lnbl9ibGFuawdyZW9yZGVyAnVsAm9sDXN0cmlrZXRocm91Z2gJdW5kZXJsaW5lBXRhYmxlBW1hZ2ljBXRydWNrCXBpbnRlcmVzdA5waW50ZXJlc3Rfc2lnbhBnb29nbGVfcGx1c19zaWduC2dvb2dsZV9wbHVzBW1vbmV5CmNhcmV0X2Rvd24IY2FyZXRfdXAKY2FyZXRfbGVmdAtjYXJldF9yaWdodAdjb2x1bW5zBHNvcnQJc29ydF9kb3duB3NvcnRfdXAMZW52ZWxvcGVfYWx0CGxpbmtlZGluBHVuZG8FbGVnYWwJZGFzaGJvYXJkC2NvbW1lbnRfYWx0DGNvbW1lbnRzX2FsdARib2x0B3NpdGVtYXAIdW1icmVsbGEFcGFzdGUKbGlnaHRfYnVsYghleGNoYW5nZQ5jbG91ZF9kb3dubG9hZAxjbG91ZF91cGxvYWQHdXNlcl9tZAtzdGV0aG9zY29wZQhzdWl0Y2FzZQhiZWxsX2FsdAZjb2ZmZWUEZm9vZA1maWxlX3RleHRfYWx0CGJ1aWxkaW5nCGhvc3BpdGFsCWFtYnVsYW5jZQZtZWRraXQLZmlnaHRlcl9qZXQEYmVlcgZoX3NpZ24EZjBmZRFkb3VibGVfYW5nbGVfbGVmdBJkb3VibGVfYW5nbGVfcmlnaHQPZG91YmxlX2FuZ2xlX3VwEWRvdWJsZV9hbmdsZV9kb3duCmFuZ2xlX2xlZnQLYW5nbGVfcmlnaHQIYW5nbGVfdXAKYW5nbGVfZG93bgdkZXNrdG9wBmxhcHRvcAZ0YWJsZXQMbW9iaWxlX3Bob25lDGNpcmNsZV9ibGFuawpxdW90ZV9sZWZ0C3F1b3RlX3JpZ2h0B3NwaW5uZXIGY2lyY2xlBXJlcGx5CmdpdGh1Yl9hbHQQZm9sZGVyX2Nsb3NlX2FsdA9mb2xkZXJfb3Blbl9hbHQKZXhwYW5kX2FsdAxjb2xsYXBzZV9hbHQFc21pbGUFZnJvd24DbWVoB2dhbWVwYWQIa2V5Ym9hcmQIZmxhZ19hbHQOZmxhZ19jaGVja2VyZWQIdGVybWluYWwEY29kZQlyZXBseV9hbGwPc3Rhcl9oYWxmX2VtcHR5DmxvY2F0aW9uX2Fycm93BGNyb3AJY29kZV9mb3JrBnVubGluawRfMjc5C2V4Y2xhbWF0aW9uC3N1cGVyc2NyaXB0CXN1YnNjcmlwdARfMjgzDHB1enpsZV9waWVjZQptaWNyb3Bob25lDm1pY3JvcGhvbmVfb2ZmBnNoaWVsZA5jYWxlbmRhcl9lbXB0eRFmaXJlX2V4dGluZ3Vpc2hlcgZyb2NrZXQGbWF4Y2RuEWNoZXZyb25fc2lnbl9sZWZ0EmNoZXZyb25fc2lnbl9yaWdodA9jaGV2cm9uX3NpZ25fdXARY2hldnJvbl9zaWduX2Rvd24FaHRtbDUEY3NzMwZhbmNob3IKdW5sb2NrX2FsdAhidWxsc2V5ZRNlbGxpcHNpc19ob3Jpem9udGFsEWVsbGlwc2lzX3ZlcnRpY2FsBF8zMDMJcGxheV9zaWduBnRpY2tldA5taW51c19zaWduX2FsdAtjaGVja19taW51cwhsZXZlbF91cApsZXZlbF9kb3duCmNoZWNrX3NpZ24JZWRpdF9zaWduBF8zMTIKc2hhcmVfc2lnbgdjb21wYXNzCGNvbGxhcHNlDGNvbGxhcHNlX3RvcARfMzE3A2V1cgNnYnADdXNkA2lucgNqcHkDcnViA2tydwNidGMEZmlsZQlmaWxlX3RleHQQc29ydF9ieV9hbHBoYWJldARfMzI5EnNvcnRfYnlfYXR0cmlidXRlcxZzb3J0X2J5X2F0dHJpYnV0ZXNfYWx0DXNvcnRfYnlfb3JkZXIRc29ydF9ieV9vcmRlcl9hbHQEXzMzNARfMzM1DHlvdXR1YmVfc2lnbgd5b3V0dWJlBHhpbmcJeGluZ19zaWduDHlvdXR1YmVfcGxheQdkcm9wYm94DXN0YWNrZXhjaGFuZ2UJaW5zdGFncmFtBmZsaWNrcgNhZG4EZjE3MQ5iaXRidWNrZXRfc2lnbgZ0dW1ibHILdHVtYmxyX3NpZ24PbG9uZ19hcnJvd19kb3duDWxvbmdfYXJyb3dfdXAPbG9uZ19hcnJvd19sZWZ0EGxvbmdfYXJyb3dfcmlnaHQHd2luZG93cwdhbmRyb2lkBWxpbnV4B2RyaWJibGUFc2t5cGUKZm91cnNxdWFyZQZ0cmVsbG8GZmVtYWxlBG1hbGUGZ2l0dGlwA3N1bgRfMzY2B2FyY2hpdmUDYnVnAnZrBXdlaWJvBnJlbnJlbgRfMzcyDnN0YWNrX2V4Y2hhbmdlBF8zNzQVYXJyb3dfY2lyY2xlX2FsdF9sZWZ0BF8zNzYOZG90X2NpcmNsZV9hbHQEXzM3OAx2aW1lb19zcXVhcmUEXzM4MA1wbHVzX3NxdWFyZV9vBF8zODIEXzM4MwRfMzg0BF8zODUEXzM4NgRfMzg3BF8zODgEXzM4OQd1bmlGMUEwBGYxYTEEXzM5MgRfMzkzBGYxYTQEXzM5NQRfMzk2BF8zOTcEXzM5OARfMzk5BF80MDAEZjFhYgRfNDAyBF80MDMEXzQwNAd1bmlGMUIxBF80MDYEXzQwNwRfNDA4BF80MDkEXzQxMARfNDExBF80MTIEXzQxMwRfNDE0BF80MTUEXzQxNgRfNDE3BF80MTgEXzQxOQd1bmlGMUMwB3VuaUYxQzEEXzQyMgRfNDIzBF80MjQEXzQyNQRfNDI2BF80MjcEXzQyOARfNDI5BF80MzAEXzQzMQRfNDMyBF80MzMEXzQzNAd1bmlGMUQwB3VuaUYxRDEHdW5pRjFEMgRfNDM4BF80MzkHdW5pRjFENQd1bmlGMUQ2B3VuaUYxRDcEXzQ0MwRfNDQ0BF80NDUEXzQ0NgRfNDQ3BF80NDgEXzQ0OQd1bmlGMUUwBF80NTEEXzQ1MgRfNDUzBF80NTQEXzQ1NQRfNDU2BF80NTcEXzQ1OARfNDU5BF80NjAEXzQ2MQRfNDYyBF80NjMEXzQ2NAd1bmlGMUYwBF80NjYEXzQ2NwRmMWYzBF80NjkEXzQ3MARfNDcxBF80NzIEXzQ3MwRfNDc0BF80NzUEXzQ3NgRmMWZjBF80NzgEXzQ3OQRfNDgwBF80ODEEXzQ4MgRfNDgzBF80ODQEXzQ4NQRfNDg2BF80ODcEXzQ4OARfNDg5BF80OTAEXzQ5MQRfNDkyBF80OTMEXzQ5NARmMjEwBF80OTYEZjIxMgRfNDk4BF80OTkEXzUwMARfNTAxBF81MDIEXzUwMwRfNTA0BF81MDUEXzUwNgRfNTA3BF81MDgEXzUwOQV2ZW51cwRfNTExBF81MTIEXzUxMwRfNTE0BF81MTUEXzUxNgRfNTE3BF81MTgEXzUxOQRfNTIwBF81MjEEXzUyMgRfNTIzBF81MjQEXzUyNQRfNTI2BF81MjcEXzUyOARfNTI5BF81MzAEXzUzMQRfNTMyBF81MzMEXzUzNARfNTM1BF81MzYEXzUzNwRfNTM4BF81MzkEXzU0MARfNTQxBF81NDIEXzU0MwRfNTQ0BF81NDUEXzU0NgRfNTQ3BF81NDgEXzU0OQRfNTUwBF81NTEEXzU1MgRfNTUzBF81NTQEXzU1NQRfNTU2BF81NTcEXzU1OARfNTU5BF81NjAEXzU2MQRfNTYyBF81NjMEXzU2NARfNTY1BF81NjYEXzU2NwRfNTY4BF81NjkEZjI2MARmMjYxBF81NzIEZjI2MwRfNTc0BF81NzUEXzU3NgRfNTc3BF81NzgEXzU3OQRfNTgwBF81ODEEXzU4MgRfNTgzBF81ODQEXzU4NQRfNTg2BF81ODcEXzU4OARfNTg5BF81OTAEXzU5MQRfNTkyBF81OTMEXzU5NARfNTk1BF81OTYEXzU5NwRfNTk4BGYyN2UHdW5pRjI4MAd1bmlGMjgxBF82MDIEXzYwMwRfNjA0B3VuaUYyODUHdW5pRjI4NgRfNjA3BF82MDgEXzYwOQRfNjEwBF82MTEEXzYxMgRfNjEzBF82MTQEXzYxNQRfNjE2BF82MTcEXzYxOARfNjE5BF82MjAEXzYyMQRfNjIyBF82MjMEXzYyNARfNjI1BF82MjYEXzYyNwRfNjI4BF82MjkHdW5pRjJBMAd1bmlGMkExB3VuaUYyQTIHdW5pRjJBMwd1bmlGMkE0B3VuaUYyQTUHdW5pRjJBNgd1bmlGMkE3B3VuaUYyQTgHdW5pRjJBOQd1bmlGMkFBB3VuaUYyQUIHdW5pRjJBQwd1bmlGMkFEB3VuaUYyQUUHdW5pRjJCMAd1bmlGMkIxB3VuaUYyQjIHdW5pRjJCMwd1bmlGMkI0B3VuaUYyQjUHdW5pRjJCNgd1bmlGMkI3B3VuaUYyQjgHdW5pRjJCOQd1bmlGMkJBB3VuaUYyQkIHdW5pRjJCQwd1bmlGMkJEB3VuaUYyQkUHdW5pRjJDMAd1bmlGMkMxB3VuaUYyQzIHdW5pRjJDMwd1bmlGMkM0B3VuaUYyQzUHdW5pRjJDNgd1bmlGMkM3B3VuaUYyQzgHdW5pRjJDOQd1bmlGMkNBB3VuaUYyQ0IHdW5pRjJDQwd1bmlGMkNEB3VuaUYyQ0UHdW5pRjJEMAd1bmlGMkQxB3VuaUYyRDIHdW5pRjJEMwd1bmlGMkQ0B3VuaUYyRDUHdW5pRjJENgd1bmlGMkQ3B3VuaUYyRDgHdW5pRjJEOQd1bmlGMkRBB3VuaUYyREIHdW5pRjJEQwd1bmlGMkREB3VuaUYyREUHdW5pRjJFMAd1bmlGMkUxB3VuaUYyRTIHdW5pRjJFMwd1bmlGMkU0B3VuaUYyRTUHdW5pRjJFNgd1bmlGMkU3BF82OTgHdW5pRjJFOQd1bmlGMkVBB3VuaUYyRUIHdW5pRjJFQwd1bmlGMkVEB3VuaUYyRUUAAAAAAAAB//8AAgABAAAADgAAABgAAAAAAAIAAQABAsIAAQAEAAAAAgAAAAAAAQAAAADMPaLPAAAAAMtPPDAAAAAA1DFouQ=="},function(A,M,t){"use strict";t.r(M),M.default="data:font/woff2;base64,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"},function(A,M,t){"use strict";t.r(M),M.default="data:font/woff;base64,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"},function(A,M,t){"use strict";t.r(M),M.default="data:font/ttf;base64,AAEAAAANAIAAAwBQRkZUTWu+R7kAAoaQAAAAHEdERUYC8AAEAAKGcAAAACBPUy8yiDJ6QAAAAVgAAABgY21hcAq/On8AAAyoAAAC8mdhc3D//wADAAKGaAAAAAhnbHlmj/euTQAAGqwAAky8aGVhZBCJ5S0AAADcAAAANmhoZWEPAwq1AAABFAAAACRobXR4RXkYhQAAAbgAAArwbG9jYQL1olwAAA+cAAALEG1heHADLAIcAAABOAAAACBuYW1l45eLrAACZ2gAAASGcG9zdK+Pm6EAAmvwAAAadQABAAAABAHLkM94WV8PPPUACwcAAAAAANQzzTIAAAAA1DPNMv///wAJAQYAAAAACAACAAEAAAAAAAEAAAYA/wAAAAkA/////wkBAAEAAAAAAAAAAAAAAAAAAAK1AAEAAALDAhkAJwAAAAAAAgAAAAEAAQAAAEAAAAAAAAAAAwZpAZAABQAABIwEMwAAAIYEjAQzAAACcwAAAYoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcHlycwBAACD1AAYA/wAAAAYAAQAAAAABAAAAAAAAAAAAAAAgAAEDgABwAAAAAAJVAAABwAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAXQYAAAAGgAAABwAAAAcAAAAGgAAABoAAAAUAAAAHgAAABoAAAAcAAAAHAAAABwAAeQWAAG4GgAAABoAAAAYAAAAHAAAABgAAAAWAAAAGgAAaBgAAAAYAAAAHgAAyBoAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAcAAAAEgAAABwAAQAaAAAADAAAABIAAAAaAAAAFgAAABwAAAAYAAAAHgAAABoAACgUAAAAGgAAAB4AAAAaAAAAFgAAABAAAAAcAAAAGAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHgAAABgAAAAQAAAAGAAAABAAAAAcAAAAGgAAABoAAAAcAAAAEAAAABwAAAAaAAHoFgAAABgAAAAYAAAAGgAAABwAAAAQAAAAGAgABBQAAmgUAAFoGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAQAYAAAAGgAA1BoAANQcAAAAGAAAABgAADQWAAAAFgAAABoAAegYAAAAGAAAABwAAAAWAAAAHAAAABwAAAAcAABAFgAAABoAAAAcAAAAHAAAABgAAAAcAAFoHAABaB4AAAAaAAAAGgAAAB4AAAAMAAEAHAAAACAAAAAYAAAAGAAAABwAAAAcAAAAHgAAABwAAAAYAAAAGAAAAA4AAAAcAAAAGgAAABgAAAASAAAAHAAAABgAAAAaAAAAGAAAABoAAAAYAAAAFgAAABYAAAAUAAAAGAAAABoAALAQAAF8GAAAABoAAAAeAAAAFgAAABgAAAAcAAAAHAABABgAAAgcAAAAHAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAaAABUHAAAABYAABQcAAAAGAAAAB4AAAAaAABAHgAAABoAAcwcAAAEHAAAABYAABAYAAAAGAAAABgAAAAcAAAAHAAAPBwAAAAYAAAAGgAAABoAAGwcAAEAGAAAABgAAAAYAAAAJAAAAB4AAAAQAAAAEAAAAAoAAQAKAAAAGgAAABAAAAAQAAAAEAAAABwAAAAYAAAAGAAAABwAAKAcAAAAHAAAABwAAAAOAAAEHAAAABoAAAAcAAAAEAAAABwAAAAeAAAAHgAAABYAAAAWAAAAHAAAABwAAQAeAAAAFgAAABgAAAAWAAAAFgAAAB4AAQAcAAAAHgAAABoAAQAYAAAAGAAAABAAALQQAAA0EgABNBIAATQKAAC0CgAANBIAATQSAAE0HgAAAB4AAAASAAAADAAAABgAAAAaAAAAGgAAABwAAQAYAAAAHAAAABoAAAAaAAAAHgAAABwAAAAcAAAAGAAAABgAAAAYAAAAHgAAAB4AAAAcAAEAHAABABoAADQeAAC0HAAAABoAAAgWAAAIGgAAABAAAAAaAAAAEAABgAoAAAAKAAGIGAAAFBgAABQeAAAEGgAAABIAAAAWAAA0FAAAABoAAAAWAAAMGgAAkBwAAAAYAAAAGAAAABgAAAAYAAAAFgAAABwAADAcAAAAEgAAABgAAAAWAAAABgAAABgAAAAYAAAAHAAA2BgAAAAWAAAAEAAADBAAAAwYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABAAAAAQAAAAEAAA0A4IAAAQDAAQFAAAABwAAAAUAADgGAAAABgAAAAaAACIGgAAiBwAAIgcAACIGAAAiBgAAIgaAAAAGgAAABgAAAAYAABsFgAAFBgAAAAcAAAAHAABABgAACwYAAAAGAAAABgAAAAWAAAAGAAAABAAARAYAAAADAAADAwAAAwcAAEAHAAAABYAAAAaAAAAFgAAABgAACwYAAAAGAAAABQAALAYAAAAFAAAABAAAAAYAAAAHAAAsBgAAAAcAAEAGgAAgB4D//wcAAAAGAAAABYAAAAUAABUGAAAABgAAAAYAAAAGAAAABoAAAAYAAAAEgAAABYAAAAiAAAAGgAAABgAAAAcAAAAHAAAACAAAAAkAAAAGAABtBgAAAAcAAAAGAAAABgAAAAeAAAAGAAAACAAAAAYAAAAH9gApBgAAAAYAAAAGAAAABwAAAAYAAAAFAABABoAAAAMAAEAHAAAACQAAAAgAAAAGAAAABwAAAAYAAAAHAAAQCAAAAAgAAAAGAAAgBgAAAAQAAAAJAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAACcHAAAACAAAAAcAAAAHAAAgBwAAEwcAAAAGAAAABwAARAYAAAAFAAA5BwAAEggAAAAHAAAABwAAAAYAAAAGAAAABwAAPgUAABgGAAAABgAAAAYAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAABkHAABkBgAAWQgAAAAIAAAqBwAAAAYAAAkHAAAnCQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACAAADggAAA4FgAAABgAAAAYAAAAHAAAABwAAAAcAAAAIAAAABwAAAAgAAAAHAAAABgAAAAgAAAAIAAAACQAAAAYAAAAIAAAABQAACwgAAAAGAAAABgAAAAYAAAAIAAAABgAAAAYAAAAIAAAACAAAAAYAAAAIAAAACAAAAAaAAAAGgAAACAAAAAgAABMGAAAACQAAAAYAAAAHAAAABQAAAgYAAAAFAAAABgAAAgcAAAAHAAACB4AAAQgAAAYGAAAABQAAAggAAAQFAAAABQAAAAcAAAAHAAAABgAAAAUAAAAGAAAABwAAAAgAAAAIAAAACAAAAAYAAAAGAAAABgAAAAcAAAAGAAAACPgAVAkAAAAHAAAACQAAAAkAAAAJAAAACQAAAAkAAAAFAAAABAAAAAgAAAAJAAAABgAAAAYAAAAJAAAACQAAAAcAAAAJAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABwAAAAcAAAAIAAAA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AAD8uAAA/QwAAP1gAAD9tAAA/ggAAP64AAD/CAABABQAAQW0AAEG9AABB/gAAQjQAAEJZAABChAAAQqYAAELGAABDAQAAQykAAENLAABDgAAAQ+IAAERLAABEaAAARLMAAETOAABE+QAARSQAAEVKAABFaQAARZYAAEW/AABF8AAARiEAAEZeAABGnwAARtUAAEc1AABHUAAAR3UAAEekAABHwQAAR98AAEgpAABIcAAASJ4AAEjCAABI2wAASQEAAEkzAABJ2gAASjoAAEqTAABLFQAAS5MAAExdAABMfQAATLgAAEzMAABM7AAATSoAAE1dAABNlQAATckAAE4DAABOUgAAToQAAE68AABO5AAATyEAAE82AABP1gAAUAcAAFBwAABQsgAAUPIAAFEnAABRUgAAUZIAAFHcAABSEgAAUl4AAFKIAABSuQAAUvUAAFMoAABTRgAAU5AAAFQQAABUaAAAVLgAAFTRAABVCAAAVVMAAFWYAABVtQAAVdYAAFYNAABWKAAAVoEAAFaiAABW2QAAVvgAAFcfAABXdgAAV6gAAFglAABYUgAAWG8AAFi8AABY1gAAWSsAAFldAABZmgAAWfcAAFotAABaVwAAWp4AAFuhAABcEAAAXPgAAF2EAABd8gAAXiQAAF5iAABeowAAXtoAAF8jAABfRwAAX2kAAF/XAABf5gAAX/4AAGAbAABgXQAAYKQAAGDNAABg6QAAYTIAAGFsAABhqQAAYh0AAGJjAABijgAAYs4AAGLoAABjkwAAY6oAAGPVAABkBAAAZEUAAGTkAABlBQAAZUEAAGV/AABlvgAAZegAAGZfAABmsgAAZwQAAGdCAABndgAAZ58AAGfGAABn+gAAaDEAAGiDAABozQAAaR4AAGlsAABpoAAAadMAAGoHAABqJAAAajsAAGo7AABqOwAAalYAAGqKAABqyAAAavMAAGsrAABragAAa4gAAGuiAABrwQAAa+oAAGwQAABsIgAAba8AAG3bAABuOAAAbl0AAG6BAABupQAAbskAAG7pAABvAgAAbx4AAG9TAABvkwAAb6kAAG/IAABwEgAAcEYAAHBxAABwwQAAcPkAAHEoAABxVQAAcYoAAHG7AAByAwAAckMAAHKiAABy6AAAcz4AAHOHAABz5QAAdBsAAHRZAAB0twAAdNQAAHT+AAB1YQAAdZ4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AgoCCwIMAg0CDgIPAhACEQISAhMCFAIVAhYCFwIYAhkCGgIbAhwCHQIeAh8CIAIhAiICIwIkAiUCJgInAigCKQIqAisCLAItAi4CLwIwAjECMgIzAjQCNQI2AjcCOAI5AjoCOwI8Aj0CPgI/AkACQQJCAkMCRAJFAkYCRwJIAkkCSgJLAkwCTQJOAk8CUAJRAlICUwDSAlQCVQJWAlcCWAJZAloCWwJcAl0CXgJfAmACYQJiAmMCZAJlAmYCZwJoAmkCagJrAmwCbQJuAm8CcAJxAnICcwJ0AnUCdgJ3AngCeQJ6AnsCfAJ9An4CfwKAAoECggKDAoQChQKGAocCiAKJAooCiwKMAo0CjgKPApACkQKSApMClAKVApYClwKYApkCmgKbApwCnQKeAp8CoAKhAqICowKkAqUCpgKnAqgCqQKqAqsCrAKtAq4CrwKwArECsgKzArQCtQK2ArcCuAK5AroCuwK8Ar0CvgK/AsACwQLCAsMCxALFAsYCxwLIAskCygLLAswCzQLOAs8C0ALRAtIC0wLUAtUC1gLXAtgC2QLaAtsC3ALdAt4C3wLgAuEC4gLjAuQC5QLmAucC6ALpAuoC6wLsAu0C7gLvAvAC8QLyAvMC9AL1AvYC9wL4AvkC+gL7AvwC/QL+Av8DAAMBAwIDAwMEAwUDBgMHAwgDCQMKAwsDDAMNAw4DDwMQAxEDEgMTAxQDFQMWAxcDGAMZAxoDGwMcAx0DHgMfAyADIQMiAyMDJAMlAyYDJwMoAykDKgMrAywDLQMuAy8DMAMxAzIDMwM0AzUDNgM3AzgDOQM6AzsDPAM9Az4DPwNAA0EDQgNDA0QDRQNGA0cDSANJA0oDSwNMA00DTgNPA1ADUQNSA1MDVANVA1YDVwNYA1kDWgNbA1wDXQNeA18DYANhA2IDYwNkA2UDZgNnA2gDaQNqA2sDbANtA24DbwNwA3EDcgNzA3QDdQN2A3cDeAN5A3oDewN8A30DfgN/A4ADgQOCA4MDhAOFA4YDhwOIA4kDigOLA4wDjQOOA48DkAORA5IDkwOUA5UDlgOXA5gDmQOaA5sDnAOdA54DnwOgA6EDogOjA6QDpQOmA6cDqAOpA6oDqwOsA60DrgOvA7ADsQCUBWdsYXNzBW11c2ljBnNlYXJjaAhlbnZlbG9wZQVoZWFydARzdGFyCnN0YXJfZW1wdHkEdXNlcgRmaWxtCHRoX2xhcmdlAnRoB3RoX2xpc3QCb2sGcmVtb3ZlB3pvb21faW4Iem9vbV9vdXQDb2ZmBnNpZ25hbANjb2cFdHJhc2gEaG9tZQhmaWxlX2FsdAR0aW1lBHJvYWQMZG93bmxvYWRfYWx0CGRvd25sb2FkBnVwbG9hZAVpbmJveAtwbGF5X2NpcmNsZQZyZXBlYXQHcmVmcmVzaAhsaXN0X2FsdARsb2NrBGZsYWcKaGVhZHBob25lcwp2b2x1bWVfb2ZmC3ZvbHVtZV9kb3duCXZvbHVtZV91cAZxcmNvZGUHYmFyY29kZQN0YWcEdGFncwRib29rCGJvb2ttYXJrBXByaW50BmNhbWVyYQRmb250BGJvbGQGaXRhbGljC3RleHRfaGVpZ2h0CnRleHRfd2lkdGgKYWxpZ25fbGVmdAxhbGlnbl9jZW50ZXILYWxpZ25fcmlnaHQNYWxpZ25fanVzdGlmeQRsaXN0C2luZGVudF9sZWZ0DGluZGVudF9yaWdodA5mYWNldGltZV92aWRlbwdwaWN0dXJlBnBlbmNpbAptYXBfbWFya2VyBmFkanVzdAR0aW50BGVkaXQFc2hhcmUFY2hlY2sEbW92ZQ1zdGVwX2JhY2t3YXJkDWZhc3RfYmFja3dhcmQIYmFja3dhcmQEcGxheQVwYXVzZQRzdG9wB2ZvcndhcmQMZmFzdF9mb3J3YXJkDHN0ZXBfZm9yd2FyZAVlamVjdAxjaGV2cm9uX2xlZnQNY2hldnJvbl9yaWdodAlwbHVzX3NpZ24KbWludXNfc2lnbgtyZW1vdmVfc2lnbgdva19zaWduDXF1ZXN0aW9uX3NpZ24JaW5mb19zaWduCnNjcmVlbnNob3QNcmVtb3ZlX2NpcmNsZQlva19jaXJjbGUKYmFuX2NpcmNsZQphcnJvd19sZWZ0C2Fycm93X3JpZ2h0CGFycm93X3VwCmFycm93X2Rvd24Jc2hhcmVfYWx0C3Jlc2l6ZV9mdWxsDHJlc2l6ZV9zbWFsbBBleGNsYW1hdGlvbl9zaWduBGdpZnQEbGVhZgRmaXJlCGV5ZV9vcGVuCWV5ZV9jbG9zZQx3YXJuaW5nX3NpZ24FcGxhbmUIY2FsZW5kYXIGcmFuZG9tB2NvbW1lbnQGbWFnbmV0CmNoZXZyb25fdXAMY2hldnJvbl9kb3duB3JldHdlZXQNc2hvcHBpbmdfY2FydAxmb2xkZXJfY2xvc2ULZm9sZGVyX29wZW4PcmVzaXplX3ZlcnRpY2FsEXJlc2l6ZV9ob3Jpem9udGFsCWJhcl9jaGFydAx0d2l0dGVyX3NpZ24NZmFjZWJvb2tfc2lnbgxjYW1lcmFfcmV0cm8Da2V5BGNvZ3MIY29tbWVudHMNdGh1bWJzX3VwX2FsdA90aHVtYnNfZG93bl9hbHQJc3Rhcl9oYWxmC2hlYXJ0X2VtcHR5B3NpZ25vdXQNbGlua2VkaW5fc2lnbgdwdXNocGluDWV4dGVybmFsX2xpbmsGc2lnbmluBnRyb3BoeQtnaXRodWJfc2lnbgp1cGxvYWRfYWx0BWxlbW9uBXBob25lC2NoZWNrX2VtcHR5DmJvb2ttYXJrX2VtcHR5CnBob25lX3NpZ24HdHdpdHRlcghmYWNlYm9vawZnaXRodWIGdW5sb2NrC2NyZWRpdF9jYXJkA3JzcwNoZGQIYnVsbGhvcm4EYmVsbAtjZXJ0aWZpY2F0ZQpoYW5kX3JpZ2h0CWhhbmRfbGVmdAdoYW5kX3VwCWhhbmRfZG93bhFjaXJjbGVfYXJyb3dfbGVmdBJjaXJjbGVfYXJyb3dfcmlnaHQPY2lyY2xlX2Fycm93X3VwEWNpcmNsZV9hcnJvd19kb3duBWdsb2JlBndyZW5jaAV0YXNrcwZmaWx0ZXIJYnJpZWZjYXNlCmZ1bGxzY3JlZW4FZ3JvdXAEbGluawVjbG91ZAZiZWFrZXIDY3V0BGNvcHkKcGFwZXJfY2xpcARzYXZlCnNpZ25fYmxhbmsHcmVvcmRlcgJ1bAJvbA1zdHJpa2V0aHJvdWdoCXVuZGVybGluZQV0YWJsZQVtYWdpYwV0cnVjawlwaW50ZXJlc3QOcGludGVyZXN0X3NpZ24QZ29vZ2xlX3BsdXNfc2lnbgtnb29nbGVfcGx1cwVtb25leQpjYXJldF9kb3duCGNhcmV0X3VwCmNhcmV0X2xlZnQLY2FyZXRfcmlnaHQHY29sdW1ucwRzb3J0CXNvcnRfZG93bgdzb3J0X3VwDGVudmVsb3BlX2FsdAhsaW5rZWRpbgR1bmRvBWxlZ2FsCWRhc2hib2FyZAtjb21tZW50X2FsdAxjb21tZW50c19hbHQEYm9sdAdzaXRlbWFwCHVtYnJlbGxhBXBhc3RlCmxpZ2h0X2J1bGIIZXhjaGFuZ2UOY2xvdWRfZG93bmxvYWQMY2xvdWRfdXBsb2FkB3VzZXJfbWQLc3RldGhvc2NvcGUIc3VpdGNhc2UIYmVsbF9hbHQGY29mZmVlBGZvb2QNZmlsZV90ZXh0X2FsdAhidWlsZGluZwhob3NwaXRhbAlhbWJ1bGFuY2UGbWVka2l0C2ZpZ2h0ZXJfamV0BGJlZXIGaF9zaWduBGYwZmURZG91YmxlX2FuZ2xlX2xlZnQSZG91YmxlX2FuZ2xlX3JpZ2h0D2RvdWJsZV9hbmdsZV91cBFkb3VibGVfYW5nbGVfZG93bgphbmdsZV9sZWZ0C2FuZ2xlX3JpZ2h0CGFuZ2xlX3VwCmFuZ2xlX2Rvd24HZGVza3RvcAZsYXB0b3AGdGFibGV0DG1vYmlsZV9waG9uZQxjaXJjbGVfYmxhbmsKcXVvdGVfbGVmdAtxdW90ZV9yaWdodAdzcGlubmVyBmNpcmNsZQVyZXBseQpnaXRodWJfYWx0EGZvbGRlcl9jbG9zZV9hbHQPZm9sZGVyX29wZW5fYWx0CmV4cGFuZF9hbHQMY29sbGFwc2VfYWx0BXNtaWxlBWZyb3duA21laAdnYW1lcGFkCGtleWJvYXJkCGZsYWdfYWx0DmZsYWdfY2hlY2tlcmVkCHRlcm1pbmFsBGNvZGUJcmVwbHlfYWxsD3N0YXJfaGFsZl9lbXB0eQ5sb2NhdGlvbl9hcnJvdwRjcm9wCWNvZGVfZm9yawZ1bmxpbmsEXzI3OQtleGNsYW1hdGlvbgtzdXBlcnNjcmlwdAlzdWJzY3JpcHQEXzI4MwxwdXp6bGVfcGllY2UKbWljcm9waG9uZQ5taWNyb3Bob25lX29mZgZzaGllbGQOY2FsZW5kYXJfZW1wdHkRZmlyZV9leHRpbmd1aXNoZXIGcm9ja2V0Bm1heGNkbhFjaGV2cm9uX3NpZ25fbGVmdBJjaGV2cm9uX3NpZ25fcmlnaHQPY2hldnJvbl9zaWduX3VwEWNoZXZyb25fc2lnbl9kb3duBWh0bWw1BGNzczMGYW5jaG9yCnVubG9ja19hbHQIYnVsbHNleWUTZWxsaXBzaXNfaG9yaXpvbnRhbBFlbGxpcHNpc192ZXJ0aWNhbARfMzAzCXBsYXlfc2lnbgZ0aWNrZXQObWludXNfc2lnbl9hbHQLY2hlY2tfbWludXMIbGV2ZWxfdXAKbGV2ZWxfZG93bgpjaGVja19zaWduCWVkaXRfc2lnbgRfMzEyCnNoYXJlX3NpZ24HY29tcGFzcwhjb2xsYXBzZQxjb2xsYXBzZV90b3AEXzMxNwNldXIDZ2JwA3VzZANpbnIDanB5A3J1YgNrcncDYnRjBGZpbGUJZmlsZV90ZXh0EHNvcnRfYnlfYWxwaGFiZXQEXzMyORJzb3J0X2J5X2F0dHJpYnV0ZXMWc29ydF9ieV9hdHRyaWJ1dGVzX2FsdA1zb3J0X2J5X29yZGVyEXNvcnRfYnlfb3JkZXJfYWx0BF8zMzQEXzMzNQx5b3V0dWJlX3NpZ24HeW91dHViZQR4aW5nCXhpbmdfc2lnbgx5b3V0dWJlX3BsYXkHZHJvcGJveA1zdGFja2V4Y2hhbmdlCWluc3RhZ3JhbQZmbGlja3IDYWRuBGYxNzEOYml0YnVja2V0X3NpZ24GdHVtYmxyC3R1bWJscl9zaWduD2xvbmdfYXJyb3dfZG93bg1sb25nX2Fycm93X3VwD2xvbmdfYXJyb3dfbGVmdBBsb25nX2Fycm93X3JpZ2h0B3dpbmRvd3MHYW5kcm9pZAVsaW51eAdkcmliYmxlBXNreXBlCmZvdXJzcXVhcmUGdHJlbGxvBmZlbWFsZQRtYWxlBmdpdHRpcANzdW4EXzM2NgdhcmNoaXZlA2J1ZwJ2awV3ZWlibwZyZW5yZW4EXzM3Mg5zdGFja19leGNoYW5nZQRfMzc0FWFycm93X2NpcmNsZV9hbHRfbGVmdARfMzc2DmRvdF9jaXJjbGVfYWx0BF8zNzgMdmltZW9fc3F1YXJlBF8zODANcGx1c19zcXVhcmVfbwRfMzgyBF8zODMEXzM4NARfMzg1BF8zODYEXzM4NwRfMzg4BF8zODkHdW5pRjFBMARmMWExBF8zOTIEXzM5MwRmMWE0BF8zOTUEXzM5NgRfMzk3BF8zOTgEXzM5OQRfNDAwBGYxYWIEXzQwMgRfNDAzBF80MDQHdW5pRjFCMQRfNDA2BF80MDcEXzQwOARfNDA5BF80MTAEXzQxMQRfNDEyBF80MTMEXzQxNARfNDE1BF80MTYEXzQxNwRfNDE4BF80MTkHdW5pRjFDMAd1bmlGMUMxBF80MjIEXzQyMwRfNDI0BF80MjUEXzQyNgRfNDI3BF80MjgEXzQyOQRfNDMwBF80MzEEXzQzMgRfNDMzBF80MzQHdW5pRjFEMAd1bmlGMUQxB3VuaUYxRDIEXzQzOARfNDM5B3VuaUYxRDUHdW5pRjFENgd1bmlGMUQ3BF80NDMEXzQ0NARfNDQ1BF80NDYEXzQ0NwRfNDQ4BF80NDkHdW5pRjFFMARfNDUxBF80NTIEXzQ1MwRfNDU0BF80NTUEXzQ1NgRfNDU3BF80NTgEXzQ1OQRfNDYwBF80NjEEXzQ2MgRfNDYzBF80NjQHdW5pRjFGMARfNDY2BF80NjcEZjFmMwRfNDY5BF80NzAEXzQ3MQRfNDcyBF80NzMEXzQ3NARfNDc1BF80NzYEZjFmYwRfNDc4BF80NzkEXzQ4MARfNDgxBF80ODIEXzQ4MwRfNDg0BF80ODUEXzQ4NgRfNDg3BF80ODgEXzQ4OQRfNDkwBF80OTEEXzQ5MgRfNDkzBF80OTQEZjIxMARfNDk2BGYyMTIEXzQ5OARfNDk5BF81MDAEXzUwMQRfNTAyBF81MDMEXzUwNARfNTA1BF81MDYEXzUwNwRfNTA4BF81MDkFdmVudXMEXzUxMQRfNTEyBF81MTMEXzUxNARfNTE1BF81MTYEXzUxNwRfNTE4BF81MTkEXzUyMARfNTIxBF81MjIEXzUyMwRfNTI0BF81MjUEXzUyNgRfNTI3BF81MjgEXzUyOQRfNTMwBF81MzEEXzUzMgRfNTMzBF81MzQEXzUzNQRfNTM2BF81MzcEXzUzOARfNTM5BF81NDAEXzU0MQRfNTQyBF81NDMEXzU0NARfNTQ1BF81NDYEXzU0NwRfNTQ4BF81NDkEXzU1MARfNTUxBF81NTIEXzU1MwRfNTU0BF81NTUEXzU1NgRfNTU3BF81NTgEXzU1OQRfNTYwBF81NjEEXzU2MgRfNTYzBF81NjQEXzU2NQRfNTY2BF81NjcEXzU2OARfNTY5BGYyNjAEZjI2MQRfNTcyBGYyNjMEXzU3NARfNTc1BF81NzYEXzU3NwRfNTc4BF81NzkEXzU4MARfNTgxBF81ODIEXzU4MwRfNTg0BF81ODUEXzU4NgRfNTg3BF81ODgEXzU4OQRfNTkwBF81OTEEXzU5MgRfNTkzBF81OTQEXzU5NQRfNTk2BF81OTcEXzU5OARmMjdlB3VuaUYyODAHdW5pRjI4MQRfNjAyBF82MDMEXzYwNAd1bmlGMjg1B3VuaUYyODYEXzYwNwRfNjA4BF82MDkEXzYxMARfNjExBF82MTIEXzYxMwRfNjE0BF82MTUEXzYxNgRfNjE3BF82MTgEXzYxOQRfNjIwBF82MjEEXzYyMgRfNjIzBF82MjQEXzYyNQRfNjI2BF82MjcEXzYyOARfNjI5B3VuaUYyQTAHdW5pRjJBMQd1bmlGMkEyB3VuaUYyQTMHdW5pRjJBNAd1bmlGMkE1B3VuaUYyQTYHdW5pRjJBNwd1bmlGMkE4B3VuaUYyQTkHdW5pRjJBQQd1bmlGMkFCB3VuaUYyQUMHdW5pRjJBRAd1bmlGMkFFB3VuaUYyQjAHdW5pRjJCMQd1bmlGMkIyB3VuaUYyQjMHdW5pRjJCNAd1bmlGMkI1B3VuaUYyQjYHdW5pRjJCNwd1bmlGMkI4B3VuaUYyQjkHdW5pRjJCQQd1bmlGMkJCB3VuaUYyQkMHdW5pRjJCRAd1bmlGMkJFB3VuaUYyQzAHdW5pRjJDMQd1bmlGMkMyB3VuaUYyQzMHdW5pRjJDNAd1bmlGMkM1B3VuaUYyQzYHdW5pRjJDNwd1bmlGMkM4B3VuaUYyQzkHdW5pRjJDQQd1bmlGMkNCB3VuaUYyQ0MHdW5pRjJDRAd1bmlGMkNFB3VuaUYyRDAHdW5pRjJEMQd1bmlGMkQyB3VuaUYyRDMHdW5pRjJENAd1bmlGMkQ1B3VuaUYyRDYHdW5pRjJENwd1bmlGMkQ4B3VuaUYyRDkHdW5pRjJEQQd1bmlGMkRCB3VuaUYyREMHdW5pRjJERAd1bmlGMkRFB3VuaUYyRTAHdW5pRjJFMQd1bmlGMkUyB3VuaUYyRTMHdW5pRjJFNAd1bmlGMkU1B3VuaUYyRTYHdW5pRjJFNwRfNjk4B3VuaUYyRTkHdW5pRjJFQQd1bmlGMkVCB3VuaUYyRUMHdW5pRjJFRAd1bmlGMkVFAAAAAAAAAf//AAIAAQAAAA4AAAAYAAAAAAACAAEAAQLCAAEABAAAAAIAAAAAAAEAAAAAzD2izwAAAADLTzwwAAAAANQxaLk="},function(A,M,t){"use strict";t.r(M),M.default="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pg0KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIiA+DQo8c3ZnPg0KPG1ldGFkYXRhPg0KQ3JlYXRlZCBieSBGb250Rm9yZ2UgMjAxMjA3MzEgYXQgTW9uIE9jdCAyNCAxNzozNzo0MCAyMDE2DQogQnkgLCwsDQpDb3B5cmlnaHQgRGF2ZSBHYW5keSAyMDE2LiBBbGwgcmlnaHRzIHJlc2VydmVkLg0KPC9tZXRhZGF0YT4NCjxkZWZzPg0KPGZvbnQgaWQ9IkZvbnRBd2Vzb21lIiBob3Jpei1hZHYteD0iMTUzNiIgPg0KICA8Zm9udC1mYWNlIA0KICAgIGZvbnQtZmFtaWx5PSJGb250QXdlc29tZSINCiAgICBmb250LXdlaWdodD0iNDAwIg0KICAgIGZvbnQtc3RyZXRjaD0ibm9ybWFsIg0KICAgIHVuaXRzLXBlci1lbT0iMTc5MiINCiAgICBwYW5vc2UtMT0iMCAwIDAgMCAwIDAgMCAwIDAgMCINCiAgICBhc2NlbnQ9IjE1MzYiDQogICAgZGVzY2VudD0iLTI1NiINCiAgICBiYm94PSItMS4wMjA4MyAtMjU2Ljk2MiAyMzA0LjYgMTUzNy4wMiINCiAgICB1bmRlcmxpbmUtdGhpY2tuZXNzPSIwIg0KICAgIHVuZGVybGluZS1wb3NpdGlvbj0iMCINCiAgICB1bmljb2RlLXJhbmdlPSJVKzAwMjAtRjUwMCINCiAgLz4NCjxtaXNzaW5nLWdseXBoIGhvcml6LWFkdi14PSI4OTYiIA0KZD0iTTIyNCAxMTJoNDQ4djEzMTJoLTQ0OHYtMTMxMnpNMTEyIDB2MTUzNmg2NzJ2LTE1MzZoLTY3MnoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Ii5ub3RkZWYiIGhvcml6LWFkdi14PSI4OTYiIA0KZD0iTTIyNCAxMTJoNDQ4djEzMTJoLTQ0OHYtMTMxMnpNMTEyIDB2MTUzNmg2NzJ2LTE1MzZoLTY3MnoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Ii5udWxsIiBob3Jpei1hZHYteD0iMCIgDQogLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ibm9ubWFya2luZ3JldHVybiIgaG9yaXotYWR2LXg9IjU5NyIgDQogLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ic3BhY2UiIHVuaWNvZGU9IiAiIGhvcml6LWFkdi14PSI0NDgiIA0KIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImRpZXJlc2lzIiB1bmljb2RlPSImI3hhODsiIGhvcml6LWFkdi14PSIxNzkyIiANCiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJjb3B5cmlnaHQiIHVuaWNvZGU9IiYjeGE5OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InJlZ2lzdGVyZWQiIHVuaWNvZGU9IiYjeGFlOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImFjdXRlIiB1bmljb2RlPSImI3hiNDsiIGhvcml6LWFkdi14PSIxNzkyIiANCiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJBRSIgdW5pY29kZT0iJiN4YzY7IiBob3Jpei1hZHYteD0iMTc5MiIgDQogLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iT3NsYXNoIiB1bmljb2RlPSImI3hkODsiIGhvcml6LWFkdi14PSIxNzkyIiANCiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ0cmFkZW1hcmsiIHVuaWNvZGU9IiYjeDIxMjI7IiBob3Jpei1hZHYteD0iMTc5MiIgDQogLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iaW5maW5pdHkiIHVuaWNvZGU9IiYjeDIyMWU7IiBob3Jpei1hZHYteD0iMTc5MiIgDQogLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ibm90ZXF1YWwiIHVuaWNvZGU9IiYjeDIyNjA7IiBob3Jpei1hZHYteD0iMTc5MiIgDQogLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZ2xhc3MiIHVuaWNvZGU9IiYjeGYwMDA7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTY5OSAxMzUwcTAgLTM1IC00MyAtNzhsLTYzMiAtNjMydi03NjhoMzIwcTI2IDAgNDUgLTE5dDE5IC00NXQtMTkgLTQ1dC00NSAtMTloLTg5NnEtMjYgMCAtNDUgMTl0LTE5IDQ1dDE5IDQ1dDQ1IDE5aDMyMHY3NjhsLTYzMiA2MzJxLTQzIDQzIC00MyA3OHEwIDIzIDE4IDM2LjV0MzggMTcuNXQ0MyA0aDE0MDhxMjMgMCA0MyAtNHQzOCAtMTcuNXQxOCAtMzYuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Im11c2ljIiB1bmljb2RlPSImI3hmMDAxOyIgDQpkPSJNMTUzNiAxMzEydi0xMTIwcTAgLTUwIC0zNCAtODl0LTg2IC02MC41dC0xMDMuNSAtMzJ0LTk2LjUgLTEwLjV0LTk2LjUgMTAuNXQtMTAzLjUgMzJ0LTg2IDYwLjV0LTM0IDg5dDM0IDg5dDg2IDYwLjV0MTAzLjUgMzJ0OTYuNSAxMC41cTEwNSAwIDE5MiAtMzl2NTM3bC03NjggLTIzN3YtNzA5cTAgLTUwIC0zNCAtODl0LTg2IC02MC41dC0xMDMuNSAtMzJ0LTk2LjUgLTEwLjV0LTk2LjUgMTAuNXQtMTAzLjUgMzJ0LTg2IDYwLjV0LTM0IDg5DQp0MzQgODl0ODYgNjAuNXQxMDMuNSAzMnQ5Ni41IDEwLjVxMTA1IDAgMTkyIC0zOXY5NjdxMCAzMSAxOSA1Ni41dDQ5IDM1LjVsODMyIDI1NnExMiA0IDI4IDRxNDAgMCA2OCAtMjh0MjggLTY4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ic2VhcmNoIiB1bmljb2RlPSImI3hmMDAyOyIgaG9yaXotYWR2LXg9IjE2NjQiIA0KZD0iTTExNTIgNzA0cTAgMTg1IC0xMzEuNSAzMTYuNXQtMzE2LjUgMTMxLjV0LTMxNi41IC0xMzEuNXQtMTMxLjUgLTMxNi41dDEzMS41IC0zMTYuNXQzMTYuNSAtMTMxLjV0MzE2LjUgMTMxLjV0MTMxLjUgMzE2LjV6TTE2NjQgLTEyOHEwIC01MiAtMzggLTkwdC05MCAtMzhxLTU0IDAgLTkwIDM4bC0zNDMgMzQycS0xNzkgLTEyNCAtMzk5IC0xMjRxLTE0MyAwIC0yNzMuNSA1NS41dC0yMjUgMTUwdC0xNTAgMjI1dC01NS41IDI3My41DQp0NTUuNSAyNzMuNXQxNTAgMjI1dDIyNSAxNTB0MjczLjUgNTUuNXQyNzMuNSAtNTUuNXQyMjUgLTE1MHQxNTAgLTIyNXQ1NS41IC0yNzMuNXEwIC0yMjAgLTEyNCAtMzk5bDM0MyAtMzQzcTM3IC0zNyAzNyAtOTB6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJlbnZlbG9wZSIgdW5pY29kZT0iJiN4ZjAwMzsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNjY0IDMydjc2OHEtMzIgLTM2IC02OSAtNjZxLTI2OCAtMjA2IC00MjYgLTMzOHEtNTEgLTQzIC04MyAtNjd0LTg2LjUgLTQ4LjV0LTEwMi41IC0yNC41aC0xaC0xcS00OCAwIC0xMDIuNSAyNC41dC04Ni41IDQ4LjV0LTgzIDY3cS0xNTggMTMyIC00MjYgMzM4cS0zNyAzMCAtNjkgNjZ2LTc2OHEwIC0xMyA5LjUgLTIyLjV0MjIuNSAtOS41aDE0NzJxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXpNMTY2NCAxMDgzdjExdjEzLjV0LTAuNSAxMw0KdC0zIDEyLjV0LTUuNSA5dC05IDcuNXQtMTQgMi41aC0xNDcycS0xMyAwIC0yMi41IC05LjV0LTkuNSAtMjIuNXEwIC0xNjggMTQ3IC0yODRxMTkzIC0xNTIgNDAxIC0zMTdxNiAtNSAzNSAtMjkuNXQ0NiAtMzcuNXQ0NC41IC0zMS41dDUwLjUgLTI3LjV0NDMgLTloMWgxcTIwIDAgNDMgOXQ1MC41IDI3LjV0NDQuNSAzMS41dDQ2IDM3LjV0MzUgMjkuNXEyMDggMTY1IDQwMSAzMTdxNTQgNDMgMTAwLjUgMTE1LjV0NDYuNSAxMzEuNXoNCk0xNzkyIDExMjB2LTEwODhxMCAtNjYgLTQ3IC0xMTN0LTExMyAtNDdoLTE0NzJxLTY2IDAgLTExMyA0N3QtNDcgMTEzdjEwODhxMCA2NiA0NyAxMTN0MTEzIDQ3aDE0NzJxNjYgMCAxMTMgLTQ3dDQ3IC0xMTN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJoZWFydCIgdW5pY29kZT0iJiN4ZjAwNDsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik04OTYgLTEyOHEtMjYgMCAtNDQgMThsLTYyNCA2MDJxLTEwIDggLTI3LjUgMjZ0LTU1LjUgNjUuNXQtNjggOTcuNXQtNTMuNSAxMjF0LTIzLjUgMTM4cTAgMjIwIDEyNyAzNDR0MzUxIDEyNHE2MiAwIDEyNi41IC0yMS41dDEyMCAtNTh0OTUuNSAtNjguNXQ3NiAtNjhxMzYgMzYgNzYgNjh0OTUuNSA2OC41dDEyMCA1OHQxMjYuNSAyMS41cTIyNCAwIDM1MSAtMTI0dDEyNyAtMzQ0cTAgLTIyMSAtMjI5IC00NTBsLTYyMyAtNjAwDQpxLTE4IC0xOCAtNDQgLTE4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ic3RhciIgdW5pY29kZT0iJiN4ZjAwNTsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik0xNjY0IDg4OXEwIC0yMiAtMjYgLTQ4bC0zNjMgLTM1NGw4NiAtNTAwcTEgLTcgMSAtMjBxMCAtMjEgLTEwLjUgLTM1LjV0LTMwLjUgLTE0LjVxLTE5IDAgLTQwIDEybC00NDkgMjM2bC00NDkgLTIzNnEtMjIgLTEyIC00MCAtMTJxLTIxIDAgLTMxLjUgMTQuNXQtMTAuNSAzNS41cTAgNiAyIDIwbDg2IDUwMGwtMzY0IDM1NHEtMjUgMjcgLTI1IDQ4cTAgMzcgNTYgNDZsNTAyIDczbDIyNSA0NTVxMTkgNDEgNDkgNDF0NDkgLTQxbDIyNSAtNDU1DQpsNTAyIC03M3E1NiAtOSA1NiAtNDZ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJzdGFyX2VtcHR5IiB1bmljb2RlPSImI3hmMDA2OyIgaG9yaXotYWR2LXg9IjE2NjQiIA0KZD0iTTExMzcgNTMybDMwNiAyOTdsLTQyMiA2MmwtMTg5IDM4MmwtMTg5IC0zODJsLTQyMiAtNjJsMzA2IC0yOTdsLTczIC00MjFsMzc4IDE5OWwzNzcgLTE5OXpNMTY2NCA4ODlxMCAtMjIgLTI2IC00OGwtMzYzIC0zNTRsODYgLTUwMHExIC03IDEgLTIwcTAgLTUwIC00MSAtNTBxLTE5IDAgLTQwIDEybC00NDkgMjM2bC00NDkgLTIzNnEtMjIgLTEyIC00MCAtMTJxLTIxIDAgLTMxLjUgMTQuNXQtMTAuNSAzNS41cTAgNiAyIDIwbDg2IDUwMA0KbC0zNjQgMzU0cS0yNSAyNyAtMjUgNDhxMCAzNyA1NiA0Nmw1MDIgNzNsMjI1IDQ1NXExOSA0MSA0OSA0MXQ0OSAtNDFsMjI1IC00NTVsNTAyIC03M3E1NiAtOSA1NiAtNDZ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1c2VyIiB1bmljb2RlPSImI3hmMDA3OyIgaG9yaXotYWR2LXg9IjEyODAiIA0KZD0iTTEyODAgMTM3cTAgLTEwOSAtNjIuNSAtMTg3dC0xNTAuNSAtNzhoLTg1NHEtODggMCAtMTUwLjUgNzh0LTYyLjUgMTg3cTAgODUgOC41IDE2MC41dDMxLjUgMTUydDU4LjUgMTMxdDk0IDg5dDEzNC41IDM0LjVxMTMxIC0xMjggMzEzIC0xMjh0MzEzIDEyOHE3NiAwIDEzNC41IC0zNC41dDk0IC04OXQ1OC41IC0xMzF0MzEuNSAtMTUydDguNSAtMTYwLjV6TTEwMjQgMTAyNHEwIC0xNTkgLTExMi41IC0yNzEuNXQtMjcxLjUgLTExMi41DQp0LTI3MS41IDExMi41dC0xMTIuNSAyNzEuNXQxMTIuNSAyNzEuNXQyNzEuNSAxMTIuNXQyNzEuNSAtMTEyLjV0MTEyLjUgLTI3MS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZmlsbSIgdW5pY29kZT0iJiN4ZjAwODsiIGhvcml6LWFkdi14PSIxOTIwIiANCmQ9Ik0zODQgLTY0djEyOHEwIDI2IC0xOSA0NXQtNDUgMTloLTEyOHEtMjYgMCAtNDUgLTE5dC0xOSAtNDV2LTEyOHEwIC0yNiAxOSAtNDV0NDUgLTE5aDEyOHEyNiAwIDQ1IDE5dDE5IDQ1ek0zODQgMzIwdjEyOHEwIDI2IC0xOSA0NXQtNDUgMTloLTEyOHEtMjYgMCAtNDUgLTE5dC0xOSAtNDV2LTEyOHEwIC0yNiAxOSAtNDV0NDUgLTE5aDEyOHEyNiAwIDQ1IDE5dDE5IDQ1ek0zODQgNzA0djEyOHEwIDI2IC0xOSA0NXQtNDUgMTloLTEyOA0KcS0yNiAwIC00NSAtMTl0LTE5IC00NXYtMTI4cTAgLTI2IDE5IC00NXQ0NSAtMTloMTI4cTI2IDAgNDUgMTl0MTkgNDV6TTE0MDggLTY0djUxMnEwIDI2IC0xOSA0NXQtNDUgMTloLTc2OHEtMjYgMCAtNDUgLTE5dC0xOSAtNDV2LTUxMnEwIC0yNiAxOSAtNDV0NDUgLTE5aDc2OHEyNiAwIDQ1IDE5dDE5IDQ1ek0zODQgMTA4OHYxMjhxMCAyNiAtMTkgNDV0LTQ1IDE5aC0xMjhxLTI2IDAgLTQ1IC0xOXQtMTkgLTQ1di0xMjhxMCAtMjYgMTkgLTQ1DQp0NDUgLTE5aDEyOHEyNiAwIDQ1IDE5dDE5IDQ1ek0xNzkyIC02NHYxMjhxMCAyNiAtMTkgNDV0LTQ1IDE5aC0xMjhxLTI2IDAgLTQ1IC0xOXQtMTkgLTQ1di0xMjhxMCAtMjYgMTkgLTQ1dDQ1IC0xOWgxMjhxMjYgMCA0NSAxOXQxOSA0NXpNMTQwOCA3MDR2NTEycTAgMjYgLTE5IDQ1dC00NSAxOWgtNzY4cS0yNiAwIC00NSAtMTl0LTE5IC00NXYtNTEycTAgLTI2IDE5IC00NXQ0NSAtMTloNzY4cTI2IDAgNDUgMTl0MTkgNDV6TTE3OTIgMzIwdjEyOA0KcTAgMjYgLTE5IDQ1dC00NSAxOWgtMTI4cS0yNiAwIC00NSAtMTl0LTE5IC00NXYtMTI4cTAgLTI2IDE5IC00NXQ0NSAtMTloMTI4cTI2IDAgNDUgMTl0MTkgNDV6TTE3OTIgNzA0djEyOHEwIDI2IC0xOSA0NXQtNDUgMTloLTEyOHEtMjYgMCAtNDUgLTE5dC0xOSAtNDV2LTEyOHEwIC0yNiAxOSAtNDV0NDUgLTE5aDEyOHEyNiAwIDQ1IDE5dDE5IDQ1ek0xNzkyIDEwODh2MTI4cTAgMjYgLTE5IDQ1dC00NSAxOWgtMTI4cS0yNiAwIC00NSAtMTkNCnQtMTkgLTQ1di0xMjhxMCAtMjYgMTkgLTQ1dDQ1IC0xOWgxMjhxMjYgMCA0NSAxOXQxOSA0NXpNMTkyMCAxMjQ4di0xMzQ0cTAgLTY2IC00NyAtMTEzdC0xMTMgLTQ3aC0xNjAwcS02NiAwIC0xMTMgNDd0LTQ3IDExM3YxMzQ0cTAgNjYgNDcgMTEzdDExMyA0N2gxNjAwcTY2IDAgMTEzIC00N3Q0NyAtMTEzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idGhfbGFyZ2UiIHVuaWNvZGU9IiYjeGYwMDk7IiBob3Jpei1hZHYteD0iMTY2NCIgDQpkPSJNNzY4IDUxMnYtMzg0cTAgLTUyIC0zOCAtOTB0LTkwIC0zOGgtNTEycS01MiAwIC05MCAzOHQtMzggOTB2Mzg0cTAgNTIgMzggOTB0OTAgMzhoNTEycTUyIDAgOTAgLTM4dDM4IC05MHpNNzY4IDEyODB2LTM4NHEwIC01MiAtMzggLTkwdC05MCAtMzhoLTUxMnEtNTIgMCAtOTAgMzh0LTM4IDkwdjM4NHEwIDUyIDM4IDkwdDkwIDM4aDUxMnE1MiAwIDkwIC0zOHQzOCAtOTB6TTE2NjQgNTEydi0zODRxMCAtNTIgLTM4IC05MHQtOTAgLTM4DQpoLTUxMnEtNTIgMCAtOTAgMzh0LTM4IDkwdjM4NHEwIDUyIDM4IDkwdDkwIDM4aDUxMnE1MiAwIDkwIC0zOHQzOCAtOTB6TTE2NjQgMTI4MHYtMzg0cTAgLTUyIC0zOCAtOTB0LTkwIC0zOGgtNTEycS01MiAwIC05MCAzOHQtMzggOTB2Mzg0cTAgNTIgMzggOTB0OTAgMzhoNTEycTUyIDAgOTAgLTM4dDM4IC05MHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InRoIiB1bmljb2RlPSImI3hmMDBhOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTUxMiAyODh2LTE5MnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTMyMHEtNDAgMCAtNjggMjh0LTI4IDY4djE5MnEwIDQwIDI4IDY4dDY4IDI4aDMyMHE0MCAwIDY4IC0yOHQyOCAtNjh6TTUxMiA4MDB2LTE5MnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTMyMHEtNDAgMCAtNjggMjh0LTI4IDY4djE5MnEwIDQwIDI4IDY4dDY4IDI4aDMyMHE0MCAwIDY4IC0yOHQyOCAtNjh6TTExNTIgMjg4di0xOTJxMCAtNDAgLTI4IC02OHQtNjggLTI4aC0zMjANCnEtNDAgMCAtNjggMjh0LTI4IDY4djE5MnEwIDQwIDI4IDY4dDY4IDI4aDMyMHE0MCAwIDY4IC0yOHQyOCAtNjh6TTUxMiAxMzEydi0xOTJxMCAtNDAgLTI4IC02OHQtNjggLTI4aC0zMjBxLTQwIDAgLTY4IDI4dC0yOCA2OHYxOTJxMCA0MCAyOCA2OHQ2OCAyOGgzMjBxNDAgMCA2OCAtMjh0MjggLTY4ek0xMTUyIDgwMHYtMTkycTAgLTQwIC0yOCAtNjh0LTY4IC0yOGgtMzIwcS00MCAwIC02OCAyOHQtMjggNjh2MTkycTAgNDAgMjggNjh0NjggMjgNCmgzMjBxNDAgMCA2OCAtMjh0MjggLTY4ek0xNzkyIDI4OHYtMTkycTAgLTQwIC0yOCAtNjh0LTY4IC0yOGgtMzIwcS00MCAwIC02OCAyOHQtMjggNjh2MTkycTAgNDAgMjggNjh0NjggMjhoMzIwcTQwIDAgNjggLTI4dDI4IC02OHpNMTE1MiAxMzEydi0xOTJxMCAtNDAgLTI4IC02OHQtNjggLTI4aC0zMjBxLTQwIDAgLTY4IDI4dC0yOCA2OHYxOTJxMCA0MCAyOCA2OHQ2OCAyOGgzMjBxNDAgMCA2OCAtMjh0MjggLTY4ek0xNzkyIDgwMHYtMTkyDQpxMCAtNDAgLTI4IC02OHQtNjggLTI4aC0zMjBxLTQwIDAgLTY4IDI4dC0yOCA2OHYxOTJxMCA0MCAyOCA2OHQ2OCAyOGgzMjBxNDAgMCA2OCAtMjh0MjggLTY4ek0xNzkyIDEzMTJ2LTE5MnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTMyMHEtNDAgMCAtNjggMjh0LTI4IDY4djE5MnEwIDQwIDI4IDY4dDY4IDI4aDMyMHE0MCAwIDY4IC0yOHQyOCAtNjh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ0aF9saXN0IiB1bmljb2RlPSImI3hmMDBiOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTUxMiAyODh2LTE5MnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTMyMHEtNDAgMCAtNjggMjh0LTI4IDY4djE5MnEwIDQwIDI4IDY4dDY4IDI4aDMyMHE0MCAwIDY4IC0yOHQyOCAtNjh6TTUxMiA4MDB2LTE5MnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTMyMHEtNDAgMCAtNjggMjh0LTI4IDY4djE5MnEwIDQwIDI4IDY4dDY4IDI4aDMyMHE0MCAwIDY4IC0yOHQyOCAtNjh6TTE3OTIgMjg4di0xOTJxMCAtNDAgLTI4IC02OHQtNjggLTI4aC05NjANCnEtNDAgMCAtNjggMjh0LTI4IDY4djE5MnEwIDQwIDI4IDY4dDY4IDI4aDk2MHE0MCAwIDY4IC0yOHQyOCAtNjh6TTUxMiAxMzEydi0xOTJxMCAtNDAgLTI4IC02OHQtNjggLTI4aC0zMjBxLTQwIDAgLTY4IDI4dC0yOCA2OHYxOTJxMCA0MCAyOCA2OHQ2OCAyOGgzMjBxNDAgMCA2OCAtMjh0MjggLTY4ek0xNzkyIDgwMHYtMTkycTAgLTQwIC0yOCAtNjh0LTY4IC0yOGgtOTYwcS00MCAwIC02OCAyOHQtMjggNjh2MTkycTAgNDAgMjggNjh0NjggMjgNCmg5NjBxNDAgMCA2OCAtMjh0MjggLTY4ek0xNzkyIDEzMTJ2LTE5MnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTk2MHEtNDAgMCAtNjggMjh0LTI4IDY4djE5MnEwIDQwIDI4IDY4dDY4IDI4aDk2MHE0MCAwIDY4IC0yOHQyOCAtNjh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJvayIgdW5pY29kZT0iJiN4ZjAwYzsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNjcxIDk3MHEwIC00MCAtMjggLTY4bC03MjQgLTcyNGwtMTM2IC0xMzZxLTI4IC0yOCAtNjggLTI4dC02OCAyOGwtMTM2IDEzNmwtMzYyIDM2MnEtMjggMjggLTI4IDY4dDI4IDY4bDEzNiAxMzZxMjggMjggNjggMjh0NjggLTI4bDI5NCAtMjk1bDY1NiA2NTdxMjggMjggNjggMjh0NjggLTI4bDEzNiAtMTM2cTI4IC0yOCAyOCAtNjh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJyZW1vdmUiIHVuaWNvZGU9IiYjeGYwMGQ7IiBob3Jpei1hZHYteD0iMTQwOCIgDQpkPSJNMTI5OCAyMTRxMCAtNDAgLTI4IC02OGwtMTM2IC0xMzZxLTI4IC0yOCAtNjggLTI4dC02OCAyOGwtMjk0IDI5NGwtMjk0IC0yOTRxLTI4IC0yOCAtNjggLTI4dC02OCAyOGwtMTM2IDEzNnEtMjggMjggLTI4IDY4dDI4IDY4bDI5NCAyOTRsLTI5NCAyOTRxLTI4IDI4IC0yOCA2OHQyOCA2OGwxMzYgMTM2cTI4IDI4IDY4IDI4dDY4IC0yOGwyOTQgLTI5NGwyOTQgMjk0cTI4IDI4IDY4IDI4dDY4IC0yOGwxMzYgLTEzNnEyOCAtMjggMjggLTY4DQp0LTI4IC02OGwtMjk0IC0yOTRsMjk0IC0yOTRxMjggLTI4IDI4IC02OHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Inpvb21faW4iIHVuaWNvZGU9IiYjeGYwMGU7IiBob3Jpei1hZHYteD0iMTY2NCIgDQpkPSJNMTAyNCA3MzZ2LTY0cTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtMjI0di0yMjRxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC02NHEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djIyNGgtMjI0cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2NjRxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWgyMjR2MjI0cTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoNjRxMTMgMCAyMi41IC05LjV0OS41IC0yMi41di0yMjRoMjI0DQpxMTMgMCAyMi41IC05LjV0OS41IC0yMi41ek0xMTUyIDcwNHEwIDE4NSAtMTMxLjUgMzE2LjV0LTMxNi41IDEzMS41dC0zMTYuNSAtMTMxLjV0LTEzMS41IC0zMTYuNXQxMzEuNSAtMzE2LjV0MzE2LjUgLTEzMS41dDMxNi41IDEzMS41dDEzMS41IDMxNi41ek0xNjY0IC0xMjhxMCAtNTMgLTM3LjUgLTkwLjV0LTkwLjUgLTM3LjVxLTU0IDAgLTkwIDM4bC0zNDMgMzQycS0xNzkgLTEyNCAtMzk5IC0xMjRxLTE0MyAwIC0yNzMuNSA1NS41DQp0LTIyNSAxNTB0LTE1MCAyMjV0LTU1LjUgMjczLjV0NTUuNSAyNzMuNXQxNTAgMjI1dDIyNSAxNTB0MjczLjUgNTUuNXQyNzMuNSAtNTUuNXQyMjUgLTE1MHQxNTAgLTIyNXQ1NS41IC0yNzMuNXEwIC0yMjAgLTEyNCAtMzk5bDM0MyAtMzQzcTM3IC0zNyAzNyAtOTB6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ6b29tX291dCIgdW5pY29kZT0iJiN4ZjAxMDsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik0xMDI0IDczNnYtNjRxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC01NzZxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXY2NHEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDU3NnExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6TTExNTIgNzA0cTAgMTg1IC0xMzEuNSAzMTYuNXQtMzE2LjUgMTMxLjV0LTMxNi41IC0xMzEuNXQtMTMxLjUgLTMxNi41dDEzMS41IC0zMTYuNXQzMTYuNSAtMTMxLjV0MzE2LjUgMTMxLjV0MTMxLjUgMzE2LjV6DQpNMTY2NCAtMTI4cTAgLTUzIC0zNy41IC05MC41dC05MC41IC0zNy41cS01NCAwIC05MCAzOGwtMzQzIDM0MnEtMTc5IC0xMjQgLTM5OSAtMTI0cS0xNDMgMCAtMjczLjUgNTUuNXQtMjI1IDE1MHQtMTUwIDIyNXQtNTUuNSAyNzMuNXQ1NS41IDI3My41dDE1MCAyMjV0MjI1IDE1MHQyNzMuNSA1NS41dDI3My41IC01NS41dDIyNSAtMTUwdDE1MCAtMjI1dDU1LjUgLTI3My41cTAgLTIyMCAtMTI0IC0zOTlsMzQzIC0zNDNxMzcgLTM3IDM3IC05MHoNCiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ib2ZmIiB1bmljb2RlPSImI3hmMDExOyIgDQpkPSJNMTUzNiA2NDBxMCAtMTU2IC02MSAtMjk4dC0xNjQgLTI0NXQtMjQ1IC0xNjR0LTI5OCAtNjF0LTI5OCA2MXQtMjQ1IDE2NHQtMTY0IDI0NXQtNjEgMjk4cTAgMTgyIDgwLjUgMzQzdDIyNi41IDI3MHE0MyAzMiA5NS41IDI1dDgzLjUgLTUwcTMyIC00MiAyNC41IC05NC41dC00OS41IC04NC41cS05OCAtNzQgLTE1MS41IC0xODF0LTUzLjUgLTIyOHEwIC0xMDQgNDAuNSAtMTk4LjV0MTA5LjUgLTE2My41dDE2My41IC0xMDkuNQ0KdDE5OC41IC00MC41dDE5OC41IDQwLjV0MTYzLjUgMTA5LjV0MTA5LjUgMTYzLjV0NDAuNSAxOTguNXEwIDEyMSAtNTMuNSAyMjh0LTE1MS41IDE4MXEtNDIgMzIgLTQ5LjUgODQuNXQyNC41IDk0LjVxMzEgNDMgODQgNTB0OTUgLTI1cTE0NiAtMTA5IDIyNi41IC0yNzB0ODAuNSAtMzQzek04OTYgMTQwOHYtNjQwcTAgLTUyIC0zOCAtOTB0LTkwIC0zOHQtOTAgMzh0LTM4IDkwdjY0MHEwIDUyIDM4IDkwdDkwIDM4dDkwIC0zOHQzOCAtOTB6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJzaWduYWwiIHVuaWNvZGU9IiYjeGYwMTI7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMjU2IDk2di0xOTJxMCAtMTQgLTkgLTIzdC0yMyAtOWgtMTkycS0xNCAwIC0yMyA5dC05IDIzdjE5MnEwIDE0IDkgMjN0MjMgOWgxOTJxMTQgMCAyMyAtOXQ5IC0yM3pNNjQwIDIyNHYtMzIwcTAgLTE0IC05IC0yM3QtMjMgLTloLTE5MnEtMTQgMCAtMjMgOXQtOSAyM3YzMjBxMCAxNCA5IDIzdDIzIDloMTkycTE0IDAgMjMgLTl0OSAtMjN6TTEwMjQgNDgwdi01NzZxMCAtMTQgLTkgLTIzdC0yMyAtOWgtMTkycS0xNCAwIC0yMyA5dC05IDIzDQp2NTc2cTAgMTQgOSAyM3QyMyA5aDE5MnExNCAwIDIzIC05dDkgLTIzek0xNDA4IDg2NHYtOTYwcTAgLTE0IC05IC0yM3QtMjMgLTloLTE5MnEtMTQgMCAtMjMgOXQtOSAyM3Y5NjBxMCAxNCA5IDIzdDIzIDloMTkycTE0IDAgMjMgLTl0OSAtMjN6TTE3OTIgMTM3NnYtMTQ3MnEwIC0xNCAtOSAtMjN0LTIzIC05aC0xOTJxLTE0IDAgLTIzIDl0LTkgMjN2MTQ3MnEwIDE0IDkgMjN0MjMgOWgxOTJxMTQgMCAyMyAtOXQ5IC0yM3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImNvZyIgdW5pY29kZT0iJiN4ZjAxMzsiIA0KZD0iTTEwMjQgNjQwcTAgMTA2IC03NSAxODF0LTE4MSA3NXQtMTgxIC03NXQtNzUgLTE4MXQ3NSAtMTgxdDE4MSAtNzV0MTgxIDc1dDc1IDE4MXpNMTUzNiA3NDl2LTIyMnEwIC0xMiAtOCAtMjN0LTIwIC0xM2wtMTg1IC0yOHEtMTkgLTU0IC0zOSAtOTFxMzUgLTUwIDEwNyAtMTM4cTEwIC0xMiAxMCAtMjV0LTkgLTIzcS0yNyAtMzcgLTk5IC0xMDh0LTk0IC03MXEtMTIgMCAtMjYgOWwtMTM4IDEwOHEtNDQgLTIzIC05MSAtMzgNCnEtMTYgLTEzNiAtMjkgLTE4NnEtNyAtMjggLTM2IC0yOGgtMjIycS0xNCAwIC0yNC41IDguNXQtMTEuNSAyMS41bC0yOCAxODRxLTQ5IDE2IC05MCAzN2wtMTQxIC0xMDdxLTEwIC05IC0yNSAtOXEtMTQgMCAtMjUgMTFxLTEyNiAxMTQgLTE2NSAxNjhxLTcgMTAgLTcgMjNxMCAxMiA4IDIzcTE1IDIxIDUxIDY2LjV0NTQgNzAuNXEtMjcgNTAgLTQxIDk5bC0xODMgMjdxLTEzIDIgLTIxIDEyLjV0LTggMjMuNXYyMjJxMCAxMiA4IDIzdDE5IDEzDQpsMTg2IDI4cTE0IDQ2IDM5IDkycS00MCA1NyAtMTA3IDEzOHEtMTAgMTIgLTEwIDI0cTAgMTAgOSAyM3EyNiAzNiA5OC41IDEwNy41dDk0LjUgNzEuNXExMyAwIDI2IC0xMGwxMzggLTEwN3E0NCAyMyA5MSAzOHExNiAxMzYgMjkgMTg2cTcgMjggMzYgMjhoMjIycTE0IDAgMjQuNSAtOC41dDExLjUgLTIxLjVsMjggLTE4NHE0OSAtMTYgOTAgLTM3bDE0MiAxMDdxOSA5IDI0IDlxMTMgMCAyNSAtMTBxMTI5IC0xMTkgMTY1IC0xNzBxNyAtOCA3IC0yMg0KcTAgLTEyIC04IC0yM3EtMTUgLTIxIC01MSAtNjYuNXQtNTQgLTcwLjVxMjYgLTUwIDQxIC05OGwxODMgLTI4cTEzIC0yIDIxIC0xMi41dDggLTIzLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ0cmFzaCIgdW5pY29kZT0iJiN4ZjAxNDsiIGhvcml6LWFkdi14PSIxNDA4IiANCmQ9Ik01MTIgODAwdi01NzZxMCAtMTQgLTkgLTIzdC0yMyAtOWgtNjRxLTE0IDAgLTIzIDl0LTkgMjN2NTc2cTAgMTQgOSAyM3QyMyA5aDY0cTE0IDAgMjMgLTl0OSAtMjN6TTc2OCA4MDB2LTU3NnEwIC0xNCAtOSAtMjN0LTIzIC05aC02NHEtMTQgMCAtMjMgOXQtOSAyM3Y1NzZxMCAxNCA5IDIzdDIzIDloNjRxMTQgMCAyMyAtOXQ5IC0yM3pNMTAyNCA4MDB2LTU3NnEwIC0xNCAtOSAtMjN0LTIzIC05aC02NHEtMTQgMCAtMjMgOXQtOSAyM3Y1NzYNCnEwIDE0IDkgMjN0MjMgOWg2NHExNCAwIDIzIC05dDkgLTIzek0xMTUyIDc2djk0OGgtODk2di05NDhxMCAtMjIgNyAtNDAuNXQxNC41IC0yN3QxMC41IC04LjVoODMycTMgMCAxMC41IDguNXQxNC41IDI3dDcgNDAuNXpNNDgwIDExNTJoNDQ4bC00OCAxMTdxLTcgOSAtMTcgMTFoLTMxN3EtMTAgLTIgLTE3IC0xMXpNMTQwOCAxMTIwdi02NHEwIC0xNCAtOSAtMjN0LTIzIC05aC05NnYtOTQ4cTAgLTgzIC00NyAtMTQzLjV0LTExMyAtNjAuNWgtODMyDQpxLTY2IDAgLTExMyA1OC41dC00NyAxNDEuNXY5NTJoLTk2cS0xNCAwIC0yMyA5dC05IDIzdjY0cTAgMTQgOSAyM3QyMyA5aDMwOWw3MCAxNjdxMTUgMzcgNTQgNjN0NzkgMjZoMzIwcTQwIDAgNzkgLTI2dDU0IC02M2w3MCAtMTY3aDMwOXExNCAwIDIzIC05dDkgLTIzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iaG9tZSIgdW5pY29kZT0iJiN4ZjAxNTsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik0xNDA4IDU0NHYtNDgwcTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMzg0djM4NGgtMjU2di0zODRoLTM4NHEtMjYgMCAtNDUgMTl0LTE5IDQ1djQ4MHEwIDEgMC41IDN0MC41IDNsNTc1IDQ3NGw1NzUgLTQ3NHExIC0yIDEgLTZ6TTE2MzEgNjEzbC02MiAtNzRxLTggLTkgLTIxIC0xMWgtM3EtMTMgMCAtMjEgN2wtNjkyIDU3N2wtNjkyIC01NzdxLTEyIC04IC0yNCAtN3EtMTMgMiAtMjEgMTFsLTYyIDc0cS04IDEwIC03IDIzLjV0MTEgMjEuNQ0KbDcxOSA1OTlxMzIgMjYgNzYgMjZ0NzYgLTI2bDI0NCAtMjA0djE5NXEwIDE0IDkgMjN0MjMgOWgxOTJxMTQgMCAyMyAtOXQ5IC0yM3YtNDA4bDIxOSAtMTgycTEwIC04IDExIC0yMS41dC03IC0yMy41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZmlsZV9hbHQiIHVuaWNvZGU9IiYjeGYwMTY7IiANCmQ9Ik0xNDY4IDExNTZxMjggLTI4IDQ4IC03NnQyMCAtODh2LTExNTJxMCAtNDAgLTI4IC02OHQtNjggLTI4aC0xMzQ0cS00MCAwIC02OCAyOHQtMjggNjh2MTYwMHEwIDQwIDI4IDY4dDY4IDI4aDg5NnE0MCAwIDg4IC0yMHQ3NiAtNDh6TTEwMjQgMTQwMHYtMzc2aDM3NnEtMTAgMjkgLTIyIDQxbC0zMTMgMzEzcS0xMiAxMiAtNDEgMjJ6TTE0MDggLTEyOHYxMDI0aC00MTZxLTQwIDAgLTY4IDI4dC0yOCA2OHY0MTZoLTc2OHYtMTUzNmgxMjgweg0KIiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ0aW1lIiB1bmljb2RlPSImI3hmMDE3OyIgDQpkPSJNODk2IDk5MnYtNDQ4cTAgLTE0IC05IC0yM3QtMjMgLTloLTMyMHEtMTQgMCAtMjMgOXQtOSAyM3Y2NHEwIDE0IDkgMjN0MjMgOWgyMjR2MzUycTAgMTQgOSAyM3QyMyA5aDY0cTE0IDAgMjMgLTl0OSAtMjN6TTEzMTIgNjQwcTAgMTQ4IC03MyAyNzN0LTE5OCAxOTh0LTI3MyA3M3QtMjczIC03M3QtMTk4IC0xOTh0LTczIC0yNzN0NzMgLTI3M3QxOTggLTE5OHQyNzMgLTczdDI3MyA3M3QxOTggMTk4dDczIDI3M3pNMTUzNiA2NDANCnEwIC0yMDkgLTEwMyAtMzg1LjV0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzdC0yNzkuNSAyNzkuNXQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN0Mzg1LjUgLTEwM3QyNzkuNSAtMjc5LjV0MTAzIC0zODUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InJvYWQiIHVuaWNvZGU9IiYjeGYwMTg7IiBob3Jpei1hZHYteD0iMTkyMCIgDQpkPSJNMTExMSA1NDB2NGwtMjQgMzIwcS0xIDEzIC0xMSAyMi41dC0yMyA5LjVoLTE4NnEtMTMgMCAtMjMgLTkuNXQtMTEgLTIyLjVsLTI0IC0zMjB2LTRxLTEgLTEyIDggLTIwdDIxIC04aDI0NHExMiAwIDIxIDh0OCAyMHpNMTg3MCA3M3EwIC03MyAtNDYgLTczaC03MDRxMTMgMCAyMiA5LjV0OCAyMi41bC0yMCAyNTZxLTEgMTMgLTExIDIyLjV0LTIzIDkuNWgtMjcycS0xMyAwIC0yMyAtOS41dC0xMSAtMjIuNWwtMjAgLTI1Ng0KcS0xIC0xMyA4IC0yMi41dDIyIC05LjVoLTcwNHEtNDYgMCAtNDYgNzNxMCA1NCAyNiAxMTZsNDE3IDEwNDRxOCAxOSAyNiAzM3QzOCAxNGgzMzlxLTEzIDAgLTIzIC05LjV0LTExIC0yMi41bC0xNSAtMTkycS0xIC0xNCA4IC0yM3QyMiAtOWgxNjZxMTMgMCAyMiA5dDggMjNsLTE1IDE5MnEtMSAxMyAtMTEgMjIuNXQtMjMgOS41aDMzOXEyMCAwIDM4IC0xNHQyNiAtMzNsNDE3IC0xMDQ0cTI2IC02MiAyNiAtMTE2eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZG93bmxvYWRfYWx0IiB1bmljb2RlPSImI3hmMDE5OyIgaG9yaXotYWR2LXg9IjE2NjQiIA0KZD0iTTEyODAgMTkycTAgMjYgLTE5IDQ1dC00NSAxOXQtNDUgLTE5dC0xOSAtNDV0MTkgLTQ1dDQ1IC0xOXQ0NSAxOXQxOSA0NXpNMTUzNiAxOTJxMCAyNiAtMTkgNDV0LTQ1IDE5dC00NSAtMTl0LTE5IC00NXQxOSAtNDV0NDUgLTE5dDQ1IDE5dDE5IDQ1ek0xNjY0IDQxNnYtMzIwcTAgLTQwIC0yOCAtNjh0LTY4IC0yOGgtMTQ3MnEtNDAgMCAtNjggMjh0LTI4IDY4djMyMHEwIDQwIDI4IDY4dDY4IDI4aDQ2NWwxMzUgLTEzNg0KcTU4IC01NiAxMzYgLTU2dDEzNiA1NmwxMzYgMTM2aDQ2NHE0MCAwIDY4IC0yOHQyOCAtNjh6TTEzMzkgOTg1cTE3IC00MSAtMTQgLTcwbC00NDggLTQ0OHEtMTggLTE5IC00NSAtMTl0LTQ1IDE5bC00NDggNDQ4cS0zMSAyOSAtMTQgNzBxMTcgMzkgNTkgMzloMjU2djQ0OHEwIDI2IDE5IDQ1dDQ1IDE5aDI1NnEyNiAwIDQ1IC0xOXQxOSAtNDV2LTQ0OGgyNTZxNDIgMCA1OSAtMzl6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJkb3dubG9hZCIgdW5pY29kZT0iJiN4ZjAxYTsiIA0KZD0iTTExMjAgNjA4cTAgLTEyIC0xMCAtMjRsLTMxOSAtMzE5cS0xMSAtOSAtMjMgLTl0LTIzIDlsLTMyMCAzMjBxLTE1IDE2IC03IDM1cTggMjAgMzAgMjBoMTkydjM1MnEwIDE0IDkgMjN0MjMgOWgxOTJxMTQgMCAyMyAtOXQ5IC0yM3YtMzUyaDE5MnExNCAwIDIzIC05dDkgLTIzek03NjggMTE4NHEtMTQ4IDAgLTI3MyAtNzN0LTE5OCAtMTk4dC03MyAtMjczdDczIC0yNzN0MTk4IC0xOTh0MjczIC03M3QyNzMgNzN0MTk4IDE5OHQ3MyAyNzMNCnQtNzMgMjczdC0xOTggMTk4dC0yNzMgNzN6TTE1MzYgNjQwcTAgLTIwOSAtMTAzIC0zODUuNXQtMjc5LjUgLTI3OS41dC0zODUuNSAtMTAzdC0zODUuNSAxMDN0LTI3OS41IDI3OS41dC0xMDMgMzg1LjV0MTAzIDM4NS41dDI3OS41IDI3OS41dDM4NS41IDEwM3QzODUuNSAtMTAzdDI3OS41IC0yNzkuNXQxMDMgLTM4NS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idXBsb2FkIiB1bmljb2RlPSImI3hmMDFiOyIgDQpkPSJNMTExOCA2NjBxLTggLTIwIC0zMCAtMjBoLTE5MnYtMzUycTAgLTE0IC05IC0yM3QtMjMgLTloLTE5MnEtMTQgMCAtMjMgOXQtOSAyM3YzNTJoLTE5MnEtMTQgMCAtMjMgOXQtOSAyM3EwIDEyIDEwIDI0bDMxOSAzMTlxMTEgOSAyMyA5dDIzIC05bDMyMCAtMzIwcTE1IC0xNiA3IC0zNXpNNzY4IDExODRxLTE0OCAwIC0yNzMgLTczdC0xOTggLTE5OHQtNzMgLTI3M3Q3MyAtMjczdDE5OCAtMTk4dDI3MyAtNzN0MjczIDczdDE5OCAxOTgNCnQ3MyAyNzN0LTczIDI3M3QtMTk4IDE5OHQtMjczIDczek0xNTM2IDY0MHEwIC0yMDkgLTEwMyAtMzg1LjV0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzdC0yNzkuNSAyNzkuNXQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN0Mzg1LjUgLTEwM3QyNzkuNSAtMjc5LjV0MTAzIC0zODUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImluYm94IiB1bmljb2RlPSImI3hmMDFjOyIgDQpkPSJNMTAyMyA1NzZoMzE2cS0xIDMgLTIuNSA4LjV0LTIuNSA3LjVsLTIxMiA0OTZoLTcwOGwtMjEyIC00OTZxLTEgLTMgLTIuNSAtOC41dC0yLjUgLTcuNWgzMTZsOTUgLTE5MmgzMjB6TTE1MzYgNTQ2di00ODJxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC0xNDA4cS0yNiAwIC00NSAxOXQtMTkgNDV2NDgycTAgNjIgMjUgMTIzbDIzOCA1NTJxMTAgMjUgMzYuNSA0MnQ1Mi41IDE3aDgzMnEyNiAwIDUyLjUgLTE3dDM2LjUgLTQybDIzOCAtNTUyDQpxMjUgLTYxIDI1IC0xMjN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJwbGF5X2NpcmNsZSIgdW5pY29kZT0iJiN4ZjAxZDsiIA0KZD0iTTExODQgNjQwcTAgLTM3IC0zMiAtNTVsLTU0NCAtMzIwcS0xNSAtOSAtMzIgLTlxLTE2IDAgLTMyIDhxLTMyIDE5IC0zMiA1NnY2NDBxMCAzNyAzMiA1NnEzMyAxOCA2NCAtMWw1NDQgLTMyMHEzMiAtMTggMzIgLTU1ek0xMzEyIDY0MHEwIDE0OCAtNzMgMjczdC0xOTggMTk4dC0yNzMgNzN0LTI3MyAtNzN0LTE5OCAtMTk4dC03MyAtMjczdDczIC0yNzN0MTk4IC0xOTh0MjczIC03M3QyNzMgNzN0MTk4IDE5OHQ3MyAyNzN6TTE1MzYgNjQwDQpxMCAtMjA5IC0xMDMgLTM4NS41dC0yNzkuNSAtMjc5LjV0LTM4NS41IC0xMDN0LTM4NS41IDEwM3QtMjc5LjUgMjc5LjV0LTEwMyAzODUuNXQxMDMgMzg1LjV0Mjc5LjUgMjc5LjV0Mzg1LjUgMTAzdDM4NS41IC0xMDN0Mjc5LjUgLTI3OS41dDEwMyAtMzg1LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJyZXBlYXQiIHVuaWNvZGU9IiYjeGYwMWU7IiANCmQ9Ik0xNTM2IDEyODB2LTQ0OHEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTQ0OHEtNDIgMCAtNTkgNDBxLTE3IDM5IDE0IDY5bDEzOCAxMzhxLTE0OCAxMzcgLTM0OSAxMzdxLTEwNCAwIC0xOTguNSAtNDAuNXQtMTYzLjUgLTEwOS41dC0xMDkuNSAtMTYzLjV0LTQwLjUgLTE5OC41dDQwLjUgLTE5OC41dDEwOS41IC0xNjMuNXQxNjMuNSAtMTA5LjV0MTk4LjUgLTQwLjVxMTE5IDAgMjI1IDUydDE3OSAxNDdxNyAxMCAyMyAxMnExNSAwIDI1IC05DQpsMTM3IC0xMzhxOSAtOCA5LjUgLTIwLjV0LTcuNSAtMjIuNXEtMTA5IC0xMzIgLTI2NCAtMjA0LjV0LTMyNyAtNzIuNXEtMTU2IDAgLTI5OCA2MXQtMjQ1IDE2NHQtMTY0IDI0NXQtNjEgMjk4dDYxIDI5OHQxNjQgMjQ1dDI0NSAxNjR0Mjk4IDYxcTE0NyAwIDI4NC41IC01NS41dDI0NC41IC0xNTYuNWwxMzAgMTI5cTI5IDMxIDcwIDE0cTM5IC0xNyAzOSAtNTl6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJyZWZyZXNoIiB1bmljb2RlPSImI3hmMDIxOyIgDQpkPSJNMTUxMSA0ODBxMCAtNSAtMSAtN3EtNjQgLTI2OCAtMjY4IC00MzQuNXQtNDc4IC0xNjYuNXEtMTQ2IDAgLTI4Mi41IDU1dC0yNDMuNSAxNTdsLTEyOSAtMTI5cS0xOSAtMTkgLTQ1IC0xOXQtNDUgMTl0LTE5IDQ1djQ0OHEwIDI2IDE5IDQ1dDQ1IDE5aDQ0OHEyNiAwIDQ1IC0xOXQxOSAtNDV0LTE5IC00NWwtMTM3IC0xMzdxNzEgLTY2IDE2MSAtMTAydDE4NyAtMzZxMTM0IDAgMjUwIDY1dDE4NiAxNzlxMTEgMTcgNTMgMTE3DQpxOCAyMyAzMCAyM2gxOTJxMTMgMCAyMi41IC05LjV0OS41IC0yMi41ek0xNTM2IDEyODB2LTQ0OHEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTQ0OHEtMjYgMCAtNDUgMTl0LTE5IDQ1dDE5IDQ1bDEzOCAxMzhxLTE0OCAxMzcgLTM0OSAxMzdxLTEzNCAwIC0yNTAgLTY1dC0xODYgLTE3OXEtMTEgLTE3IC01MyAtMTE3cS04IC0yMyAtMzAgLTIzaC0xOTlxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXY3cTY1IDI2OCAyNzAgNDM0LjV0NDgwIDE2Ni41DQpxMTQ2IDAgMjg0IC01NS41dDI0NSAtMTU2LjVsMTMwIDEyOXExOSAxOSA0NSAxOXQ0NSAtMTl0MTkgLTQ1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ibGlzdF9hbHQiIHVuaWNvZGU9IiYjeGYwMjI7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMzg0IDM1MnYtNjRxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC02NHEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djY0cTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoNjRxMTMgMCAyMi41IC05LjV0OS41IC0yMi41ek0zODQgNjA4di02NHEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTY0cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2NjRxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWg2NHExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6DQpNMzg0IDg2NHYtNjRxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC02NHEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djY0cTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoNjRxMTMgMCAyMi41IC05LjV0OS41IC0yMi41ek0xNTM2IDM1MnYtNjRxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC05NjBxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXY2NHEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDk2MHExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6DQpNMTUzNiA2MDh2LTY0cTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtOTYwcS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2NjRxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWg5NjBxMTMgMCAyMi41IC05LjV0OS41IC0yMi41ek0xNTM2IDg2NHYtNjRxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC05NjBxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXY2NHEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDk2MHExMyAwIDIyLjUgLTkuNQ0KdDkuNSAtMjIuNXpNMTY2NCAxNjB2ODMycTAgMTMgLTkuNSAyMi41dC0yMi41IDkuNWgtMTQ3MnEtMTMgMCAtMjIuNSAtOS41dC05LjUgLTIyLjV2LTgzMnEwIC0xMyA5LjUgLTIyLjV0MjIuNSAtOS41aDE0NzJxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXpNMTc5MiAxMjQ4di0xMDg4cTAgLTY2IC00NyAtMTEzdC0xMTMgLTQ3aC0xNDcycS02NiAwIC0xMTMgNDd0LTQ3IDExM3YxMDg4cTAgNjYgNDcgMTEzdDExMyA0N2gxNDcycTY2IDAgMTEzIC00Nw0KdDQ3IC0xMTN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJsb2NrIiB1bmljb2RlPSImI3hmMDIzOyIgaG9yaXotYWR2LXg9IjExNTIiIA0KZD0iTTMyMCA3NjhoNTEydjE5MnEwIDEwNiAtNzUgMTgxdC0xODEgNzV0LTE4MSAtNzV0LTc1IC0xODF2LTE5MnpNMTE1MiA2NzJ2LTU3NnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTk2MHEtNDAgMCAtNjggMjh0LTI4IDY4djU3NnEwIDQwIDI4IDY4dDY4IDI4aDMydjE5MnEwIDE4NCAxMzIgMzE2dDMxNiAxMzJ0MzE2IC0xMzJ0MTMyIC0zMTZ2LTE5MmgzMnE0MCAwIDY4IC0yOHQyOCAtNjh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJmbGFnIiB1bmljb2RlPSImI3hmMDI0OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTMyMCAxMjgwcTAgLTcyIC02NCAtMTEwdi0xMjY2cTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtNjRxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXYxMjY2cS02NCAzOCAtNjQgMTEwcTAgNTMgMzcuNSA5MC41dDkwLjUgMzcuNXQ5MC41IC0zNy41dDM3LjUgLTkwLjV6TTE3OTIgMTIxNnYtNzYzcTAgLTI1IC0xMi41IC0zOC41dC0zOS41IC0yNy41cS0yMTUgLTExNiAtMzY5IC0xMTZxLTYxIDAgLTEyMy41IDIydC0xMDguNSA0OA0KdC0xMTUuNSA0OHQtMTQyLjUgMjJxLTE5MiAwIC00NjQgLTE0NnEtMTcgLTkgLTMzIC05cS0yNiAwIC00NSAxOXQtMTkgNDV2NzQycTAgMzIgMzEgNTVxMjEgMTQgNzkgNDNxMjM2IDEyMCA0MjEgMTIwcTEwNyAwIDIwMCAtMjl0MjE5IC04OHEzOCAtMTkgODggLTE5cTU0IDAgMTE3LjUgMjF0MTEwIDQ3dDg4IDQ3dDU0LjUgMjFxMjYgMCA0NSAtMTl0MTkgLTQ1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iaGVhZHBob25lcyIgdW5pY29kZT0iJiN4ZjAyNTsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik0xNjY0IDY1MHEwIC0xNjYgLTYwIC0zMTRsLTIwIC00OWwtMTg1IC0zM3EtMjIgLTgzIC05MC41IC0xMzYuNXQtMTU2LjUgLTUzLjV2LTMycTAgLTE0IC05IC0yM3QtMjMgLTloLTY0cS0xNCAwIC0yMyA5dC05IDIzdjU3NnEwIDE0IDkgMjN0MjMgOWg2NHExNCAwIDIzIC05dDkgLTIzdi0zMnE3MSAwIDEzMCAtMzUuNXQ5MyAtOTUuNWw2OCAxMnEyOSA5NSAyOSAxOTNxMCAxNDggLTg4IDI3OXQtMjM2LjUgMjA5dC0zMTUuNSA3OA0KdC0zMTUuNSAtNzh0LTIzNi41IC0yMDl0LTg4IC0yNzlxMCAtOTggMjkgLTE5M2w2OCAtMTJxMzQgNjAgOTMgOTUuNXQxMzAgMzUuNXYzMnEwIDE0IDkgMjN0MjMgOWg2NHExNCAwIDIzIC05dDkgLTIzdi01NzZxMCAtMTQgLTkgLTIzdC0yMyAtOWgtNjRxLTE0IDAgLTIzIDl0LTkgMjN2MzJxLTg4IDAgLTE1Ni41IDUzLjV0LTkwLjUgMTM2LjVsLTE4NSAzM2wtMjAgNDlxLTYwIDE0OCAtNjAgMzE0cTAgMTUxIDY3IDI5MXQxNzkgMjQyLjUNCnQyNjYgMTYzLjV0MzIwIDYxdDMyMCAtNjF0MjY2IC0xNjMuNXQxNzkgLTI0Mi41dDY3IC0yOTF6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ2b2x1bWVfb2ZmIiB1bmljb2RlPSImI3hmMDI2OyIgaG9yaXotYWR2LXg9Ijc2OCIgDQpkPSJNNzY4IDExODR2LTEwODhxMCAtMjYgLTE5IC00NXQtNDUgLTE5dC00NSAxOWwtMzMzIDMzM2gtMjYycS0yNiAwIC00NSAxOXQtMTkgNDV2Mzg0cTAgMjYgMTkgNDV0NDUgMTloMjYybDMzMyAzMzNxMTkgMTkgNDUgMTl0NDUgLTE5dDE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InZvbHVtZV9kb3duIiB1bmljb2RlPSImI3hmMDI3OyIgaG9yaXotYWR2LXg9IjExNTIiIA0KZD0iTTc2OCAxMTg0di0xMDg4cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOXQtNDUgMTlsLTMzMyAzMzNoLTI2MnEtMjYgMCAtNDUgMTl0LTE5IDQ1djM4NHEwIDI2IDE5IDQ1dDQ1IDE5aDI2MmwzMzMgMzMzcTE5IDE5IDQ1IDE5dDQ1IC0xOXQxOSAtNDV6TTExNTIgNjQwcTAgLTc2IC00Mi41IC0xNDEuNXQtMTEyLjUgLTkzLjVxLTEwIC01IC0yNSAtNXEtMjYgMCAtNDUgMTguNXQtMTkgNDUuNXEwIDIxIDEyIDM1LjV0MjkgMjV0MzQgMjN0MjkgMzYNCnQxMiA1Ni41dC0xMiA1Ni41dC0yOSAzNnQtMzQgMjN0LTI5IDI1dC0xMiAzNS41cTAgMjcgMTkgNDUuNXQ0NSAxOC41cTE1IDAgMjUgLTVxNzAgLTI3IDExMi41IC05M3Q0Mi41IC0xNDJ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ2b2x1bWVfdXAiIHVuaWNvZGU9IiYjeGYwMjg7IiBob3Jpei1hZHYteD0iMTY2NCIgDQpkPSJNNzY4IDExODR2LTEwODhxMCAtMjYgLTE5IC00NXQtNDUgLTE5dC00NSAxOWwtMzMzIDMzM2gtMjYycS0yNiAwIC00NSAxOXQtMTkgNDV2Mzg0cTAgMjYgMTkgNDV0NDUgMTloMjYybDMzMyAzMzNxMTkgMTkgNDUgMTl0NDUgLTE5dDE5IC00NXpNMTE1MiA2NDBxMCAtNzYgLTQyLjUgLTE0MS41dC0xMTIuNSAtOTMuNXEtMTAgLTUgLTI1IC01cS0yNiAwIC00NSAxOC41dC0xOSA0NS41cTAgMjEgMTIgMzUuNXQyOSAyNXQzNCAyM3QyOSAzNg0KdDEyIDU2LjV0LTEyIDU2LjV0LTI5IDM2dC0zNCAyM3QtMjkgMjV0LTEyIDM1LjVxMCAyNyAxOSA0NS41dDQ1IDE4LjVxMTUgMCAyNSAtNXE3MCAtMjcgMTEyLjUgLTkzdDQyLjUgLTE0MnpNMTQwOCA2NDBxMCAtMTUzIC04NSAtMjgyLjV0LTIyNSAtMTg4LjVxLTEzIC01IC0yNSAtNXEtMjcgMCAtNDYgMTl0LTE5IDQ1cTAgMzkgMzkgNTlxNTYgMjkgNzYgNDRxNzQgNTQgMTE1LjUgMTM1LjV0NDEuNSAxNzMuNXQtNDEuNSAxNzMuNQ0KdC0xMTUuNSAxMzUuNXEtMjAgMTUgLTc2IDQ0cS0zOSAyMCAtMzkgNTlxMCAyNiAxOSA0NXQ0NSAxOXExMyAwIDI2IC01cTE0MCAtNTkgMjI1IC0xODguNXQ4NSAtMjgyLjV6TTE2NjQgNjQwcTAgLTIzMCAtMTI3IC00MjIuNXQtMzM4IC0yODMuNXEtMTMgLTUgLTI2IC01cS0yNiAwIC00NSAxOXQtMTkgNDVxMCAzNiAzOSA1OXE3IDQgMjIuNSAxMC41dDIyLjUgMTAuNXE0NiAyNSA4MiA1MXExMjMgOTEgMTkyIDIyN3Q2OSAyODl0LTY5IDI4OQ0KdC0xOTIgMjI3cS0zNiAyNiAtODIgNTFxLTcgNCAtMjIuNSAxMC41dC0yMi41IDEwLjVxLTM5IDIzIC0zOSA1OXEwIDI2IDE5IDQ1dDQ1IDE5cTEzIDAgMjYgLTVxMjExIC05MSAzMzggLTI4My41dDEyNyAtNDIyLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJxcmNvZGUiIHVuaWNvZGU9IiYjeGYwMjk7IiBob3Jpei1hZHYteD0iMTQwOCIgDQpkPSJNMzg0IDM4NHYtMTI4aC0xMjh2MTI4aDEyOHpNMzg0IDExNTJ2LTEyOGgtMTI4djEyOGgxMjh6TTExNTIgMTE1MnYtMTI4aC0xMjh2MTI4aDEyOHpNMTI4IDEyOWgzODR2MzgzaC0zODR2LTM4M3pNMTI4IDg5NmgzODR2Mzg0aC0zODR2LTM4NHpNODk2IDg5NmgzODR2Mzg0aC0zODR2LTM4NHpNNjQwIDY0MHYtNjQwaC02NDB2NjQwaDY0MHpNMTE1MiAxMjh2LTEyOGgtMTI4djEyOGgxMjh6TTE0MDggMTI4di0xMjhoLTEyOHYxMjhoMTI4eg0KTTE0MDggNjQwdi0zODRoLTM4NHYxMjhoLTEyOHYtMzg0aC0xMjh2NjQwaDM4NHYtMTI4aDEyOHYxMjhoMTI4ek02NDAgMTQwOHYtNjQwaC02NDB2NjQwaDY0MHpNMTQwOCAxNDA4di02NDBoLTY0MHY2NDBoNjQweiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iYmFyY29kZSIgdW5pY29kZT0iJiN4ZjAyYTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik02MyAwaC02M3YxNDA4aDYzdi0xNDA4ek0xMjYgMWgtMzJ2MTQwN2gzMnYtMTQwN3pNMjIwIDFoLTMxdjE0MDdoMzF2LTE0MDd6TTM3NyAxaC0zMXYxNDA3aDMxdi0xNDA3ek01MzQgMWgtNjJ2MTQwN2g2MnYtMTQwN3pNNjYwIDFoLTMxdjE0MDdoMzF2LTE0MDd6TTcyMyAxaC0zMXYxNDA3aDMxdi0xNDA3ek03ODYgMWgtMzF2MTQwN2gzMXYtMTQwN3pNOTQzIDFoLTYzdjE0MDdoNjN2LTE0MDd6TTExMDAgMWgtNjN2MTQwN2g2M3YtMTQwN3oNCk0xMjI2IDFoLTYzdjE0MDdoNjN2LTE0MDd6TTEzNTIgMWgtNjN2MTQwN2g2M3YtMTQwN3pNMTQ0NiAxaC02M3YxNDA3aDYzdi0xNDA3ek0xNjM1IDFoLTk0djE0MDdoOTR2LTE0MDd6TTE2OTggMWgtMzJ2MTQwN2gzMnYtMTQwN3pNMTc5MiAwaC02M3YxNDA4aDYzdi0xNDA4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idGFnIiB1bmljb2RlPSImI3hmMDJiOyIgDQpkPSJNNDQ4IDEwODhxMCA1MyAtMzcuNSA5MC41dC05MC41IDM3LjV0LTkwLjUgLTM3LjV0LTM3LjUgLTkwLjV0MzcuNSAtOTAuNXQ5MC41IC0zNy41dDkwLjUgMzcuNXQzNy41IDkwLjV6TTE1MTUgNTEycTAgLTUzIC0zNyAtOTBsLTQ5MSAtNDkycS0zOSAtMzcgLTkxIC0zN3EtNTMgMCAtOTAgMzdsLTcxNSA3MTZxLTM4IDM3IC02NC41IDEwMXQtMjYuNSAxMTd2NDE2cTAgNTIgMzggOTB0OTAgMzhoNDE2cTUzIDAgMTE3IC0yNi41dDEwMiAtNjQuNQ0KbDcxNSAtNzE0cTM3IC0zOSAzNyAtOTF6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ0YWdzIiB1bmljb2RlPSImI3hmMDJjOyIgaG9yaXotYWR2LXg9IjE5MjAiIA0KZD0iTTQ0OCAxMDg4cTAgNTMgLTM3LjUgOTAuNXQtOTAuNSAzNy41dC05MC41IC0zNy41dC0zNy41IC05MC41dDM3LjUgLTkwLjV0OTAuNSAtMzcuNXQ5MC41IDM3LjV0MzcuNSA5MC41ek0xNTE1IDUxMnEwIC01MyAtMzcgLTkwbC00OTEgLTQ5MnEtMzkgLTM3IC05MSAtMzdxLTUzIDAgLTkwIDM3bC03MTUgNzE2cS0zOCAzNyAtNjQuNSAxMDF0LTI2LjUgMTE3djQxNnEwIDUyIDM4IDkwdDkwIDM4aDQxNnE1MyAwIDExNyAtMjYuNXQxMDIgLTY0LjUNCmw3MTUgLTcxNHEzNyAtMzkgMzcgLTkxek0xODk5IDUxMnEwIC01MyAtMzcgLTkwbC00OTEgLTQ5MnEtMzkgLTM3IC05MSAtMzdxLTM2IDAgLTU5IDE0dC01MyA0NWw0NzAgNDcwcTM3IDM3IDM3IDkwcTAgNTIgLTM3IDkxbC03MTUgNzE0cS0zOCAzOCAtMTAyIDY0LjV0LTExNyAyNi41aDIyNHE1MyAwIDExNyAtMjYuNXQxMDIgLTY0LjVsNzE1IC03MTRxMzcgLTM5IDM3IC05MXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImJvb2siIHVuaWNvZGU9IiYjeGYwMmQ7IiBob3Jpei1hZHYteD0iMTY2NCIgDQpkPSJNMTYzOSAxMDU4cTQwIC01NyAxOCAtMTI5bC0yNzUgLTkwNnEtMTkgLTY0IC03Ni41IC0xMDcuNXQtMTIyLjUgLTQzLjVoLTkyM3EtNzcgMCAtMTQ4LjUgNTMuNXQtOTkuNSAxMzEuNXEtMjQgNjcgLTIgMTI3cTAgNCAzIDI3dDQgMzdxMSA4IC0zIDIxLjV0LTMgMTkuNXEyIDExIDggMjF0MTYuNSAyMy41dDE2LjUgMjMuNXEyMyAzOCA0NSA5MS41dDMwIDkxLjVxMyAxMCAwLjUgMzB0LTAuNSAyOHEzIDExIDE3IDI4dDE3IDIzDQpxMjEgMzYgNDIgOTJ0MjUgOTBxMSA5IC0yLjUgMzJ0MC41IDI4cTQgMTMgMjIgMzAuNXQyMiAyMi41cTE5IDI2IDQyLjUgODQuNXQyNy41IDk2LjVxMSA4IC0zIDI1LjV0LTIgMjYuNXEyIDggOSAxOHQxOCAyM3QxNyAyMXE4IDEyIDE2LjUgMzAuNXQxNSAzNXQxNiAzNnQxOS41IDMydDI2LjUgMjMuNXQzNiAxMS41dDQ3LjUgLTUuNWwtMSAtM3EzOCA5IDUxIDloNzYxcTc0IDAgMTE0IC01NnQxOCAtMTMwbC0yNzQgLTkwNg0KcS0zNiAtMTE5IC03MS41IC0xNTMuNXQtMTI4LjUgLTM0LjVoLTg2OXEtMjcgMCAtMzggLTE1cS0xMSAtMTYgLTEgLTQzcTI0IC03MCAxNDQgLTcwaDkyM3EyOSAwIDU2IDE1LjV0MzUgNDEuNWwzMDAgOTg3cTcgMjIgNSA1N3EzOCAtMTUgNTkgLTQzek01NzUgMTA1NnEtNCAtMTMgMiAtMjIuNXQyMCAtOS41aDYwOHExMyAwIDI1LjUgOS41dDE2LjUgMjIuNWwyMSA2NHE0IDEzIC0yIDIyLjV0LTIwIDkuNWgtNjA4cS0xMyAwIC0yNS41IC05LjUNCnQtMTYuNSAtMjIuNXpNNDkyIDgwMHEtNCAtMTMgMiAtMjIuNXQyMCAtOS41aDYwOHExMyAwIDI1LjUgOS41dDE2LjUgMjIuNWwyMSA2NHE0IDEzIC0yIDIyLjV0LTIwIDkuNWgtNjA4cS0xMyAwIC0yNS41IC05LjV0LTE2LjUgLTIyLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJib29rbWFyayIgdW5pY29kZT0iJiN4ZjAyZTsiIGhvcml6LWFkdi14PSIxMjgwIiANCmQ9Ik0xMTY0IDE0MDhxMjMgMCA0NCAtOXEzMyAtMTMgNTIuNSAtNDF0MTkuNSAtNjJ2LTEyODlxMCAtMzQgLTE5LjUgLTYydC01Mi41IC00MXEtMTkgLTggLTQ0IC04cS00OCAwIC04MyAzMmwtNDQxIDQyNGwtNDQxIC00MjRxLTM2IC0zMyAtODMgLTMzcS0yMyAwIC00NCA5cS0zMyAxMyAtNTIuNSA0MXQtMTkuNSA2MnYxMjg5cTAgMzQgMTkuNSA2MnQ1Mi41IDQxcTIxIDkgNDQgOWgxMDQ4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0icHJpbnQiIHVuaWNvZGU9IiYjeGYwMmY7IiBob3Jpei1hZHYteD0iMTY2NCIgDQpkPSJNMzg0IDBoODk2djI1NmgtODk2di0yNTZ6TTM4NCA2NDBoODk2djM4NGgtMTYwcS00MCAwIC02OCAyOHQtMjggNjh2MTYwaC02NDB2LTY0MHpNMTUzNiA1NzZxMCAyNiAtMTkgNDV0LTQ1IDE5dC00NSAtMTl0LTE5IC00NXQxOSAtNDV0NDUgLTE5dDQ1IDE5dDE5IDQ1ek0xNjY0IDU3NnYtNDE2cTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtMjI0di0xNjBxMCAtNDAgLTI4IC02OHQtNjggLTI4aC05NjBxLTQwIDAgLTY4IDI4dC0yOCA2OA0KdjE2MGgtMjI0cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2NDE2cTAgNzkgNTYuNSAxMzUuNXQxMzUuNSA1Ni41aDY0djU0NHEwIDQwIDI4IDY4dDY4IDI4aDY3MnE0MCAwIDg4IC0yMHQ3NiAtNDhsMTUyIC0xNTJxMjggLTI4IDQ4IC03NnQyMCAtODh2LTI1Nmg2NHE3OSAwIDEzNS41IC01Ni41dDU2LjUgLTEzNS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY2FtZXJhIiB1bmljb2RlPSImI3hmMDMwOyIgaG9yaXotYWR2LXg9IjE5MjAiIA0KZD0iTTk2MCA4NjRxMTE5IDAgMjAzLjUgLTg0LjV0ODQuNSAtMjAzLjV0LTg0LjUgLTIwMy41dC0yMDMuNSAtODQuNXQtMjAzLjUgODQuNXQtODQuNSAyMDMuNXQ4NC41IDIwMy41dDIwMy41IDg0LjV6TTE2NjQgMTI4MHExMDYgMCAxODEgLTc1dDc1IC0xODF2LTg5NnEwIC0xMDYgLTc1IC0xODF0LTE4MSAtNzVoLTE0MDhxLTEwNiAwIC0xODEgNzV0LTc1IDE4MXY4OTZxMCAxMDYgNzUgMTgxdDE4MSA3NWgyMjRsNTEgMTM2DQpxMTkgNDkgNjkuNSA4NC41dDEwMy41IDM1LjVoNTEycTUzIDAgMTAzLjUgLTM1LjV0NjkuNSAtODQuNWw1MSAtMTM2aDIyNHpNOTYwIDEyOHExODUgMCAzMTYuNSAxMzEuNXQxMzEuNSAzMTYuNXQtMTMxLjUgMzE2LjV0LTMxNi41IDEzMS41dC0zMTYuNSAtMTMxLjV0LTEzMS41IC0zMTYuNXQxMzEuNSAtMzE2LjV0MzE2LjUgLTEzMS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZm9udCIgdW5pY29kZT0iJiN4ZjAzMTsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik03MjUgOTc3bC0xNzAgLTQ1MHEzMyAwIDEzNi41IC0ydDE2MC41IC0ycTE5IDAgNTcgMnEtODcgMjUzIC0xODQgNDUyek0wIC0xMjhsMiA3OXEyMyA3IDU2IDEyLjV0NTcgMTAuNXQ0OS41IDE0LjV0NDQuNSAyOXQzMSA1MC41bDIzNyA2MTZsMjgwIDcyNGg3NWg1M3E4IC0xNCAxMSAtMjFsMjA1IC00ODBxMzMgLTc4IDEwNiAtMjU3LjV0MTE0IC0yNzQuNXExNSAtMzQgNTggLTE0NC41dDcyIC0xNjguNXEyMCAtNDUgMzUgLTU3DQpxMTkgLTE1IDg4IC0yOS41dDg0IC0yMC41cTYgLTM4IDYgLTU3cTAgLTUgLTAuNSAtMTMuNXQtMC41IC0xMi41cS02MyAwIC0xOTAgOHQtMTkxIDhxLTc2IDAgLTIxNSAtN3QtMTc4IC04cTAgNDMgNCA3OGwxMzEgMjhxMSAwIDEyLjUgMi41dDE1LjUgMy41dDE0LjUgNC41dDE1IDYuNXQxMSA4dDkgMTF0Mi41IDE0cTAgMTYgLTMxIDk2LjV0LTcyIDE3Ny41dC00MiAxMDBsLTQ1MCAycS0yNiAtNTggLTc2LjUgLTE5NS41dC01MC41IC0xNjIuNQ0KcTAgLTIyIDE0IC0zNy41dDQzLjUgLTI0LjV0NDguNSAtMTMuNXQ1NyAtOC41dDQxIC00cTEgLTE5IDEgLTU4cTAgLTkgLTIgLTI3cS01OCAwIC0xNzQuNSAxMHQtMTc0LjUgMTBxLTggMCAtMjYuNSAtNHQtMjEuNSAtNHEtODAgLTE0IC0xODggLTE0eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iYm9sZCIgdW5pY29kZT0iJiN4ZjAzMjsiIGhvcml6LWFkdi14PSIxNDA4IiANCmQ9Ik01NTUgMTVxNzQgLTMyIDE0MCAtMzJxMzc2IDAgMzc2IDMzNXEwIDExNCAtNDEgMTgwcS0yNyA0NCAtNjEuNSA3NHQtNjcuNSA0Ni41dC04MC41IDI1dC04NCAxMC41dC05NC41IDJxLTczIDAgLTEwMSAtMTBxMCAtNTMgLTAuNSAtMTU5dC0wLjUgLTE1OHEwIC04IC0xIC02Ny41dC0wLjUgLTk2LjV0NC41IC04My41dDEyIC02Ni41ek01NDEgNzYxcTQyIC03IDEwOSAtN3E4MiAwIDE0MyAxM3QxMTAgNDQuNXQ3NC41IDg5LjV0MjUuNSAxNDINCnEwIDcwIC0yOSAxMjIuNXQtNzkgODJ0LTEwOCA0My41dC0xMjQgMTRxLTUwIDAgLTEzMCAtMTNxMCAtNTAgNCAtMTUxdDQgLTE1MnEwIC0yNyAtMC41IC04MHQtMC41IC03OXEwIC00NiAxIC02OXpNMCAtMTI4bDIgOTRxMTUgNCA4NSAxNnQxMDYgMjdxNyAxMiAxMi41IDI3dDguNSAzMy41dDUuNSAzMi41dDMgMzcuNXQwLjUgMzR2MzUuNXYzMHEwIDk4MiAtMjIgMTAyNXEtNCA4IC0yMiAxNC41dC00NC41IDExdC00OS41IDd0LTQ4LjUgNC41DQp0LTMwLjUgM2wtNCA4M3E5OCAyIDM0MCAxMS41dDM3MyA5LjVxMjMgMCA2OCAtMC41dDY4IC0wLjVxNzAgMCAxMzYuNSAtMTN0MTI4LjUgLTQydDEwOCAtNzF0NzQgLTEwNC41dDI4IC0xMzcuNXEwIC01MiAtMTYuNSAtOTUuNXQtMzkgLTcydC02NC41IC01Ny41dC03MyAtNDV0LTg0IC00MHExNTQgLTM1IDI1Ni41IC0xMzR0MTAyLjUgLTI0OHEwIC0xMDAgLTM1IC0xNzkuNXQtOTMuNSAtMTMwLjV0LTEzOCAtODUuNXQtMTYzLjUgLTQ4LjUNCnQtMTc2IC0xNHEtNDQgMCAtMTMyIDN0LTEzMiAzcS0xMDYgMCAtMzA3IC0xMXQtMjMxIC0xMnoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Iml0YWxpYyIgdW5pY29kZT0iJiN4ZjAzMzsiIGhvcml6LWFkdi14PSIxMDI0IiANCmQ9Ik0wIC0xMjZsMTcgODVxMjIgNyA2MS41IDE2LjV0NzIgMTl0NTkuNSAyMy41cTI4IDM1IDQxIDEwMXExIDcgNjIgMjg5dDExNCA1NDMuNXQ1MiAyOTYuNXYyNXEtMjQgMTMgLTU0LjUgMTguNXQtNjkuNSA4dC01OCA1LjVsMTkgMTAzcTMzIC0yIDEyMCAtNi41dDE0OS41IC03dDEyMC41IC0yLjVxNDggMCA5OC41IDIuNXQxMjEgN3Q5OC41IDYuNXEtNSAtMzkgLTE5IC04OXEtMzAgLTEwIC0xMDEuNSAtMjguNXQtMTA4LjUgLTMzLjUNCnEtOCAtMTkgLTE0IC00Mi41dC05IC00MHQtNy41IC00NS41dC02LjUgLTQycS0yNyAtMTQ4IC04Ny41IC00MTkuNXQtNzcuNSAtMzU1LjVxLTIgLTkgLTEzIC01OHQtMjAgLTkwdC0xNiAtODMuNXQtNiAtNTcuNWwxIC0xOHExNyAtNCAxODUgLTMxcS0zIC00NCAtMTYgLTk5cS0xMSAwIC0zMi41IC0xLjV0LTMyLjUgLTEuNXEtMjkgMCAtODcgMTB0LTg2IDEwcS0xMzggMiAtMjA2IDJxLTUxIDAgLTE0MyAtOXQtMTIxIC0xMXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InRleHRfaGVpZ2h0IiB1bmljb2RlPSImI3hmMDM0OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE3NDQgMTI4cTMzIDAgNDIgLTE4LjV0LTExIC00NC41bC0xMjYgLTE2MnEtMjAgLTI2IC00OSAtMjZ0LTQ5IDI2bC0xMjYgMTYycS0yMCAyNiAtMTEgNDQuNXQ0MiAxOC41aDgwdjEwMjRoLTgwcS0zMyAwIC00MiAxOC41dDExIDQ0LjVsMTI2IDE2MnEyMCAyNiA0OSAyNnQ0OSAtMjZsMTI2IC0xNjJxMjAgLTI2IDExIC00NC41dC00MiAtMTguNWgtODB2LTEwMjRoODB6TTgxIDE0MDdsNTQgLTI3cTEyIC01IDIxMSAtNXE0NCAwIDEzMiAyDQp0MTMyIDJxMzYgMCAxMDcuNSAtMC41dDEwNy41IC0wLjVoMjkzcTYgMCAyMSAtMC41dDIwLjUgMHQxNiAzdDE3LjUgOXQxNSAxNy41bDQyIDFxNCAwIDE0IC0wLjV0MTQgLTAuNXEyIC0xMTIgMiAtMzM2cTAgLTgwIC01IC0xMDlxLTM5IC0xNCAtNjggLTE4cS0yNSA0NCAtNTQgMTI4cS0zIDkgLTExIDQ4dC0xNC41IDczLjV0LTcuNSAzNS41cS02IDggLTEyIDEyLjV0LTE1LjUgNnQtMTMgMi41dC0xOCAwLjV0LTE2LjUgLTAuNQ0KcS0xNyAwIC02Ni41IDAuNXQtNzQuNSAwLjV0LTY0IC0ydC03MSAtNnEtOSAtODEgLTggLTEzNnEwIC05NCAyIC0zODh0MiAtNDU1cTAgLTE2IC0yLjUgLTcxLjV0MCAtOTEuNXQxMi41IC02OXE0MCAtMjEgMTI0IC00Mi41dDEyMCAtMzcuNXE1IC00MCA1IC01MHEwIC0xNCAtMyAtMjlsLTM0IC0xcS03NiAtMiAtMjE4IDh0LTIwNyAxMHEtNTAgMCAtMTUxIC05dC0xNTIgLTlxLTMgNTEgLTMgNTJ2OXExNyAyNyA2MS41IDQzdDk4LjUgMjl0NzggMjcNCnExOSA0MiAxOSAzODNxMCAxMDEgLTMgMzAzdC0zIDMwM3YxMTdxMCAyIDAuNSAxNS41dDAuNSAyNXQtMSAyNS41dC0zIDI0dC01IDE0cS0xMSAxMiAtMTYyIDEycS0zMyAwIC05MyAtMTJ0LTgwIC0yNnEtMTkgLTEzIC0zNCAtNzIuNXQtMzEuNSAtMTExdC00Mi41IC01My41cS00MiAyNiAtNTYgNDR2MzgzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idGV4dF93aWR0aCIgdW5pY29kZT0iJiN4ZjAzNTsiIA0KZD0iTTgxIDE0MDdsNTQgLTI3cTEyIC01IDIxMSAtNXE0NCAwIDEzMiAydDEzMiAycTcwIDAgMjQ2LjUgMXQzMDQuNSAwLjV0MjQ3IC00LjVxMzMgLTEgNTYgMzFsNDIgMXE0IDAgMTQgLTAuNXQxNCAtMC41cTIgLTExMiAyIC0zMzZxMCAtODAgLTUgLTEwOXEtMzkgLTE0IC02OCAtMThxLTI1IDQ0IC01NCAxMjhxLTMgOSAtMTEgNDcuNXQtMTUgNzMuNXQtNyAzNnEtMTAgMTMgLTI3IDE5cS01IDIgLTY2IDJxLTMwIDAgLTkzIDF0LTEwMyAxDQp0LTk0IC0ydC05NiAtN3EtOSAtODEgLTggLTEzNmwxIC0xNTJ2NTJxMCAtNTUgMSAtMTU0dDEuNSAtMTgwdDAuNSAtMTUzcTAgLTE2IC0yLjUgLTcxLjV0MCAtOTEuNXQxMi41IC02OXE0MCAtMjEgMTI0IC00Mi41dDEyMCAtMzcuNXE1IC00MCA1IC01MHEwIC0xNCAtMyAtMjlsLTM0IC0xcS03NiAtMiAtMjE4IDh0LTIwNyAxMHEtNTAgMCAtMTUxIC05dC0xNTIgLTlxLTMgNTEgLTMgNTJ2OXExNyAyNyA2MS41IDQzdDk4LjUgMjl0NzggMjcNCnE3IDE2IDExLjUgNzR0NiAxNDUuNXQxLjUgMTU1dC0wLjUgMTUzLjV0LTAuNSA4OXEwIDcgLTIuNSAyMS41dC0yLjUgMjIuNXEwIDcgMC41IDQ0dDEgNzN0MCA3Ni41dC0zIDY3LjV0LTYuNSAzMnEtMTEgMTIgLTE2MiAxMnEtNDEgMCAtMTYzIC0xMy41dC0xMzggLTI0LjVxLTE5IC0xMiAtMzQgLTcxLjV0LTMxLjUgLTExMS41dC00Mi41IC01NHEtNDIgMjYgLTU2IDQ0djM4M3pNMTMxMCAxMjVxMTIgMCA0MiAtMTkuNXQ1Ny41IC00MS41DQp0NTkuNSAtNDl0MzYgLTMwcTI2IC0yMSAyNiAtNDl0LTI2IC00OXEtNCAtMyAtMzYgLTMwdC01OS41IC00OXQtNTcuNSAtNDEuNXQtNDIgLTE5LjVxLTEzIDAgLTIwLjUgMTAuNXQtMTAgMjguNXQtMi41IDMzLjV0MS41IDMzdDEuNSAxOS41aC0xMDI0cTAgLTIgMS41IC0xOS41dDEuNSAtMzN0LTIuNSAtMzMuNXQtMTAgLTI4LjV0LTIwLjUgLTEwLjVxLTEyIDAgLTQyIDE5LjV0LTU3LjUgNDEuNXQtNTkuNSA0OXQtMzYgMzBxLTI2IDIxIC0yNiA0OQ0KdDI2IDQ5cTQgMyAzNiAzMHQ1OS41IDQ5dDU3LjUgNDEuNXQ0MiAxOS41cTEzIDAgMjAuNSAtMTAuNXQxMCAtMjguNXQyLjUgLTMzLjV0LTEuNSAtMzN0LTEuNSAtMTkuNWgxMDI0cTAgMiAtMS41IDE5LjV0LTEuNSAzM3QyLjUgMzMuNXQxMCAyOC41dDIwLjUgMTAuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImFsaWduX2xlZnQiIHVuaWNvZGU9IiYjeGYwMzY7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTc5MiAxOTJ2LTEyOHEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTE2NjRxLTI2IDAgLTQ1IDE5dC0xOSA0NXYxMjhxMCAyNiAxOSA0NXQ0NSAxOWgxNjY0cTI2IDAgNDUgLTE5dDE5IC00NXpNMTQwOCA1NzZ2LTEyOHEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTEyODBxLTI2IDAgLTQ1IDE5dC0xOSA0NXYxMjhxMCAyNiAxOSA0NXQ0NSAxOWgxMjgwcTI2IDAgNDUgLTE5dDE5IC00NXpNMTY2NCA5NjB2LTEyOHEwIC0yNiAtMTkgLTQ1DQp0LTQ1IC0xOWgtMTUzNnEtMjYgMCAtNDUgMTl0LTE5IDQ1djEyOHEwIDI2IDE5IDQ1dDQ1IDE5aDE1MzZxMjYgMCA0NSAtMTl0MTkgLTQ1ek0xMjgwIDEzNDR2LTEyOHEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTExNTJxLTI2IDAgLTQ1IDE5dC0xOSA0NXYxMjhxMCAyNiAxOSA0NXQ0NSAxOWgxMTUycTI2IDAgNDUgLTE5dDE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImFsaWduX2NlbnRlciIgdW5pY29kZT0iJiN4ZjAzNzsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNzkyIDE5MnYtMTI4cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTY2NHEtMjYgMCAtNDUgMTl0LTE5IDQ1djEyOHEwIDI2IDE5IDQ1dDQ1IDE5aDE2NjRxMjYgMCA0NSAtMTl0MTkgLTQ1ek0xNDA4IDU3NnYtMTI4cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtODk2cS0yNiAwIC00NSAxOXQtMTkgNDV2MTI4cTAgMjYgMTkgNDV0NDUgMTloODk2cTI2IDAgNDUgLTE5dDE5IC00NXpNMTY2NCA5NjB2LTEyOHEwIC0yNiAtMTkgLTQ1dC00NSAtMTkNCmgtMTQwOHEtMjYgMCAtNDUgMTl0LTE5IDQ1djEyOHEwIDI2IDE5IDQ1dDQ1IDE5aDE0MDhxMjYgMCA0NSAtMTl0MTkgLTQ1ek0xMjgwIDEzNDR2LTEyOHEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTY0MHEtMjYgMCAtNDUgMTl0LTE5IDQ1djEyOHEwIDI2IDE5IDQ1dDQ1IDE5aDY0MHEyNiAwIDQ1IC0xOXQxOSAtNDV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJhbGlnbl9yaWdodCIgdW5pY29kZT0iJiN4ZjAzODsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNzkyIDE5MnYtMTI4cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTY2NHEtMjYgMCAtNDUgMTl0LTE5IDQ1djEyOHEwIDI2IDE5IDQ1dDQ1IDE5aDE2NjRxMjYgMCA0NSAtMTl0MTkgLTQ1ek0xNzkyIDU3NnYtMTI4cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTI4MHEtMjYgMCAtNDUgMTl0LTE5IDQ1djEyOHEwIDI2IDE5IDQ1dDQ1IDE5aDEyODBxMjYgMCA0NSAtMTl0MTkgLTQ1ek0xNzkyIDk2MHYtMTI4cTAgLTI2IC0xOSAtNDUNCnQtNDUgLTE5aC0xNTM2cS0yNiAwIC00NSAxOXQtMTkgNDV2MTI4cTAgMjYgMTkgNDV0NDUgMTloMTUzNnEyNiAwIDQ1IC0xOXQxOSAtNDV6TTE3OTIgMTM0NHYtMTI4cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTE1MnEtMjYgMCAtNDUgMTl0LTE5IDQ1djEyOHEwIDI2IDE5IDQ1dDQ1IDE5aDExNTJxMjYgMCA0NSAtMTl0MTkgLTQ1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iYWxpZ25fanVzdGlmeSIgdW5pY29kZT0iJiN4ZjAzOTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNzkyIDE5MnYtMTI4cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTY2NHEtMjYgMCAtNDUgMTl0LTE5IDQ1djEyOHEwIDI2IDE5IDQ1dDQ1IDE5aDE2NjRxMjYgMCA0NSAtMTl0MTkgLTQ1ek0xNzkyIDU3NnYtMTI4cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTY2NHEtMjYgMCAtNDUgMTl0LTE5IDQ1djEyOHEwIDI2IDE5IDQ1dDQ1IDE5aDE2NjRxMjYgMCA0NSAtMTl0MTkgLTQ1ek0xNzkyIDk2MHYtMTI4cTAgLTI2IC0xOSAtNDUNCnQtNDUgLTE5aC0xNjY0cS0yNiAwIC00NSAxOXQtMTkgNDV2MTI4cTAgMjYgMTkgNDV0NDUgMTloMTY2NHEyNiAwIDQ1IC0xOXQxOSAtNDV6TTE3OTIgMTM0NHYtMTI4cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTY2NHEtMjYgMCAtNDUgMTl0LTE5IDQ1djEyOHEwIDI2IDE5IDQ1dDQ1IDE5aDE2NjRxMjYgMCA0NSAtMTl0MTkgLTQ1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ibGlzdCIgdW5pY29kZT0iJiN4ZjAzYTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0yNTYgMjI0di0xOTJxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC0xOTJxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXYxOTJxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWgxOTJxMTMgMCAyMi41IC05LjV0OS41IC0yMi41ek0yNTYgNjA4di0xOTJxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC0xOTJxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXYxOTJxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWgxOTJxMTMgMCAyMi41IC05LjUNCnQ5LjUgLTIyLjV6TTI1NiA5OTJ2LTE5MnEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTE5MnEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djE5MnEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDE5MnExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6TTE3OTIgMjI0di0xOTJxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC0xMzQ0cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2MTkycTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoMTM0NA0KcTEzIDAgMjIuNSAtOS41dDkuNSAtMjIuNXpNMjU2IDEzNzZ2LTE5MnEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTE5MnEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djE5MnEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDE5MnExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6TTE3OTIgNjA4di0xOTJxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC0xMzQ0cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2MTkycTAgMTMgOS41IDIyLjUNCnQyMi41IDkuNWgxMzQ0cTEzIDAgMjIuNSAtOS41dDkuNSAtMjIuNXpNMTc5MiA5OTJ2LTE5MnEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTEzNDRxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXYxOTJxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWgxMzQ0cTEzIDAgMjIuNSAtOS41dDkuNSAtMjIuNXpNMTc5MiAxMzc2di0xOTJxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC0xMzQ0cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2MTkyDQpxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWgxMzQ0cTEzIDAgMjIuNSAtOS41dDkuNSAtMjIuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImluZGVudF9sZWZ0IiB1bmljb2RlPSImI3hmMDNiOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTM4NCA5OTJ2LTU3NnEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVxLTE0IDAgLTIzIDlsLTI4OCAyODhxLTkgOSAtOSAyM3Q5IDIzbDI4OCAyODhxOSA5IDIzIDlxMTMgMCAyMi41IC05LjV0OS41IC0yMi41ek0xNzkyIDIyNHYtMTkycTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtMTcyOHEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djE5MnEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDE3MjhxMTMgMCAyMi41IC05LjUNCnQ5LjUgLTIyLjV6TTE3OTIgNjA4di0xOTJxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC0xMDg4cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2MTkycTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoMTA4OHExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6TTE3OTIgOTkydi0xOTJxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC0xMDg4cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2MTkycTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoMTA4OA0KcTEzIDAgMjIuNSAtOS41dDkuNSAtMjIuNXpNMTc5MiAxMzc2di0xOTJxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC0xNzI4cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2MTkycTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoMTcyOHExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJpbmRlbnRfcmlnaHQiIHVuaWNvZGU9IiYjeGYwM2M7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMzUyIDcwNHEwIC0xNCAtOSAtMjNsLTI4OCAtMjg4cS05IC05IC0yMyAtOXEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djU3NnEwIDEzIDkuNSAyMi41dDIyLjUgOS41cTE0IDAgMjMgLTlsMjg4IC0yODhxOSAtOSA5IC0yM3pNMTc5MiAyMjR2LTE5MnEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTE3MjhxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXYxOTJxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWgxNzI4cTEzIDAgMjIuNSAtOS41DQp0OS41IC0yMi41ek0xNzkyIDYwOHYtMTkycTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtMTA4OHEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djE5MnEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDEwODhxMTMgMCAyMi41IC05LjV0OS41IC0yMi41ek0xNzkyIDk5MnYtMTkycTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtMTA4OHEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djE5MnEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDEwODgNCnExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6TTE3OTIgMTM3NnYtMTkycTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtMTcyOHEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djE5MnEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDE3MjhxMTMgMCAyMi41IC05LjV0OS41IC0yMi41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZmFjZXRpbWVfdmlkZW8iIHVuaWNvZGU9IiYjeGYwM2Q7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTc5MiAxMTg0di0xMDg4cTAgLTQyIC0zOSAtNTlxLTEzIC01IC0yNSAtNXEtMjcgMCAtNDUgMTlsLTQwMyA0MDN2LTE2NnEwIC0xMTkgLTg0LjUgLTIwMy41dC0yMDMuNSAtODQuNWgtNzA0cS0xMTkgMCAtMjAzLjUgODQuNXQtODQuNSAyMDMuNXY3MDRxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDcwNHExMTkgMCAyMDMuNSAtODQuNXQ4NC41IC0yMDMuNXYtMTY1bDQwMyA0MDJxMTggMTkgNDUgMTlxMTIgMCAyNSAtNQ0KcTM5IC0xNyAzOSAtNTl6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJwaWN0dXJlIiB1bmljb2RlPSImI3hmMDNlOyIgaG9yaXotYWR2LXg9IjE5MjAiIA0KZD0iTTY0MCA5NjBxMCAtODAgLTU2IC0xMzZ0LTEzNiAtNTZ0LTEzNiA1NnQtNTYgMTM2dDU2IDEzNnQxMzYgNTZ0MTM2IC01NnQ1NiAtMTM2ek0xNjY0IDU3NnYtNDQ4aC0xNDA4djE5MmwzMjAgMzIwbDE2MCAtMTYwbDUxMiA1MTJ6TTE3NjAgMTI4MGgtMTYwMHEtMTMgMCAtMjIuNSAtOS41dC05LjUgLTIyLjV2LTEyMTZxMCAtMTMgOS41IC0yMi41dDIyLjUgLTkuNWgxNjAwcTEzIDAgMjIuNSA5LjV0OS41IDIyLjV2MTIxNg0KcTAgMTMgLTkuNSAyMi41dC0yMi41IDkuNXpNMTkyMCAxMjQ4di0xMjE2cTAgLTY2IC00NyAtMTEzdC0xMTMgLTQ3aC0xNjAwcS02NiAwIC0xMTMgNDd0LTQ3IDExM3YxMjE2cTAgNjYgNDcgMTEzdDExMyA0N2gxNjAwcTY2IDAgMTEzIC00N3Q0NyAtMTEzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0icGVuY2lsIiB1bmljb2RlPSImI3hmMDQwOyIgDQpkPSJNMzYzIDBsOTEgOTFsLTIzNSAyMzVsLTkxIC05MXYtMTA3aDEyOHYtMTI4aDEwN3pNODg2IDkyOHEwIDIyIC0yMiAyMnEtMTAgMCAtMTcgLTdsLTU0MiAtNTQycS03IC03IC03IC0xN3EwIC0yMiAyMiAtMjJxMTAgMCAxNyA3bDU0MiA1NDJxNyA3IDcgMTd6TTgzMiAxMTIwbDQxNiAtNDE2bC04MzIgLTgzMmgtNDE2djQxNnpNMTUxNSAxMDI0cTAgLTUzIC0zNyAtOTBsLTE2NiAtMTY2bC00MTYgNDE2bDE2NiAxNjVxMzYgMzggOTAgMzgNCnE1MyAwIDkxIC0zOGwyMzUgLTIzNHEzNyAtMzkgMzcgLTkxeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ibWFwX21hcmtlciIgdW5pY29kZT0iJiN4ZjA0MTsiIGhvcml6LWFkdi14PSIxMDI0IiANCmQ9Ik03NjggODk2cTAgMTA2IC03NSAxODF0LTE4MSA3NXQtMTgxIC03NXQtNzUgLTE4MXQ3NSAtMTgxdDE4MSAtNzV0MTgxIDc1dDc1IDE4MXpNMTAyNCA4OTZxMCAtMTA5IC0zMyAtMTc5bC0zNjQgLTc3NHEtMTYgLTMzIC00Ny41IC01MnQtNjcuNSAtMTl0LTY3LjUgMTl0LTQ2LjUgNTJsLTM2NSA3NzRxLTMzIDcwIC0zMyAxNzlxMCAyMTIgMTUwIDM2MnQzNjIgMTUwdDM2MiAtMTUwdDE1MCAtMzYyeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iYWRqdXN0IiB1bmljb2RlPSImI3hmMDQyOyIgDQpkPSJNNzY4IDk2djEwODhxLTE0OCAwIC0yNzMgLTczdC0xOTggLTE5OHQtNzMgLTI3M3Q3MyAtMjczdDE5OCAtMTk4dDI3MyAtNzN6TTE1MzYgNjQwcTAgLTIwOSAtMTAzIC0zODUuNXQtMjc5LjUgLTI3OS41dC0zODUuNSAtMTAzdC0zODUuNSAxMDN0LTI3OS41IDI3OS41dC0xMDMgMzg1LjV0MTAzIDM4NS41dDI3OS41IDI3OS41dDM4NS41IDEwM3QzODUuNSAtMTAzdDI3OS41IC0yNzkuNXQxMDMgLTM4NS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idGludCIgdW5pY29kZT0iJiN4ZjA0MzsiIGhvcml6LWFkdi14PSIxMDI0IiANCmQ9Ik01MTIgMzg0cTAgMzYgLTIwIDY5cS0xIDEgLTE1LjUgMjIuNXQtMjUuNSAzOHQtMjUgNDR0LTIxIDUwLjVxLTQgMTYgLTIxIDE2dC0yMSAtMTZxLTcgLTIzIC0yMSAtNTAuNXQtMjUgLTQ0dC0yNS41IC0zOHQtMTUuNSAtMjIuNXEtMjAgLTMzIC0yMCAtNjlxMCAtNTMgMzcuNSAtOTAuNXQ5MC41IC0zNy41dDkwLjUgMzcuNXQzNy41IDkwLjV6TTEwMjQgNTEycTAgLTIxMiAtMTUwIC0zNjJ0LTM2MiAtMTUwdC0zNjIgMTUwdC0xNTAgMzYyDQpxMCAxNDUgODEgMjc1cTYgOSA2Mi41IDkwLjV0MTAxIDE1MXQ5OS41IDE3OHQ4MyAyMDEuNXE5IDMwIDM0IDQ3dDUxIDE3dDUxLjUgLTE3dDMzLjUgLTQ3cTI4IC05MyA4MyAtMjAxLjV0OTkuNSAtMTc4dDEwMSAtMTUxdDYyLjUgLTkwLjVxODEgLTEyNyA4MSAtMjc1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZWRpdCIgdW5pY29kZT0iJiN4ZjA0NDsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik04ODggMzUybDExNiAxMTZsLTE1MiAxNTJsLTExNiAtMTE2di01Nmg5NnYtOTZoNTZ6TTEzMjggMTA3MnEtMTYgMTYgLTMzIC0xbC0zNTAgLTM1MHEtMTcgLTE3IC0xIC0zM3QzMyAxbDM1MCAzNTBxMTcgMTcgMSAzM3pNMTQwOCA0Nzh2LTE5MHEwIC0xMTkgLTg0LjUgLTIwMy41dC0yMDMuNSAtODQuNWgtODMycS0xMTkgMCAtMjAzLjUgODQuNXQtODQuNSAyMDMuNXY4MzJxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDgzMg0KcTYzIDAgMTE3IC0yNXExNSAtNyAxOCAtMjNxMyAtMTcgLTkgLTI5bC00OSAtNDlxLTE0IC0xNCAtMzIgLThxLTIzIDYgLTQ1IDZoLTgzMnEtNjYgMCAtMTEzIC00N3QtNDcgLTExM3YtODMycTAgLTY2IDQ3IC0xMTN0MTEzIC00N2g4MzJxNjYgMCAxMTMgNDd0NDcgMTEzdjEyNnEwIDEzIDkgMjJsNjQgNjRxMTUgMTUgMzUgN3QyMCAtMjl6TTEzMTIgMTIxNmwyODggLTI4OGwtNjcyIC02NzJoLTI4OHYyODh6TTE3NTYgMTA4NGwtOTIgLTkyDQpsLTI4OCAyODhsOTIgOTJxMjggMjggNjggMjh0NjggLTI4bDE1MiAtMTUycTI4IC0yOCAyOCAtNjh0LTI4IC02OHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InNoYXJlIiB1bmljb2RlPSImI3hmMDQ1OyIgaG9yaXotYWR2LXg9IjE2NjQiIA0KZD0iTTE0MDggNTQ3di0yNTlxMCAtMTE5IC04NC41IC0yMDMuNXQtMjAzLjUgLTg0LjVoLTgzMnEtMTE5IDAgLTIwMy41IDg0LjV0LTg0LjUgMjAzLjV2ODMycTAgMTE5IDg0LjUgMjAzLjV0MjAzLjUgODQuNWgyNTV2MHExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjVxMCAtMjcgLTI2IC0zMnEtNzcgLTI2IC0xMzMgLTYwcS0xMCAtNCAtMTYgLTRoLTExMnEtNjYgMCAtMTEzIC00N3QtNDcgLTExM3YtODMycTAgLTY2IDQ3IC0xMTN0MTEzIC00N2g4MzINCnE2NiAwIDExMyA0N3Q0NyAxMTN2MjE0cTAgMTkgMTggMjlxMjggMTMgNTQgMzdxMTYgMTYgMzUgOHEyMSAtOSAyMSAtMjl6TTE2NDUgMTA0M2wtMzg0IC0zODRxLTE4IC0xOSAtNDUgLTE5cS0xMiAwIC0yNSA1cS0zOSAxNyAtMzkgNTl2MTkyaC0xNjBxLTMyMyAwIC00MzggLTEzMXEtMTE5IC0xMzcgLTc0IC00NzNxMyAtMjMgLTIwIC0zNHEtOCAtMiAtMTIgLTJxLTE2IDAgLTI2IDEzcS0xMCAxNCAtMjEgMzF0LTM5LjUgNjguNXQtNDkuNSA5OS41DQp0LTM4LjUgMTE0dC0xNy41IDEyMnEwIDQ5IDMuNSA5MXQxNCA5MHQyOCA4OHQ0NyA4MS41dDY4LjUgNzR0OTQuNSA2MS41dDEyNC41IDQ4LjV0MTU5LjUgMzAuNXQxOTYuNSAxMWgxNjB2MTkycTAgNDIgMzkgNTlxMTMgNSAyNSA1cTI2IDAgNDUgLTE5bDM4NCAtMzg0cTE5IC0xOSAxOSAtNDV0LTE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImNoZWNrIiB1bmljb2RlPSImI3hmMDQ2OyIgaG9yaXotYWR2LXg9IjE2NjQiIA0KZD0iTTE0MDggNjA2di0zMThxMCAtMTE5IC04NC41IC0yMDMuNXQtMjAzLjUgLTg0LjVoLTgzMnEtMTE5IDAgLTIwMy41IDg0LjV0LTg0LjUgMjAzLjV2ODMycTAgMTE5IDg0LjUgMjAzLjV0MjAzLjUgODQuNWg4MzJxNjMgMCAxMTcgLTI1cTE1IC03IDE4IC0yM3EzIC0xNyAtOSAtMjlsLTQ5IC00OXEtMTAgLTEwIC0yMyAtMTBxLTMgMCAtOSAycS0yMyA2IC00NSA2aC04MzJxLTY2IDAgLTExMyAtNDd0LTQ3IC0xMTN2LTgzMg0KcTAgLTY2IDQ3IC0xMTN0MTEzIC00N2g4MzJxNjYgMCAxMTMgNDd0NDcgMTEzdjI1NHEwIDEzIDkgMjJsNjQgNjRxMTAgMTAgMjMgMTBxNiAwIDEyIC0zcTIwIC04IDIwIC0yOXpNMTYzOSAxMDk1bC04MTQgLTgxNHEtMjQgLTI0IC01NyAtMjR0LTU3IDI0bC00MzAgNDMwcS0yNCAyNCAtMjQgNTd0MjQgNTdsMTEwIDExMHEyNCAyNCA1NyAyNHQ1NyAtMjRsMjYzIC0yNjNsNjQ3IDY0N3EyNCAyNCA1NyAyNHQ1NyAtMjRsMTEwIC0xMTANCnEyNCAtMjQgMjQgLTU3dC0yNCAtNTd6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJtb3ZlIiB1bmljb2RlPSImI3hmMDQ3OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE3OTIgNjQwcTAgLTI2IC0xOSAtNDVsLTI1NiAtMjU2cS0xOSAtMTkgLTQ1IC0xOXQtNDUgMTl0LTE5IDQ1djEyOGgtMzg0di0zODRoMTI4cTI2IDAgNDUgLTE5dDE5IC00NXQtMTkgLTQ1bC0yNTYgLTI1NnEtMTkgLTE5IC00NSAtMTl0LTQ1IDE5bC0yNTYgMjU2cS0xOSAxOSAtMTkgNDV0MTkgNDV0NDUgMTloMTI4djM4NGgtMzg0di0xMjhxMCAtMjYgLTE5IC00NXQtNDUgLTE5dC00NSAxOWwtMjU2IDI1NnEtMTkgMTkgLTE5IDQ1DQp0MTkgNDVsMjU2IDI1NnExOSAxOSA0NSAxOXQ0NSAtMTl0MTkgLTQ1di0xMjhoMzg0djM4NGgtMTI4cS0yNiAwIC00NSAxOXQtMTkgNDV0MTkgNDVsMjU2IDI1NnExOSAxOSA0NSAxOXQ0NSAtMTlsMjU2IC0yNTZxMTkgLTE5IDE5IC00NXQtMTkgLTQ1dC00NSAtMTloLTEyOHYtMzg0aDM4NHYxMjhxMCAyNiAxOSA0NXQ0NSAxOXQ0NSAtMTlsMjU2IC0yNTZxMTkgLTE5IDE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InN0ZXBfYmFja3dhcmQiIHVuaWNvZGU9IiYjeGYwNDg7IiBob3Jpei1hZHYteD0iMTAyNCIgDQpkPSJNOTc5IDEzOTVxMTkgMTkgMzIgMTN0MTMgLTMydi0xNDcycTAgLTI2IC0xMyAtMzJ0LTMyIDEzbC03MTAgNzEwcS05IDkgLTEzIDE5di02NzhxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC0xMjhxLTI2IDAgLTQ1IDE5dC0xOSA0NXYxNDA4cTAgMjYgMTkgNDV0NDUgMTloMTI4cTI2IDAgNDUgLTE5dDE5IC00NXYtNjc4cTQgMTAgMTMgMTl6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJmYXN0X2JhY2t3YXJkIiB1bmljb2RlPSImI3hmMDQ5OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE3NDcgMTM5NXExOSAxOSAzMiAxM3QxMyAtMzJ2LTE0NzJxMCAtMjYgLTEzIC0zMnQtMzIgMTNsLTcxMCA3MTBxLTkgOSAtMTMgMTl2LTcxMHEwIC0yNiAtMTMgLTMydC0zMiAxM2wtNzEwIDcxMHEtOSA5IC0xMyAxOXYtNjc4cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTI4cS0yNiAwIC00NSAxOXQtMTkgNDV2MTQwOHEwIDI2IDE5IDQ1dDQ1IDE5aDEyOHEyNiAwIDQ1IC0xOXQxOSAtNDV2LTY3OHE0IDEwIDEzIDE5bDcxMCA3MTANCnExOSAxOSAzMiAxM3QxMyAtMzJ2LTcxMHE0IDEwIDEzIDE5eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iYmFja3dhcmQiIHVuaWNvZGU9IiYjeGYwNGE7IiBob3Jpei1hZHYteD0iMTY2NCIgDQpkPSJNMTYxOSAxMzk1cTE5IDE5IDMyIDEzdDEzIC0zMnYtMTQ3MnEwIC0yNiAtMTMgLTMydC0zMiAxM2wtNzEwIDcxMHEtOSA5IC0xMyAxOXYtNzEwcTAgLTI2IC0xMyAtMzJ0LTMyIDEzbC03MTAgNzEwcS0xOSAxOSAtMTkgNDV0MTkgNDVsNzEwIDcxMHExOSAxOSAzMiAxM3QxMyAtMzJ2LTcxMHE0IDEwIDEzIDE5eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0icGxheSIgdW5pY29kZT0iJiN4ZjA0YjsiIGhvcml6LWFkdi14PSIxNDA4IiANCmQ9Ik0xMzg0IDYwOWwtMTMyOCAtNzM4cS0yMyAtMTMgLTM5LjUgLTN0LTE2LjUgMzZ2MTQ3MnEwIDI2IDE2LjUgMzZ0MzkuNSAtM2wxMzI4IC03MzhxMjMgLTEzIDIzIC0zMXQtMjMgLTMxeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0icGF1c2UiIHVuaWNvZGU9IiYjeGYwNGM7IiANCmQ9Ik0xNTM2IDEzNDR2LTE0MDhxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC01MTJxLTI2IDAgLTQ1IDE5dC0xOSA0NXYxNDA4cTAgMjYgMTkgNDV0NDUgMTloNTEycTI2IDAgNDUgLTE5dDE5IC00NXpNNjQwIDEzNDR2LTE0MDhxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC01MTJxLTI2IDAgLTQ1IDE5dC0xOSA0NXYxNDA4cTAgMjYgMTkgNDV0NDUgMTloNTEycTI2IDAgNDUgLTE5dDE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InN0b3AiIHVuaWNvZGU9IiYjeGYwNGQ7IiANCmQ9Ik0xNTM2IDEzNDR2LTE0MDhxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC0xNDA4cS0yNiAwIC00NSAxOXQtMTkgNDV2MTQwOHEwIDI2IDE5IDQ1dDQ1IDE5aDE0MDhxMjYgMCA0NSAtMTl0MTkgLTQ1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZm9yd2FyZCIgdW5pY29kZT0iJiN4ZjA0ZTsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik00NSAtMTE1cS0xOSAtMTkgLTMyIC0xM3QtMTMgMzJ2MTQ3MnEwIDI2IDEzIDMydDMyIC0xM2w3MTAgLTcxMHE5IC05IDEzIC0xOXY3MTBxMCAyNiAxMyAzMnQzMiAtMTNsNzEwIC03MTBxMTkgLTE5IDE5IC00NXQtMTkgLTQ1bC03MTAgLTcxMHEtMTkgLTE5IC0zMiAtMTN0LTEzIDMydjcxMHEtNCAtMTAgLTEzIC0xOXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImZhc3RfZm9yd2FyZCIgdW5pY29kZT0iJiN4ZjA1MDsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik00NSAtMTE1cS0xOSAtMTkgLTMyIC0xM3QtMTMgMzJ2MTQ3MnEwIDI2IDEzIDMydDMyIC0xM2w3MTAgLTcxMHE5IC05IDEzIC0xOXY3MTBxMCAyNiAxMyAzMnQzMiAtMTNsNzEwIC03MTBxOSAtOSAxMyAtMTl2Njc4cTAgMjYgMTkgNDV0NDUgMTloMTI4cTI2IDAgNDUgLTE5dDE5IC00NXYtMTQwOHEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTEyOHEtMjYgMCAtNDUgMTl0LTE5IDQ1djY3OHEtNCAtMTAgLTEzIC0xOWwtNzEwIC03MTANCnEtMTkgLTE5IC0zMiAtMTN0LTEzIDMydjcxMHEtNCAtMTAgLTEzIC0xOXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InN0ZXBfZm9yd2FyZCIgdW5pY29kZT0iJiN4ZjA1MTsiIGhvcml6LWFkdi14PSIxMDI0IiANCmQ9Ik00NSAtMTE1cS0xOSAtMTkgLTMyIC0xM3QtMTMgMzJ2MTQ3MnEwIDI2IDEzIDMydDMyIC0xM2w3MTAgLTcxMHE5IC05IDEzIC0xOXY2NzhxMCAyNiAxOSA0NXQ0NSAxOWgxMjhxMjYgMCA0NSAtMTl0MTkgLTQ1di0xNDA4cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTI4cS0yNiAwIC00NSAxOXQtMTkgNDV2Njc4cS00IC0xMCAtMTMgLTE5eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZWplY3QiIHVuaWNvZGU9IiYjeGYwNTI7IiBob3Jpei1hZHYteD0iMTUzOCIgDQpkPSJNMTQgNTU3bDcxMCA3MTBxMTkgMTkgNDUgMTl0NDUgLTE5bDcxMCAtNzEwcTE5IC0xOSAxMyAtMzJ0LTMyIC0xM2gtMTQ3MnEtMjYgMCAtMzIgMTN0MTMgMzJ6TTE0NzMgMGgtMTQwOHEtMjYgMCAtNDUgMTl0LTE5IDQ1djI1NnEwIDI2IDE5IDQ1dDQ1IDE5aDE0MDhxMjYgMCA0NSAtMTl0MTkgLTQ1di0yNTZxMCAtMjYgLTE5IC00NXQtNDUgLTE5eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY2hldnJvbl9sZWZ0IiB1bmljb2RlPSImI3hmMDUzOyIgaG9yaXotYWR2LXg9IjEyODAiIA0KZD0iTTExNzEgMTIzNWwtNTMxIC01MzFsNTMxIC01MzFxMTkgLTE5IDE5IC00NXQtMTkgLTQ1bC0xNjYgLTE2NnEtMTkgLTE5IC00NSAtMTl0LTQ1IDE5bC03NDIgNzQycS0xOSAxOSAtMTkgNDV0MTkgNDVsNzQyIDc0MnExOSAxOSA0NSAxOXQ0NSAtMTlsMTY2IC0xNjZxMTkgLTE5IDE5IC00NXQtMTkgLTQ1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY2hldnJvbl9yaWdodCIgdW5pY29kZT0iJiN4ZjA1NDsiIGhvcml6LWFkdi14PSIxMjgwIiANCmQ9Ik0xMTA3IDY1OWwtNzQyIC03NDJxLTE5IC0xOSAtNDUgLTE5dC00NSAxOWwtMTY2IDE2NnEtMTkgMTkgLTE5IDQ1dDE5IDQ1bDUzMSA1MzFsLTUzMSA1MzFxLTE5IDE5IC0xOSA0NXQxOSA0NWwxNjYgMTY2cTE5IDE5IDQ1IDE5dDQ1IC0xOWw3NDIgLTc0MnExOSAtMTkgMTkgLTQ1dC0xOSAtNDV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJwbHVzX3NpZ24iIHVuaWNvZGU9IiYjeGYwNTU7IiANCmQ9Ik0xMjE2IDU3NnYxMjhxMCAyNiAtMTkgNDV0LTQ1IDE5aC0yNTZ2MjU2cTAgMjYgLTE5IDQ1dC00NSAxOWgtMTI4cS0yNiAwIC00NSAtMTl0LTE5IC00NXYtMjU2aC0yNTZxLTI2IDAgLTQ1IC0xOXQtMTkgLTQ1di0xMjhxMCAtMjYgMTkgLTQ1dDQ1IC0xOWgyNTZ2LTI1NnEwIC0yNiAxOSAtNDV0NDUgLTE5aDEyOHEyNiAwIDQ1IDE5dDE5IDQ1djI1NmgyNTZxMjYgMCA0NSAxOXQxOSA0NXpNMTUzNiA2NDBxMCAtMjA5IC0xMDMgLTM4NS41DQp0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzdC0yNzkuNSAyNzkuNXQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN0Mzg1LjUgLTEwM3QyNzkuNSAtMjc5LjV0MTAzIC0zODUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Im1pbnVzX3NpZ24iIHVuaWNvZGU9IiYjeGYwNTY7IiANCmQ9Ik0xMjE2IDU3NnYxMjhxMCAyNiAtMTkgNDV0LTQ1IDE5aC03NjhxLTI2IDAgLTQ1IC0xOXQtMTkgLTQ1di0xMjhxMCAtMjYgMTkgLTQ1dDQ1IC0xOWg3NjhxMjYgMCA0NSAxOXQxOSA0NXpNMTUzNiA2NDBxMCAtMjA5IC0xMDMgLTM4NS41dC0yNzkuNSAtMjc5LjV0LTM4NS41IC0xMDN0LTM4NS41IDEwM3QtMjc5LjUgMjc5LjV0LTEwMyAzODUuNXQxMDMgMzg1LjV0Mjc5LjUgMjc5LjV0Mzg1LjUgMTAzdDM4NS41IC0xMDN0Mjc5LjUgLTI3OS41DQp0MTAzIC0zODUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InJlbW92ZV9zaWduIiB1bmljb2RlPSImI3hmMDU3OyIgDQpkPSJNMTE0OSA0MTRxMCAyNiAtMTkgNDVsLTE4MSAxODFsMTgxIDE4MXExOSAxOSAxOSA0NXEwIDI3IC0xOSA0NmwtOTAgOTBxLTE5IDE5IC00NiAxOXEtMjYgMCAtNDUgLTE5bC0xODEgLTE4MWwtMTgxIDE4MXEtMTkgMTkgLTQ1IDE5cS0yNyAwIC00NiAtMTlsLTkwIC05MHEtMTkgLTE5IC0xOSAtNDZxMCAtMjYgMTkgLTQ1bDE4MSAtMTgxbC0xODEgLTE4MXEtMTkgLTE5IC0xOSAtNDVxMCAtMjcgMTkgLTQ2bDkwIC05MHExOSAtMTkgNDYgLTE5DQpxMjYgMCA0NSAxOWwxODEgMTgxbDE4MSAtMTgxcTE5IC0xOSA0NSAtMTlxMjcgMCA0NiAxOWw5MCA5MHExOSAxOSAxOSA0NnpNMTUzNiA2NDBxMCAtMjA5IC0xMDMgLTM4NS41dC0yNzkuNSAtMjc5LjV0LTM4NS41IC0xMDN0LTM4NS41IDEwM3QtMjc5LjUgMjc5LjV0LTEwMyAzODUuNXQxMDMgMzg1LjV0Mjc5LjUgMjc5LjV0Mzg1LjUgMTAzdDM4NS41IC0xMDN0Mjc5LjUgLTI3OS41dDEwMyAtMzg1LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJva19zaWduIiB1bmljb2RlPSImI3hmMDU4OyIgDQpkPSJNMTI4NCA4MDJxMCAyOCAtMTggNDZsLTkxIDkwcS0xOSAxOSAtNDUgMTl0LTQ1IC0xOWwtNDA4IC00MDdsLTIyNiAyMjZxLTE5IDE5IC00NSAxOXQtNDUgLTE5bC05MSAtOTBxLTE4IC0xOCAtMTggLTQ2cTAgLTI3IDE4IC00NWwzNjIgLTM2MnExOSAtMTkgNDUgLTE5cTI3IDAgNDYgMTlsNTQzIDU0M3ExOCAxOCAxOCA0NXpNMTUzNiA2NDBxMCAtMjA5IC0xMDMgLTM4NS41dC0yNzkuNSAtMjc5LjV0LTM4NS41IC0xMDN0LTM4NS41IDEwMw0KdC0yNzkuNSAyNzkuNXQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN0Mzg1LjUgLTEwM3QyNzkuNSAtMjc5LjV0MTAzIC0zODUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InF1ZXN0aW9uX3NpZ24iIHVuaWNvZGU9IiYjeGYwNTk7IiANCmQ9Ik04OTYgMTYwdjE5MnEwIDE0IC05IDIzdC0yMyA5aC0xOTJxLTE0IDAgLTIzIC05dC05IC0yM3YtMTkycTAgLTE0IDkgLTIzdDIzIC05aDE5MnExNCAwIDIzIDl0OSAyM3pNMTE1MiA4MzJxMCA4OCAtNTUuNSAxNjN0LTEzOC41IDExNnQtMTcwIDQxcS0yNDMgMCAtMzcxIC0yMTNxLTE1IC0yNCA4IC00MmwxMzIgLTEwMHE3IC02IDE5IC02cTE2IDAgMjUgMTJxNTMgNjggODYgOTJxMzQgMjQgODYgMjRxNDggMCA4NS41IC0yNnQzNy41IC01OQ0KcTAgLTM4IC0yMCAtNjF0LTY4IC00NXEtNjMgLTI4IC0xMTUuNSAtODYuNXQtNTIuNSAtMTI1LjV2LTM2cTAgLTE0IDkgLTIzdDIzIC05aDE5MnExNCAwIDIzIDl0OSAyM3EwIDE5IDIxLjUgNDkuNXQ1NC41IDQ5LjVxMzIgMTggNDkgMjguNXQ0NiAzNXQ0NC41IDQ4dDI4IDYwLjV0MTIuNSA4MXpNMTUzNiA2NDBxMCAtMjA5IC0xMDMgLTM4NS41dC0yNzkuNSAtMjc5LjV0LTM4NS41IC0xMDN0LTM4NS41IDEwM3QtMjc5LjUgMjc5LjUNCnQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN0Mzg1LjUgLTEwM3QyNzkuNSAtMjc5LjV0MTAzIC0zODUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImluZm9fc2lnbiIgdW5pY29kZT0iJiN4ZjA1YTsiIA0KZD0iTTEwMjQgMTYwdjE2MHEwIDE0IC05IDIzdC0yMyA5aC05NnY1MTJxMCAxNCAtOSAyM3QtMjMgOWgtMzIwcS0xNCAwIC0yMyAtOXQtOSAtMjN2LTE2MHEwIC0xNCA5IC0yM3QyMyAtOWg5NnYtMzIwaC05NnEtMTQgMCAtMjMgLTl0LTkgLTIzdi0xNjBxMCAtMTQgOSAtMjN0MjMgLTloNDQ4cTE0IDAgMjMgOXQ5IDIzek04OTYgMTA1NnYxNjBxMCAxNCAtOSAyM3QtMjMgOWgtMTkycS0xNCAwIC0yMyAtOXQtOSAtMjN2LTE2MHEwIC0xNCA5IC0yMw0KdDIzIC05aDE5MnExNCAwIDIzIDl0OSAyM3pNMTUzNiA2NDBxMCAtMjA5IC0xMDMgLTM4NS41dC0yNzkuNSAtMjc5LjV0LTM4NS41IC0xMDN0LTM4NS41IDEwM3QtMjc5LjUgMjc5LjV0LTEwMyAzODUuNXQxMDMgMzg1LjV0Mjc5LjUgMjc5LjV0Mzg1LjUgMTAzdDM4NS41IC0xMDN0Mjc5LjUgLTI3OS41dDEwMyAtMzg1LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJzY3JlZW5zaG90IiB1bmljb2RlPSImI3hmMDViOyIgDQpkPSJNMTE5NyA1MTJoLTEwOXEtMjYgMCAtNDUgMTl0LTE5IDQ1djEyOHEwIDI2IDE5IDQ1dDQ1IDE5aDEwOXEtMzIgMTA4IC0xMTIuNSAxODguNXQtMTg4LjUgMTEyLjV2LTEwOXEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTEyOHEtMjYgMCAtNDUgMTl0LTE5IDQ1djEwOXEtMTA4IC0zMiAtMTg4LjUgLTExMi41dC0xMTIuNSAtMTg4LjVoMTA5cTI2IDAgNDUgLTE5dDE5IC00NXYtMTI4cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTA5DQpxMzIgLTEwOCAxMTIuNSAtMTg4LjV0MTg4LjUgLTExMi41djEwOXEwIDI2IDE5IDQ1dDQ1IDE5aDEyOHEyNiAwIDQ1IC0xOXQxOSAtNDV2LTEwOXExMDggMzIgMTg4LjUgMTEyLjV0MTEyLjUgMTg4LjV6TTE1MzYgNzA0di0xMjhxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC0xNDNxLTM3IC0xNjEgLTE1NC41IC0yNzguNXQtMjc4LjUgLTE1NC41di0xNDNxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC0xMjhxLTI2IDAgLTQ1IDE5dC0xOSA0NXYxNDMNCnEtMTYxIDM3IC0yNzguNSAxNTQuNXQtMTU0LjUgMjc4LjVoLTE0M3EtMjYgMCAtNDUgMTl0LTE5IDQ1djEyOHEwIDI2IDE5IDQ1dDQ1IDE5aDE0M3EzNyAxNjEgMTU0LjUgMjc4LjV0Mjc4LjUgMTU0LjV2MTQzcTAgMjYgMTkgNDV0NDUgMTloMTI4cTI2IDAgNDUgLTE5dDE5IC00NXYtMTQzcTE2MSAtMzcgMjc4LjUgLTE1NC41dDE1NC41IC0yNzguNWgxNDNxMjYgMCA0NSAtMTl0MTkgLTQ1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0icmVtb3ZlX2NpcmNsZSIgdW5pY29kZT0iJiN4ZjA1YzsiIA0KZD0iTTEwOTcgNDU3bC0xNDYgLTE0NnEtMTAgLTEwIC0yMyAtMTB0LTIzIDEwbC0xMzcgMTM3bC0xMzcgLTEzN3EtMTAgLTEwIC0yMyAtMTB0LTIzIDEwbC0xNDYgMTQ2cS0xMCAxMCAtMTAgMjN0MTAgMjNsMTM3IDEzN2wtMTM3IDEzN3EtMTAgMTAgLTEwIDIzdDEwIDIzbDE0NiAxNDZxMTAgMTAgMjMgMTB0MjMgLTEwbDEzNyAtMTM3bDEzNyAxMzdxMTAgMTAgMjMgMTB0MjMgLTEwbDE0NiAtMTQ2cTEwIC0xMCAxMCAtMjN0LTEwIC0yMw0KbC0xMzcgLTEzN2wxMzcgLTEzN3ExMCAtMTAgMTAgLTIzdC0xMCAtMjN6TTEzMTIgNjQwcTAgMTQ4IC03MyAyNzN0LTE5OCAxOTh0LTI3MyA3M3QtMjczIC03M3QtMTk4IC0xOTh0LTczIC0yNzN0NzMgLTI3M3QxOTggLTE5OHQyNzMgLTczdDI3MyA3M3QxOTggMTk4dDczIDI3M3pNMTUzNiA2NDBxMCAtMjA5IC0xMDMgLTM4NS41dC0yNzkuNSAtMjc5LjV0LTM4NS41IC0xMDN0LTM4NS41IDEwM3QtMjc5LjUgMjc5LjV0LTEwMyAzODUuNQ0KdDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN0Mzg1LjUgLTEwM3QyNzkuNSAtMjc5LjV0MTAzIC0zODUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Im9rX2NpcmNsZSIgdW5pY29kZT0iJiN4ZjA1ZDsiIA0KZD0iTTExNzEgNzIzbC00MjIgLTQyMnEtMTkgLTE5IC00NSAtMTl0LTQ1IDE5bC0yOTQgMjk0cS0xOSAxOSAtMTkgNDV0MTkgNDVsMTAyIDEwMnExOSAxOSA0NSAxOXQ0NSAtMTlsMTQ3IC0xNDdsMjc1IDI3NXExOSAxOSA0NSAxOXQ0NSAtMTlsMTAyIC0xMDJxMTkgLTE5IDE5IC00NXQtMTkgLTQ1ek0xMzEyIDY0MHEwIDE0OCAtNzMgMjczdC0xOTggMTk4dC0yNzMgNzN0LTI3MyAtNzN0LTE5OCAtMTk4dC03MyAtMjczdDczIC0yNzN0MTk4IC0xOTgNCnQyNzMgLTczdDI3MyA3M3QxOTggMTk4dDczIDI3M3pNMTUzNiA2NDBxMCAtMjA5IC0xMDMgLTM4NS41dC0yNzkuNSAtMjc5LjV0LTM4NS41IC0xMDN0LTM4NS41IDEwM3QtMjc5LjUgMjc5LjV0LTEwMyAzODUuNXQxMDMgMzg1LjV0Mjc5LjUgMjc5LjV0Mzg1LjUgMTAzdDM4NS41IC0xMDN0Mjc5LjUgLTI3OS41dDEwMyAtMzg1LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJiYW5fY2lyY2xlIiB1bmljb2RlPSImI3hmMDVlOyIgDQpkPSJNMTMxMiA2NDNxMCAxNjEgLTg3IDI5NWwtNzU0IC03NTNxMTM3IC04OSAyOTcgLTg5cTExMSAwIDIxMS41IDQzLjV0MTczLjUgMTE2LjV0MTE2IDE3NC41dDQzIDIxMi41ek0zMTMgMzQ0bDc1NSA3NTRxLTEzNSA5MSAtMzAwIDkxcS0xNDggMCAtMjczIC03M3QtMTk4IC0xOTl0LTczIC0yNzRxMCAtMTYyIDg5IC0yOTl6TTE1MzYgNjQzcTAgLTE1NyAtNjEgLTMwMHQtMTYzLjUgLTI0NnQtMjQ1IC0xNjR0LTI5OC41IC02MXQtMjk4LjUgNjENCnQtMjQ1IDE2NHQtMTYzLjUgMjQ2dC02MSAzMDB0NjEgMjk5LjV0MTYzLjUgMjQ1LjV0MjQ1IDE2NHQyOTguNSA2MXQyOTguNSAtNjF0MjQ1IC0xNjR0MTYzLjUgLTI0NS41dDYxIC0yOTkuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImFycm93X2xlZnQiIHVuaWNvZGU9IiYjeGYwNjA7IiANCmQ9Ik0xNTM2IDY0MHYtMTI4cTAgLTUzIC0zMi41IC05MC41dC04NC41IC0zNy41aC03MDRsMjkzIC0yOTRxMzggLTM2IDM4IC05MHQtMzggLTkwbC03NSAtNzZxLTM3IC0zNyAtOTAgLTM3cS01MiAwIC05MSAzN2wtNjUxIDY1MnEtMzcgMzcgLTM3IDkwcTAgNTIgMzcgOTFsNjUxIDY1MHEzOCAzOCA5MSAzOHE1MiAwIDkwIC0zOGw3NSAtNzRxMzggLTM4IDM4IC05MXQtMzggLTkxbC0yOTMgLTI5M2g3MDRxNTIgMCA4NC41IC0zNy41DQp0MzIuNSAtOTAuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImFycm93X3JpZ2h0IiB1bmljb2RlPSImI3hmMDYxOyIgDQpkPSJNMTQ3MiA1NzZxMCAtNTQgLTM3IC05MWwtNjUxIC02NTFxLTM5IC0zNyAtOTEgLTM3cS01MSAwIC05MCAzN2wtNzUgNzVxLTM4IDM4IC0zOCA5MXQzOCA5MWwyOTMgMjkzaC03MDRxLTUyIDAgLTg0LjUgMzcuNXQtMzIuNSA5MC41djEyOHEwIDUzIDMyLjUgOTAuNXQ4NC41IDM3LjVoNzA0bC0yOTMgMjk0cS0zOCAzNiAtMzggOTB0MzggOTBsNzUgNzVxMzggMzggOTAgMzhxNTMgMCA5MSAtMzhsNjUxIC02NTFxMzcgLTM1IDM3IC05MHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImFycm93X3VwIiB1bmljb2RlPSImI3hmMDYyOyIgaG9yaXotYWR2LXg9IjE2NjQiIA0KZD0iTTE2MTEgNTY1cTAgLTUxIC0zNyAtOTBsLTc1IC03NXEtMzggLTM4IC05MSAtMzhxLTU0IDAgLTkwIDM4bC0yOTQgMjkzdi03MDRxMCAtNTIgLTM3LjUgLTg0LjV0LTkwLjUgLTMyLjVoLTEyOHEtNTMgMCAtOTAuNSAzMi41dC0zNy41IDg0LjV2NzA0bC0yOTQgLTI5M3EtMzYgLTM4IC05MCAtMzh0LTkwIDM4bC03NSA3NXEtMzggMzggLTM4IDkwcTAgNTMgMzggOTFsNjUxIDY1MXEzNSAzNyA5MCAzN3E1NCAwIDkxIC0zN2w2NTEgLTY1MQ0KcTM3IC0zOSAzNyAtOTF6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJhcnJvd19kb3duIiB1bmljb2RlPSImI3hmMDYzOyIgaG9yaXotYWR2LXg9IjE2NjQiIA0KZD0iTTE2MTEgNzA0cTAgLTUzIC0zNyAtOTBsLTY1MSAtNjUycS0zOSAtMzcgLTkxIC0zN3EtNTMgMCAtOTAgMzdsLTY1MSA2NTJxLTM4IDM2IC0zOCA5MHEwIDUzIDM4IDkxbDc0IDc1cTM5IDM3IDkxIDM3cTUzIDAgOTAgLTM3bDI5NCAtMjk0djcwNHEwIDUyIDM4IDkwdDkwIDM4aDEyOHE1MiAwIDkwIC0zOHQzOCAtOTB2LTcwNGwyOTQgMjk0cTM3IDM3IDkwIDM3cTUyIDAgOTEgLTM3bDc1IC03NXEzNyAtMzkgMzcgLTkxeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ic2hhcmVfYWx0IiB1bmljb2RlPSImI3hmMDY0OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE3OTIgODk2cTAgLTI2IC0xOSAtNDVsLTUxMiAtNTEycS0xOSAtMTkgLTQ1IC0xOXQtNDUgMTl0LTE5IDQ1djI1NmgtMjI0cS05OCAwIC0xNzUuNSAtNnQtMTU0IC0yMS41dC0xMzMgLTQyLjV0LTEwNS41IC02OS41dC04MCAtMTAxdC00OC41IC0xMzguNXQtMTcuNSAtMTgxcTAgLTU1IDUgLTEyM3EwIC02IDIuNSAtMjMuNXQyLjUgLTI2LjVxMCAtMTUgLTguNSAtMjV0LTIzLjUgLTEwcS0xNiAwIC0yOCAxN3EtNyA5IC0xMyAyMg0KdC0xMy41IDMwdC0xMC41IDI0cS0xMjcgMjg1IC0xMjcgNDUxcTAgMTk5IDUzIDMzM3ExNjIgNDAzIDg3NSA0MDNoMjI0djI1NnEwIDI2IDE5IDQ1dDQ1IDE5dDQ1IC0xOWw1MTIgLTUxMnExOSAtMTkgMTkgLTQ1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0icmVzaXplX2Z1bGwiIHVuaWNvZGU9IiYjeGYwNjU7IiANCmQ9Ik03NTUgNDgwcTAgLTEzIC0xMCAtMjNsLTMzMiAtMzMybDE0NCAtMTQ0cTE5IC0xOSAxOSAtNDV0LTE5IC00NXQtNDUgLTE5aC00NDhxLTI2IDAgLTQ1IDE5dC0xOSA0NXY0NDhxMCAyNiAxOSA0NXQ0NSAxOXQ0NSAtMTlsMTQ0IC0xNDRsMzMyIDMzMnExMCAxMCAyMyAxMHQyMyAtMTBsMTE0IC0xMTRxMTAgLTEwIDEwIC0yM3pNMTUzNiAxMzQ0di00NDhxMCAtMjYgLTE5IC00NXQtNDUgLTE5dC00NSAxOWwtMTQ0IDE0NGwtMzMyIC0zMzINCnEtMTAgLTEwIC0yMyAtMTB0LTIzIDEwbC0xMTQgMTE0cS0xMCAxMCAtMTAgMjN0MTAgMjNsMzMyIDMzMmwtMTQ0IDE0NHEtMTkgMTkgLTE5IDQ1dDE5IDQ1dDQ1IDE5aDQ0OHEyNiAwIDQ1IC0xOXQxOSAtNDV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJyZXNpemVfc21hbGwiIHVuaWNvZGU9IiYjeGYwNjY7IiANCmQ9Ik03NjggNTc2di00NDhxMCAtMjYgLTE5IC00NXQtNDUgLTE5dC00NSAxOWwtMTQ0IDE0NGwtMzMyIC0zMzJxLTEwIC0xMCAtMjMgLTEwdC0yMyAxMGwtMTE0IDExNHEtMTAgMTAgLTEwIDIzdDEwIDIzbDMzMiAzMzJsLTE0NCAxNDRxLTE5IDE5IC0xOSA0NXQxOSA0NXQ0NSAxOWg0NDhxMjYgMCA0NSAtMTl0MTkgLTQ1ek0xNTIzIDEyNDhxMCAtMTMgLTEwIC0yM2wtMzMyIC0zMzJsMTQ0IC0xNDRxMTkgLTE5IDE5IC00NXQtMTkgLTQ1DQp0LTQ1IC0xOWgtNDQ4cS0yNiAwIC00NSAxOXQtMTkgNDV2NDQ4cTAgMjYgMTkgNDV0NDUgMTl0NDUgLTE5bDE0NCAtMTQ0bDMzMiAzMzJxMTAgMTAgMjMgMTB0MjMgLTEwbDExNCAtMTE0cTEwIC0xMCAxMCAtMjN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJwbHVzIiB1bmljb2RlPSImI3hmMDY3OyIgaG9yaXotYWR2LXg9IjE0MDgiIA0KZD0iTTE0MDggODAwdi0xOTJxMCAtNDAgLTI4IC02OHQtNjggLTI4aC00MTZ2LTQxNnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTE5MnEtNDAgMCAtNjggMjh0LTI4IDY4djQxNmgtNDE2cS00MCAwIC02OCAyOHQtMjggNjh2MTkycTAgNDAgMjggNjh0NjggMjhoNDE2djQxNnEwIDQwIDI4IDY4dDY4IDI4aDE5MnE0MCAwIDY4IC0yOHQyOCAtNjh2LTQxNmg0MTZxNDAgMCA2OCAtMjh0MjggLTY4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ibWludXMiIHVuaWNvZGU9IiYjeGYwNjg7IiBob3Jpei1hZHYteD0iMTQwOCIgDQpkPSJNMTQwOCA4MDB2LTE5MnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTEyMTZxLTQwIDAgLTY4IDI4dC0yOCA2OHYxOTJxMCA0MCAyOCA2OHQ2OCAyOGgxMjE2cTQwIDAgNjggLTI4dDI4IC02OHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImFzdGVyaXNrIiB1bmljb2RlPSImI3hmMDY5OyIgaG9yaXotYWR2LXg9IjE2NjQiIA0KZD0iTTE0ODIgNDg2cTQ2IC0yNiA1OS41IC03Ny41dC0xMi41IC05Ny41bC02NCAtMTEwcS0yNiAtNDYgLTc3LjUgLTU5LjV0LTk3LjUgMTIuNWwtMjY2IDE1M3YtMzA3cTAgLTUyIC0zOCAtOTB0LTkwIC0zOGgtMTI4cS01MiAwIC05MCAzOHQtMzggOTB2MzA3bC0yNjYgLTE1M3EtNDYgLTI2IC05Ny41IC0xMi41dC03Ny41IDU5LjVsLTY0IDExMHEtMjYgNDYgLTEyLjUgOTcuNXQ1OS41IDc3LjVsMjY2IDE1NGwtMjY2IDE1NA0KcS00NiAyNiAtNTkuNSA3Ny41dDEyLjUgOTcuNWw2NCAxMTBxMjYgNDYgNzcuNSA1OS41dDk3LjUgLTEyLjVsMjY2IC0xNTN2MzA3cTAgNTIgMzggOTB0OTAgMzhoMTI4cTUyIDAgOTAgLTM4dDM4IC05MHYtMzA3bDI2NiAxNTNxNDYgMjYgOTcuNSAxMi41dDc3LjUgLTU5LjVsNjQgLTExMHEyNiAtNDYgMTIuNSAtOTcuNXQtNTkuNSAtNzcuNWwtMjY2IC0xNTR6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJleGNsYW1hdGlvbl9zaWduIiB1bmljb2RlPSImI3hmMDZhOyIgDQpkPSJNNzY4IDE0MDhxMjA5IDAgMzg1LjUgLTEwM3QyNzkuNSAtMjc5LjV0MTAzIC0zODUuNXQtMTAzIC0zODUuNXQtMjc5LjUgLTI3OS41dC0zODUuNSAtMTAzdC0zODUuNSAxMDN0LTI3OS41IDI3OS41dC0xMDMgMzg1LjV0MTAzIDM4NS41dDI3OS41IDI3OS41dDM4NS41IDEwM3pNODk2IDE2MXYxOTBxMCAxNCAtOSAyMy41dC0yMiA5LjVoLTE5MnEtMTMgMCAtMjMgLTEwdC0xMCAtMjN2LTE5MHEwIC0xMyAxMCAtMjN0MjMgLTEwaDE5Mg0KcTEzIDAgMjIgOS41dDkgMjMuNXpNODk0IDUwNWwxOCA2MjFxMCAxMiAtMTAgMThxLTEwIDggLTI0IDhoLTIyMHEtMTQgMCAtMjQgLThxLTEwIC02IC0xMCAtMThsMTcgLTYyMXEwIC0xMCAxMCAtMTcuNXQyNCAtNy41aDE4NXExNCAwIDIzLjUgNy41dDEwLjUgMTcuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImdpZnQiIHVuaWNvZGU9IiYjeGYwNmI7IiANCmQ9Ik05MjggMTgwdjU2djQ2OHYxOTJoLTMyMHYtMTkydi00Njh2LTU2cTAgLTI1IDE4IC0zOC41dDQ2IC0xMy41aDE5MnEyOCAwIDQ2IDEzLjV0MTggMzguNXpNNDcyIDEwMjRoMTk1bC0xMjYgMTYxcS0yNiAzMSAtNjkgMzFxLTQwIDAgLTY4IC0yOHQtMjggLTY4dDI4IC02OHQ2OCAtMjh6TTExNjAgMTEyMHEwIDQwIC0yOCA2OHQtNjggMjhxLTQzIDAgLTY5IC0zMWwtMTI1IC0xNjFoMTk0cTQwIDAgNjggMjh0MjggNjh6TTE1MzYgODY0di0zMjANCnEwIC0xNCAtOSAtMjN0LTIzIC05aC05NnYtNDE2cTAgLTQwIC0yOCAtNjh0LTY4IC0yOGgtMTA4OHEtNDAgMCAtNjggMjh0LTI4IDY4djQxNmgtOTZxLTE0IDAgLTIzIDl0LTkgMjN2MzIwcTAgMTQgOSAyM3QyMyA5aDQ0MHEtOTMgMCAtMTU4LjUgNjUuNXQtNjUuNSAxNTguNXQ2NS41IDE1OC41dDE1OC41IDY1LjVxMTA3IDAgMTY4IC03N2wxMjggLTE2NWwxMjggMTY1cTYxIDc3IDE2OCA3N3E5MyAwIDE1OC41IC02NS41dDY1LjUgLTE1OC41DQp0LTY1LjUgLTE1OC41dC0xNTguNSAtNjUuNWg0NDBxMTQgMCAyMyAtOXQ5IC0yM3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImxlYWYiIHVuaWNvZGU9IiYjeGYwNmM7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTI4MCA4MzJxMCAyNiAtMTkgNDV0LTQ1IDE5cS0xNzIgMCAtMzE4IC00OS41dC0yNTkuNSAtMTM0dC0yMzUuNSAtMjE5LjVxLTE5IC0yMSAtMTkgLTQ1cTAgLTI2IDE5IC00NXQ0NSAtMTlxMjQgMCA0NSAxOXEyNyAyNCA3NCA3MXQ2NyA2NnExMzcgMTI0IDI2OC41IDE3NnQzMTMuNSA1MnEyNiAwIDQ1IDE5dDE5IDQ1ek0xNzkyIDEwMzBxMCAtOTUgLTIwIC0xOTNxLTQ2IC0yMjQgLTE4NC41IC0zODN0LTM1Ny41IC0yNjgNCnEtMjE0IC0xMDggLTQzOCAtMTA4cS0xNDggMCAtMjg2IDQ3cS0xNSA1IC04OCA0MnQtOTYgMzdxLTE2IDAgLTM5LjUgLTMydC00NSAtNzB0LTUyLjUgLTcwdC02MCAtMzJxLTQzIDAgLTYzLjUgMTcuNXQtNDUuNSA1OS41cS0yIDQgLTYgMTF0LTUuNSAxMHQtMyA5LjV0LTEuNSAxMy41cTAgMzUgMzEgNzMuNXQ2OCA2NS41dDY4IDU2dDMxIDQ4cTAgNCAtMTQgMzh0LTE2IDQ0cS05IDUxIC05IDEwNHEwIDExNSA0My41IDIyMHQxMTkgMTg0LjUNCnQxNzAuNSAxMzl0MjA0IDk1LjVxNTUgMTggMTQ1IDI1LjV0MTc5LjUgOXQxNzguNSA2dDE2My41IDI0dDExMy41IDU2LjVsMjkuNSAyOS41dDI5LjUgMjh0MjcgMjB0MzYuNSAxNnQ0My41IDQuNXEzOSAwIDcwLjUgLTQ2dDQ3LjUgLTExMnQyNCAtMTI0dDggLTk2eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZmlyZSIgdW5pY29kZT0iJiN4ZjA2ZDsiIGhvcml6LWFkdi14PSIxNDA4IiANCmQ9Ik0xNDA4IC0xNjB2LTY0cTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtMTM0NHEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djY0cTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoMTM0NHExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6TTExNTIgODk2cTAgLTc4IC0yNC41IC0xNDR0LTY0IC0xMTIuNXQtODcuNSAtODh0LTk2IC03Ny41dC04Ny41IC03MnQtNjQgLTgxLjV0LTI0LjUgLTk2LjVxMCAtOTYgNjcgLTIyNGwtNCAxbDEgLTENCnEtOTAgNDEgLTE2MCA4M3QtMTM4LjUgMTAwdC0xMTMuNSAxMjIuNXQtNzIuNSAxNTAuNXQtMjcuNSAxODRxMCA3OCAyNC41IDE0NHQ2NCAxMTIuNXQ4Ny41IDg4dDk2IDc3LjV0ODcuNSA3MnQ2NCA4MS41dDI0LjUgOTYuNXEwIDk0IC02NiAyMjRsMyAtMWwtMSAxcTkwIC00MSAxNjAgLTgzdDEzOC41IC0xMDB0MTEzLjUgLTEyMi41dDcyLjUgLTE1MC41dDI3LjUgLTE4NHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImV5ZV9vcGVuIiB1bmljb2RlPSImI3hmMDZlOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE2NjQgNTc2cS0xNTIgMjM2IC0zODEgMzUzcTYxIC0xMDQgNjEgLTIyNXEwIC0xODUgLTEzMS41IC0zMTYuNXQtMzE2LjUgLTEzMS41dC0zMTYuNSAxMzEuNXQtMTMxLjUgMzE2LjVxMCAxMjEgNjEgMjI1cS0yMjkgLTExNyAtMzgxIC0zNTNxMTMzIC0yMDUgMzMzLjUgLTMyNi41dDQzNC41IC0xMjEuNXQ0MzQuNSAxMjEuNXQzMzMuNSAzMjYuNXpNOTQ0IDk2MHEwIDIwIC0xNCAzNHQtMzQgMTRxLTEyNSAwIC0yMTQuNSAtODkuNQ0KdC04OS41IC0yMTQuNXEwIC0yMCAxNCAtMzR0MzQgLTE0dDM0IDE0dDE0IDM0cTAgODYgNjEgMTQ3dDE0NyA2MXEyMCAwIDM0IDE0dDE0IDM0ek0xNzkyIDU3NnEwIC0zNCAtMjAgLTY5cS0xNDAgLTIzMCAtMzc2LjUgLTM2OC41dC00OTkuNSAtMTM4LjV0LTQ5OS41IDEzOXQtMzc2LjUgMzY4cS0yMCAzNSAtMjAgNjl0MjAgNjlxMTQwIDIyOSAzNzYuNSAzNjh0NDk5LjUgMTM5dDQ5OS41IC0xMzl0Mzc2LjUgLTM2OHEyMCAtMzUgMjAgLTY5eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZXllX2Nsb3NlIiB1bmljb2RlPSImI3hmMDcwOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTU1NSAyMDFsNzggMTQxcS04NyA2MyAtMTM2IDE1OXQtNDkgMjAzcTAgMTIxIDYxIDIyNXEtMjI5IC0xMTcgLTM4MSAtMzUzcTE2NyAtMjU4IDQyNyAtMzc1ek05NDQgOTYwcTAgMjAgLTE0IDM0dC0zNCAxNHEtMTI1IDAgLTIxNC41IC04OS41dC04OS41IC0yMTQuNXEwIC0yMCAxNCAtMzR0MzQgLTE0dDM0IDE0dDE0IDM0cTAgODYgNjEgMTQ3dDE0NyA2MXEyMCAwIDM0IDE0dDE0IDM0ek0xMzA3IDExNTFxMCAtNyAtMSAtOQ0KcS0xMDYgLTE4OSAtMzE2IC01Njd0LTMxNSAtNTY2bC00OSAtODlxLTEwIC0xNiAtMjggLTE2cS0xMiAwIC0xMzQgNzBxLTE2IDEwIC0xNiAyOHEwIDEyIDQ0IDg3cS0xNDMgNjUgLTI2My41IDE3M3QtMjA4LjUgMjQ1cS0yMCAzMSAtMjAgNjl0MjAgNjlxMTUzIDIzNSAzODAgMzcxdDQ5NiAxMzZxODkgMCAxODAgLTE3bDU0IDk3cTEwIDE2IDI4IDE2cTUgMCAxOCAtNnQzMSAtMTUuNXQzMyAtMTguNXQzMS41IC0xOC41dDE5LjUgLTExLjUNCnExNiAtMTAgMTYgLTI3ek0xMzQ0IDcwNHEwIC0xMzkgLTc5IC0yNTMuNXQtMjA5IC0xNjQuNWwyODAgNTAycTggLTQ1IDggLTg0ek0xNzkyIDU3NnEwIC0zNSAtMjAgLTY5cS0zOSAtNjQgLTEwOSAtMTQ1cS0xNTAgLTE3MiAtMzQ3LjUgLTI2N3QtNDE5LjUgLTk1bDc0IDEzMnEyMTIgMTggMzkyLjUgMTM3dDMwMS41IDMwN3EtMTE1IDE3OSAtMjgyIDI5NGw2MyAxMTJxOTUgLTY0IDE4Mi41IC0xNTN0MTQ0LjUgLTE4NHEyMCAtMzQgMjAgLTY5eg0KIiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ3YXJuaW5nX3NpZ24iIHVuaWNvZGU9IiYjeGYwNzE7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTAyNCAxNjF2MTkwcTAgMTQgLTkuNSAyMy41dC0yMi41IDkuNWgtMTkycS0xMyAwIC0yMi41IC05LjV0LTkuNSAtMjMuNXYtMTkwcTAgLTE0IDkuNSAtMjMuNXQyMi41IC05LjVoMTkycTEzIDAgMjIuNSA5LjV0OS41IDIzLjV6TTEwMjIgNTM1bDE4IDQ1OXEwIDEyIC0xMCAxOXEtMTMgMTEgLTI0IDExaC0yMjBxLTExIDAgLTI0IC0xMXEtMTAgLTcgLTEwIC0yMWwxNyAtNDU3cTAgLTEwIDEwIC0xNi41dDI0IC02LjVoMTg1DQpxMTQgMCAyMy41IDYuNXQxMC41IDE2LjV6TTEwMDggMTQ2OWw3NjggLTE0MDhxMzUgLTYzIC0yIC0xMjZxLTE3IC0yOSAtNDYuNSAtNDZ0LTYzLjUgLTE3aC0xNTM2cS0zNCAwIC02My41IDE3dC00Ni41IDQ2cS0zNyA2MyAtMiAxMjZsNzY4IDE0MDhxMTcgMzEgNDcgNDl0NjUgMTh0NjUgLTE4dDQ3IC00OXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InBsYW5lIiB1bmljb2RlPSImI3hmMDcyOyIgaG9yaXotYWR2LXg9IjE0MDgiIA0KZD0iTTEzNzYgMTM3NnE0NCAtNTIgMTIgLTE0OHQtMTA4IC0xNzJsLTE2MSAtMTYxbDE2MCAtNjk2cTUgLTE5IC0xMiAtMzNsLTEyOCAtOTZxLTcgLTYgLTE5IC02cS00IDAgLTcgMXEtMTUgMyAtMjEgMTZsLTI3OSA1MDhsLTI1OSAtMjU5bDUzIC0xOTRxNSAtMTcgLTggLTMxbC05NiAtOTZxLTkgLTkgLTIzIC05aC0ycS0xNSAyIC0yNCAxM2wtMTg5IDI1MmwtMjUyIDE4OXEtMTEgNyAtMTMgMjNxLTEgMTMgOSAyNWw5NiA5N3E5IDkgMjMgOQ0KcTYgMCA4IC0xbDE5NCAtNTNsMjU5IDI1OWwtNTA4IDI3OXEtMTQgOCAtMTcgMjRxLTIgMTYgOSAyN2wxMjggMTI4cTE0IDEzIDMwIDhsNjY1IC0xNTlsMTYwIDE2MHE3NiA3NiAxNzIgMTA4dDE0OCAtMTJ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJjYWxlbmRhciIgdW5pY29kZT0iJiN4ZjA3MzsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik0xMjggLTEyOGgyODh2Mjg4aC0yODh2LTI4OHpNNDgwIC0xMjhoMzIwdjI4OGgtMzIwdi0yODh6TTEyOCAyMjRoMjg4djMyMGgtMjg4di0zMjB6TTQ4MCAyMjRoMzIwdjMyMGgtMzIwdi0zMjB6TTEyOCA2MDhoMjg4djI4OGgtMjg4di0yODh6TTg2NCAtMTI4aDMyMHYyODhoLTMyMHYtMjg4ek00ODAgNjA4aDMyMHYyODhoLTMyMHYtMjg4ek0xMjQ4IC0xMjhoMjg4djI4OGgtMjg4di0yODh6TTg2NCAyMjRoMzIwdjMyMGgtMzIwdi0zMjB6DQpNNTEyIDEwODh2Mjg4cTAgMTMgLTkuNSAyMi41dC0yMi41IDkuNWgtNjRxLTEzIDAgLTIyLjUgLTkuNXQtOS41IC0yMi41di0yODhxMCAtMTMgOS41IC0yMi41dDIyLjUgLTkuNWg2NHExMyAwIDIyLjUgOS41dDkuNSAyMi41ek0xMjQ4IDIyNGgyODh2MzIwaC0yODh2LTMyMHpNODY0IDYwOGgzMjB2Mjg4aC0zMjB2LTI4OHpNMTI0OCA2MDhoMjg4djI4OGgtMjg4di0yODh6TTEyODAgMTA4OHYyODhxMCAxMyAtOS41IDIyLjV0LTIyLjUgOS41aC02NA0KcS0xMyAwIC0yMi41IC05LjV0LTkuNSAtMjIuNXYtMjg4cTAgLTEzIDkuNSAtMjIuNXQyMi41IC05LjVoNjRxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXpNMTY2NCAxMTUydi0xMjgwcTAgLTUyIC0zOCAtOTB0LTkwIC0zOGgtMTQwOHEtNTIgMCAtOTAgMzh0LTM4IDkwdjEyODBxMCA1MiAzOCA5MHQ5MCAzOGgxMjh2OTZxMCA2NiA0NyAxMTN0MTEzIDQ3aDY0cTY2IDAgMTEzIC00N3Q0NyAtMTEzdi05NmgzODR2OTZxMCA2NiA0NyAxMTN0MTEzIDQ3DQpoNjRxNjYgMCAxMTMgLTQ3dDQ3IC0xMTN2LTk2aDEyOHE1MiAwIDkwIC0zOHQzOCAtOTB6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJyYW5kb20iIHVuaWNvZGU9IiYjeGYwNzQ7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNNjY2IDEwNTVxLTYwIC05MiAtMTM3IC0yNzNxLTIyIDQ1IC0zNyA3Mi41dC00MC41IDYzLjV0LTUxIDU2LjV0LTYzIDM1dC04MS41IDE0LjVoLTIyNHEtMTQgMCAtMjMgOXQtOSAyM3YxOTJxMCAxNCA5IDIzdDIzIDloMjI0cTI1MCAwIDQxMCAtMjI1ek0xNzkyIDI1NnEwIC0xNCAtOSAtMjNsLTMyMCAtMzIwcS05IC05IC0yMyAtOXEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djE5MnEtMzIgMCAtODUgLTAuNXQtODEgLTF0LTczIDENCnQtNzEgNXQtNjQgMTAuNXQtNjMgMTguNXQtNTggMjguNXQtNTkgNDB0LTU1IDUzLjV0LTU2IDY5LjVxNTkgOTMgMTM2IDI3M3EyMiAtNDUgMzcgLTcyLjV0NDAuNSAtNjMuNXQ1MSAtNTYuNXQ2MyAtMzV0ODEuNSAtMTQuNWgyNTZ2MTkycTAgMTQgOSAyM3QyMyA5cTEyIDAgMjQgLTEwbDMxOSAtMzE5cTkgLTkgOSAtMjN6TTE3OTIgMTE1MnEwIC0xNCAtOSAtMjNsLTMyMCAtMzIwcS05IC05IC0yMyAtOXEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41DQp2MTkyaC0yNTZxLTQ4IDAgLTg3IC0xNXQtNjkgLTQ1dC01MSAtNjEuNXQtNDUgLTc3LjVxLTMyIC02MiAtNzggLTE3MXEtMjkgLTY2IC00OS41IC0xMTF0LTU0IC0xMDV0LTY0IC0xMDB0LTc0IC04M3QtOTAgLTY4LjV0LTEwNi41IC00MnQtMTI4IC0xNi41aC0yMjRxLTE0IDAgLTIzIDl0LTkgMjN2MTkycTAgMTQgOSAyM3QyMyA5aDIyNHE0OCAwIDg3IDE1dDY5IDQ1dDUxIDYxLjV0NDUgNzcuNXEzMiA2MiA3OCAxNzFxMjkgNjYgNDkuNSAxMTENCnQ1NCAxMDV0NjQgMTAwdDc0IDgzdDkwIDY4LjV0MTA2LjUgNDJ0MTI4IDE2LjVoMjU2djE5MnEwIDE0IDkgMjN0MjMgOXExMiAwIDI0IC0xMGwzMTkgLTMxOXE5IC05IDkgLTIzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY29tbWVudCIgdW5pY29kZT0iJiN4ZjA3NTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNzkyIDY0MHEwIC0xNzQgLTEyMCAtMzIxLjV0LTMyNiAtMjMzdC00NTAgLTg1LjVxLTcwIDAgLTE0NSA4cS0xOTggLTE3NSAtNDYwIC0yNDJxLTQ5IC0xNCAtMTE0IC0yMnEtMTcgLTIgLTMwLjUgOXQtMTcuNSAyOXYxcS0zIDQgLTAuNSAxMnQyIDEwdDQuNSA5LjVsNiA5dDcgOC41dDggOXE3IDggMzEgMzQuNXQzNC41IDM4dDMxIDM5LjV0MzIuNSA1MXQyNyA1OXQyNiA3NnEtMTU3IDg5IC0yNDcuNSAyMjB0LTkwLjUgMjgxDQpxMCAxMzAgNzEgMjQ4LjV0MTkxIDIwNC41dDI4NiAxMzYuNXQzNDggNTAuNXEyNDQgMCA0NTAgLTg1LjV0MzI2IC0yMzN0MTIwIC0zMjEuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Im1hZ25ldCIgdW5pY29kZT0iJiN4ZjA3NjsiIA0KZD0iTTE1MzYgNzA0di0xMjhxMCAtMjAxIC05OC41IC0zNjJ0LTI3NCAtMjUxLjV0LTM5NS41IC05MC41dC0zOTUuNSA5MC41dC0yNzQgMjUxLjV0LTk4LjUgMzYydjEyOHEwIDI2IDE5IDQ1dDQ1IDE5aDM4NHEyNiAwIDQ1IC0xOXQxOSAtNDV2LTEyOHEwIC01MiAyMy41IC05MHQ1My41IC01N3Q3MSAtMzB0NjQgLTEzdDQ0IC0ydDQ0IDJ0NjQgMTN0NzEgMzB0NTMuNSA1N3QyMy41IDkwdjEyOHEwIDI2IDE5IDQ1dDQ1IDE5aDM4NA0KcTI2IDAgNDUgLTE5dDE5IC00NXpNNTEyIDEzNDR2LTM4NHEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTM4NHEtMjYgMCAtNDUgMTl0LTE5IDQ1djM4NHEwIDI2IDE5IDQ1dDQ1IDE5aDM4NHEyNiAwIDQ1IC0xOXQxOSAtNDV6TTE1MzYgMTM0NHYtMzg0cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMzg0cS0yNiAwIC00NSAxOXQtMTkgNDV2Mzg0cTAgMjYgMTkgNDV0NDUgMTloMzg0cTI2IDAgNDUgLTE5dDE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImNoZXZyb25fdXAiIHVuaWNvZGU9IiYjeGYwNzc7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTY4MyAyMDVsLTE2NiAtMTY1cS0xOSAtMTkgLTQ1IC0xOXQtNDUgMTlsLTUzMSA1MzFsLTUzMSAtNTMxcS0xOSAtMTkgLTQ1IC0xOXQtNDUgMTlsLTE2NiAxNjVxLTE5IDE5IC0xOSA0NS41dDE5IDQ1LjVsNzQyIDc0MXExOSAxOSA0NSAxOXQ0NSAtMTlsNzQyIC03NDFxMTkgLTE5IDE5IC00NS41dC0xOSAtNDUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImNoZXZyb25fZG93biIgdW5pY29kZT0iJiN4ZjA3ODsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNjgzIDcyOGwtNzQyIC03NDFxLTE5IC0xOSAtNDUgLTE5dC00NSAxOWwtNzQyIDc0MXEtMTkgMTkgLTE5IDQ1LjV0MTkgNDUuNWwxNjYgMTY1cTE5IDE5IDQ1IDE5dDQ1IC0xOWw1MzEgLTUzMWw1MzEgNTMxcTE5IDE5IDQ1IDE5dDQ1IC0xOWwxNjYgLTE2NXExOSAtMTkgMTkgLTQ1LjV0LTE5IC00NS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0icmV0d2VldCIgdW5pY29kZT0iJiN4ZjA3OTsiIGhvcml6LWFkdi14PSIxOTIwIiANCmQ9Ik0xMjgwIDMycTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtOTYwcS04IDAgLTEzLjUgMnQtOSA3dC01LjUgOHQtMyAxMS41dC0xIDExLjV2MTN2MTF2MTYwdjQxNmgtMTkycS0yNiAwIC00NSAxOXQtMTkgNDVxMCAyNCAxNSA0MWwzMjAgMzg0cTE5IDIyIDQ5IDIydDQ5IC0yMmwzMjAgLTM4NHExNSAtMTcgMTUgLTQxcTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTkydi0zODRoNTc2cTE2IDAgMjUgLTExbDE2MCAtMTkycTcgLTEwIDcgLTIxDQp6TTE5MjAgNDQ4cTAgLTI0IC0xNSAtNDFsLTMyMCAtMzg0cS0yMCAtMjMgLTQ5IC0yM3QtNDkgMjNsLTMyMCAzODRxLTE1IDE3IC0xNSA0MXEwIDI2IDE5IDQ1dDQ1IDE5aDE5MnYzODRoLTU3NnEtMTYgMCAtMjUgMTJsLTE2MCAxOTJxLTcgOSAtNyAyMHEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDk2MHE4IDAgMTMuNSAtMnQ5IC03dDUuNSAtOHQzIC0xMS41dDEgLTExLjV2LTEzdi0xMXYtMTYwdi00MTZoMTkycTI2IDAgNDUgLTE5dDE5IC00NXoNCiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ic2hvcHBpbmdfY2FydCIgdW5pY29kZT0iJiN4ZjA3YTsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik02NDAgMHEwIC01MiAtMzggLTkwdC05MCAtMzh0LTkwIDM4dC0zOCA5MHQzOCA5MHQ5MCAzOHQ5MCAtMzh0MzggLTkwek0xNTM2IDBxMCAtNTIgLTM4IC05MHQtOTAgLTM4dC05MCAzOHQtMzggOTB0MzggOTB0OTAgMzh0OTAgLTM4dDM4IC05MHpNMTY2NCAxMDg4di01MTJxMCAtMjQgLTE2LjUgLTQyLjV0LTQwLjUgLTIxLjVsLTEwNDQgLTEyMnExMyAtNjAgMTMgLTcwcTAgLTE2IC0yNCAtNjRoOTIwcTI2IDAgNDUgLTE5dDE5IC00NQ0KdC0xOSAtNDV0LTQ1IC0xOWgtMTAyNHEtMjYgMCAtNDUgMTl0LTE5IDQ1cTAgMTEgOCAzMS41dDE2IDM2dDIxLjUgNDB0MTUuNSAyOS41bC0xNzcgODIzaC0yMDRxLTI2IDAgLTQ1IDE5dC0xOSA0NXQxOSA0NXQ0NSAxOWgyNTZxMTYgMCAyOC41IC02LjV0MTkuNSAtMTUuNXQxMyAtMjQuNXQ4IC0yNnQ1LjUgLTI5LjV0NC41IC0yNmgxMjAxcTI2IDAgNDUgLTE5dDE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImZvbGRlcl9jbG9zZSIgdW5pY29kZT0iJiN4ZjA3YjsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik0xNjY0IDkyOHYtNzA0cTAgLTkyIC02NiAtMTU4dC0xNTggLTY2aC0xMjE2cS05MiAwIC0xNTggNjZ0LTY2IDE1OHY5NjBxMCA5MiA2NiAxNTh0MTU4IDY2aDMyMHE5MiAwIDE1OCAtNjZ0NjYgLTE1OHYtMzJoNjcycTkyIDAgMTU4IC02NnQ2NiAtMTU4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZm9sZGVyX29wZW4iIHVuaWNvZGU9IiYjeGYwN2M7IiBob3Jpei1hZHYteD0iMTkyMCIgDQpkPSJNMTg3OSA1ODRxMCAtMzEgLTMxIC02NmwtMzM2IC0zOTZxLTQzIC01MSAtMTIwLjUgLTg2LjV0LTE0My41IC0zNS41aC0xMDg4cS0zNCAwIC02MC41IDEzdC0yNi41IDQzcTAgMzEgMzEgNjZsMzM2IDM5NnE0MyA1MSAxMjAuNSA4Ni41dDE0My41IDM1LjVoMTA4OHEzNCAwIDYwLjUgLTEzdDI2LjUgLTQzek0xNTM2IDkyOHYtMTYwaC04MzJxLTk0IDAgLTE5NyAtNDcuNXQtMTY0IC0xMTkuNWwtMzM3IC0zOTZsLTUgLTZxMCA0IC0wLjUgMTIuNQ0KdC0wLjUgMTIuNXY5NjBxMCA5MiA2NiAxNTh0MTU4IDY2aDMyMHE5MiAwIDE1OCAtNjZ0NjYgLTE1OHYtMzJoNTQ0cTkyIDAgMTU4IC02NnQ2NiAtMTU4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0icmVzaXplX3ZlcnRpY2FsIiB1bmljb2RlPSImI3hmMDdkOyIgaG9yaXotYWR2LXg9Ijc2OCIgDQpkPSJNNzA0IDEyMTZxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC0xMjh2LTEwMjRoMTI4cTI2IDAgNDUgLTE5dDE5IC00NXQtMTkgLTQ1bC0yNTYgLTI1NnEtMTkgLTE5IC00NSAtMTl0LTQ1IDE5bC0yNTYgMjU2cS0xOSAxOSAtMTkgNDV0MTkgNDV0NDUgMTloMTI4djEwMjRoLTEyOHEtMjYgMCAtNDUgMTl0LTE5IDQ1dDE5IDQ1bDI1NiAyNTZxMTkgMTkgNDUgMTl0NDUgLTE5bDI1NiAtMjU2cTE5IC0xOSAxOSAtNDV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJyZXNpemVfaG9yaXpvbnRhbCIgdW5pY29kZT0iJiN4ZjA3ZTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNzkyIDY0MHEwIC0yNiAtMTkgLTQ1bC0yNTYgLTI1NnEtMTkgLTE5IC00NSAtMTl0LTQ1IDE5dC0xOSA0NXYxMjhoLTEwMjR2LTEyOHEwIC0yNiAtMTkgLTQ1dC00NSAtMTl0LTQ1IDE5bC0yNTYgMjU2cS0xOSAxOSAtMTkgNDV0MTkgNDVsMjU2IDI1NnExOSAxOSA0NSAxOXQ0NSAtMTl0MTkgLTQ1di0xMjhoMTAyNHYxMjhxMCAyNiAxOSA0NXQ0NSAxOXQ0NSAtMTlsMjU2IC0yNTZxMTkgLTE5IDE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImJhcl9jaGFydCIgdW5pY29kZT0iJiN4ZjA4MDsiIGhvcml6LWFkdi14PSIyMDQ4IiANCmQ9Ik02NDAgNjQwdi01MTJoLTI1NnY1MTJoMjU2ek0xMDI0IDExNTJ2LTEwMjRoLTI1NnYxMDI0aDI1NnpNMjA0OCAwdi0xMjhoLTIwNDh2MTUzNmgxMjh2LTE0MDhoMTkyMHpNMTQwOCA4OTZ2LTc2OGgtMjU2djc2OGgyNTZ6TTE3OTIgMTI4MHYtMTE1MmgtMjU2djExNTJoMjU2eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idHdpdHRlcl9zaWduIiB1bmljb2RlPSImI3hmMDgxOyIgDQpkPSJNMTI4MCA5MjZxLTU2IC0yNSAtMTIxIC0zNHE2OCA0MCA5MyAxMTdxLTY1IC0zOCAtMTM0IC01MXEtNjEgNjYgLTE1MyA2NnEtODcgMCAtMTQ4LjUgLTYxLjV0LTYxLjUgLTE0OC41cTAgLTI5IDUgLTQ4cS0xMjkgNyAtMjQyIDY1dC0xOTIgMTU1cS0yOSAtNTAgLTI5IC0xMDZxMCAtMTE0IDkxIC0xNzVxLTQ3IDEgLTEwMCAyNnYtMnEwIC03NSA1MCAtMTMzLjV0MTIzIC03Mi41cS0yOSAtOCAtNTEgLThxLTEzIDAgLTM5IDQNCnEyMSAtNjMgNzQuNSAtMTA0dDEyMS41IC00MnEtMTE2IC05MCAtMjYxIC05MHEtMjYgMCAtNTAgM3ExNDggLTk0IDMyMiAtOTRxMTEyIDAgMjEwIDM1LjV0MTY4IDk1dDEyMC41IDEzN3Q3NSAxNjJ0MjQuNSAxNjguNXEwIDE4IC0xIDI3cTYzIDQ1IDEwNSAxMDl6TTE1MzYgMTEyMHYtOTYwcTAgLTExOSAtODQuNSAtMjAzLjV0LTIwMy41IC04NC41aC05NjBxLTExOSAwIC0yMDMuNSA4NC41dC04NC41IDIwMy41djk2MHEwIDExOSA4NC41IDIwMy41DQp0MjAzLjUgODQuNWg5NjBxMTE5IDAgMjAzLjUgLTg0LjV0ODQuNSAtMjAzLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJmYWNlYm9va19zaWduIiB1bmljb2RlPSImI3hmMDgyOyIgDQpkPSJNMTI0OCAxNDA4cTExOSAwIDIwMy41IC04NC41dDg0LjUgLTIwMy41di05NjBxMCAtMTE5IC04NC41IC0yMDMuNXQtMjAzLjUgLTg0LjVoLTE4OHY1OTVoMTk5bDMwIDIzMmgtMjI5djE0OHEwIDU2IDIzLjUgODR0OTEuNSAyOGwxMjIgMXYyMDdxLTYzIDkgLTE3OCA5cS0xMzYgMCAtMjE3LjUgLTgwdC04MS41IC0yMjZ2LTE3MWgtMjAwdi0yMzJoMjAwdi01OTVoLTUzMnEtMTE5IDAgLTIwMy41IDg0LjV0LTg0LjUgMjAzLjV2OTYwDQpxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDk2MHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImNhbWVyYV9yZXRybyIgdW5pY29kZT0iJiN4ZjA4MzsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik05MjggNzA0cTAgMTQgLTkgMjN0LTIzIDlxLTY2IDAgLTExMyAtNDd0LTQ3IC0xMTNxMCAtMTQgOSAtMjN0MjMgLTl0MjMgOXQ5IDIzcTAgNDAgMjggNjh0NjggMjhxMTQgMCAyMyA5dDkgMjN6TTExNTIgNTc0cTAgLTEwNiAtNzUgLTE4MXQtMTgxIC03NXQtMTgxIDc1dC03NSAxODF0NzUgMTgxdDE4MSA3NXQxODEgLTc1dDc1IC0xODF6TTEyOCAwaDE1MzZ2MTI4aC0xNTM2di0xMjh6TTEyODAgNTc0cTAgMTU5IC0xMTIuNSAyNzEuNQ0KdC0yNzEuNSAxMTIuNXQtMjcxLjUgLTExMi41dC0xMTIuNSAtMjcxLjV0MTEyLjUgLTI3MS41dDI3MS41IC0xMTIuNXQyNzEuNSAxMTIuNXQxMTIuNSAyNzEuNXpNMjU2IDEyMTZoMzg0djEyOGgtMzg0di0xMjh6TTEyOCAxMDI0aDE1MzZ2MTE4djEzOGgtODI4bC02NCAtMTI4aC02NDR2LTEyOHpNMTc5MiAxMjgwdi0xMjgwcTAgLTUzIC0zNy41IC05MC41dC05MC41IC0zNy41aC0xNTM2cS01MyAwIC05MC41IDM3LjV0LTM3LjUgOTAuNXYxMjgwDQpxMCA1MyAzNy41IDkwLjV0OTAuNSAzNy41aDE1MzZxNTMgMCA5MC41IC0zNy41dDM3LjUgLTkwLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJrZXkiIHVuaWNvZGU9IiYjeGYwODQ7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNODMyIDEwMjRxMCA4MCAtNTYgMTM2dC0xMzYgNTZ0LTEzNiAtNTZ0LTU2IC0xMzZxMCAtNDIgMTkgLTgzcS00MSAxOSAtODMgMTlxLTgwIDAgLTEzNiAtNTZ0LTU2IC0xMzZ0NTYgLTEzNnQxMzYgLTU2dDEzNiA1NnQ1NiAxMzZxMCA0MiAtMTkgODNxNDEgLTE5IDgzIC0xOXE4MCAwIDEzNiA1NnQ1NiAxMzZ6TTE2ODMgMzIwcTAgLTE3IC00OSAtNjZ0LTY2IC00OXEtOSAwIC0yOC41IDE2dC0zNi41IDMzdC0zOC41IDQwdC0yNC41IDI2DQpsLTk2IC05NmwyMjAgLTIyMHEyOCAtMjggMjggLTY4cTAgLTQyIC0zOSAtODF0LTgxIC0zOXEtNDAgMCAtNjggMjhsLTY3MSA2NzFxLTE3NiAtMTMxIC0zNjUgLTEzMXEtMTYzIDAgLTI2NS41IDEwMi41dC0xMDIuNSAyNjUuNXEwIDE2MCA5NSAzMTN0MjQ4IDI0OHQzMTMgOTVxMTYzIDAgMjY1LjUgLTEwMi41dDEwMi41IC0yNjUuNXEwIC0xODkgLTEzMSAtMzY1bDM1NSAtMzU1bDk2IDk2cS0zIDMgLTI2IDI0LjV0LTQwIDM4LjV0LTMzIDM2LjUNCnQtMTYgMjguNXEwIDE3IDQ5IDY2dDY2IDQ5cTEzIDAgMjMgLTEwcTYgLTYgNDYgLTQ0LjV0ODIgLTc5LjV0ODYuNSAtODZ0NzMgLTc4dDI4LjUgLTQxeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY29ncyIgdW5pY29kZT0iJiN4ZjA4NTsiIGhvcml6LWFkdi14PSIxOTIwIiANCmQ9Ik04OTYgNjQwcTAgMTA2IC03NSAxODF0LTE4MSA3NXQtMTgxIC03NXQtNzUgLTE4MXQ3NSAtMTgxdDE4MSAtNzV0MTgxIDc1dDc1IDE4MXpNMTY2NCAxMjhxMCA1MiAtMzggOTB0LTkwIDM4dC05MCAtMzh0LTM4IC05MHEwIC01MyAzNy41IC05MC41dDkwLjUgLTM3LjV0OTAuNSAzNy41dDM3LjUgOTAuNXpNMTY2NCAxMTUycTAgNTIgLTM4IDkwdC05MCAzOHQtOTAgLTM4dC0zOCAtOTBxMCAtNTMgMzcuNSAtOTAuNXQ5MC41IC0zNy41DQp0OTAuNSAzNy41dDM3LjUgOTAuNXpNMTI4MCA3MzF2LTE4NXEwIC0xMCAtNyAtMTkuNXQtMTYgLTEwLjVsLTE1NSAtMjRxLTExIC0zNSAtMzIgLTc2cTM0IC00OCA5MCAtMTE1cTcgLTExIDcgLTIwcTAgLTEyIC03IC0xOXEtMjMgLTMwIC04Mi41IC04OS41dC03OC41IC01OS41cS0xMSAwIC0yMSA3bC0xMTUgOTBxLTM3IC0xOSAtNzcgLTMxcS0xMSAtMTA4IC0yMyAtMTU1cS03IC0yNCAtMzAgLTI0aC0xODZxLTExIDAgLTIwIDcuNXQtMTAgMTcuNQ0KbC0yMyAxNTNxLTM0IDEwIC03NSAzMWwtMTE4IC04OXEtNyAtNyAtMjAgLTdxLTExIDAgLTIxIDhxLTE0NCAxMzMgLTE0NCAxNjBxMCA5IDcgMTlxMTAgMTQgNDEgNTN0NDcgNjFxLTIzIDQ0IC0zNSA4MmwtMTUyIDI0cS0xMCAxIC0xNyA5LjV0LTcgMTkuNXYxODVxMCAxMCA3IDE5LjV0MTYgMTAuNWwxNTUgMjRxMTEgMzUgMzIgNzZxLTM0IDQ4IC05MCAxMTVxLTcgMTEgLTcgMjBxMCAxMiA3IDIwcTIyIDMwIDgyIDg5dDc5IDU5cTExIDAgMjEgLTcNCmwxMTUgLTkwcTM0IDE4IDc3IDMycTExIDEwOCAyMyAxNTRxNyAyNCAzMCAyNGgxODZxMTEgMCAyMCAtNy41dDEwIC0xNy41bDIzIC0xNTNxMzQgLTEwIDc1IC0zMWwxMTggODlxOCA3IDIwIDdxMTEgMCAyMSAtOHExNDQgLTEzMyAxNDQgLTE2MHEwIC04IC03IC0xOXEtMTIgLTE2IC00MiAtNTR0LTQ1IC02MHEyMyAtNDggMzQgLTgybDE1MiAtMjNxMTAgLTIgMTcgLTEwLjV0NyAtMTkuNXpNMTkyMCAxOTh2LTE0MHEwIC0xNiAtMTQ5IC0zMQ0KcS0xMiAtMjcgLTMwIC01MnE1MSAtMTEzIDUxIC0xMzhxMCAtNCAtNCAtN3EtMTIyIC03MSAtMTI0IC03MXEtOCAwIC00NiA0N3QtNTIgNjhxLTIwIC0yIC0zMCAtMnQtMzAgMnEtMTQgLTIxIC01MiAtNjh0LTQ2IC00N3EtMiAwIC0xMjQgNzFxLTQgMyAtNCA3cTAgMjUgNTEgMTM4cS0xOCAyNSAtMzAgNTJxLTE0OSAxNSAtMTQ5IDMxdjE0MHEwIDE2IDE0OSAzMXExMyAyOSAzMCA1MnEtNTEgMTEzIC01MSAxMzhxMCA0IDQgN3E0IDIgMzUgMjANCnQ1OSAzNHQzMCAxNnE4IDAgNDYgLTQ2LjV0NTIgLTY3LjVxMjAgMiAzMCAydDMwIC0ycTUxIDcxIDkyIDExMmw2IDJxNCAwIDEyNCAtNzBxNCAtMyA0IC03cTAgLTI1IC01MSAtMTM4cTE3IC0yMyAzMCAtNTJxMTQ5IC0xNSAxNDkgLTMxek0xOTIwIDEyMjJ2LTE0MHEwIC0xNiAtMTQ5IC0zMXEtMTIgLTI3IC0zMCAtNTJxNTEgLTExMyA1MSAtMTM4cTAgLTQgLTQgLTdxLTEyMiAtNzEgLTEyNCAtNzFxLTggMCAtNDYgNDd0LTUyIDY4DQpxLTIwIC0yIC0zMCAtMnQtMzAgMnEtMTQgLTIxIC01MiAtNjh0LTQ2IC00N3EtMiAwIC0xMjQgNzFxLTQgMyAtNCA3cTAgMjUgNTEgMTM4cS0xOCAyNSAtMzAgNTJxLTE0OSAxNSAtMTQ5IDMxdjE0MHEwIDE2IDE0OSAzMXExMyAyOSAzMCA1MnEtNTEgMTEzIC01MSAxMzhxMCA0IDQgN3E0IDIgMzUgMjB0NTkgMzR0MzAgMTZxOCAwIDQ2IC00Ni41dDUyIC02Ny41cTIwIDIgMzAgMnQzMCAtMnE1MSA3MSA5MiAxMTJsNiAycTQgMCAxMjQgLTcwDQpxNCAtMyA0IC03cTAgLTI1IC01MSAtMTM4cTE3IC0yMyAzMCAtNTJxMTQ5IC0xNSAxNDkgLTMxeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY29tbWVudHMiIHVuaWNvZGU9IiYjeGYwODY7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTQwOCA3NjhxMCAtMTM5IC05NCAtMjU3dC0yNTYuNSAtMTg2LjV0LTM1My41IC02OC41cS04NiAwIC0xNzYgMTZxLTEyNCAtODggLTI3OCAtMTI4cS0zNiAtOSAtODYgLTE2aC0zcS0xMSAwIC0yMC41IDh0LTExLjUgMjFxLTEgMyAtMSA2LjV0MC41IDYuNXQyIDZsMi41IDV0My41IDUuNXQ0IDV0NC41IDV0NCA0LjVxNSA2IDIzIDI1dDI2IDI5LjV0MjIuNSAyOXQyNSAzOC41dDIwLjUgNDRxLTEyNCA3MiAtMTk1IDE3N3QtNzEgMjI0DQpxMCAxMzkgOTQgMjU3dDI1Ni41IDE4Ni41dDM1My41IDY4LjV0MzUzLjUgLTY4LjV0MjU2LjUgLTE4Ni41dDk0IC0yNTd6TTE3OTIgNTEycTAgLTEyMCAtNzEgLTIyNC41dC0xOTUgLTE3Ni41cTEwIC0yNCAyMC41IC00NHQyNSAtMzguNXQyMi41IC0yOXQyNiAtMjkuNXQyMyAtMjVxMSAtMSA0IC00LjV0NC41IC01dDQgLTV0My41IC01LjVsMi41IC01dDIgLTZ0MC41IC02LjV0LTEgLTYuNXEtMyAtMTQgLTEzIC0yMnQtMjIgLTcNCnEtNTAgNyAtODYgMTZxLTE1NCA0MCAtMjc4IDEyOHEtOTAgLTE2IC0xNzYgLTE2cS0yNzEgMCAtNDcyIDEzMnE1OCAtNCA4OCAtNHExNjEgMCAzMDkgNDV0MjY0IDEyOXExMjUgOTIgMTkyIDIxMnQ2NyAyNTRxMCA3NyAtMjMgMTUycTEyOSAtNzEgMjA0IC0xNzh0NzUgLTIzMHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InRodW1ic191cF9hbHQiIHVuaWNvZGU9IiYjeGYwODc7IiANCmQ9Ik0yNTYgMTkycTAgMjYgLTE5IDQ1dC00NSAxOXQtNDUgLTE5dC0xOSAtNDV0MTkgLTQ1dDQ1IC0xOXQ0NSAxOXQxOSA0NXpNMTQwOCA3NjhxMCA1MSAtMzkgODkuNXQtODkgMzguNWgtMzUycTAgNTggNDggMTU5LjV0NDggMTYwLjVxMCA5OCAtMzIgMTQ1dC0xMjggNDdxLTI2IC0yNiAtMzggLTg1dC0zMC41IC0xMjUuNXQtNTkuNSAtMTA5LjVxLTIyIC0yMyAtNzcgLTkxcS00IC01IC0yMyAtMzB0LTMxLjUgLTQxdC0zNC41IC00Mi41DQp0LTQwIC00NHQtMzguNSAtMzUuNXQtNDAgLTI3dC0zNS41IC05aC0zMnYtNjQwaDMycTEzIDAgMzEuNSAtM3QzMyAtNi41dDM4IC0xMXQzNSAtMTEuNXQzNS41IC0xMi41dDI5IC0xMC41cTIxMSAtNzMgMzQyIC03M2gxMjFxMTkyIDAgMTkyIDE2N3EwIDI2IC01IDU2cTMwIDE2IDQ3LjUgNTIuNXQxNy41IDczLjV0LTE4IDY5cTUzIDUwIDUzIDExOXEwIDI1IC0xMCA1NS41dC0yNSA0Ny41cTMyIDEgNTMuNSA0N3QyMS41IDgxek0xNTM2IDc2OQ0KcTAgLTg5IC00OSAtMTYzcTkgLTMzIDkgLTY5cTAgLTc3IC0zOCAtMTQ0cTMgLTIxIDMgLTQzcTAgLTEwMSAtNjAgLTE3OHExIC0xMzkgLTg1IC0yMTkuNXQtMjI3IC04MC41aC0zNmgtOTNxLTk2IDAgLTE4OS41IDIyLjV0LTIxNi41IDY1LjVxLTExNiA0MCAtMTM4IDQwaC0yODhxLTUzIDAgLTkwLjUgMzcuNXQtMzcuNSA5MC41djY0MHEwIDUzIDM3LjUgOTAuNXQ5MC41IDM3LjVoMjc0cTM2IDI0IDEzNyAxNTVxNTggNzUgMTA3IDEyOA0KcTI0IDI1IDM1LjUgODUuNXQzMC41IDEyNi41dDYyIDEwOHEzOSAzNyA5MCAzN3E4NCAwIDE1MSAtMzIuNXQxMDIgLTEwMS41dDM1IC0xODZxMCAtOTMgLTQ4IC0xOTJoMTc2cTEwNCAwIDE4MCAtNzZ0NzYgLTE3OXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InRodW1ic19kb3duX2FsdCIgdW5pY29kZT0iJiN4ZjA4ODsiIA0KZD0iTTI1NiAxMDg4cTAgMjYgLTE5IDQ1dC00NSAxOXQtNDUgLTE5dC0xOSAtNDV0MTkgLTQ1dDQ1IC0xOXQ0NSAxOXQxOSA0NXpNMTQwOCA1MTJxMCAzNSAtMjEuNSA4MXQtNTMuNSA0N3ExNSAxNyAyNSA0Ny41dDEwIDU1LjVxMCA2OSAtNTMgMTE5cTE4IDMxIDE4IDY5cTAgMzcgLTE3LjUgNzMuNXQtNDcuNSA1Mi41cTUgMzAgNSA1NnEwIDg1IC00OSAxMjZ0LTEzNiA0MWgtMTI4cS0xMzEgMCAtMzQyIC03M3EtNSAtMiAtMjkgLTEwLjUNCnQtMzUuNSAtMTIuNXQtMzUgLTExLjV0LTM4IC0xMXQtMzMgLTYuNXQtMzEuNSAtM2gtMzJ2LTY0MGgzMnExNiAwIDM1LjUgLTl0NDAgLTI3dDM4LjUgLTM1LjV0NDAgLTQ0dDM0LjUgLTQyLjV0MzEuNSAtNDF0MjMgLTMwcTU1IC02OCA3NyAtOTFxNDEgLTQzIDU5LjUgLTEwOS41dDMwLjUgLTEyNS41dDM4IC04NXE5NiAwIDEyOCA0N3QzMiAxNDVxMCA1OSAtNDggMTYwLjV0LTQ4IDE1OS41aDM1MnE1MCAwIDg5IDM4LjV0MzkgODkuNXoNCk0xNTM2IDUxMXEwIC0xMDMgLTc2IC0xNzl0LTE4MCAtNzZoLTE3NnE0OCAtOTkgNDggLTE5MnEwIC0xMTggLTM1IC0xODZxLTM1IC02OSAtMTAyIC0xMDEuNXQtMTUxIC0zMi41cS01MSAwIC05MCAzN3EtMzQgMzMgLTU0IDgydC0yNS41IDkwLjV0LTE3LjUgODQuNXQtMzEgNjRxLTQ4IDUwIC0xMDcgMTI3cS0xMDEgMTMxIC0xMzcgMTU1aC0yNzRxLTUzIDAgLTkwLjUgMzcuNXQtMzcuNSA5MC41djY0MHEwIDUzIDM3LjUgOTAuNXQ5MC41IDM3LjUNCmgyODhxMjIgMCAxMzggNDBxMTI4IDQ0IDIyMyA2NnQyMDAgMjJoMTEycTE0MCAwIDIyNi41IC03OXQ4NS41IC0yMTZ2LTVxNjAgLTc3IDYwIC0xNzhxMCAtMjIgLTMgLTQzcTM4IC02NyAzOCAtMTQ0cTAgLTM2IC05IC02OXE0OSAtNzMgNDkgLTE2M3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InN0YXJfaGFsZiIgdW5pY29kZT0iJiN4ZjA4OTsiIGhvcml6LWFkdi14PSI4OTYiIA0KZD0iTTgzMiAxNTA0di0xMzM5bC00NDkgLTIzNnEtMjIgLTEyIC00MCAtMTJxLTIxIDAgLTMxLjUgMTQuNXQtMTAuNSAzNS41cTAgNiAyIDIwbDg2IDUwMGwtMzY0IDM1NHEtMjUgMjcgLTI1IDQ4cTAgMzcgNTYgNDZsNTAyIDczbDIyNSA0NTVxMTkgNDEgNDkgNDF6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJoZWFydF9lbXB0eSIgdW5pY29kZT0iJiN4ZjA4YTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNjY0IDk0MHEwIDgxIC0yMS41IDE0M3QtNTUgOTguNXQtODEuNSA1OS41dC05NCAzMXQtOTggOHQtMTEyIC0yNS41dC0xMTAuNSAtNjR0LTg2LjUgLTcydC02MCAtNjEuNXEtMTggLTIyIC00OSAtMjJ0LTQ5IDIycS0yNCAyOCAtNjAgNjEuNXQtODYuNSA3MnQtMTEwLjUgNjR0LTExMiAyNS41dC05OCAtOHQtOTQgLTMxdC04MS41IC01OS41dC01NSAtOTguNXQtMjEuNSAtMTQzcTAgLTE2OCAxODcgLTM1NWw1ODEgLTU2MGw1ODAgNTU5DQpxMTg4IDE4OCAxODggMzU2ek0xNzkyIDk0MHEwIC0yMjEgLTIyOSAtNDUwbC02MjMgLTYwMHEtMTggLTE4IC00NCAtMTh0LTQ0IDE4bC02MjQgNjAycS0xMCA4IC0yNy41IDI2dC01NS41IDY1LjV0LTY4IDk3LjV0LTUzLjUgMTIxdC0yMy41IDEzOHEwIDIyMCAxMjcgMzQ0dDM1MSAxMjRxNjIgMCAxMjYuNSAtMjEuNXQxMjAgLTU4dDk1LjUgLTY4LjV0NzYgLTY4cTM2IDM2IDc2IDY4dDk1LjUgNjguNXQxMjAgNTh0MTI2LjUgMjEuNQ0KcTIyNCAwIDM1MSAtMTI0dDEyNyAtMzQ0eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ic2lnbm91dCIgdW5pY29kZT0iJiN4ZjA4YjsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik02NDAgOTZxMCAtNCAxIC0yMHQwLjUgLTI2LjV0LTMgLTIzLjV0LTEwIC0xOS41dC0yMC41IC02LjVoLTMyMHEtMTE5IDAgLTIwMy41IDg0LjV0LTg0LjUgMjAzLjV2NzA0cTAgMTE5IDg0LjUgMjAzLjV0MjAzLjUgODQuNWgzMjBxMTMgMCAyMi41IC05LjV0OS41IC0yMi41cTAgLTQgMSAtMjB0MC41IC0yNi41dC0zIC0yMy41dC0xMCAtMTkuNXQtMjAuNSAtNi41aC0zMjBxLTY2IDAgLTExMyAtNDd0LTQ3IC0xMTN2LTcwNA0KcTAgLTY2IDQ3IC0xMTN0MTEzIC00N2gyODhoMTFoMTN0MTEuNSAtMXQxMS41IC0zdDggLTUuNXQ3IC05dDIgLTEzLjV6TTE1NjggNjQwcTAgLTI2IC0xOSAtNDVsLTU0NCAtNTQ0cS0xOSAtMTkgLTQ1IC0xOXQtNDUgMTl0LTE5IDQ1djI4OGgtNDQ4cS0yNiAwIC00NSAxOXQtMTkgNDV2Mzg0cTAgMjYgMTkgNDV0NDUgMTloNDQ4djI4OHEwIDI2IDE5IDQ1dDQ1IDE5dDQ1IC0xOWw1NDQgLTU0NHExOSAtMTkgMTkgLTQ1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ibGlua2VkaW5fc2lnbiIgdW5pY29kZT0iJiN4ZjA4YzsiIA0KZD0iTTIzNyAxMjJoMjMxdjY5NGgtMjMxdi02OTR6TTQ4MyAxMDMwcS0xIDUyIC0zNiA4NnQtOTMgMzR0LTk0LjUgLTM0dC0zNi41IC04NnEwIC01MSAzNS41IC04NS41dDkyLjUgLTM0LjVoMXE1OSAwIDk1IDM0LjV0MzYgODUuNXpNMTA2OCAxMjJoMjMxdjM5OHEwIDE1NCAtNzMgMjMzdC0xOTMgNzlxLTEzNiAwIC0yMDkgLTExN2gydjEwMWgtMjMxcTMgLTY2IDAgLTY5NGgyMzF2Mzg4cTAgMzggNyA1NnExNSAzNSA0NSA1OS41dDc0IDI0LjUNCnExMTYgMCAxMTYgLTE1N3YtMzcxek0xNTM2IDExMjB2LTk2MHEwIC0xMTkgLTg0LjUgLTIwMy41dC0yMDMuNSAtODQuNWgtOTYwcS0xMTkgMCAtMjAzLjUgODQuNXQtODQuNSAyMDMuNXY5NjBxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDk2MHExMTkgMCAyMDMuNSAtODQuNXQ4NC41IC0yMDMuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InB1c2hwaW4iIHVuaWNvZGU9IiYjeGYwOGQ7IiBob3Jpei1hZHYteD0iMTE1MiIgDQpkPSJNNDgwIDY3MnY0NDhxMCAxNCAtOSAyM3QtMjMgOXQtMjMgLTl0LTkgLTIzdi00NDhxMCAtMTQgOSAtMjN0MjMgLTl0MjMgOXQ5IDIzek0xMTUyIDMyMHEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTQyOWwtNTEgLTQ4M3EtMiAtMTIgLTEwLjUgLTIwLjV0LTIwLjUgLTguNWgtMXEtMjcgMCAtMzIgMjdsLTc2IDQ4NWgtNDA0cS0yNiAwIC00NSAxOXQtMTkgNDVxMCAxMjMgNzguNSAyMjEuNXQxNzcuNSA5OC41djUxMnEtNTIgMCAtOTAgMzgNCnQtMzggOTB0MzggOTB0OTAgMzhoNjQwcTUyIDAgOTAgLTM4dDM4IC05MHQtMzggLTkwdC05MCAtMzh2LTUxMnE5OSAwIDE3Ny41IC05OC41dDc4LjUgLTIyMS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZXh0ZXJuYWxfbGluayIgdW5pY29kZT0iJiN4ZjA4ZTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNDA4IDYwOHYtMzIwcTAgLTExOSAtODQuNSAtMjAzLjV0LTIwMy41IC04NC41aC04MzJxLTExOSAwIC0yMDMuNSA4NC41dC04NC41IDIwMy41djgzMnEwIDExOSA4NC41IDIwMy41dDIwMy41IDg0LjVoNzA0cTE0IDAgMjMgLTl0OSAtMjN2LTY0cTAgLTE0IC05IC0yM3QtMjMgLTloLTcwNHEtNjYgMCAtMTEzIC00N3QtNDcgLTExM3YtODMycTAgLTY2IDQ3IC0xMTN0MTEzIC00N2g4MzJxNjYgMCAxMTMgNDd0NDcgMTEzdjMyMA0KcTAgMTQgOSAyM3QyMyA5aDY0cTE0IDAgMjMgLTl0OSAtMjN6TTE3OTIgMTQ3MnYtNTEycTAgLTI2IC0xOSAtNDV0LTQ1IC0xOXQtNDUgMTlsLTE3NiAxNzZsLTY1MiAtNjUycS0xMCAtMTAgLTIzIC0xMHQtMjMgMTBsLTExNCAxMTRxLTEwIDEwIC0xMCAyM3QxMCAyM2w2NTIgNjUybC0xNzYgMTc2cS0xOSAxOSAtMTkgNDV0MTkgNDV0NDUgMTloNTEycTI2IDAgNDUgLTE5dDE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InNpZ25pbiIgdW5pY29kZT0iJiN4ZjA5MDsiIA0KZD0iTTExODQgNjQwcTAgLTI2IC0xOSAtNDVsLTU0NCAtNTQ0cS0xOSAtMTkgLTQ1IC0xOXQtNDUgMTl0LTE5IDQ1djI4OGgtNDQ4cS0yNiAwIC00NSAxOXQtMTkgNDV2Mzg0cTAgMjYgMTkgNDV0NDUgMTloNDQ4djI4OHEwIDI2IDE5IDQ1dDQ1IDE5dDQ1IC0xOWw1NDQgLTU0NHExOSAtMTkgMTkgLTQ1ek0xNTM2IDk5MnYtNzA0cTAgLTExOSAtODQuNSAtMjAzLjV0LTIwMy41IC04NC41aC0zMjBxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNQ0KcTAgNCAtMSAyMHQtMC41IDI2LjV0MyAyMy41dDEwIDE5LjV0MjAuNSA2LjVoMzIwcTY2IDAgMTEzIDQ3dDQ3IDExM3Y3MDRxMCA2NiAtNDcgMTEzdC0xMTMgNDdoLTI4OGgtMTFoLTEzdC0xMS41IDF0LTExLjUgM3QtOCA1LjV0LTcgOXQtMiAxMy41cTAgNCAtMSAyMHQtMC41IDI2LjV0MyAyMy41dDEwIDE5LjV0MjAuNSA2LjVoMzIwcTExOSAwIDIwMy41IC04NC41dDg0LjUgLTIwMy41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idHJvcGh5IiB1bmljb2RlPSImI3hmMDkxOyIgaG9yaXotYWR2LXg9IjE2NjQiIA0KZD0iTTQ1OCA2NTNxLTc0IDE2MiAtNzQgMzcxaC0yNTZ2LTk2cTAgLTc4IDk0LjUgLTE2MnQyMzUuNSAtMTEzek0xNTM2IDkyOHY5NmgtMjU2cTAgLTIwOSAtNzQgLTM3MXExNDEgMjkgMjM1LjUgMTEzdDk0LjUgMTYyek0xNjY0IDEwNTZ2LTEyOHEwIC03MSAtNDEuNSAtMTQzdC0xMTIgLTEzMHQtMTczIC05Ny41dC0yMTUuNSAtNDQuNXEtNDIgLTU0IC05NSAtOTVxLTM4IC0zNCAtNTIuNSAtNzIuNXQtMTQuNSAtODkuNXEwIC01NCAzMC41IC05MQ0KdDk3LjUgLTM3cTc1IDAgMTMzLjUgLTQ1LjV0NTguNSAtMTE0LjV2LTY0cTAgLTE0IC05IC0yM3QtMjMgLTloLTgzMnEtMTQgMCAtMjMgOXQtOSAyM3Y2NHEwIDY5IDU4LjUgMTE0LjV0MTMzLjUgNDUuNXE2NyAwIDk3LjUgMzd0MzAuNSA5MXEwIDUxIC0xNC41IDg5LjV0LTUyLjUgNzIuNXEtNTMgNDEgLTk1IDk1cS0xMTMgNSAtMjE1LjUgNDQuNXQtMTczIDk3LjV0LTExMiAxMzB0LTQxLjUgMTQzdjEyOHEwIDQwIDI4IDY4dDY4IDI4aDI4OHY5Ng0KcTAgNjYgNDcgMTEzdDExMyA0N2g1NzZxNjYgMCAxMTMgLTQ3dDQ3IC0xMTN2LTk2aDI4OHE0MCAwIDY4IC0yOHQyOCAtNjh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJnaXRodWJfc2lnbiIgdW5pY29kZT0iJiN4ZjA5MjsiIA0KZD0iTTUxOSAzMzZxNCA2IC0zIDEzcS05IDcgLTE0IDJxLTQgLTYgMyAtMTNxOSAtNyAxNCAtMnpNNDkxIDM3N3EtNSA3IC0xMiA0cS02IC00IDAgLTEycTcgLTggMTIgLTVxNiA0IDAgMTN6TTQ1MCA0MTdxMiA0IC01IDhxLTcgMiAtOCAtMnEtMyAtNSA0IC04cTggLTIgOSAyek00NzEgMzk0cTIgMSAxLjUgNC41dC0zLjUgNS41cS02IDcgLTEwIDN0MSAtMTFxNiAtNiAxMSAtMnpNNTU3IDMxOXEyIDcgLTkgMTFxLTkgMyAtMTMgLTQNCnEtMiAtNyA5IC0xMXE5IC0zIDEzIDR6TTU5OSAzMTZxMCA4IC0xMiA4cS0xMCAwIC0xMCAtOHQxMSAtOHQxMSA4ek02MzggMzIzcS0yIDcgLTEzIDV0LTkgLTlxMiAtOCAxMiAtNnQxMCAxMHpNMTI4MCA2NDBxMCAyMTIgLTE1MCAzNjJ0LTM2MiAxNTB0LTM2MiAtMTUwdC0xNTAgLTM2MnEwIC0xNjcgOTggLTMwMC41dDI1MiAtMTg1LjVxMTggLTMgMjYuNSA1dDguNSAyMHEwIDUyIC0xIDk1cS02IC0xIC0xNS41IC0yLjV0LTM1LjUgLTJ0LTQ4IDQNCnQtNDMuNSAyMHQtMjkuNSA0MS41cS0yMyA1OSAtNTcgNzRxLTIgMSAtNC41IDMuNWwtOCA4dC03IDkuNXQ0IDcuNXQxOS41IDMuNXE2IDAgMTUgLTJ0MzAgLTE1LjV0MzMgLTM1LjVxMTYgLTI4IDM3LjUgLTQydDQzLjUgLTE0dDM4IDMuNXQzMCA5LjVxNyA0NyAzMyA2OXEtNDkgNiAtODYgMTguNXQtNzMgMzl0LTU1LjUgNzZ0LTE5LjUgMTE5LjVxMCA3OSA1MyAxMzdxLTI0IDYyIDUgMTM2cTE5IDYgNTQuNSAtNy41dDYwLjUgLTI5LjVsMjYgLTE2DQpxNTggMTcgMTI4IDE3dDEyOCAtMTdxMTEgNyAyOC41IDE4dDU1LjUgMjZ0NTcgOXEyOSAtNzQgNSAtMTM2cTUzIC01OCA1MyAtMTM3cTAgLTU3IC0xNCAtMTAwLjV0LTM1LjUgLTcwdC01My41IC00NC41dC02Mi41IC0yNnQtNjguNSAtMTJxMzUgLTMxIDM1IC05NXEwIC00MCAtMC41IC04OXQtMC41IC01MXEwIC0xMiA4LjUgLTIwdDI2LjUgLTVxMTU0IDUyIDI1MiAxODUuNXQ5OCAzMDAuNXpNMTUzNiAxMTIwdi05NjANCnEwIC0xMTkgLTg0LjUgLTIwMy41dC0yMDMuNSAtODQuNWgtOTYwcS0xMTkgMCAtMjAzLjUgODQuNXQtODQuNSAyMDMuNXY5NjBxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDk2MHExMTkgMCAyMDMuNSAtODQuNXQ4NC41IC0yMDMuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVwbG9hZF9hbHQiIHVuaWNvZGU9IiYjeGYwOTM7IiBob3Jpei1hZHYteD0iMTY2NCIgDQpkPSJNMTI4MCA2NHEwIDI2IC0xOSA0NXQtNDUgMTl0LTQ1IC0xOXQtMTkgLTQ1dDE5IC00NXQ0NSAtMTl0NDUgMTl0MTkgNDV6TTE1MzYgNjRxMCAyNiAtMTkgNDV0LTQ1IDE5dC00NSAtMTl0LTE5IC00NXQxOSAtNDV0NDUgLTE5dDQ1IDE5dDE5IDQ1ek0xNjY0IDI4OHYtMzIwcTAgLTQwIC0yOCAtNjh0LTY4IC0yOGgtMTQ3MnEtNDAgMCAtNjggMjh0LTI4IDY4djMyMHEwIDQwIDI4IDY4dDY4IDI4aDQyN3EyMSAtNTYgNzAuNSAtOTINCnQxMTAuNSAtMzZoMjU2cTYxIDAgMTEwLjUgMzZ0NzAuNSA5Mmg0MjdxNDAgMCA2OCAtMjh0MjggLTY4ek0xMzM5IDkzNnEtMTcgLTQwIC01OSAtNDBoLTI1NnYtNDQ4cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMjU2cS0yNiAwIC00NSAxOXQtMTkgNDV2NDQ4aC0yNTZxLTQyIDAgLTU5IDQwcS0xNyAzOSAxNCA2OWw0NDggNDQ4cTE4IDE5IDQ1IDE5dDQ1IC0xOWw0NDggLTQ0OHEzMSAtMzAgMTQgLTY5eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ibGVtb24iIHVuaWNvZGU9IiYjeGYwOTQ7IiANCmQ9Ik0xNDA3IDcxMHEwIDQ0IC03IDExMy41dC0xOCA5Ni41cS0xMiAzMCAtMTcgNDR0LTkgMzYuNXQtNCA0OC41cTAgMjMgNSA2OC41dDUgNjcuNXEwIDM3IC0xMCA1NXEtNCAxIC0xMyAxcS0xOSAwIC01OCAtNC41dC01OSAtNC41cS02MCAwIC0xNzYgMjR0LTE3NSAyNHEtNDMgMCAtOTQuNSAtMTEuNXQtODUgLTIzLjV0LTg5LjUgLTM0cS0xMzcgLTU0IC0yMDIgLTEwM3EtOTYgLTczIC0xNTkuNSAtMTg5LjV0LTg4IC0yMzZ0LTI0LjUgLTI0OC41DQpxMCAtNDAgMTIuNSAtMTIwdDEyLjUgLTEyMXEwIC0yMyAtMTEgLTY2LjV0LTExIC02NS41dDEyIC0zNi41dDM0IC0xNC41cTI0IDAgNzIuNSAxMXQ3My41IDExcTU3IDAgMTY5LjUgLTE1LjV0MTY5LjUgLTE1LjVxMTgxIDAgMjg0IDM2cTEyOSA0NSAyMzUuNSAxNTIuNXQxNjYgMjQ1LjV0NTkuNSAyNzV6TTE1MzUgNzEycTAgLTE2NSAtNzAgLTMyNy41dC0xOTYgLTI4OHQtMjgxIC0xODAuNXEtMTI0IC00NCAtMzI2IC00NA0KcS01NyAwIC0xNzAgMTQuNXQtMTY5IDE0LjVxLTI0IDAgLTcyLjUgLTE0LjV0LTczLjUgLTE0LjVxLTczIDAgLTEyMy41IDU1LjV0LTUwLjUgMTI4LjVxMCAyNCAxMSA2OHQxMSA2N3EwIDQwIC0xMi41IDEyMC41dC0xMi41IDEyMS41cTAgMTExIDE4IDIxNy41dDU0LjUgMjA5LjV0MTAwLjUgMTk0dDE1MCAxNTZxNzggNTkgMjMyIDEyMHExOTQgNzggMzE2IDc4cTYwIDAgMTc1LjUgLTI0dDE3My41IC0yNHExOSAwIDU3IDV0NTggNQ0KcTgxIDAgMTE4IC01MC41dDM3IC0xMzQuNXEwIC0yMyAtNSAtNjh0LTUgLTY4cTAgLTEzIDIgLTI1dDMuNSAtMTYuNXQ3LjUgLTIwLjV0OCAtMjBxMTYgLTQwIDI1IC0xMTguNXQ5IC0xMzYuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InBob25lIiB1bmljb2RlPSImI3hmMDk1OyIgaG9yaXotYWR2LXg9IjE0MDgiIA0KZD0iTTE0MDggMjk2cTAgLTI3IC0xMCAtNzAuNXQtMjEgLTY4LjVxLTIxIC01MCAtMTIyIC0xMDZxLTk0IC01MSAtMTg2IC01MXEtMjcgMCAtNTMgMy41dC01Ny41IDEyLjV0LTQ3IDE0LjV0LTU1LjUgMjAuNXQtNDkgMThxLTk4IDM1IC0xNzUgODNxLTEyNyA3OSAtMjY0IDIxNnQtMjE2IDI2NHEtNDggNzcgLTgzIDE3NXEtMyA5IC0xOCA0OXQtMjAuNSA1NS41dC0xNC41IDQ3dC0xMi41IDU3LjV0LTMuNSA1M3EwIDkyIDUxIDE4Ng0KcTU2IDEwMSAxMDYgMTIycTI1IDExIDY4LjUgMjF0NzAuNSAxMHExNCAwIDIxIC0zcTE4IC02IDUzIC03NnExMSAtMTkgMzAgLTU0dDM1IC02My41dDMxIC01My41cTMgLTQgMTcuNSAtMjV0MjEuNSAtMzUuNXQ3IC0yOC41cTAgLTIwIC0yOC41IC01MHQtNjIgLTU1dC02MiAtNTN0LTI4LjUgLTQ2cTAgLTkgNSAtMjIuNXQ4LjUgLTIwLjV0MTQgLTI0dDExLjUgLTE5cTc2IC0xMzcgMTc0IC0yMzV0MjM1IC0xNzRxMiAtMSAxOSAtMTEuNXQyNCAtMTQNCnQyMC41IC04LjV0MjIuNSAtNXExOCAwIDQ2IDI4LjV0NTMgNjJ0NTUgNjJ0NTAgMjguNXExNCAwIDI4LjUgLTd0MzUuNSAtMjEuNXQyNSAtMTcuNXEyNSAtMTUgNTMuNSAtMzF0NjMuNSAtMzV0NTQgLTMwcTcwIC0zNSA3NiAtNTNxMyAtNyAzIC0yMXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImNoZWNrX2VtcHR5IiB1bmljb2RlPSImI3hmMDk2OyIgaG9yaXotYWR2LXg9IjE0MDgiIA0KZD0iTTExMjAgMTI4MGgtODMycS02NiAwIC0xMTMgLTQ3dC00NyAtMTEzdi04MzJxMCAtNjYgNDcgLTExM3QxMTMgLTQ3aDgzMnE2NiAwIDExMyA0N3Q0NyAxMTN2ODMycTAgNjYgLTQ3IDExM3QtMTEzIDQ3ek0xNDA4IDExMjB2LTgzMnEwIC0xMTkgLTg0LjUgLTIwMy41dC0yMDMuNSAtODQuNWgtODMycS0xMTkgMCAtMjAzLjUgODQuNXQtODQuNSAyMDMuNXY4MzJxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDgzMg0KcTExOSAwIDIwMy41IC04NC41dDg0LjUgLTIwMy41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iYm9va21hcmtfZW1wdHkiIHVuaWNvZGU9IiYjeGYwOTc7IiBob3Jpei1hZHYteD0iMTI4MCIgDQpkPSJNMTE1MiAxMjgwaC0xMDI0di0xMjQybDQyMyA0MDZsODkgODVsODkgLTg1bDQyMyAtNDA2djEyNDJ6TTExNjQgMTQwOHEyMyAwIDQ0IC05cTMzIC0xMyA1Mi41IC00MXQxOS41IC02MnYtMTI4OXEwIC0zNCAtMTkuNSAtNjJ0LTUyLjUgLTQxcS0xOSAtOCAtNDQgLThxLTQ4IDAgLTgzIDMybC00NDEgNDI0bC00NDEgLTQyNHEtMzYgLTMzIC04MyAtMzNxLTIzIDAgLTQ0IDlxLTMzIDEzIC01Mi41IDQxdC0xOS41IDYydjEyODkNCnEwIDM0IDE5LjUgNjJ0NTIuNSA0MXEyMSA5IDQ0IDloMTA0OHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InBob25lX3NpZ24iIHVuaWNvZGU9IiYjeGYwOTg7IiANCmQ9Ik0xMjgwIDM0M3EwIDExIC0yIDE2dC0xOCAxNi41dC00MC41IDI1dC00Ny41IDI2LjV0LTQ1LjUgMjV0LTI4LjUgMTVxLTUgMyAtMTkgMTN0LTI1IDE1dC0yMSA1cS0xNSAwIC0zNi41IC0yMC41dC0zOS41IC00NXQtMzguNSAtNDV0LTMzLjUgLTIwLjVxLTcgMCAtMTYuNSAzLjV0LTE1LjUgNi41dC0xNyA5LjV0LTE0IDguNXEtOTkgNTUgLTE3MCAxMjYuNXQtMTI3IDE3MC41cS0yIDMgLTguNSAxNHQtOS41IDE3dC02LjUgMTUuNQ0KdC0zLjUgMTYuNXEwIDEzIDIwLjUgMzMuNXQ0NSAzOC41dDQ1IDM5LjV0MjAuNSAzNi41cTAgMTAgLTUgMjF0LTE1IDI1dC0xMyAxOXEtMyA2IC0xNSAyOC41dC0yNSA0NS41dC0yNi41IDQ3LjV0LTI1IDQwLjV0LTE2LjUgMTh0LTE2IDJxLTQ4IDAgLTEwMSAtMjJxLTQ2IC0yMSAtODAgLTk0LjV0LTM0IC0xMzAuNXEwIC0xNiAyLjUgLTM0dDUgLTMwLjV0OSAtMzN0MTAgLTI5LjV0MTIuNSAtMzN0MTEgLTMwcTYwIC0xNjQgMjE2LjUgLTMyMC41DQp0MzIwLjUgLTIxNi41cTYgLTIgMzAgLTExdDMzIC0xMi41dDI5LjUgLTEwdDMzIC05dDMwLjUgLTV0MzQgLTIuNXE1NyAwIDEzMC41IDM0dDk0LjUgODBxMjIgNTMgMjIgMTAxek0xNTM2IDExMjB2LTk2MHEwIC0xMTkgLTg0LjUgLTIwMy41dC0yMDMuNSAtODQuNWgtOTYwcS0xMTkgMCAtMjAzLjUgODQuNXQtODQuNSAyMDMuNXY5NjBxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDk2MHExMTkgMCAyMDMuNSAtODQuNXQ4NC41IC0yMDMuNXoNCiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idHdpdHRlciIgdW5pY29kZT0iJiN4ZjA5OTsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik0xNjIwIDExMjhxLTY3IC05OCAtMTYyIC0xNjdxMSAtMTQgMSAtNDJxMCAtMTMwIC0zOCAtMjU5LjV0LTExNS41IC0yNDguNXQtMTg0LjUgLTIxMC41dC0yNTggLTE0NnQtMzIzIC01NC41cS0yNzEgMCAtNDk2IDE0NXEzNSAtNCA3OCAtNHEyMjUgMCA0MDEgMTM4cS0xMDUgMiAtMTg4IDY0LjV0LTExNCAxNTkuNXEzMyAtNSA2MSAtNXE0MyAwIDg1IDExcS0xMTIgMjMgLTE4NS41IDExMS41dC03My41IDIwNS41djRxNjggLTM4IDE0NiAtNDENCnEtNjYgNDQgLTEwNSAxMTV0LTM5IDE1NHEwIDg4IDQ0IDE2M3ExMjEgLTE0OSAyOTQuNSAtMjM4LjV0MzcxLjUgLTk5LjVxLTggMzggLTggNzRxMCAxMzQgOTQuNSAyMjguNXQyMjguNSA5NC41cTE0MCAwIDIzNiAtMTAycTEwOSAyMSAyMDUgNzhxLTM3IC0xMTUgLTE0MiAtMTc4cTkzIDEwIDE4NiA1MHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImZhY2Vib29rIiB1bmljb2RlPSImI3hmMDlhOyIgaG9yaXotYWR2LXg9IjEwMjQiIA0KZD0iTTk1OSAxNTI0di0yNjRoLTE1N3EtODYgMCAtMTE2IC0zNnQtMzAgLTEwOHYtMTg5aDI5M2wtMzkgLTI5NmgtMjU0di03NTloLTMwNnY3NTloLTI1NXYyOTZoMjU1djIxOHEwIDE4NiAxMDQgMjg4LjV0Mjc3IDEwMi41cTE0NyAwIDIyOCAtMTJ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJnaXRodWIiIHVuaWNvZGU9IiYjeGYwOWI7IiANCmQ9Ik03NjggMTQwOHEyMDkgMCAzODUuNSAtMTAzdDI3OS41IC0yNzkuNXQxMDMgLTM4NS41cTAgLTI1MSAtMTQ2LjUgLTQ1MS41dC0zNzguNSAtMjc3LjVxLTI3IC01IC00MCA3dC0xMyAzMHEwIDMgMC41IDc2LjV0MC41IDEzNC41cTAgOTcgLTUyIDE0MnE1NyA2IDEwMi41IDE4dDk0IDM5dDgxIDY2LjV0NTMgMTA1dDIwLjUgMTUwLjVxMCAxMTkgLTc5IDIwNnEzNyA5MSAtOCAyMDRxLTI4IDkgLTgxIC0xMXQtOTIgLTQ0bC0zOCAtMjQNCnEtOTMgMjYgLTE5MiAyNnQtMTkyIC0yNnEtMTYgMTEgLTQyLjUgMjd0LTgzLjUgMzguNXQtODUgMTMuNXEtNDUgLTExMyAtOCAtMjA0cS03OSAtODcgLTc5IC0yMDZxMCAtODUgMjAuNSAtMTUwdDUyLjUgLTEwNXQ4MC41IC02N3Q5NCAtMzl0MTAyLjUgLTE4cS0zOSAtMzYgLTQ5IC0xMDNxLTIxIC0xMCAtNDUgLTE1dC01NyAtNXQtNjUuNSAyMS41dC01NS41IDYyLjVxLTE5IDMyIC00OC41IDUydC00OS41IDI0bC0yMCAzcS0yMSAwIC0yOSAtNC41DQp0LTUgLTExLjV0OSAtMTR0MTMgLTEybDcgLTVxMjIgLTEwIDQzLjUgLTM4dDMxLjUgLTUxbDEwIC0yM3ExMyAtMzggNDQgLTYxLjV0NjcgLTMwdDY5LjUgLTd0NTUuNSAzLjVsMjMgNHEwIC0zOCAwLjUgLTg4LjV0MC41IC01NC41cTAgLTE4IC0xMyAtMzB0LTQwIC03cS0yMzIgNzcgLTM3OC41IDI3Ny41dC0xNDYuNSA0NTEuNXEwIDIwOSAxMDMgMzg1LjV0Mjc5LjUgMjc5LjV0Mzg1LjUgMTAzek0yOTEgMzA1cTMgNyAtNyAxMg0KcS0xMCAzIC0xMyAtMnEtMyAtNyA3IC0xMnE5IC02IDEzIDJ6TTMyMiAyNzFxNyA1IC0yIDE2cS0xMCA5IC0xNiAzcS03IC01IDIgLTE2cTEwIC0xMCAxNiAtM3pNMzUyIDIyNnE5IDcgMCAxOXEtOCAxMyAtMTcgNnEtOSAtNSAwIC0xOHQxNyAtN3pNMzk0IDE4NHE4IDggLTQgMTlxLTEyIDEyIC0yMCAzcS05IC04IDQgLTE5cTEyIC0xMiAyMCAtM3pNNDUxIDE1OXEzIDExIC0xMyAxNnEtMTUgNCAtMTkgLTd0MTMgLTE1cTE1IC02IDE5IDZ6DQpNNTE0IDE1NHEwIDEzIC0xNyAxMXEtMTYgMCAtMTYgLTExcTAgLTEzIDE3IC0xMXExNiAwIDE2IDExek01NzIgMTY0cS0yIDExIC0xOCA5cS0xNiAtMyAtMTQgLTE1dDE4IC04dDE0IDE0eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5sb2NrIiB1bmljb2RlPSImI3hmMDljOyIgaG9yaXotYWR2LXg9IjE2NjQiIA0KZD0iTTE2NjQgOTYwdi0yNTZxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC02NHEtMjYgMCAtNDUgMTl0LTE5IDQ1djI1NnEwIDEwNiAtNzUgMTgxdC0xODEgNzV0LTE4MSAtNzV0LTc1IC0xODF2LTE5Mmg5NnE0MCAwIDY4IC0yOHQyOCAtNjh2LTU3NnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTk2MHEtNDAgMCAtNjggMjh0LTI4IDY4djU3NnEwIDQwIDI4IDY4dDY4IDI4aDY3MnYxOTJxMCAxODUgMTMxLjUgMzE2LjV0MzE2LjUgMTMxLjUNCnQzMTYuNSAtMTMxLjV0MTMxLjUgLTMxNi41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY3JlZGl0X2NhcmQiIHVuaWNvZGU9IiYjeGYwOWQ7IiBob3Jpei1hZHYteD0iMTkyMCIgDQpkPSJNMTc2MCAxNDA4cTY2IDAgMTEzIC00N3Q0NyAtMTEzdi0xMjE2cTAgLTY2IC00NyAtMTEzdC0xMTMgLTQ3aC0xNjAwcS02NiAwIC0xMTMgNDd0LTQ3IDExM3YxMjE2cTAgNjYgNDcgMTEzdDExMyA0N2gxNjAwek0xNjAgMTI4MHEtMTMgMCAtMjIuNSAtOS41dC05LjUgLTIyLjV2LTIyNGgxNjY0djIyNHEwIDEzIC05LjUgMjIuNXQtMjIuNSA5LjVoLTE2MDB6TTE3NjAgMHExMyAwIDIyLjUgOS41dDkuNSAyMi41djYwOGgtMTY2NHYtNjA4DQpxMCAtMTMgOS41IC0yMi41dDIyLjUgLTkuNWgxNjAwek0yNTYgMTI4djEyOGgyNTZ2LTEyOGgtMjU2ek02NDAgMTI4djEyOGgzODR2LTEyOGgtMzg0eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0icnNzIiB1bmljb2RlPSImI3hmMDllOyIgaG9yaXotYWR2LXg9IjE0MDgiIA0KZD0iTTM4NCAxOTJxMCAtODAgLTU2IC0xMzZ0LTEzNiAtNTZ0LTEzNiA1NnQtNTYgMTM2dDU2IDEzNnQxMzYgNTZ0MTM2IC01NnQ1NiAtMTM2ek04OTYgNjlxMiAtMjggLTE3IC00OHEtMTggLTIxIC00NyAtMjFoLTEzNXEtMjUgMCAtNDMgMTYuNXQtMjAgNDEuNXEtMjIgMjI5IC0xODQuNSAzOTEuNXQtMzkxLjUgMTg0LjVxLTI1IDIgLTQxLjUgMjB0LTE2LjUgNDN2MTM1cTAgMjkgMjEgNDdxMTcgMTcgNDMgMTdoNXExNjAgLTEzIDMwNiAtODAuNQ0KdDI1OSAtMTgxLjVxMTE0IC0xMTMgMTgxLjUgLTI1OXQ4MC41IC0zMDZ6TTE0MDggNjdxMiAtMjcgLTE4IC00N3EtMTggLTIwIC00NiAtMjBoLTE0M3EtMjYgMCAtNDQuNSAxNy41dC0xOS41IDQyLjVxLTEyIDIxNSAtMTAxIDQwOC41dC0yMzEuNSAzMzZ0LTMzNiAyMzEuNXQtNDA4LjUgMTAycS0yNSAxIC00Mi41IDE5LjV0LTE3LjUgNDMuNXYxNDNxMCAyOCAyMCA0NnExOCAxOCA0NCAxOGgzcTI2MiAtMTMgNTAxLjUgLTEyMHQ0MjUuNSAtMjk0DQpxMTg3IC0xODYgMjk0IC00MjUuNXQxMjAgLTUwMS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iaGRkIiB1bmljb2RlPSImI3hmMGEwOyIgDQpkPSJNMTA0MCAzMjBxMCAtMzMgLTIzLjUgLTU2LjV0LTU2LjUgLTIzLjV0LTU2LjUgMjMuNXQtMjMuNSA1Ni41dDIzLjUgNTYuNXQ1Ni41IDIzLjV0NTYuNSAtMjMuNXQyMy41IC01Ni41ek0xMjk2IDMyMHEwIC0zMyAtMjMuNSAtNTYuNXQtNTYuNSAtMjMuNXQtNTYuNSAyMy41dC0yMy41IDU2LjV0MjMuNSA1Ni41dDU2LjUgMjMuNXQ1Ni41IC0yMy41dDIzLjUgLTU2LjV6TTE0MDggMTYwdjMyMHEwIDEzIC05LjUgMjIuNXQtMjIuNSA5LjUNCmgtMTIxNnEtMTMgMCAtMjIuNSAtOS41dC05LjUgLTIyLjV2LTMyMHEwIC0xMyA5LjUgLTIyLjV0MjIuNSAtOS41aDEyMTZxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXpNMTc4IDY0MGgxMTgwbC0xNTcgNDgycS00IDEzIC0xNiAyMS41dC0yNiA4LjVoLTc4MnEtMTQgMCAtMjYgLTguNXQtMTYgLTIxLjV6TTE1MzYgNDgwdi0zMjBxMCAtNjYgLTQ3IC0xMTN0LTExMyAtNDdoLTEyMTZxLTY2IDAgLTExMyA0N3QtNDcgMTEzdjMyMHEwIDI1IDE2IDc1DQpsMTk3IDYwNnExNyA1MyA2MyA4NnQxMDEgMzNoNzgycTU1IDAgMTAxIC0zM3Q2MyAtODZsMTk3IC02MDZxMTYgLTUwIDE2IC03NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImJ1bGxob3JuIiB1bmljb2RlPSImI3hmMGExOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE2NjQgODk2cTUzIDAgOTAuNSAtMzcuNXQzNy41IC05MC41dC0zNy41IC05MC41dC05MC41IC0zNy41di0zODRxMCAtNTIgLTM4IC05MHQtOTAgLTM4cS00MTcgMzQ3IC04MTIgMzgwcS01OCAtMTkgLTkxIC02NnQtMzEgLTEwMC41dDQwIC05Mi41cS0yMCAtMzMgLTIzIC02NS41dDYgLTU4dDMzLjUgLTU1dDQ4IC01MHQ2MS41IC01MC41cS0yOSAtNTggLTExMS41IC04M3QtMTY4LjUgLTExLjV0LTEzMiA1NS41cS03IDIzIC0yOS41IDg3LjUNCnQtMzIgOTQuNXQtMjMgODl0LTE1IDEwMXQzLjUgOTguNXQyMiAxMTAuNWgtMTIycS02NiAwIC0xMTMgNDd0LTQ3IDExM3YxOTJxMCA2NiA0NyAxMTN0MTEzIDQ3aDQ4MHE0MzUgMCA4OTYgMzg0cTUyIDAgOTAgLTM4dDM4IC05MHYtMzg0ek0xNTM2IDI5MnY5NTRxLTM5NCAtMzAyIC03NjggLTM0M3YtMjcwcTM3NyAtNDIgNzY4IC0zNDF6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJiZWxsIiB1bmljb2RlPSImI3hmMGEyOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTkxMiAtMTYwcTAgMTYgLTE2IDE2cS01OSAwIC0xMDEuNSA0Mi41dC00Mi41IDEwMS41cTAgMTYgLTE2IDE2dC0xNiAtMTZxMCAtNzMgNTEuNSAtMTI0LjV0MTI0LjUgLTUxLjVxMTYgMCAxNiAxNnpNMjQ2IDEyOGgxMzAwcS0yNjYgMzAwIC0yNjYgODMycTAgNTEgLTI0IDEwNXQtNjkgMTAzdC0xMjEuNSA4MC41dC0xNjkuNSAzMS41dC0xNjkuNSAtMzEuNXQtMTIxLjUgLTgwLjV0LTY5IC0xMDN0LTI0IC0xMDVxMCAtNTMyIC0yNjYgLTgzMnoNCk0xNzI4IDEyOHEwIC01MiAtMzggLTkwdC05MCAtMzhoLTQ0OHEwIC0xMDYgLTc1IC0xODF0LTE4MSAtNzV0LTE4MSA3NXQtNzUgMTgxaC00NDhxLTUyIDAgLTkwIDM4dC0zOCA5MHE1MCA0MiA5MSA4OHQ4NSAxMTkuNXQ3NC41IDE1OC41dDUwIDIwNnQxOS41IDI2MHEwIDE1MiAxMTcgMjgyLjV0MzA3IDE1OC41cS04IDE5IC04IDM5cTAgNDAgMjggNjh0NjggMjh0NjggLTI4dDI4IC02OHEwIC0yMCAtOCAtMzlxMTkwIC0yOCAzMDcgLTE1OC41DQp0MTE3IC0yODIuNXEwIC0xMzkgMTkuNSAtMjYwdDUwIC0yMDZ0NzQuNSAtMTU4LjV0ODUgLTExOS41dDkxIC04OHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImNlcnRpZmljYXRlIiB1bmljb2RlPSImI3hmMGEzOyIgDQpkPSJNMTM3NiA2NDBsMTM4IC0xMzVxMzAgLTI4IDIwIC03MHEtMTIgLTQxIC01MiAtNTFsLTE4OCAtNDhsNTMgLTE4NnExMiAtNDEgLTE5IC03MHEtMjkgLTMxIC03MCAtMTlsLTE4NiA1M2wtNDggLTE4OHEtMTAgLTQwIC01MSAtNTJxLTEyIC0yIC0xOSAtMnEtMzEgMCAtNTEgMjJsLTEzNSAxMzhsLTEzNSAtMTM4cS0yOCAtMzAgLTcwIC0yMHEtNDEgMTEgLTUxIDUybC00OCAxODhsLTE4NiAtNTNxLTQxIC0xMiAtNzAgMTlxLTMxIDI5IC0xOSA3MA0KbDUzIDE4NmwtMTg4IDQ4cS00MCAxMCAtNTIgNTFxLTEwIDQyIDIwIDcwbDEzOCAxMzVsLTEzOCAxMzVxLTMwIDI4IC0yMCA3MHExMiA0MSA1MiA1MWwxODggNDhsLTUzIDE4NnEtMTIgNDEgMTkgNzBxMjkgMzEgNzAgMTlsMTg2IC01M2w0OCAxODhxMTAgNDEgNTEgNTFxNDEgMTIgNzAgLTE5bDEzNSAtMTM5bDEzNSAxMzlxMjkgMzAgNzAgMTlxNDEgLTEwIDUxIC01MWw0OCAtMTg4bDE4NiA1M3E0MSAxMiA3MCAtMTlxMzEgLTI5IDE5IC03MA0KbC01MyAtMTg2bDE4OCAtNDhxNDAgLTEwIDUyIC01MXExMCAtNDIgLTIwIC03MHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImhhbmRfcmlnaHQiIHVuaWNvZGU9IiYjeGYwYTQ7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMjU2IDE5MnEwIDI2IC0xOSA0NXQtNDUgMTl0LTQ1IC0xOXQtMTkgLTQ1dDE5IC00NXQ0NSAtMTl0NDUgMTl0MTkgNDV6TTE2NjQgNzY4cTAgNTEgLTM5IDg5LjV0LTg5IDM4LjVoLTU3NnEwIDIwIDE1IDQ4LjV0MzMgNTV0MzMgNjh0MTUgODQuNXEwIDY3IC00NC41IDk3LjV0LTExNS41IDMwLjVxLTI0IDAgLTkwIC0xMzlxLTI0IC00NCAtMzcgLTY1cS00MCAtNjQgLTExMiAtMTQ1cS03MSAtODEgLTEwMSAtMTA2DQpxLTY5IC01NyAtMTQwIC01N2gtMzJ2LTY0MGgzMnE3MiAwIDE2NyAtMzJ0MTkzLjUgLTY0dDE3OS41IC0zMnExODkgMCAxODkgMTY3cTAgMjYgLTUgNTZxMzAgMTYgNDcuNSA1Mi41dDE3LjUgNzMuNXQtMTggNjlxNTMgNTAgNTMgMTE5cTAgMjUgLTEwIDU1LjV0LTI1IDQ3LjVoMzMxcTUyIDAgOTAgMzh0MzggOTB6TTE3OTIgNzY5cTAgLTEwNSAtNzUuNSAtMTgxdC0xODAuNSAtNzZoLTE2OXEtNCAtNjIgLTM3IC0xMTlxMyAtMjEgMyAtNDMNCnEwIC0xMDEgLTYwIC0xNzhxMSAtMTM5IC04NSAtMjE5LjV0LTIyNyAtODAuNXEtMTMzIDAgLTMyMiA2OXEtMTY0IDU5IC0yMjMgNTloLTI4OHEtNTMgMCAtOTAuNSAzNy41dC0zNy41IDkwLjV2NjQwcTAgNTMgMzcuNSA5MC41dDkwLjUgMzcuNWgyODhxMTAgMCAyMS41IDQuNXQyMy41IDE0dDIyLjUgMTh0MjQgMjIuNXQyMC41IDIxLjV0MTkgMjEuNXQxNCAxN3E2NSA3NCAxMDAgMTI5cTEzIDIxIDMzIDYydDM3IDcydDQwLjUgNjN0NTUgNDkuNQ0KdDY5LjUgMTcuNXExMjUgMCAyMDYuNSAtNjd0ODEuNSAtMTg5cTAgLTY4IC0yMiAtMTI4aDM3NHExMDQgMCAxODAgLTc2dDc2IC0xNzl6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJoYW5kX2xlZnQiIHVuaWNvZGU9IiYjeGYwYTU7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTM3NiAxMjhoMzJ2NjQwaC0zMnEtMzUgMCAtNjcuNSAxMnQtNjIuNSAzN3QtNTAgNDZ0LTQ5IDU0cS04IDkgLTEyIDE0cS03MiA4MSAtMTEyIDE0NXEtMTQgMjIgLTM4IDY4cS0xIDMgLTEwLjUgMjIuNXQtMTguNSAzNnQtMjAgMzUuNXQtMjEuNSAzMC41dC0xOC41IDExLjVxLTcxIDAgLTExNS41IC0zMC41dC00NC41IC05Ny41cTAgLTQzIDE1IC04NC41dDMzIC02OHQzMyAtNTV0MTUgLTQ4LjVoLTU3NnEtNTAgMCAtODkgLTM4LjUNCnQtMzkgLTg5LjVxMCAtNTIgMzggLTkwdDkwIC0zOGgzMzFxLTE1IC0xNyAtMjUgLTQ3LjV0LTEwIC01NS41cTAgLTY5IDUzIC0xMTlxLTE4IC0zMiAtMTggLTY5dDE3LjUgLTczLjV0NDcuNSAtNTIuNXEtNCAtMjQgLTQgLTU2cTAgLTg1IDQ4LjUgLTEyNnQxMzUuNSAtNDFxODQgMCAxODMgMzJ0MTk0IDY0dDE2NyAzMnpNMTY2NCAxOTJxMCAyNiAtMTkgNDV0LTQ1IDE5dC00NSAtMTl0LTE5IC00NXQxOSAtNDV0NDUgLTE5dDQ1IDE5dDE5IDQ1eg0KTTE3OTIgNzY4di02NDBxMCAtNTMgLTM3LjUgLTkwLjV0LTkwLjUgLTM3LjVoLTI4OHEtNTkgMCAtMjIzIC01OXEtMTkwIC02OSAtMzE3IC02OXEtMTQyIDAgLTIzMCA3Ny41dC04NyAyMTcuNWwxIDVxLTYxIDc2IC02MSAxNzhxMCAyMiAzIDQzcS0zMyA1NyAtMzcgMTE5aC0xNjlxLTEwNSAwIC0xODAuNSA3NnQtNzUuNSAxODFxMCAxMDMgNzYgMTc5dDE4MCA3NmgzNzRxLTIyIDYwIC0yMiAxMjhxMCAxMjIgODEuNSAxODl0MjA2LjUgNjcNCnEzOCAwIDY5LjUgLTE3LjV0NTUgLTQ5LjV0NDAuNSAtNjN0MzcgLTcydDMzIC02MnEzNSAtNTUgMTAwIC0xMjlxMiAtMyAxNCAtMTd0MTkgLTIxLjV0MjAuNSAtMjEuNXQyNCAtMjIuNXQyMi41IC0xOHQyMy41IC0xNHQyMS41IC00LjVoMjg4cTUzIDAgOTAuNSAtMzcuNXQzNy41IC05MC41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iaGFuZF91cCIgdW5pY29kZT0iJiN4ZjBhNjsiIA0KZD0iTTEyODAgLTY0cTAgMjYgLTE5IDQ1dC00NSAxOXQtNDUgLTE5dC0xOSAtNDV0MTkgLTQ1dDQ1IC0xOXQ0NSAxOXQxOSA0NXpNMTQwOCA3MDBxMCAxODkgLTE2NyAxODlxLTI2IDAgLTU2IC01cS0xNiAzMCAtNTIuNSA0Ny41dC03My41IDE3LjV0LTY5IC0xOHEtNTAgNTMgLTExOSA1M3EtMjUgMCAtNTUuNSAtMTB0LTQ3LjUgLTI1djMzMXEwIDUyIC0zOCA5MHQtOTAgMzhxLTUxIDAgLTg5LjUgLTM5dC0zOC41IC04OXYtNTc2DQpxLTIwIDAgLTQ4LjUgMTV0LTU1IDMzdC02OCAzM3QtODQuNSAxNXEtNjcgMCAtOTcuNSAtNDQuNXQtMzAuNSAtMTE1LjVxMCAtMjQgMTM5IC05MHE0NCAtMjQgNjUgLTM3cTY0IC00MCAxNDUgLTExMnE4MSAtNzEgMTA2IC0xMDFxNTcgLTY5IDU3IC0xNDB2LTMyaDY0MHYzMnEwIDcyIDMyIDE2N3Q2NCAxOTMuNXQzMiAxNzkuNXpNMTUzNiA3MDVxMCAtMTMzIC02OSAtMzIycS01OSAtMTY0IC01OSAtMjIzdi0yODhxMCAtNTMgLTM3LjUgLTkwLjUNCnQtOTAuNSAtMzcuNWgtNjQwcS01MyAwIC05MC41IDM3LjV0LTM3LjUgOTAuNXYyODhxMCAxMCAtNC41IDIxLjV0LTE0IDIzLjV0LTE4IDIyLjV0LTIyLjUgMjR0LTIxLjUgMjAuNXQtMjEuNSAxOXQtMTcgMTRxLTc0IDY1IC0xMjkgMTAwcS0yMSAxMyAtNjIgMzN0LTcyIDM3dC02MyA0MC41dC00OS41IDU1dC0xNy41IDY5LjVxMCAxMjUgNjcgMjA2LjV0MTg5IDgxLjVxNjggMCAxMjggLTIydjM3NHEwIDEwNCA3NiAxODB0MTc5IDc2DQpxMTA1IDAgMTgxIC03NS41dDc2IC0xODAuNXYtMTY5cTYyIC00IDExOSAtMzdxMjEgMyA0MyAzcTEwMSAwIDE3OCAtNjBxMTM5IDEgMjE5LjUgLTg1dDgwLjUgLTIyN3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImhhbmRfZG93biIgdW5pY29kZT0iJiN4ZjBhNzsiIA0KZD0iTTE0MDggNTc2cTAgODQgLTMyIDE4M3QtNjQgMTk0dC0zMiAxNjd2MzJoLTY0MHYtMzJxMCAtMzUgLTEyIC02Ny41dC0zNyAtNjIuNXQtNDYgLTUwdC01NCAtNDlxLTkgLTggLTE0IC0xMnEtODEgLTcyIC0xNDUgLTExMnEtMjIgLTE0IC02OCAtMzhxLTMgLTEgLTIyLjUgLTEwLjV0LTM2IC0xOC41dC0zNS41IC0yMHQtMzAuNSAtMjEuNXQtMTEuNSAtMTguNXEwIC03MSAzMC41IC0xMTUuNXQ5Ny41IC00NC41cTQzIDAgODQuNSAxNXQ2OCAzMw0KdDU1IDMzdDQ4LjUgMTV2LTU3NnEwIC01MCAzOC41IC04OXQ4OS41IC0zOXE1MiAwIDkwIDM4dDM4IDkwdjMzMXE0NiAtMzUgMTAzIC0zNXE2OSAwIDExOSA1M3EzMiAtMTggNjkgLTE4dDczLjUgMTcuNXQ1Mi41IDQ3LjVxMjQgLTQgNTYgLTRxODUgMCAxMjYgNDguNXQ0MSAxMzUuNXpNMTI4MCAxMzQ0cTAgMjYgLTE5IDQ1dC00NSAxOXQtNDUgLTE5dC0xOSAtNDV0MTkgLTQ1dDQ1IC0xOXQ0NSAxOXQxOSA0NXpNMTUzNiA1ODANCnEwIC0xNDIgLTc3LjUgLTIzMHQtMjE3LjUgLTg3bC01IDFxLTc2IC02MSAtMTc4IC02MXEtMjIgMCAtNDMgM3EtNTQgLTMwIC0xMTkgLTM3di0xNjlxMCAtMTA1IC03NiAtMTgwLjV0LTE4MSAtNzUuNXEtMTAzIDAgLTE3OSA3NnQtNzYgMTgwdjM3NHEtNTQgLTIyIC0xMjggLTIycS0xMjEgMCAtMTg4LjUgODEuNXQtNjcuNSAyMDYuNXEwIDM4IDE3LjUgNjkuNXQ0OS41IDU1dDYzIDQwLjV0NzIgMzd0NjIgMzNxNTUgMzUgMTI5IDEwMA0KcTMgMiAxNyAxNHQyMS41IDE5dDIxLjUgMjAuNXQyMi41IDI0dDE4IDIyLjV0MTQgMjMuNXQ0LjUgMjEuNXYyODhxMCA1MyAzNy41IDkwLjV0OTAuNSAzNy41aDY0MHE1MyAwIDkwLjUgLTM3LjV0MzcuNSAtOTAuNXYtMjg4cTAgLTU5IDU5IC0yMjNxNjkgLTE5MCA2OSAtMzE3eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY2lyY2xlX2Fycm93X2xlZnQiIHVuaWNvZGU9IiYjeGYwYTg7IiANCmQ9Ik0xMjgwIDU3NnYxMjhxMCAyNiAtMTkgNDV0LTQ1IDE5aC01MDJsMTg5IDE4OXExOSAxOSAxOSA0NXQtMTkgNDVsLTkxIDkxcS0xOCAxOCAtNDUgMTh0LTQ1IC0xOGwtMzYyIC0zNjJsLTkxIC05MXEtMTggLTE4IC0xOCAtNDV0MTggLTQ1bDkxIC05MWwzNjIgLTM2MnExOCAtMTggNDUgLTE4dDQ1IDE4bDkxIDkxcTE4IDE4IDE4IDQ1dC0xOCA0NWwtMTg5IDE4OWg1MDJxMjYgMCA0NSAxOXQxOSA0NXpNMTUzNiA2NDANCnEwIC0yMDkgLTEwMyAtMzg1LjV0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzdC0yNzkuNSAyNzkuNXQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN0Mzg1LjUgLTEwM3QyNzkuNSAtMjc5LjV0MTAzIC0zODUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImNpcmNsZV9hcnJvd19yaWdodCIgdW5pY29kZT0iJiN4ZjBhOTsiIA0KZD0iTTEyODUgNjQwcTAgMjcgLTE4IDQ1bC05MSA5MWwtMzYyIDM2MnEtMTggMTggLTQ1IDE4dC00NSAtMThsLTkxIC05MXEtMTggLTE4IC0xOCAtNDV0MTggLTQ1bDE4OSAtMTg5aC01MDJxLTI2IDAgLTQ1IC0xOXQtMTkgLTQ1di0xMjhxMCAtMjYgMTkgLTQ1dDQ1IC0xOWg1MDJsLTE4OSAtMTg5cS0xOSAtMTkgLTE5IC00NXQxOSAtNDVsOTEgLTkxcTE4IC0xOCA0NSAtMTh0NDUgMThsMzYyIDM2Mmw5MSA5MXExOCAxOCAxOCA0NXpNMTUzNiA2NDANCnEwIC0yMDkgLTEwMyAtMzg1LjV0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzdC0yNzkuNSAyNzkuNXQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN0Mzg1LjUgLTEwM3QyNzkuNSAtMjc5LjV0MTAzIC0zODUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImNpcmNsZV9hcnJvd191cCIgdW5pY29kZT0iJiN4ZjBhYTsiIA0KZD0iTTEyODQgNjQxcTAgMjcgLTE4IDQ1bC0zNjIgMzYybC05MSA5MXEtMTggMTggLTQ1IDE4dC00NSAtMThsLTkxIC05MWwtMzYyIC0zNjJxLTE4IC0xOCAtMTggLTQ1dDE4IC00NWw5MSAtOTFxMTggLTE4IDQ1IC0xOHQ0NSAxOGwxODkgMTg5di01MDJxMCAtMjYgMTkgLTQ1dDQ1IC0xOWgxMjhxMjYgMCA0NSAxOXQxOSA0NXY1MDJsMTg5IC0xODlxMTkgLTE5IDQ1IC0xOXQ0NSAxOWw5MSA5MXExOCAxOCAxOCA0NXpNMTUzNiA2NDANCnEwIC0yMDkgLTEwMyAtMzg1LjV0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzdC0yNzkuNSAyNzkuNXQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN0Mzg1LjUgLTEwM3QyNzkuNSAtMjc5LjV0MTAzIC0zODUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImNpcmNsZV9hcnJvd19kb3duIiB1bmljb2RlPSImI3hmMGFiOyIgDQpkPSJNMTI4NCA2MzlxMCAyNyAtMTggNDVsLTkxIDkxcS0xOCAxOCAtNDUgMTh0LTQ1IC0xOGwtMTg5IC0xODl2NTAycTAgMjYgLTE5IDQ1dC00NSAxOWgtMTI4cS0yNiAwIC00NSAtMTl0LTE5IC00NXYtNTAybC0xODkgMTg5cS0xOSAxOSAtNDUgMTl0LTQ1IC0xOWwtOTEgLTkxcS0xOCAtMTggLTE4IC00NXQxOCAtNDVsMzYyIC0zNjJsOTEgLTkxcTE4IC0xOCA0NSAtMTh0NDUgMThsOTEgOTFsMzYyIDM2MnExOCAxOCAxOCA0NXpNMTUzNiA2NDANCnEwIC0yMDkgLTEwMyAtMzg1LjV0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzdC0yNzkuNSAyNzkuNXQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN0Mzg1LjUgLTEwM3QyNzkuNSAtMjc5LjV0MTAzIC0zODUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Imdsb2JlIiB1bmljb2RlPSImI3hmMGFjOyIgDQpkPSJNNzY4IDE0MDhxMjA5IDAgMzg1LjUgLTEwM3QyNzkuNSAtMjc5LjV0MTAzIC0zODUuNXQtMTAzIC0zODUuNXQtMjc5LjUgLTI3OS41dC0zODUuNSAtMTAzdC0zODUuNSAxMDN0LTI3OS41IDI3OS41dC0xMDMgMzg1LjV0MTAzIDM4NS41dDI3OS41IDI3OS41dDM4NS41IDEwM3pNMTA0MiA4ODdxLTIgLTEgLTkuNSAtOS41dC0xMy41IC05LjVxMiAwIDQuNSA1dDUgMTF0My41IDdxNiA3IDIyIDE1cTE0IDYgNTIgMTJxMzQgOCA1MSAtMTENCnEtMiAyIDkuNSAxM3QxNC41IDEycTMgMiAxNSA0LjV0MTUgNy41bDIgMjJxLTEyIC0xIC0xNy41IDd0LTYuNSAyMXEwIC0yIC02IC04cTAgNyAtNC41IDh0LTExLjUgLTF0LTkgLTFxLTEwIDMgLTE1IDcuNXQtOCAxNi41dC00IDE1cS0yIDUgLTkuNSAxMXQtOS41IDEwcS0xIDIgLTIuNSA1LjV0LTMgNi41dC00IDUuNXQtNS41IDIuNXQtNyAtNXQtNy41IC0xMHQtNC41IC01cS0zIDIgLTYgMS41dC00LjUgLTF0LTQuNSAtM3QtNSAtMy41DQpxLTMgLTIgLTguNSAtM3QtOC41IC0ycTE1IDUgLTEgMTFxLTEwIDQgLTE2IDNxOSA0IDcuNSAxMnQtOC41IDE0aDVxLTEgNCAtOC41IDguNXQtMTcuNSA4LjV0LTEzIDZxLTggNSAtMzQgOS41dC0zMyAwLjVxLTUgLTYgLTQuNSAtMTAuNXQ0IC0xNHQzLjUgLTEyLjVxMSAtNiAtNS41IC0xM3QtNi41IC0xMnEwIC03IDE0IC0xNS41dDEwIC0yMS41cS0zIC04IC0xNiAtMTZ0LTE2IC0xMnEtNSAtOCAtMS41IC0xOC41dDEwLjUgLTE2LjUNCnEyIC0yIDEuNSAtNHQtMy41IC00LjV0LTUuNSAtNHQtNi41IC0zLjVsLTMgLTJxLTExIC01IC0yMC41IDZ0LTEzLjUgMjZxLTcgMjUgLTE2IDMwcS0yMyA4IC0yOSAtMXEtNSAxMyAtNDEgMjZxLTI1IDkgLTU4IDRxNiAxIDAgMTVxLTcgMTUgLTE5IDEycTMgNiA0IDE3LjV0MSAxMy41cTMgMTMgMTIgMjNxMSAxIDcgOC41dDkuNSAxMy41dDAuNSA2cTM1IC00IDUwIDExcTUgNSAxMS41IDE3dDEwLjUgMTdxOSA2IDE0IDUuNXQxNC41IC01LjUNCnQxNC41IC01cTE0IC0xIDE1LjUgMTF0LTcuNSAyMHExMiAtMSAzIDE3cS00IDcgLTggOXEtMTIgNCAtMjcgLTVxLTggLTQgMiAtOHEtMSAxIC05LjUgLTEwLjV0LTE2LjUgLTE3LjV0LTE2IDVxLTEgMSAtNS41IDEzLjV0LTkuNSAxMy41cS04IDAgLTE2IC0xNXEzIDggLTExIDE1dC0yNCA4cTE5IDEyIC04IDI3cS03IDQgLTIwLjUgNXQtMTkuNSAtNHEtNSAtNyAtNS41IC0xMS41dDUgLTh0MTAuNSAtNS41dDExLjUgLTR0OC41IC0zDQpxMTQgLTEwIDggLTE0cS0yIC0xIC04LjUgLTMuNXQtMTEuNSAtNC41dC02IC00cS0zIC00IDAgLTE0dC0yIC0xNHEtNSA1IC05IDE3LjV0LTcgMTYuNXE3IC05IC0yNSAtNmwtMTAgMXEtNCAwIC0xNiAtMnQtMjAuNSAtMXQtMTMuNSA4cS00IDggMCAyMHExIDQgNCAycS00IDMgLTExIDkuNXQtMTAgOC41cS00NiAtMTUgLTk0IC00MXE2IC0xIDEyIDFxNSAyIDEzIDYuNXQxMCA1LjVxMzQgMTQgNDIgN2w1IDVxMTQgLTE2IDIwIC0yNQ0KcS03IDQgLTMwIDFxLTIwIC02IC0yMiAtMTJxNyAtMTIgNSAtMThxLTQgMyAtMTEuNSAxMHQtMTQuNSAxMXQtMTUgNXEtMTYgMCAtMjIgLTFxLTE0NiAtODAgLTIzNSAtMjIycTcgLTcgMTIgLThxNCAtMSA1IC05dDIuNSAtMTF0MTEuNSAzcTkgLTggMyAtMTlxMSAxIDQ0IC0yN3ExOSAtMTcgMjEgLTIxcTMgLTExIC0xMCAtMThxLTEgMiAtOSA5dC05IDRxLTMgLTUgMC41IC0xOC41dDEwLjUgLTEyLjVxLTcgMCAtOS41IC0xNnQtMi41IC0zNS41DQp0LTEgLTIzLjVsMiAtMXEtMyAtMTIgNS41IC0zNC41dDIxLjUgLTE5LjVxLTEzIC0zIDIwIC00M3E2IC04IDggLTlxMyAtMiAxMiAtNy41dDE1IC0xMHQxMCAtMTAuNXE0IC01IDEwIC0yMi41dDE0IC0yMy41cS0yIC02IDkuNSAtMjB0MTAuNSAtMjNxLTEgMCAtMi41IC0xdC0yLjUgLTFxMyAtNyAxNS41IC0xNHQxNS41IC0xM3ExIC0zIDIgLTEwdDMgLTExdDggLTJxMiAyMCAtMjQgNjJxLTE1IDI1IC0xNyAyOXEtMyA1IC01LjUgMTUuNQ0KdC00LjUgMTQuNXEyIDAgNiAtMS41dDguNSAtMy41dDcuNSAtNHQyIC0zcS0zIC03IDIgLTE3LjV0MTIgLTE4LjV0MTcgLTE5dDEyIC0xM3E2IC02IDE0IC0xOS41dDAgLTEzLjVxOSAwIDIwIC0xMC41dDE3IC0xOS41cTUgLTggOCAtMjZ0NSAtMjRxMiAtNyA4LjUgLTEzLjV0MTIuNSAtOS41bDE2IC04dDEzIC03cTUgLTIgMTguNSAtMTAuNXQyMS41IC0xMS41cTEwIC00IDE2IC00dDE0LjUgMi41dDEzLjUgMy41cTE1IDIgMjkgLTE1dDIxIC0yMQ0KcTM2IC0xOSA1NSAtMTFxLTIgLTEgMC41IC03LjV0OCAtMTUuNXQ5IC0xNC41dDUuNSAtOC41cTUgLTYgMTggLTE1dDE4IC0xNXE2IDQgNyA5cS0zIC04IDcgLTIwdDE4IC0xMHExNCAzIDE0IDMycS0zMSAtMTUgLTQ5IDE4cTAgMSAtMi41IDUuNXQtNCA4LjV0LTIuNSA4LjV0MCA3LjV0NSAzcTkgMCAxMCAzLjV0LTIgMTIuNXQtNCAxM3EtMSA4IC0xMSAyMHQtMTIgMTVxLTUgLTkgLTE2IC04dC0xNiA5cTAgLTEgLTEuNSAtNS41dC0xLjUgLTYuNQ0KcS0xMyAwIC0xNSAxcTEgMyAyLjUgMTcuNXQzLjUgMjIuNXExIDQgNS41IDEydDcuNSAxNC41dDQgMTIuNXQtNC41IDkuNXQtMTcuNSAyLjVxLTE5IC0xIC0yNiAtMjBxLTEgLTMgLTMgLTEwLjV0LTUgLTExLjV0LTkgLTdxLTcgLTMgLTI0IC0ydC0yNCA1cS0xMyA4IC0yMi41IDI5dC05LjUgMzdxMCAxMCAyLjUgMjYuNXQzIDI1dC01LjUgMjQuNXEzIDIgOSA5LjV0MTAgMTAuNXEyIDEgNC41IDEuNXQ0LjUgMHQ0IDEuNXQzIDZxLTEgMSAtNCAzDQpxLTMgMyAtNCAzcTcgLTMgMjguNSAxLjV0MjcuNSAtMS41cTE1IC0xMSAyMiAycTAgMSAtMi41IDkuNXQtMC41IDEzLjVxNSAtMjcgMjkgLTlxMyAtMyAxNS41IC01dDE3LjUgLTVxMyAtMiA3IC01LjV0NS41IC00LjV0NSAwLjV0OC41IDYuNXExMCAtMTQgMTIgLTI0cTExIC00MCAxOSAtNDRxNyAtMyAxMSAtMnQ0LjUgOS41dDAgMTR0LTEuNSAxMi41bC0xIDh2MThsLTEgOHEtMTUgMyAtMTguNSAxMnQxLjUgMTguNXQxNSAxOC41cTEgMSA4IDMuNQ0KdDE1LjUgNi41dDEyLjUgOHEyMSAxOSAxNSAzNXE3IDAgMTEgOXEtMSAwIC01IDN0LTcuNSA1dC00LjUgMnE5IDUgMiAxNnE1IDMgNy41IDExdDcuNSAxMHE5IC0xMiAyMSAtMnE4IDggMSAxNnE1IDcgMjAuNSAxMC41dDE4LjUgOS41cTcgLTIgOCAydDEgMTJ0MyAxMnE0IDUgMTUgOXQxMyA1bDE3IDExcTMgNCAwIDRxMTggLTIgMzEgMTFxMTAgMTEgLTYgMjBxMyA2IC0zIDkuNXQtMTUgNS41cTMgMSAxMS41IDAuNXQxMC41IDEuNQ0KcTE1IDEwIC03IDE2cS0xNyA1IC00MyAtMTJ6TTg3OSAxMHEyMDYgMzYgMzUxIDE4OXEtMyAzIC0xMi41IDQuNXQtMTIuNSAzLjVxLTE4IDcgLTI0IDhxMSA3IC0yLjUgMTN0LTggOXQtMTIuNSA4dC0xMSA3cS0yIDIgLTcgNnQtNyA1LjV0LTcuNSA0LjV0LTguNSAydC0xMCAtMWwtMyAtMXEtMyAtMSAtNS41IC0yLjV0LTUuNSAtM3QtNCAtM3QwIC0yLjVxLTIxIDE3IC0zNiAyMnEtNSAxIC0xMSA1LjV0LTEwLjUgN3QtMTAgMS41dC0xMS41IC03DQpxLTUgLTUgLTYgLTE1dC0yIC0xM3EtNyA1IDAgMTcuNXQyIDE4LjVxLTMgNiAtMTAuNSA0LjV0LTEyIC00LjV0LTExLjUgLTguNXQtOSAtNi41dC04LjUgLTUuNXQtOC41IC03LjVxLTMgLTQgLTYgLTEydC01IC0xMXEtMiA0IC0xMS41IDYuNXQtOS41IDUuNXEyIC0xMCA0IC0zNXQ1IC0zOHE3IC0zMSAtMTIgLTQ4cS0yNyAtMjUgLTI5IC00MHEtNCAtMjIgMTIgLTI2cTAgLTcgLTggLTIwLjV0LTcgLTIxLjVxMCAtNiAyIC0xNnoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9IndyZW5jaCIgdW5pY29kZT0iJiN4ZjBhZDsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik0zODQgNjRxMCAyNiAtMTkgNDV0LTQ1IDE5dC00NSAtMTl0LTE5IC00NXQxOSAtNDV0NDUgLTE5dDQ1IDE5dDE5IDQ1ek0xMDI4IDQ4NGwtNjgyIC02ODJxLTM3IC0zNyAtOTAgLTM3cS01MiAwIC05MSAzN2wtMTA2IDEwOHEtMzggMzYgLTM4IDkwcTAgNTMgMzggOTFsNjgxIDY4MXEzOSAtOTggMTE0LjUgLTE3My41dDE3My41IC0xMTQuNXpNMTY2MiA5MTlxMCAtMzkgLTIzIC0xMDZxLTQ3IC0xMzQgLTE2NC41IC0yMTcuNQ0KdC0yNTguNSAtODMuNXEtMTg1IDAgLTMxNi41IDEzMS41dC0xMzEuNSAzMTYuNXQxMzEuNSAzMTYuNXQzMTYuNSAxMzEuNXE1OCAwIDEyMS41IC0xNi41dDEwNy41IC00Ni41cTE2IC0xMSAxNiAtMjh0LTE2IC0yOGwtMjkzIC0xNjl2LTIyNGwxOTMgLTEwN3E1IDMgNzkgNDguNXQxMzUuNSA4MXQ3MC41IDM1LjVxMTUgMCAyMy41IC0xMHQ4LjUgLTI1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idGFza3MiIHVuaWNvZGU9IiYjeGYwYWU7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTAyNCAxMjhoNjQwdjEyOGgtNjQwdi0xMjh6TTY0MCA2NDBoMTAyNHYxMjhoLTEwMjR2LTEyOHpNMTI4MCAxMTUyaDM4NHYxMjhoLTM4NHYtMTI4ek0xNzkyIDMyMHYtMjU2cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTY2NHEtMjYgMCAtNDUgMTl0LTE5IDQ1djI1NnEwIDI2IDE5IDQ1dDQ1IDE5aDE2NjRxMjYgMCA0NSAtMTl0MTkgLTQ1ek0xNzkyIDgzMnYtMjU2cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTY2NHEtMjYgMCAtNDUgMTkNCnQtMTkgNDV2MjU2cTAgMjYgMTkgNDV0NDUgMTloMTY2NHEyNiAwIDQ1IC0xOXQxOSAtNDV6TTE3OTIgMTM0NHYtMjU2cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTY2NHEtMjYgMCAtNDUgMTl0LTE5IDQ1djI1NnEwIDI2IDE5IDQ1dDQ1IDE5aDE2NjRxMjYgMCA0NSAtMTl0MTkgLTQ1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZmlsdGVyIiB1bmljb2RlPSImI3hmMGIwOyIgaG9yaXotYWR2LXg9IjE0MDgiIA0KZD0iTTE0MDMgMTI0MXExNyAtNDEgLTE0IC03MGwtNDkzIC00OTN2LTc0MnEwIC00MiAtMzkgLTU5cS0xMyAtNSAtMjUgLTVxLTI3IDAgLTQ1IDE5bC0yNTYgMjU2cS0xOSAxOSAtMTkgNDV2NDg2bC00OTMgNDkzcS0zMSAyOSAtMTQgNzBxMTcgMzkgNTkgMzloMTI4MHE0MiAwIDU5IC0zOXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImJyaWVmY2FzZSIgdW5pY29kZT0iJiN4ZjBiMTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik02NDAgMTI4MGg1MTJ2MTI4aC01MTJ2LTEyOHpNMTc5MiA2NDB2LTQ4MHEwIC02NiAtNDcgLTExM3QtMTEzIC00N2gtMTQ3MnEtNjYgMCAtMTEzIDQ3dC00NyAxMTN2NDgwaDY3MnYtMTYwcTAgLTI2IDE5IC00NXQ0NSAtMTloMzIwcTI2IDAgNDUgMTl0MTkgNDV2MTYwaDY3MnpNMTAyNCA2NDB2LTEyOGgtMjU2djEyOGgyNTZ6TTE3OTIgMTEyMHYtMzg0aC0xNzkydjM4NHEwIDY2IDQ3IDExM3QxMTMgNDdoMzUydjE2MHEwIDQwIDI4IDY4DQp0NjggMjhoNTc2cTQwIDAgNjggLTI4dDI4IC02OHYtMTYwaDM1MnE2NiAwIDExMyAtNDd0NDcgLTExM3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImZ1bGxzY3JlZW4iIHVuaWNvZGU9IiYjeGYwYjI7IiANCmQ9Ik0xMjgzIDk5NWwtMzU1IC0zNTVsMzU1IC0zNTVsMTQ0IDE0NHEyOSAzMSA3MCAxNHEzOSAtMTcgMzkgLTU5di00NDhxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC00NDhxLTQyIDAgLTU5IDQwcS0xNyAzOSAxNCA2OWwxNDQgMTQ0bC0zNTUgMzU1bC0zNTUgLTM1NWwxNDQgLTE0NHEzMSAtMzAgMTQgLTY5cS0xNyAtNDAgLTU5IC00MGgtNDQ4cS0yNiAwIC00NSAxOXQtMTkgNDV2NDQ4cTAgNDIgNDAgNTlxMzkgMTcgNjkgLTE0bDE0NCAtMTQ0DQpsMzU1IDM1NWwtMzU1IDM1NWwtMTQ0IC0xNDRxLTE5IC0xOSAtNDUgLTE5cS0xMiAwIC0yNCA1cS00MCAxNyAtNDAgNTl2NDQ4cTAgMjYgMTkgNDV0NDUgMTloNDQ4cTQyIDAgNTkgLTQwcTE3IC0zOSAtMTQgLTY5bC0xNDQgLTE0NGwzNTUgLTM1NWwzNTUgMzU1bC0xNDQgMTQ0cS0zMSAzMCAtMTQgNjlxMTcgNDAgNTkgNDBoNDQ4cTI2IDAgNDUgLTE5dDE5IC00NXYtNDQ4cTAgLTQyIC0zOSAtNTlxLTEzIC01IC0yNSAtNXEtMjYgMCAtNDUgMTl6DQoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Imdyb3VwIiB1bmljb2RlPSImI3hmMGMwOyIgaG9yaXotYWR2LXg9IjE5MjAiIA0KZD0iTTU5MyA2NDBxLTE2MiAtNSAtMjY1IC0xMjhoLTEzNHEtODIgMCAtMTM4IDQwLjV0LTU2IDExOC41cTAgMzUzIDEyNCAzNTNxNiAwIDQzLjUgLTIxdDk3LjUgLTQyLjV0MTE5IC0yMS41cTY3IDAgMTMzIDIzcS01IC0zNyAtNSAtNjZxMCAtMTM5IDgxIC0yNTZ6TTE2NjQgM3EwIC0xMjAgLTczIC0xODkuNXQtMTk0IC02OS41aC04NzRxLTEyMSAwIC0xOTQgNjkuNXQtNzMgMTg5LjVxMCA1MyAzLjUgMTAzLjV0MTQgMTA5dDI2LjUgMTA4LjUNCnQ0MyA5Ny41dDYyIDgxdDg1LjUgNTMuNXQxMTEuNSAyMHExMCAwIDQzIC0yMS41dDczIC00OHQxMDcgLTQ4dDEzNSAtMjEuNXQxMzUgMjEuNXQxMDcgNDh0NzMgNDh0NDMgMjEuNXE2MSAwIDExMS41IC0yMHQ4NS41IC01My41dDYyIC04MXQ0MyAtOTcuNXQyNi41IC0xMDguNXQxNCAtMTA5dDMuNSAtMTAzLjV6TTY0MCAxMjgwcTAgLTEwNiAtNzUgLTE4MXQtMTgxIC03NXQtMTgxIDc1dC03NSAxODF0NzUgMTgxdDE4MSA3NXQxODEgLTc1DQp0NzUgLTE4MXpNMTM0NCA4OTZxMCAtMTU5IC0xMTIuNSAtMjcxLjV0LTI3MS41IC0xMTIuNXQtMjcxLjUgMTEyLjV0LTExMi41IDI3MS41dDExMi41IDI3MS41dDI3MS41IDExMi41dDI3MS41IC0xMTIuNXQxMTIuNSAtMjcxLjV6TTE5MjAgNjcxcTAgLTc4IC01NiAtMTE4LjV0LTEzOCAtNDAuNWgtMTM0cS0xMDMgMTIzIC0yNjUgMTI4cTgxIDExNyA4MSAyNTZxMCAyOSAtNSA2NnE2NiAtMjMgMTMzIC0yM3E1OSAwIDExOSAyMS41dDk3LjUgNDIuNQ0KdDQzLjUgMjFxMTI0IDAgMTI0IC0zNTN6TTE3OTIgMTI4MHEwIC0xMDYgLTc1IC0xODF0LTE4MSAtNzV0LTE4MSA3NXQtNzUgMTgxdDc1IDE4MXQxODEgNzV0MTgxIC03NXQ3NSAtMTgxeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ibGluayIgdW5pY29kZT0iJiN4ZjBjMTsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik0xNDU2IDMyMHEwIDQwIC0yOCA2OGwtMjA4IDIwOHEtMjggMjggLTY4IDI4cS00MiAwIC03MiAtMzJxMyAtMyAxOSAtMTguNXQyMS41IC0yMS41dDE1IC0xOXQxMyAtMjUuNXQzLjUgLTI3LjVxMCAtNDAgLTI4IC02OHQtNjggLTI4cS0xNSAwIC0yNy41IDMuNXQtMjUuNSAxM3QtMTkgMTV0LTIxLjUgMjEuNXQtMTguNSAxOXEtMzMgLTMxIC0zMyAtNzNxMCAtNDAgMjggLTY4bDIwNiAtMjA3cTI3IC0yNyA2OCAtMjdxNDAgMCA2OCAyNg0KbDE0NyAxNDZxMjggMjggMjggNjd6TTc1MyAxMDI1cTAgNDAgLTI4IDY4bC0yMDYgMjA3cS0yOCAyOCAtNjggMjhxLTM5IDAgLTY4IC0yN2wtMTQ3IC0xNDZxLTI4IC0yOCAtMjggLTY3cTAgLTQwIDI4IC02OGwyMDggLTIwOHEyNyAtMjcgNjggLTI3cTQyIDAgNzIgMzFxLTMgMyAtMTkgMTguNXQtMjEuNSAyMS41dC0xNSAxOXQtMTMgMjUuNXQtMy41IDI3LjVxMCA0MCAyOCA2OHQ2OCAyOHExNSAwIDI3LjUgLTMuNXQyNS41IC0xM3QxOSAtMTUNCnQyMS41IC0yMS41dDE4LjUgLTE5cTMzIDMxIDMzIDczek0xNjQ4IDMyMHEwIC0xMjAgLTg1IC0yMDNsLTE0NyAtMTQ2cS04MyAtODMgLTIwMyAtODNxLTEyMSAwIC0yMDQgODVsLTIwNiAyMDdxLTgzIDgzIC04MyAyMDNxMCAxMjMgODggMjA5bC04OCA4OHEtODYgLTg4IC0yMDggLTg4cS0xMjAgMCAtMjA0IDg0bC0yMDggMjA4cS04NCA4NCAtODQgMjA0dDg1IDIwM2wxNDcgMTQ2cTgzIDgzIDIwMyA4M3ExMjEgMCAyMDQgLTg1bDIwNiAtMjA3DQpxODMgLTgzIDgzIC0yMDNxMCAtMTIzIC04OCAtMjA5bDg4IC04OHE4NiA4OCAyMDggODhxMTIwIDAgMjA0IC04NGwyMDggLTIwOHE4NCAtODQgODQgLTIwNHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImNsb3VkIiB1bmljb2RlPSImI3hmMGMyOyIgaG9yaXotYWR2LXg9IjE5MjAiIA0KZD0iTTE5MjAgMzg0cTAgLTE1OSAtMTEyLjUgLTI3MS41dC0yNzEuNSAtMTEyLjVoLTEwODhxLTE4NSAwIC0zMTYuNSAxMzEuNXQtMTMxLjUgMzE2LjVxMCAxMzIgNzEgMjQxLjV0MTg3IDE2My41cS0yIDI4IC0yIDQzcTAgMjEyIDE1MCAzNjJ0MzYyIDE1MHExNTggMCAyODYuNSAtODh0MTg3LjUgLTIzMHE3MCA2MiAxNjYgNjJxMTA2IDAgMTgxIC03NXQ3NSAtMTgxcTAgLTc1IC00MSAtMTM4cTEyOSAtMzAgMjEzIC0xMzQuNXQ4NCAtMjM5LjV6DQoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImJlYWtlciIgdW5pY29kZT0iJiN4ZjBjMzsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik0xNTI3IDg4cTU2IC04OSAyMS41IC0xNTIuNXQtMTQwLjUgLTYzLjVoLTExNTJxLTEwNiAwIC0xNDAuNSA2My41dDIxLjUgMTUyLjVsNTAzIDc5M3YzOTloLTY0cS0yNiAwIC00NSAxOXQtMTkgNDV0MTkgNDV0NDUgMTloNTEycTI2IDAgNDUgLTE5dDE5IC00NXQtMTkgLTQ1dC00NSAtMTloLTY0di0zOTl6TTc0OCA4MTNsLTI3MiAtNDI5aDcxMmwtMjcyIDQyOWwtMjAgMzF2Mzd2Mzk5aC0xMjh2LTM5OXYtMzd6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJjdXQiIHVuaWNvZGU9IiYjeGYwYzQ7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNOTYwIDY0MHEyNiAwIDQ1IC0xOXQxOSAtNDV0LTE5IC00NXQtNDUgLTE5dC00NSAxOXQtMTkgNDV0MTkgNDV0NDUgMTl6TTEyNjAgNTc2bDUwNyAtMzk4cTI4IC0yMCAyNSAtNTZxLTUgLTM1IC0zNSAtNTFsLTEyOCAtNjRxLTEzIC03IC0yOSAtN3EtMTcgMCAtMzEgOGwtNjkwIDM4N2wtMTEwIC02NnEtOCAtNCAtMTIgLTVxMTQgLTQ5IDEwIC05N3EtNyAtNzcgLTU2IC0xNDcuNXQtMTMyIC0xMjMuNXEtMTMyIC04NCAtMjc3IC04NA0KcS0xMzYgMCAtMjIyIDc4cS05MCA4NCAtNzkgMjA3cTcgNzYgNTYgMTQ3dDEzMSAxMjRxMTMyIDg0IDI3OCA4NHE4MyAwIDE1MSAtMzFxOSAxMyAyMiAyMmwxMjIgNzNsLTEyMiA3M3EtMTMgOSAtMjIgMjJxLTY4IC0zMSAtMTUxIC0zMXEtMTQ2IDAgLTI3OCA4NHEtODIgNTMgLTEzMSAxMjR0LTU2IDE0N3EtNSA1OSAxNS41IDExM3Q2My41IDkzcTg1IDc5IDIyMiA3OXExNDUgMCAyNzcgLTg0cTgzIC01MiAxMzIgLTEyM3Q1NiAtMTQ4DQpxNCAtNDggLTEwIC05N3E0IC0xIDEyIC01bDExMCAtNjZsNjkwIDM4N3ExNCA4IDMxIDhxMTYgMCAyOSAtN2wxMjggLTY0cTMwIC0xNiAzNSAtNTFxMyAtMzYgLTI1IC01NnpNNTc5IDgzNnE0NiA0MiAyMSAxMDh0LTEwNiAxMTdxLTkyIDU5IC0xOTIgNTlxLTc0IDAgLTExMyAtMzZxLTQ2IC00MiAtMjEgLTEwOHQxMDYgLTExN3E5MiAtNTkgMTkyIC01OXE3NCAwIDExMyAzNnpNNDk0IDkxcTgxIDUxIDEwNiAxMTd0LTIxIDEwOA0KcS0zOSAzNiAtMTEzIDM2cS0xMDAgMCAtMTkyIC01OXEtODEgLTUxIC0xMDYgLTExN3QyMSAtMTA4cTM5IC0zNiAxMTMgLTM2cTEwMCAwIDE5MiA1OXpNNjcyIDcwNGw5NiAtNTh2MTFxMCAzNiAzMyA1NmwxNCA4bC03OSA0N2wtMjYgLTI2cS0zIC0zIC0xMCAtMTF0LTEyIC0xMnEtMiAtMiAtNCAtMy41dC0zIC0yLjV6TTg5NiA0ODBsOTYgLTMybDczNiA1NzZsLTEyOCA2NGwtNzY4IC00MzF2LTExM2wtMTYwIC05Nmw5IC04cTIgLTIgNyAtNg0KcTQgLTQgMTEgLTEydDExIC0xMmwyNiAtMjZ6TTE2MDAgNjRsMTI4IDY0bC01MjAgNDA4bC0xNzcgLTEzOHEtMiAtMyAtMTMgLTd6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJjb3B5IiB1bmljb2RlPSImI3hmMGM1OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE2OTYgMTE1MnE0MCAwIDY4IC0yOHQyOCAtNjh2LTEyMTZxMCAtNDAgLTI4IC02OHQtNjggLTI4aC05NjBxLTQwIDAgLTY4IDI4dC0yOCA2OHYyODhoLTU0NHEtNDAgMCAtNjggMjh0LTI4IDY4djY3MnEwIDQwIDIwIDg4dDQ4IDc2bDQwOCA0MDhxMjggMjggNzYgNDh0ODggMjBoNDE2cTQwIDAgNjggLTI4dDI4IC02OHYtMzI4cTY4IDQwIDEyOCA0MGg0MTZ6TTExNTIgOTM5bC0yOTkgLTI5OWgyOTl2Mjk5ek01MTIgMTMyM2wtMjk5IC0yOTkNCmgyOTl2Mjk5ek03MDggNjc2bDMxNiAzMTZ2NDE2aC0zODR2LTQxNnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTQxNnYtNjQwaDUxMnYyNTZxMCA0MCAyMCA4OHQ0OCA3NnpNMTY2NCAtMTI4djExNTJoLTM4NHYtNDE2cTAgLTQwIC0yOCAtNjh0LTY4IC0yOGgtNDE2di02NDBoODk2eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0icGFwZXJfY2xpcCIgdW5pY29kZT0iJiN4ZjBjNjsiIGhvcml6LWFkdi14PSIxNDA4IiANCmQ9Ik0xNDA0IDE1MXEwIC0xMTcgLTc5IC0xOTZ0LTE5NiAtNzlxLTEzNSAwIC0yMzUgMTAwbC03NzcgNzc2cS0xMTMgMTE1IC0xMTMgMjcxcTAgMTU5IDExMCAyNzB0MjY5IDExMXExNTggMCAyNzMgLTExM2w2MDUgLTYwNnExMCAtMTAgMTAgLTIycTAgLTE2IC0zMC41IC00Ni41dC00Ni41IC0zMC41cS0xMyAwIC0yMyAxMGwtNjA2IDYwN3EtNzkgNzcgLTE4MSA3N3EtMTA2IDAgLTE3OSAtNzV0LTczIC0xODFxMCAtMTA1IDc2IC0xODENCmw3NzYgLTc3N3E2MyAtNjMgMTQ1IC02M3E2NCAwIDEwNiA0MnQ0MiAxMDZxMCA4MiAtNjMgMTQ1bC01ODEgNTgxcS0yNiAyNCAtNjAgMjRxLTI5IDAgLTQ4IC0xOXQtMTkgLTQ4cTAgLTMyIDI1IC01OWw0MTAgLTQxMHExMCAtMTAgMTAgLTIycTAgLTE2IC0zMSAtNDd0LTQ3IC0zMXEtMTIgMCAtMjIgMTBsLTQxMCA0MTBxLTYzIDYxIC02MyAxNDlxMCA4MiA1NyAxMzl0MTM5IDU3cTg4IDAgMTQ5IC02M2w1ODEgLTU4MXExMDAgLTk4IDEwMCAtMjM1DQp6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJzYXZlIiB1bmljb2RlPSImI3hmMGM3OyIgDQpkPSJNMzg0IDBoNzY4djM4NGgtNzY4di0zODR6TTEyODAgMGgxMjh2ODk2cTAgMTQgLTEwIDM4LjV0LTIwIDM0LjVsLTI4MSAyODFxLTEwIDEwIC0zNCAyMHQtMzkgMTB2LTQxNnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTU3NnEtNDAgMCAtNjggMjh0LTI4IDY4djQxNmgtMTI4di0xMjgwaDEyOHY0MTZxMCA0MCAyOCA2OHQ2OCAyOGg4MzJxNDAgMCA2OCAtMjh0MjggLTY4di00MTZ6TTg5NiA5Mjh2MzIwcTAgMTMgLTkuNSAyMi41dC0yMi41IDkuNQ0KaC0xOTJxLTEzIDAgLTIyLjUgLTkuNXQtOS41IC0yMi41di0zMjBxMCAtMTMgOS41IC0yMi41dDIyLjUgLTkuNWgxOTJxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXpNMTUzNiA4OTZ2LTkyOHEwIC00MCAtMjggLTY4dC02OCAtMjhoLTEzNDRxLTQwIDAgLTY4IDI4dC0yOCA2OHYxMzQ0cTAgNDAgMjggNjh0NjggMjhoOTI4cTQwIDAgODggLTIwdDc2IC00OGwyODAgLTI4MHEyOCAtMjggNDggLTc2dDIwIC04OHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InNpZ25fYmxhbmsiIHVuaWNvZGU9IiYjeGYwYzg7IiANCmQ9Ik0xNTM2IDExMjB2LTk2MHEwIC0xMTkgLTg0LjUgLTIwMy41dC0yMDMuNSAtODQuNWgtOTYwcS0xMTkgMCAtMjAzLjUgODQuNXQtODQuNSAyMDMuNXY5NjBxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDk2MHExMTkgMCAyMDMuNSAtODQuNXQ4NC41IC0yMDMuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InJlb3JkZXIiIHVuaWNvZGU9IiYjeGYwYzk7IiANCmQ9Ik0xNTM2IDE5MnYtMTI4cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTQwOHEtMjYgMCAtNDUgMTl0LTE5IDQ1djEyOHEwIDI2IDE5IDQ1dDQ1IDE5aDE0MDhxMjYgMCA0NSAtMTl0MTkgLTQ1ek0xNTM2IDcwNHYtMTI4cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTQwOHEtMjYgMCAtNDUgMTl0LTE5IDQ1djEyOHEwIDI2IDE5IDQ1dDQ1IDE5aDE0MDhxMjYgMCA0NSAtMTl0MTkgLTQ1ek0xNTM2IDEyMTZ2LTEyOHEwIC0yNiAtMTkgLTQ1DQp0LTQ1IC0xOWgtMTQwOHEtMjYgMCAtNDUgMTl0LTE5IDQ1djEyOHEwIDI2IDE5IDQ1dDQ1IDE5aDE0MDhxMjYgMCA0NSAtMTl0MTkgLTQ1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idWwiIHVuaWNvZGU9IiYjeGYwY2E7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMzg0IDEyOHEwIC04MCAtNTYgLTEzNnQtMTM2IC01NnQtMTM2IDU2dC01NiAxMzZ0NTYgMTM2dDEzNiA1NnQxMzYgLTU2dDU2IC0xMzZ6TTM4NCA2NDBxMCAtODAgLTU2IC0xMzZ0LTEzNiAtNTZ0LTEzNiA1NnQtNTYgMTM2dDU2IDEzNnQxMzYgNTZ0MTM2IC01NnQ1NiAtMTM2ek0xNzkyIDIyNHYtMTkycTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtMTIxNnEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djE5MnEwIDEzIDkuNSAyMi41DQp0MjIuNSA5LjVoMTIxNnExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6TTM4NCAxMTUycTAgLTgwIC01NiAtMTM2dC0xMzYgLTU2dC0xMzYgNTZ0LTU2IDEzNnQ1NiAxMzZ0MTM2IDU2dDEzNiAtNTZ0NTYgLTEzNnpNMTc5MiA3MzZ2LTE5MnEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTEyMTZxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXYxOTJxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWgxMjE2cTEzIDAgMjIuNSAtOS41dDkuNSAtMjIuNXoNCk0xNzkyIDEyNDh2LTE5MnEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTEyMTZxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXYxOTJxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWgxMjE2cTEzIDAgMjIuNSAtOS41dDkuNSAtMjIuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Im9sIiB1bmljb2RlPSImI3hmMGNiOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTM4MSAtODRxMCAtODAgLTU0LjUgLTEyNnQtMTM1LjUgLTQ2cS0xMDYgMCAtMTcyIDY2bDU3IDg4cTQ5IC00NSAxMDYgLTQ1cTI5IDAgNTAuNSAxNC41dDIxLjUgNDIuNXEwIDY0IC0xMDUgNTZsLTI2IDU2cTggMTAgMzIuNSA0My41dDQyLjUgNTR0MzcgMzguNXYxcS0xNiAwIC00OC41IC0xdC00OC41IC0xdi01M2gtMTA2djE1MmgzMzN2LTg4bC05NSAtMTE1cTUxIC0xMiA4MSAtNDl0MzAgLTg4ek0zODMgNTQzdi0xNTloLTM2Mg0KcS02IDM2IC02IDU0cTAgNTEgMjMuNSA5M3Q1Ni41IDY4dDY2IDQ3LjV0NTYuNSA0My41dDIzLjUgNDVxMCAyNSAtMTQuNSAzOC41dC0zOS41IDEzLjVxLTQ2IDAgLTgxIC01OGwtODUgNTlxMjQgNTEgNzEuNSA3OS41dDEwNS41IDI4LjVxNzMgMCAxMjMgLTQxLjV0NTAgLTExMi41cTAgLTUwIC0zNCAtOTEuNXQtNzUgLTY0LjV0LTc1LjUgLTUwLjV0LTM1LjUgLTUyLjVoMTI3djYwaDEwNXpNMTc5MiAyMjR2LTE5MnEwIC0xMyAtOS41IC0yMi41DQp0LTIyLjUgLTkuNWgtMTIxNnEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djE5MnEwIDE0IDkgMjN0MjMgOWgxMjE2cTEzIDAgMjIuNSAtOS41dDkuNSAtMjIuNXpNMzg0IDExMjN2LTk5aC0zMzV2OTloMTA3cTAgNDEgMC41IDEyMS41dDAuNSAxMjEuNXYxMmgtMnEtOCAtMTcgLTUwIC01NGwtNzEgNzZsMTM2IDEyN2gxMDZ2LTQwNGgxMDh6TTE3OTIgNzM2di0xOTJxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC0xMjE2DQpxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXYxOTJxMCAxNCA5IDIzdDIzIDloMTIxNnExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6TTE3OTIgMTI0OHYtMTkycTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtMTIxNnEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djE5MnEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDEyMTZxMTMgMCAyMi41IC05LjV0OS41IC0yMi41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ic3RyaWtldGhyb3VnaCIgdW5pY29kZT0iJiN4ZjBjYzsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNzYwIDY0MHExNCAwIDIzIC05dDkgLTIzdi02NHEwIC0xNCAtOSAtMjN0LTIzIC05aC0xNzI4cS0xNCAwIC0yMyA5dC05IDIzdjY0cTAgMTQgOSAyM3QyMyA5aDE3Mjh6TTQ4MyA3MDRxLTI4IDM1IC01MSA4MHEtNDggOTggLTQ4IDE4OHEwIDE4MSAxMzQgMzA5cTEzMyAxMjcgMzkzIDEyN3E1MCAwIDE2NyAtMTlxNjYgLTEyIDE3NyAtNDhxMTAgLTM4IDIxIC0xMThxMTQgLTEyMyAxNCAtMTgzcTAgLTE4IC01IC00NWwtMTIgLTNsLTg0IDYNCmwtMTQgMnEtNTAgMTQ5IC0xMDMgMjA1cS04OCA5MSAtMjEwIDkxcS0xMTQgMCAtMTgyIC01OXEtNjcgLTU4IC02NyAtMTQ2cTAgLTczIDY2IC0xNDB0Mjc5IC0xMjlxNjkgLTIwIDE3MyAtNjZxNTggLTI4IDk1IC01MmgtNzQzek05OTAgNDQ4aDQxMXE3IC0zOSA3IC05MnEwIC0xMTEgLTQxIC0yMTJxLTIzIC01NiAtNzEgLTEwNHEtMzcgLTM1IC0xMDkgLTgxcS04MCAtNDggLTE1MyAtNjZxLTgwIC0yMSAtMjAzIC0yMXEtMTE0IDAgLTE5NSAyMw0KbC0xNDAgNDBxLTU3IDE2IC03MiAyOHEtOCA4IC04IDIydjEzcTAgMTA4IC0yIDE1NnEtMSAzMCAwIDY4bDIgMzd2NDRsMTAyIDJxMTUgLTM0IDMwIC03MXQyMi41IC01NnQxMi41IC0yN3EzNSAtNTcgODAgLTk0cTQzIC0zNiAxMDUgLTU3cTU5IC0yMiAxMzIgLTIycTY0IDAgMTM5IDI3cTc3IDI2IDEyMiA4NnE0NyA2MSA0NyAxMjlxMCA4NCAtODEgMTU3cS0zNCAyOSAtMTM3IDcxeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5kZXJsaW5lIiB1bmljb2RlPSImI3hmMGNkOyIgDQpkPSJNNDggMTMxM3EtMzcgMiAtNDUgNGwtMyA4OHExMyAxIDQwIDFxNjAgMCAxMTIgLTRxMTMyIC03IDE2NiAtN3E4NiAwIDE2OCAzcTExNiA0IDE0NiA1cTU2IDAgODYgMmwtMSAtMTRsMiAtNjR2LTlxLTYwIC05IC0xMjQgLTlxLTYwIDAgLTc5IC0yNXEtMTMgLTE0IC0xMyAtMTMycTAgLTEzIDAuNSAtMzIuNXQwLjUgLTI1LjVsMSAtMjI5bDE0IC0yODBxNiAtMTI0IDUxIC0yMDJxMzUgLTU5IDk2IC05MnE4OCAtNDcgMTc3IC00Nw0KcTEwNCAwIDE5MSAyOHE1NiAxOCA5OSA1MXE0OCAzNiA2NSA2NHEzNiA1NiA1MyAxMTRxMjEgNzMgMjEgMjI5cTAgNzkgLTMuNSAxMjh0LTExIDEyMi41dC0xMy41IDE1OS41bC00IDU5cS01IDY3IC0yNCA4OHEtMzQgMzUgLTc3IDM0bC0xMDAgLTJsLTE0IDNsMiA4Nmg4NGwyMDUgLTEwcTc2IC0zIDE5NiAxMGwxOCAtMnE2IC0zOCA2IC01MXEwIC03IC00IC0zMXEtNDUgLTEyIC04NCAtMTNxLTczIC0xMSAtNzkgLTE3cS0xNSAtMTUgLTE1IC00MQ0KcTAgLTcgMS41IC0yN3QxLjUgLTMxcTggLTE5IDIyIC0zOTZxNiAtMTk1IC0xNSAtMzA0cS0xNSAtNzYgLTQxIC0xMjJxLTM4IC02NSAtMTEyIC0xMjNxLTc1IC01NyAtMTgyIC04OXEtMTA5IC0zMyAtMjU1IC0zM3EtMTY3IDAgLTI4NCA0NnEtMTE5IDQ3IC0xNzkgMTIycS02MSA3NiAtODMgMTk1cS0xNiA4MCAtMTYgMjM3djMzM3EwIDE4OCAtMTcgMjEzcS0yNSAzNiAtMTQ3IDM5ek0xNTM2IC05NnY2NHEwIDE0IC05IDIzdC0yMyA5aC0xNDcyDQpxLTE0IDAgLTIzIC05dC05IC0yM3YtNjRxMCAtMTQgOSAtMjN0MjMgLTloMTQ3MnExNCAwIDIzIDl0OSAyM3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InRhYmxlIiB1bmljb2RlPSImI3hmMGNlOyIgaG9yaXotYWR2LXg9IjE2NjQiIA0KZD0iTTUxMiAxNjB2MTkycTAgMTQgLTkgMjN0LTIzIDloLTMyMHEtMTQgMCAtMjMgLTl0LTkgLTIzdi0xOTJxMCAtMTQgOSAtMjN0MjMgLTloMzIwcTE0IDAgMjMgOXQ5IDIzek01MTIgNTQ0djE5MnEwIDE0IC05IDIzdC0yMyA5aC0zMjBxLTE0IDAgLTIzIC05dC05IC0yM3YtMTkycTAgLTE0IDkgLTIzdDIzIC05aDMyMHExNCAwIDIzIDl0OSAyM3pNMTAyNCAxNjB2MTkycTAgMTQgLTkgMjN0LTIzIDloLTMyMHEtMTQgMCAtMjMgLTl0LTkgLTIzDQp2LTE5MnEwIC0xNCA5IC0yM3QyMyAtOWgzMjBxMTQgMCAyMyA5dDkgMjN6TTUxMiA5Mjh2MTkycTAgMTQgLTkgMjN0LTIzIDloLTMyMHEtMTQgMCAtMjMgLTl0LTkgLTIzdi0xOTJxMCAtMTQgOSAtMjN0MjMgLTloMzIwcTE0IDAgMjMgOXQ5IDIzek0xMDI0IDU0NHYxOTJxMCAxNCAtOSAyM3QtMjMgOWgtMzIwcS0xNCAwIC0yMyAtOXQtOSAtMjN2LTE5MnEwIC0xNCA5IC0yM3QyMyAtOWgzMjBxMTQgMCAyMyA5dDkgMjN6TTE1MzYgMTYwdjE5Mg0KcTAgMTQgLTkgMjN0LTIzIDloLTMyMHEtMTQgMCAtMjMgLTl0LTkgLTIzdi0xOTJxMCAtMTQgOSAtMjN0MjMgLTloMzIwcTE0IDAgMjMgOXQ5IDIzek0xMDI0IDkyOHYxOTJxMCAxNCAtOSAyM3QtMjMgOWgtMzIwcS0xNCAwIC0yMyAtOXQtOSAtMjN2LTE5MnEwIC0xNCA5IC0yM3QyMyAtOWgzMjBxMTQgMCAyMyA5dDkgMjN6TTE1MzYgNTQ0djE5MnEwIDE0IC05IDIzdC0yMyA5aC0zMjBxLTE0IDAgLTIzIC05dC05IC0yM3YtMTkyDQpxMCAtMTQgOSAtMjN0MjMgLTloMzIwcTE0IDAgMjMgOXQ5IDIzek0xNTM2IDkyOHYxOTJxMCAxNCAtOSAyM3QtMjMgOWgtMzIwcS0xNCAwIC0yMyAtOXQtOSAtMjN2LTE5MnEwIC0xNCA5IC0yM3QyMyAtOWgzMjBxMTQgMCAyMyA5dDkgMjN6TTE2NjQgMTI0OHYtMTA4OHEwIC02NiAtNDcgLTExM3QtMTEzIC00N2gtMTM0NHEtNjYgMCAtMTEzIDQ3dC00NyAxMTN2MTA4OHEwIDY2IDQ3IDExM3QxMTMgNDdoMTM0NHE2NiAwIDExMyAtNDd0NDcgLTExMw0KeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ibWFnaWMiIHVuaWNvZGU9IiYjeGYwZDA7IiBob3Jpei1hZHYteD0iMTY2NCIgDQpkPSJNMTE5MCA5NTVsMjkzIDI5M2wtMTA3IDEwN2wtMjkzIC0yOTN6TTE2MzcgMTI0OHEwIC0yNyAtMTggLTQ1bC0xMjg2IC0xMjg2cS0xOCAtMTggLTQ1IC0xOHQtNDUgMThsLTE5OCAxOThxLTE4IDE4IC0xOCA0NXQxOCA0NWwxMjg2IDEyODZxMTggMTggNDUgMTh0NDUgLTE4bDE5OCAtMTk4cTE4IC0xOCAxOCAtNDV6TTI4NiAxNDM4bDk4IC0zMGwtOTggLTMwbC0zMCAtOThsLTMwIDk4bC05OCAzMGw5OCAzMGwzMCA5OHpNNjM2IDEyNzYNCmwxOTYgLTYwbC0xOTYgLTYwbC02MCAtMTk2bC02MCAxOTZsLTE5NiA2MGwxOTYgNjBsNjAgMTk2ek0xNTY2IDc5OGw5OCAtMzBsLTk4IC0zMGwtMzAgLTk4bC0zMCA5OGwtOTggMzBsOTggMzBsMzAgOTh6TTkyNiAxNDM4bDk4IC0zMGwtOTggLTMwbC0zMCAtOThsLTMwIDk4bC05OCAzMGw5OCAzMGwzMCA5OHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InRydWNrIiB1bmljb2RlPSImI3hmMGQxOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTY0MCAxMjhxMCA1MiAtMzggOTB0LTkwIDM4dC05MCAtMzh0LTM4IC05MHQzOCAtOTB0OTAgLTM4dDkwIDM4dDM4IDkwek0yNTYgNjQwaDM4NHYyNTZoLTE1OHEtMTMgMCAtMjIgLTlsLTE5NSAtMTk1cS05IC05IC05IC0yMnYtMzB6TTE1MzYgMTI4cTAgNTIgLTM4IDkwdC05MCAzOHQtOTAgLTM4dC0zOCAtOTB0MzggLTkwdDkwIC0zOHQ5MCAzOHQzOCA5MHpNMTc5MiAxMjE2di0xMDI0cTAgLTE1IC00IC0yNi41dC0xMy41IC0xOC41DQp0LTE2LjUgLTExLjV0LTIzLjUgLTZ0LTIyLjUgLTJ0LTI1LjUgMHQtMjIuNSAwLjVxMCAtMTA2IC03NSAtMTgxdC0xODEgLTc1dC0xODEgNzV0LTc1IDE4MWgtMzg0cTAgLTEwNiAtNzUgLTE4MXQtMTgxIC03NXQtMTgxIDc1dC03NSAxODFoLTY0cS0zIDAgLTIyLjUgLTAuNXQtMjUuNSAwdC0yMi41IDJ0LTIzLjUgNnQtMTYuNSAxMS41dC0xMy41IDE4LjV0LTQgMjYuNXEwIDI2IDE5IDQ1dDQ1IDE5djMyMHEwIDggLTAuNSAzNXQwIDM4DQp0Mi41IDM0LjV0Ni41IDM3dDE0IDMwLjV0MjIuNSAzMGwxOTggMTk4cTE5IDE5IDUwLjUgMzJ0NTguNSAxM2gxNjB2MTkycTAgMjYgMTkgNDV0NDUgMTloMTAyNHEyNiAwIDQ1IC0xOXQxOSAtNDV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJwaW50ZXJlc3QiIHVuaWNvZGU9IiYjeGYwZDI7IiANCmQ9Ik0xNTM2IDY0MHEwIC0yMDkgLTEwMyAtMzg1LjV0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3EtMTExIDAgLTIxOCAzMnE1OSA5MyA3OCAxNjRxOSAzNCA1NCAyMTFxMjAgLTM5IDczIC02Ny41dDExNCAtMjguNXExMjEgMCAyMTYgNjguNXQxNDcgMTg4LjV0NTIgMjcwcTAgMTE0IC01OS41IDIxNHQtMTcyLjUgMTYzdC0yNTUgNjNxLTEwNSAwIC0xOTYgLTI5dC0xNTQuNSAtNzd0LTEwOSAtMTEwLjV0LTY3IC0xMjkuNXQtMjEuNSAtMTM0DQpxMCAtMTA0IDQwIC0xODN0MTE3IC0xMTFxMzAgLTEyIDM4IDIwcTIgNyA4IDMxdDggMzBxNiAyMyAtMTEgNDNxLTUxIDYxIC01MSAxNTFxMCAxNTEgMTA0LjUgMjU5LjV0MjczLjUgMTA4LjVxMTUxIDAgMjM1LjUgLTgydDg0LjUgLTIxM3EwIC0xNzAgLTY4LjUgLTI4OXQtMTc1LjUgLTExOXEtNjEgMCAtOTggNDMuNXQtMjMgMTA0LjVxOCAzNSAyNi41IDkzLjV0MzAgMTAzdDExLjUgNzUuNXEwIDUwIC0yNyA4M3QtNzcgMzMNCnEtNjIgMCAtMTA1IC01N3QtNDMgLTE0MnEwIC03MyAyNSAtMTIybC05OSAtNDE4cS0xNyAtNzAgLTEzIC0xNzdxLTIwNiA5MSAtMzMzIDI4MXQtMTI3IDQyM3EwIDIwOSAxMDMgMzg1LjV0Mjc5LjUgMjc5LjV0Mzg1LjUgMTAzdDM4NS41IC0xMDN0Mjc5LjUgLTI3OS41dDEwMyAtMzg1LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJwaW50ZXJlc3Rfc2lnbiIgdW5pY29kZT0iJiN4ZjBkMzsiIA0KZD0iTTEyNDggMTQwOHExMTkgMCAyMDMuNSAtODQuNXQ4NC41IC0yMDMuNXYtOTYwcTAgLTExOSAtODQuNSAtMjAzLjV0LTIwMy41IC04NC41aC03MjVxODUgMTIyIDEwOCAyMTBxOSAzNCA1MyAyMDlxMjEgLTM5IDczLjUgLTY3dDExMi41IC0yOHExODEgMCAyOTUuNSAxNDcuNXQxMTQuNSAzNzMuNXEwIDg0IC0zNSAxNjIuNXQtOTYuNSAxMzl0LTE1Mi41IDk3dC0xOTcgMzYuNXEtMTA0IDAgLTE5NC41IC0yOC41dC0xNTMgLTc2LjUNCnQtMTA3LjUgLTEwOS41dC02Ni41IC0xMjh0LTIxLjUgLTEzMi41cTAgLTEwMiAzOS41IC0xODB0MTE2LjUgLTExMHExMyAtNSAyMy41IDB0MTQuNSAxOXExMCA0NCAxNSA2MXE2IDIzIC0xMSA0MnEtNTAgNjIgLTUwIDE1MHEwIDE1MCAxMDMuNSAyNTYuNXQyNzAuNSAxMDYuNXExNDkgMCAyMzIuNSAtODF0ODMuNSAtMjEwcTAgLTE2OCAtNjcuNSAtMjg2dC0xNzMuNSAtMTE4cS02MCAwIC05NyA0My41dC0yMyAxMDMuNXE4IDM0IDI2LjUgOTIuNQ0KdDI5LjUgMTAydDExIDc0LjVxMCA0OSAtMjYuNSA4MS41dC03NS41IDMyLjVxLTYxIDAgLTEwMy41IC01Ni41dC00Mi41IC0xMzkuNXEwIC03MiAyNCAtMTIxbC05OCAtNDE0cS0yNCAtMTAwIC03IC0yNTRoLTE4M3EtMTE5IDAgLTIwMy41IDg0LjV0LTg0LjUgMjAzLjV2OTYwcTAgMTE5IDg0LjUgMjAzLjV0MjAzLjUgODQuNWg5NjB6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJnb29nbGVfcGx1c19zaWduIiB1bmljb2RlPSImI3hmMGQ0OyIgDQpkPSJNOTE3IDYzMXEwIDI2IC02IDY0aC0zNjJ2LTEzMmgyMTdxLTMgLTI0IC0xNi41IC01MHQtMzcuNSAtNTN0LTY2LjUgLTQ0LjV0LTk2LjUgLTE3LjVxLTk5IDAgLTE2OSA3MXQtNzAgMTcxdDcwIDE3MXQxNjkgNzFxOTIgMCAxNTMgLTU5bDEwNCAxMDFxLTEwOCAxMDAgLTI1NyAxMDBxLTE2MCAwIC0yNzIgLTExMi41dC0xMTIgLTI3MS41dDExMiAtMjcxLjV0MjcyIC0xMTIuNXExNjUgMCAyNjYuNSAxMDV0MTAxLjUgMjcwek0xMjYyIDU4NQ0KaDEwOXYxMTBoLTEwOXYxMTBoLTExMHYtMTEwaC0xMTB2LTExMGgxMTB2LTExMGgxMTB2MTEwek0xNTM2IDExMjB2LTk2MHEwIC0xMTkgLTg0LjUgLTIwMy41dC0yMDMuNSAtODQuNWgtOTYwcS0xMTkgMCAtMjAzLjUgODQuNXQtODQuNSAyMDMuNXY5NjBxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDk2MHExMTkgMCAyMDMuNSAtODQuNXQ4NC41IC0yMDMuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Imdvb2dsZV9wbHVzIiB1bmljb2RlPSImI3hmMGQ1OyIgaG9yaXotYWR2LXg9IjIzMDQiIA0KZD0iTTE0MzcgNjIzcTAgLTIwOCAtODcgLTM3MC41dC0yNDggLTI1NHQtMzY5IC05MS41cS0xNDkgMCAtMjg1IDU4dC0yMzQgMTU2dC0xNTYgMjM0dC01OCAyODV0NTggMjg1dDE1NiAyMzR0MjM0IDE1NnQyODUgNThxMjg2IDAgNDkxIC0xOTJsLTE5OSAtMTkxcS0xMTcgMTEzIC0yOTIgMTEzcS0xMjMgMCAtMjI3LjUgLTYydC0xNjUuNSAtMTY4LjV0LTYxIC0yMzIuNXQ2MSAtMjMyLjV0MTY1LjUgLTE2OC41dDIyNy41IC02Mg0KcTgzIDAgMTUyLjUgMjN0MTE0LjUgNTcuNXQ3OC41IDc4LjV0NDkgODN0MjEuNSA3NGgtNDE2djI1Mmg2OTJxMTIgLTYzIDEyIC0xMjJ6TTIzMDQgNzQ1di0yMTBoLTIwOXYtMjA5aC0yMTB2MjA5aC0yMDl2MjEwaDIwOXYyMDloMjEwdi0yMDloMjA5eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ibW9uZXkiIHVuaWNvZGU9IiYjeGYwZDY7IiBob3Jpei1hZHYteD0iMTkyMCIgDQpkPSJNNzY4IDM4NGgzODR2OTZoLTEyOHY0NDhoLTExNGwtMTQ4IC0xMzdsNzcgLTgwcTQyIDM3IDU1IDU3aDJ2LTI4OGgtMTI4di05NnpNMTI4MCA2NDBxMCAtNzAgLTIxIC0xNDJ0LTU5LjUgLTEzNHQtMTAxLjUgLTEwMXQtMTM4IC0zOXQtMTM4IDM5dC0xMDEuNSAxMDF0LTU5LjUgMTM0dC0yMSAxNDJ0MjEgMTQydDU5LjUgMTM0dDEwMS41IDEwMXQxMzggMzl0MTM4IC0zOXQxMDEuNSAtMTAxdDU5LjUgLTEzNHQyMSAtMTQyek0xNzkyIDM4NA0KdjUxMnEtMTA2IDAgLTE4MSA3NXQtNzUgMTgxaC0xMTUycTAgLTEwNiAtNzUgLTE4MXQtMTgxIC03NXYtNTEycTEwNiAwIDE4MSAtNzV0NzUgLTE4MWgxMTUycTAgMTA2IDc1IDE4MXQxODEgNzV6TTE5MjAgMTIxNnYtMTE1MnEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTE3OTJxLTI2IDAgLTQ1IDE5dC0xOSA0NXYxMTUycTAgMjYgMTkgNDV0NDUgMTloMTc5MnEyNiAwIDQ1IC0xOXQxOSAtNDV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJjYXJldF9kb3duIiB1bmljb2RlPSImI3hmMGQ3OyIgaG9yaXotYWR2LXg9IjEwMjQiIA0KZD0iTTEwMjQgODMycTAgLTI2IC0xOSAtNDVsLTQ0OCAtNDQ4cS0xOSAtMTkgLTQ1IC0xOXQtNDUgMTlsLTQ0OCA0NDhxLTE5IDE5IC0xOSA0NXQxOSA0NXQ0NSAxOWg4OTZxMjYgMCA0NSAtMTl0MTkgLTQ1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY2FyZXRfdXAiIHVuaWNvZGU9IiYjeGYwZDg7IiBob3Jpei1hZHYteD0iMTAyNCIgDQpkPSJNMTAyNCAzMjBxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC04OTZxLTI2IDAgLTQ1IDE5dC0xOSA0NXQxOSA0NWw0NDggNDQ4cTE5IDE5IDQ1IDE5dDQ1IC0xOWw0NDggLTQ0OHExOSAtMTkgMTkgLTQ1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY2FyZXRfbGVmdCIgdW5pY29kZT0iJiN4ZjBkOTsiIGhvcml6LWFkdi14PSI2NDAiIA0KZD0iTTY0MCAxMDg4di04OTZxMCAtMjYgLTE5IC00NXQtNDUgLTE5dC00NSAxOWwtNDQ4IDQ0OHEtMTkgMTkgLTE5IDQ1dDE5IDQ1bDQ0OCA0NDhxMTkgMTkgNDUgMTl0NDUgLTE5dDE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImNhcmV0X3JpZ2h0IiB1bmljb2RlPSImI3hmMGRhOyIgaG9yaXotYWR2LXg9IjY0MCIgDQpkPSJNNTc2IDY0MHEwIC0yNiAtMTkgLTQ1bC00NDggLTQ0OHEtMTkgLTE5IC00NSAtMTl0LTQ1IDE5dC0xOSA0NXY4OTZxMCAyNiAxOSA0NXQ0NSAxOXQ0NSAtMTlsNDQ4IC00NDhxMTkgLTE5IDE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImNvbHVtbnMiIHVuaWNvZGU9IiYjeGYwZGI7IiBob3Jpei1hZHYteD0iMTY2NCIgDQpkPSJNMTYwIDBoNjA4djExNTJoLTY0MHYtMTEyMHEwIC0xMyA5LjUgLTIyLjV0MjIuNSAtOS41ek0xNTM2IDMydjExMjBoLTY0MHYtMTE1Mmg2MDhxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXpNMTY2NCAxMjQ4di0xMjE2cTAgLTY2IC00NyAtMTEzdC0xMTMgLTQ3aC0xMzQ0cS02NiAwIC0xMTMgNDd0LTQ3IDExM3YxMjE2cTAgNjYgNDcgMTEzdDExMyA0N2gxMzQ0cTY2IDAgMTEzIC00N3Q0NyAtMTEzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ic29ydCIgdW5pY29kZT0iJiN4ZjBkYzsiIGhvcml6LWFkdi14PSIxMDI0IiANCmQ9Ik0xMDI0IDQ0OHEwIC0yNiAtMTkgLTQ1bC00NDggLTQ0OHEtMTkgLTE5IC00NSAtMTl0LTQ1IDE5bC00NDggNDQ4cS0xOSAxOSAtMTkgNDV0MTkgNDV0NDUgMTloODk2cTI2IDAgNDUgLTE5dDE5IC00NXpNMTAyNCA4MzJxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC04OTZxLTI2IDAgLTQ1IDE5dC0xOSA0NXQxOSA0NWw0NDggNDQ4cTE5IDE5IDQ1IDE5dDQ1IC0xOWw0NDggLTQ0OHExOSAtMTkgMTkgLTQ1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ic29ydF9kb3duIiB1bmljb2RlPSImI3hmMGRkOyIgaG9yaXotYWR2LXg9IjEwMjQiIA0KZD0iTTEwMjQgNDQ4cTAgLTI2IC0xOSAtNDVsLTQ0OCAtNDQ4cS0xOSAtMTkgLTQ1IC0xOXQtNDUgMTlsLTQ0OCA0NDhxLTE5IDE5IC0xOSA0NXQxOSA0NXQ0NSAxOWg4OTZxMjYgMCA0NSAtMTl0MTkgLTQ1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ic29ydF91cCIgdW5pY29kZT0iJiN4ZjBkZTsiIGhvcml6LWFkdi14PSIxMDI0IiANCmQ9Ik0xMDI0IDgzMnEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTg5NnEtMjYgMCAtNDUgMTl0LTE5IDQ1dDE5IDQ1bDQ0OCA0NDhxMTkgMTkgNDUgMTl0NDUgLTE5bDQ0OCAtNDQ4cTE5IC0xOSAxOSAtNDV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJlbnZlbG9wZV9hbHQiIHVuaWNvZGU9IiYjeGYwZTA7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTc5MiA4MjZ2LTc5NHEwIC02NiAtNDcgLTExM3QtMTEzIC00N2gtMTQ3MnEtNjYgMCAtMTEzIDQ3dC00NyAxMTN2Nzk0cTQ0IC00OSAxMDEgLTg3cTM2MiAtMjQ2IDQ5NyAtMzQ1cTU3IC00MiA5Mi41IC02NS41dDk0LjUgLTQ4dDExMCAtMjQuNWgxaDFxNTEgMCAxMTAgMjQuNXQ5NC41IDQ4dDkyLjUgNjUuNXExNzAgMTIzIDQ5OCAzNDVxNTcgMzkgMTAwIDg3ek0xNzkyIDExMjBxMCAtNzkgLTQ5IC0xNTF0LTEyMiAtMTIzDQpxLTM3NiAtMjYxIC00NjggLTMyNXEtMTAgLTcgLTQyLjUgLTMwLjV0LTU0IC0zOHQtNTIgLTMyLjV0LTU3LjUgLTI3dC01MCAtOWgtMWgtMXEtMjMgMCAtNTAgOXQtNTcuNSAyN3QtNTIgMzIuNXQtNTQgMzh0LTQyLjUgMzAuNXEtOTEgNjQgLTI2MiAxODIuNXQtMjA1IDE0Mi41cS02MiA0MiAtMTE3IDExNS41dC01NSAxMzYuNXEwIDc4IDQxLjUgMTMwdDExOC41IDUyaDE0NzJxNjUgMCAxMTIuNSAtNDd0NDcuNSAtMTEzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ibGlua2VkaW4iIHVuaWNvZGU9IiYjeGYwZTE7IiANCmQ9Ik0zNDkgOTExdi05OTFoLTMzMHY5OTFoMzMwek0zNzAgMTIxN3ExIC03MyAtNTAuNSAtMTIydC0xMzUuNSAtNDloLTJxLTgyIDAgLTEzMiA0OXQtNTAgMTIycTAgNzQgNTEuNSAxMjIuNXQxMzQuNSA0OC41dDEzMyAtNDguNXQ1MSAtMTIyLjV6TTE1MzYgNDg4di01NjhoLTMyOXY1MzBxMCAxMDUgLTQwLjUgMTY0LjV0LTEyNi41IDU5LjVxLTYzIDAgLTEwNS41IC0zNC41dC02My41IC04NS41cS0xMSAtMzAgLTExIC04MXYtNTUzaC0zMjkNCnEyIDM5OSAyIDY0N3QtMSAyOTZsLTEgNDhoMzI5di0xNDRoLTJxMjAgMzIgNDEgNTZ0NTYuNSA1MnQ4NyA0My41dDExNC41IDE1LjVxMTcxIDAgMjc1IC0xMTMuNXQxMDQgLTMzMi41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5kbyIgdW5pY29kZT0iJiN4ZjBlMjsiIA0KZD0iTTE1MzYgNjQwcTAgLTE1NiAtNjEgLTI5OHQtMTY0IC0yNDV0LTI0NSAtMTY0dC0yOTggLTYxcS0xNzIgMCAtMzI3IDcyLjV0LTI2NCAyMDQuNXEtNyAxMCAtNi41IDIyLjV0OC41IDIwLjVsMTM3IDEzOHExMCA5IDI1IDlxMTYgLTIgMjMgLTEycTczIC05NSAxNzkgLTE0N3QyMjUgLTUycTEwNCAwIDE5OC41IDQwLjV0MTYzLjUgMTA5LjV0MTA5LjUgMTYzLjV0NDAuNSAxOTguNXQtNDAuNSAxOTguNXQtMTA5LjUgMTYzLjUNCnQtMTYzLjUgMTA5LjV0LTE5OC41IDQwLjVxLTk4IDAgLTE4OCAtMzUuNXQtMTYwIC0xMDEuNWwxMzcgLTEzOHEzMSAtMzAgMTQgLTY5cS0xNyAtNDAgLTU5IC00MGgtNDQ4cS0yNiAwIC00NSAxOXQtMTkgNDV2NDQ4cTAgNDIgNDAgNTlxMzkgMTcgNjkgLTE0bDEzMCAtMTI5cTEwNyAxMDEgMjQ0LjUgMTU2LjV0Mjg0LjUgNTUuNXExNTYgMCAyOTggLTYxdDI0NSAtMTY0dDE2NCAtMjQ1dDYxIC0yOTh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJsZWdhbCIgdW5pY29kZT0iJiN4ZjBlMzsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNzcxIDBxMCAtNTMgLTM3IC05MGwtMTA3IC0xMDhxLTM5IC0zNyAtOTEgLTM3cS01MyAwIC05MCAzN2wtMzYzIDM2NHEtMzggMzYgLTM4IDkwcTAgNTMgNDMgOTZsLTI1NiAyNTZsLTEyNiAtMTI2cS0xNCAtMTQgLTM0IC0xNHQtMzQgMTRxMiAtMiAxMi41IC0xMnQxMi41IC0xM3QxMCAtMTEuNXQxMCAtMTMuNXQ2IC0xMy41dDUuNSAtMTYuNXQxLjUgLTE4cTAgLTM4IC0yOCAtNjhxLTMgLTMgLTE2LjUgLTE4dC0xOSAtMjAuNQ0KdC0xOC41IC0xNi41dC0yMiAtMTUuNXQtMjIgLTl0LTI2IC00LjVxLTQwIDAgLTY4IDI4bC00MDggNDA4cS0yOCAyOCAtMjggNjhxMCAxMyA0LjUgMjZ0OSAyMnQxNS41IDIydDE2LjUgMTguNXQyMC41IDE5dDE4IDE2LjVxMzAgMjggNjggMjhxMTAgMCAxOCAtMS41dDE2LjUgLTUuNXQxMy41IC02dDEzLjUgLTEwdDExLjUgLTEwdDEzIC0xMi41dDEyIC0xMi41cS0xNCAxNCAtMTQgMzR0MTQgMzRsMzQ4IDM0OHExNCAxNCAzNCAxNHQzNCAtMTQNCnEtMiAyIC0xMi41IDEydC0xMi41IDEzdC0xMCAxMS41dC0xMCAxMy41dC02IDEzLjV0LTUuNSAxNi41dC0xLjUgMThxMCAzOCAyOCA2OHEzIDMgMTYuNSAxOHQxOSAyMC41dDE4LjUgMTYuNXQyMiAxNS41dDIyIDl0MjYgNC41cTQwIDAgNjggLTI4bDQwOCAtNDA4cTI4IC0yOCAyOCAtNjhxMCAtMTMgLTQuNSAtMjZ0LTkgLTIydC0xNS41IC0yMnQtMTYuNSAtMTguNXQtMjAuNSAtMTl0LTE4IC0xNi41cS0zMCAtMjggLTY4IC0yOA0KcS0xMCAwIC0xOCAxLjV0LTE2LjUgNS41dC0xMy41IDZ0LTEzLjUgMTB0LTExLjUgMTB0LTEzIDEyLjV0LTEyIDEyLjVxMTQgLTE0IDE0IC0zNHQtMTQgLTM0bC0xMjYgLTEyNmwyNTYgLTI1NnE0MyA0MyA5NiA0M3E1MiAwIDkxIC0zN2wzNjMgLTM2M3EzNyAtMzkgMzcgLTkxeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZGFzaGJvYXJkIiB1bmljb2RlPSImI3hmMGU0OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTM4NCAzODRxMCA1MyAtMzcuNSA5MC41dC05MC41IDM3LjV0LTkwLjUgLTM3LjV0LTM3LjUgLTkwLjV0MzcuNSAtOTAuNXQ5MC41IC0zNy41dDkwLjUgMzcuNXQzNy41IDkwLjV6TTU3NiA4MzJxMCA1MyAtMzcuNSA5MC41dC05MC41IDM3LjV0LTkwLjUgLTM3LjV0LTM3LjUgLTkwLjV0MzcuNSAtOTAuNXQ5MC41IC0zNy41dDkwLjUgMzcuNXQzNy41IDkwLjV6TTEwMDQgMzUxbDEwMSAzODJxNiAyNiAtNy41IDQ4LjV0LTM4LjUgMjkuNQ0KdC00OCAtNi41dC0zMCAtMzkuNWwtMTAxIC0zODJxLTYwIC01IC0xMDcgLTQzLjV0LTYzIC05OC41cS0yMCAtNzcgMjAgLTE0NnQxMTcgLTg5dDE0NiAyMHQ4OSAxMTdxMTYgNjAgLTYgMTE3dC03MiA5MXpNMTY2NCAzODRxMCA1MyAtMzcuNSA5MC41dC05MC41IDM3LjV0LTkwLjUgLTM3LjV0LTM3LjUgLTkwLjV0MzcuNSAtOTAuNXQ5MC41IC0zNy41dDkwLjUgMzcuNXQzNy41IDkwLjV6TTEwMjQgMTAyNHEwIDUzIC0zNy41IDkwLjUNCnQtOTAuNSAzNy41dC05MC41IC0zNy41dC0zNy41IC05MC41dDM3LjUgLTkwLjV0OTAuNSAtMzcuNXQ5MC41IDM3LjV0MzcuNSA5MC41ek0xNDcyIDgzMnEwIDUzIC0zNy41IDkwLjV0LTkwLjUgMzcuNXQtOTAuNSAtMzcuNXQtMzcuNSAtOTAuNXQzNy41IC05MC41dDkwLjUgLTM3LjV0OTAuNSAzNy41dDM3LjUgOTAuNXpNMTc5MiAzODRxMCAtMjYxIC0xNDEgLTQ4M3EtMTkgLTI5IC01NCAtMjloLTE0MDJxLTM1IDAgLTU0IDI5DQpxLTE0MSAyMjEgLTE0MSA0ODNxMCAxODIgNzEgMzQ4dDE5MSAyODZ0Mjg2IDE5MXQzNDggNzF0MzQ4IC03MXQyODYgLTE5MXQxOTEgLTI4NnQ3MSAtMzQ4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY29tbWVudF9hbHQiIHVuaWNvZGU9IiYjeGYwZTU7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNODk2IDExNTJxLTIwNCAwIC0zODEuNSAtNjkuNXQtMjgyIC0xODcuNXQtMTA0LjUgLTI1NXEwIC0xMTIgNzEuNSAtMjEzLjV0MjAxLjUgLTE3NS41bDg3IC01MGwtMjcgLTk2cS0yNCAtOTEgLTcwIC0xNzJxMTUyIDYzIDI3NSAxNzFsNDMgMzhsNTcgLTZxNjkgLTggMTMwIC04cTIwNCAwIDM4MS41IDY5LjV0MjgyIDE4Ny41dDEwNC41IDI1NXQtMTA0LjUgMjU1dC0yODIgMTg3LjV0LTM4MS41IDY5LjV6TTE3OTIgNjQwDQpxMCAtMTc0IC0xMjAgLTMyMS41dC0zMjYgLTIzM3QtNDUwIC04NS41cS03MCAwIC0xNDUgOHEtMTk4IC0xNzUgLTQ2MCAtMjQycS00OSAtMTQgLTExNCAtMjJoLTVxLTE1IDAgLTI3IDEwLjV0LTE2IDI3LjV2MXEtMyA0IC0wLjUgMTJ0MiAxMHQ0LjUgOS41bDYgOXQ3IDguNXQ4IDlxNyA4IDMxIDM0LjV0MzQuNSAzOHQzMSAzOS41dDMyLjUgNTF0MjcgNTl0MjYgNzZxLTE1NyA4OSAtMjQ3LjUgMjIwdC05MC41IDI4MXEwIDE3NCAxMjAgMzIxLjUNCnQzMjYgMjMzdDQ1MCA4NS41dDQ1MCAtODUuNXQzMjYgLTIzM3QxMjAgLTMyMS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY29tbWVudHNfYWx0IiB1bmljb2RlPSImI3hmMGU2OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTcwNCAxMTUycS0xNTMgMCAtMjg2IC01MnQtMjExLjUgLTE0MXQtNzguNSAtMTkxcTAgLTgyIDUzIC0xNTh0MTQ5IC0xMzJsOTcgLTU2bC0zNSAtODRxMzQgMjAgNjIgMzlsNDQgMzFsNTMgLTEwcTc4IC0xNCAxNTMgLTE0cTE1MyAwIDI4NiA1MnQyMTEuNSAxNDF0NzguNSAxOTF0LTc4LjUgMTkxdC0yMTEuNSAxNDF0LTI4NiA1MnpNNzA0IDEyODBxMTkxIDAgMzUzLjUgLTY4LjV0MjU2LjUgLTE4Ni41dDk0IC0yNTd0LTk0IC0yNTcNCnQtMjU2LjUgLTE4Ni41dC0zNTMuNSAtNjguNXEtODYgMCAtMTc2IDE2cS0xMjQgLTg4IC0yNzggLTEyOHEtMzYgLTkgLTg2IC0xNmgtM3EtMTEgMCAtMjAuNSA4dC0xMS41IDIxcS0xIDMgLTEgNi41dDAuNSA2LjV0MiA2bDIuNSA1dDMuNSA1LjV0NCA1dDQuNSA1dDQgNC41cTUgNiAyMyAyNXQyNiAyOS41dDIyLjUgMjl0MjUgMzguNXQyMC41IDQ0cS0xMjQgNzIgLTE5NSAxNzd0LTcxIDIyNHEwIDEzOSA5NCAyNTd0MjU2LjUgMTg2LjUNCnQzNTMuNSA2OC41ek0xNTI2IDExMXExMCAtMjQgMjAuNSAtNDR0MjUgLTM4LjV0MjIuNSAtMjl0MjYgLTI5LjV0MjMgLTI1cTEgLTEgNCAtNC41dDQuNSAtNXQ0IC01dDMuNSAtNS41bDIuNSAtNXQyIC02dDAuNSAtNi41dC0xIC02LjVxLTMgLTE0IC0xMyAtMjJ0LTIyIC03cS01MCA3IC04NiAxNnEtMTU0IDQwIC0yNzggMTI4cS05MCAtMTYgLTE3NiAtMTZxLTI3MSAwIC00NzIgMTMycTU4IC00IDg4IC00cTE2MSAwIDMwOSA0NXQyNjQgMTI5DQpxMTI1IDkyIDE5MiAyMTJ0NjcgMjU0cTAgNzcgLTIzIDE1MnExMjkgLTcxIDIwNCAtMTc4dDc1IC0yMzBxMCAtMTIwIC03MSAtMjI0LjV0LTE5NSAtMTc2LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJib2x0IiB1bmljb2RlPSImI3hmMGU3OyIgaG9yaXotYWR2LXg9Ijg5NiIgDQpkPSJNODg1IDk3MHExOCAtMjAgNyAtNDRsLTU0MCAtMTE1N3EtMTMgLTI1IC00MiAtMjVxLTQgMCAtMTQgMnEtMTcgNSAtMjUuNSAxOXQtNC41IDMwbDE5NyA4MDhsLTQwNiAtMTAxcS00IC0xIC0xMiAtMXEtMTggMCAtMzEgMTFxLTE4IDE1IC0xMyAzOWwyMDEgODI1cTQgMTQgMTYgMjN0MjggOWgzMjhxMTkgMCAzMiAtMTIuNXQxMyAtMjkuNXEwIC04IC01IC0xOGwtMTcxIC00NjNsMzk2IDk4cTggMiAxMiAycTE5IDAgMzQgLTE1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ic2l0ZW1hcCIgdW5pY29kZT0iJiN4ZjBlODsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNzkyIDI4OHYtMzIwcTAgLTQwIC0yOCAtNjh0LTY4IC0yOGgtMzIwcS00MCAwIC02OCAyOHQtMjggNjh2MzIwcTAgNDAgMjggNjh0NjggMjhoOTZ2MTkyaC01MTJ2LTE5Mmg5NnE0MCAwIDY4IC0yOHQyOCAtNjh2LTMyMHEwIC00MCAtMjggLTY4dC02OCAtMjhoLTMyMHEtNDAgMCAtNjggMjh0LTI4IDY4djMyMHEwIDQwIDI4IDY4dDY4IDI4aDk2djE5MmgtNTEydi0xOTJoOTZxNDAgMCA2OCAtMjh0MjggLTY4di0zMjANCnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTMyMHEtNDAgMCAtNjggMjh0LTI4IDY4djMyMHEwIDQwIDI4IDY4dDY4IDI4aDk2djE5MnEwIDUyIDM4IDkwdDkwIDM4aDUxMnYxOTJoLTk2cS00MCAwIC02OCAyOHQtMjggNjh2MzIwcTAgNDAgMjggNjh0NjggMjhoMzIwcTQwIDAgNjggLTI4dDI4IC02OHYtMzIwcTAgLTQwIC0yOCAtNjh0LTY4IC0yOGgtOTZ2LTE5Mmg1MTJxNTIgMCA5MCAtMzh0MzggLTkwdi0xOTJoOTZxNDAgMCA2OCAtMjh0MjggLTY4DQp6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bWJyZWxsYSIgdW5pY29kZT0iJiN4ZjBlOTsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik04OTYgNzA4di01ODBxMCAtMTA0IC03NiAtMTgwdC0xODAgLTc2dC0xODAgNzZ0LTc2IDE4MHEwIDI2IDE5IDQ1dDQ1IDE5dDQ1IC0xOXQxOSAtNDVxMCAtNTAgMzkgLTg5dDg5IC0zOXQ4OSAzOXQzOSA4OXY1ODBxMzMgMTEgNjQgMTF0NjQgLTExek0xNjY0IDY4MXEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVxLTExIDAgLTIzIDEwcS00OSA0NiAtOTMgNjl0LTEwMiAyM3EtNjggMCAtMTI4IC0zN3QtMTAzIC05Nw0KcS03IC0xMCAtMTcuNSAtMjh0LTE0LjUgLTI0cS0xMSAtMTcgLTI4IC0xN3EtMTggMCAtMjkgMTdxLTQgNiAtMTQuNSAyNHQtMTcuNSAyOHEtNDMgNjAgLTEwMi41IDk3dC0xMjcuNSAzN3QtMTI3LjUgLTM3dC0xMDIuNSAtOTdxLTcgLTEwIC0xNy41IC0yOHQtMTQuNSAtMjRxLTExIC0xNyAtMjkgLTE3cS0xNyAwIC0yOCAxN3EtNCA2IC0xNC41IDI0dC0xNy41IDI4cS00MyA2MCAtMTAzIDk3dC0xMjggMzdxLTU4IDAgLTEwMiAtMjN0LTkzIC02OQ0KcS0xMiAtMTAgLTIzIC0xMHEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41cTAgNSAxIDdxNDUgMTgzIDE3Mi41IDMxOS41dDI5OCAyMDQuNXQzNjAuNSA2OHExNDAgMCAyNzQuNSAtNDB0MjQ2LjUgLTExMy41dDE5NC41IC0xODd0MTE1LjUgLTI1MS41cTEgLTIgMSAtN3pNODk2IDE0MDh2LTk4cS00MiAyIC02NCAydC02NCAtMnY5OHEwIDI2IDE5IDQ1dDQ1IDE5dDQ1IC0xOXQxOSAtNDV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJwYXN0ZSIgdW5pY29kZT0iJiN4ZjBlYTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik03NjggLTEyOGg4OTZ2NjQwaC00MTZxLTQwIDAgLTY4IDI4dC0yOCA2OHY0MTZoLTM4NHYtMTE1MnpNMTAyNCAxMzEydjY0cTAgMTMgLTkuNSAyMi41dC0yMi41IDkuNWgtNzA0cS0xMyAwIC0yMi41IC05LjV0LTkuNSAtMjIuNXYtNjRxMCAtMTMgOS41IC0yMi41dDIyLjUgLTkuNWg3MDRxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXpNMTI4MCA2NDBoMjk5bC0yOTkgMjk5di0yOTl6TTE3OTIgNTEydi02NzJxMCAtNDAgLTI4IC02OHQtNjggLTI4DQpoLTk2MHEtNDAgMCAtNjggMjh0LTI4IDY4djE2MGgtNTQ0cS00MCAwIC02OCAyOHQtMjggNjh2MTM0NHEwIDQwIDI4IDY4dDY4IDI4aDEwODhxNDAgMCA2OCAtMjh0MjggLTY4di0zMjhxMjEgLTEzIDM2IC0yOGw0MDggLTQwOHEyOCAtMjggNDggLTc2dDIwIC04OHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImxpZ2h0X2J1bGIiIHVuaWNvZGU9IiYjeGYwZWI7IiBob3Jpei1hZHYteD0iMTAyNCIgDQpkPSJNNzM2IDk2MHEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjV0LTIyLjUgOS41dC05LjUgMjIuNXEwIDQ2IC01NCA3MXQtMTA2IDI1cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV0OS41IDIyLjV0MjIuNSA5LjVxNTAgMCA5OS41IC0xNnQ4NyAtNTR0MzcuNSAtOTB6TTg5NiA5NjBxMCA3MiAtMzQuNSAxMzR0LTkwIDEwMS41dC0xMjMgNjJ0LTEzNi41IDIyLjV0LTEzNi41IC0yMi41dC0xMjMgLTYydC05MCAtMTAxLjV0LTM0LjUgLTEzNA0KcTAgLTEwMSA2OCAtMTgwcTEwIC0xMSAzMC41IC0zM3QzMC41IC0zM3ExMjggLTE1MyAxNDEgLTI5OGgyMjhxMTMgMTQ1IDE0MSAyOThxMTAgMTEgMzAuNSAzM3QzMC41IDMzcTY4IDc5IDY4IDE4MHpNMTAyNCA5NjBxMCAtMTU1IC0xMDMgLTI2OHEtNDUgLTQ5IC03NC41IC04N3QtNTkuNSAtOTUuNXQtMzQgLTEwNy41cTQ3IC0yOCA0NyAtODJxMCAtMzcgLTI1IC02NHEyNSAtMjcgMjUgLTY0cTAgLTUyIC00NSAtODFxMTMgLTIzIDEzIC00Nw0KcTAgLTQ2IC0zMS41IC03MXQtNzcuNSAtMjVxLTIwIC00NCAtNjAgLTcwdC04NyAtMjZ0LTg3IDI2dC02MCA3MHEtNDYgMCAtNzcuNSAyNXQtMzEuNSA3MXEwIDI0IDEzIDQ3cS00NSAyOSAtNDUgODFxMCAzNyAyNSA2NHEtMjUgMjcgLTI1IDY0cTAgNTQgNDcgODJxLTQgNTAgLTM0IDEwNy41dC01OS41IDk1LjV0LTc0LjUgODdxLTEwMyAxMTMgLTEwMyAyNjhxMCA5OSA0NC41IDE4NC41dDExNyAxNDJ0MTY0IDg5dDE4Ni41IDMyLjUNCnQxODYuNSAtMzIuNXQxNjQgLTg5dDExNyAtMTQydDQ0LjUgLTE4NC41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZXhjaGFuZ2UiIHVuaWNvZGU9IiYjeGYwZWM7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTc5MiAzNTJ2LTE5MnEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTEzNzZ2LTE5MnEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVxLTEyIDAgLTI0IDEwbC0zMTkgMzIwcS05IDkgLTkgMjJxMCAxNCA5IDIzbDMyMCAzMjBxOSA5IDIzIDlxMTMgMCAyMi41IC05LjV0OS41IC0yMi41di0xOTJoMTM3NnExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6TTE3OTIgODk2cTAgLTE0IC05IC0yM2wtMzIwIC0zMjBxLTkgLTkgLTIzIC05DQpxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXYxOTJoLTEzNzZxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXYxOTJxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWgxMzc2djE5MnEwIDE0IDkgMjN0MjMgOXExMiAwIDI0IC0xMGwzMTkgLTMxOXE5IC05IDkgLTIzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY2xvdWRfZG93bmxvYWQiIHVuaWNvZGU9IiYjeGYwZWQ7IiBob3Jpei1hZHYteD0iMTkyMCIgDQpkPSJNMTI4MCA2MDhxMCAxNCAtOSAyM3QtMjMgOWgtMjI0djM1MnEwIDEzIC05LjUgMjIuNXQtMjIuNSA5LjVoLTE5MnEtMTMgMCAtMjIuNSAtOS41dC05LjUgLTIyLjV2LTM1MmgtMjI0cS0xMyAwIC0yMi41IC05LjV0LTkuNSAtMjIuNXEwIC0xNCA5IC0yM2wzNTIgLTM1MnE5IC05IDIzIC05dDIzIDlsMzUxIDM1MXExMCAxMiAxMCAyNHpNMTkyMCAzODRxMCAtMTU5IC0xMTIuNSAtMjcxLjV0LTI3MS41IC0xMTIuNWgtMTA4OA0KcS0xODUgMCAtMzE2LjUgMTMxLjV0LTEzMS41IDMxNi41cTAgMTMwIDcwIDI0MHQxODggMTY1cS0yIDMwIC0yIDQzcTAgMjEyIDE1MCAzNjJ0MzYyIDE1MHExNTYgMCAyODUuNSAtODd0MTg4LjUgLTIzMXE3MSA2MiAxNjYgNjJxMTA2IDAgMTgxIC03NXQ3NSAtMTgxcTAgLTc2IC00MSAtMTM4cTEzMCAtMzEgMjEzLjUgLTEzNS41dDgzLjUgLTIzOC41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY2xvdWRfdXBsb2FkIiB1bmljb2RlPSImI3hmMGVlOyIgaG9yaXotYWR2LXg9IjE5MjAiIA0KZD0iTTEyODAgNjcycTAgMTQgLTkgMjNsLTM1MiAzNTJxLTkgOSAtMjMgOXQtMjMgLTlsLTM1MSAtMzUxcS0xMCAtMTIgLTEwIC0yNHEwIC0xNCA5IC0yM3QyMyAtOWgyMjR2LTM1MnEwIC0xMyA5LjUgLTIyLjV0MjIuNSAtOS41aDE5MnExMyAwIDIyLjUgOS41dDkuNSAyMi41djM1MmgyMjRxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXpNMTkyMCAzODRxMCAtMTU5IC0xMTIuNSAtMjcxLjV0LTI3MS41IC0xMTIuNWgtMTA4OA0KcS0xODUgMCAtMzE2LjUgMTMxLjV0LTEzMS41IDMxNi41cTAgMTMwIDcwIDI0MHQxODggMTY1cS0yIDMwIC0yIDQzcTAgMjEyIDE1MCAzNjJ0MzYyIDE1MHExNTYgMCAyODUuNSAtODd0MTg4LjUgLTIzMXE3MSA2MiAxNjYgNjJxMTA2IDAgMTgxIC03NXQ3NSAtMTgxcTAgLTc2IC00MSAtMTM4cTEzMCAtMzEgMjEzLjUgLTEzNS41dDgzLjUgLTIzOC41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idXNlcl9tZCIgdW5pY29kZT0iJiN4ZjBmMDsiIGhvcml6LWFkdi14PSIxNDA4IiANCmQ9Ik0zODQgMTkycTAgLTI2IC0xOSAtNDV0LTQ1IC0xOXQtNDUgMTl0LTE5IDQ1dDE5IDQ1dDQ1IDE5dDQ1IC0xOXQxOSAtNDV6TTE0MDggMTMxcTAgLTEyMSAtNzMgLTE5MHQtMTk0IC02OWgtODc0cS0xMjEgMCAtMTk0IDY5dC03MyAxOTBxMCA2OCA1LjUgMTMxdDI0IDEzOHQ0Ny41IDEzMi41dDgxIDEwM3QxMjAgNjAuNXEtMjIgLTUyIC0yMiAtMTIwdi0yMDNxLTU4IC0yMCAtOTMgLTcwdC0zNSAtMTExcTAgLTgwIDU2IC0xMzZ0MTM2IC01Ng0KdDEzNiA1NnQ1NiAxMzZxMCA2MSAtMzUuNSAxMTF0LTkyLjUgNzB2MjAzcTAgNjIgMjUgOTNxMTMyIC0xMDQgMjk1IC0xMDR0Mjk1IDEwNHEyNSAtMzEgMjUgLTkzdi02NHEtMTA2IDAgLTE4MSAtNzV0LTc1IC0xODF2LTg5cS0zMiAtMjkgLTMyIC03MXEwIC00MCAyOCAtNjh0NjggLTI4dDY4IDI4dDI4IDY4cTAgNDIgLTMyIDcxdjg5cTAgNTIgMzggOTB0OTAgMzh0OTAgLTM4dDM4IC05MHYtODlxLTMyIC0yOSAtMzIgLTcxcTAgLTQwIDI4IC02OA0KdDY4IC0yOHQ2OCAyOHQyOCA2OHEwIDQyIC0zMiA3MXY4OXEwIDY4IC0zNC41IDEyNy41dC05My41IDkzLjVxMCAxMCAwLjUgNDIuNXQwIDQ4dC0yLjUgNDEuNXQtNyA0N3QtMTMgNDBxNjggLTE1IDEyMCAtNjAuNXQ4MSAtMTAzdDQ3LjUgLTEzMi41dDI0IC0xMzh0NS41IC0xMzF6TTEwODggMTAyNHEwIC0xNTkgLTExMi41IC0yNzEuNXQtMjcxLjUgLTExMi41dC0yNzEuNSAxMTIuNXQtMTEyLjUgMjcxLjV0MTEyLjUgMjcxLjV0MjcxLjUgMTEyLjUNCnQyNzEuNSAtMTEyLjV0MTEyLjUgLTI3MS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ic3RldGhvc2NvcGUiIHVuaWNvZGU9IiYjeGYwZjE7IiBob3Jpei1hZHYteD0iMTQwOCIgDQpkPSJNMTI4MCA4MzJxMCAyNiAtMTkgNDV0LTQ1IDE5dC00NSAtMTl0LTE5IC00NXQxOSAtNDV0NDUgLTE5dDQ1IDE5dDE5IDQ1ek0xNDA4IDgzMnEwIC02MiAtMzUuNSAtMTExdC05Mi41IC03MHYtMzk1cTAgLTE1OSAtMTMxLjUgLTI3MS41dC0zMTYuNSAtMTEyLjV0LTMxNi41IDExMi41dC0xMzEuNSAyNzEuNXYxMzJxLTE2NCAyMCAtMjc0IDEyOHQtMTEwIDI1MnY1MTJxMCAyNiAxOSA0NXQ0NSAxOXE2IDAgMTYgLTJxMTcgMzAgNDcgNDgNCnQ2NSAxOHE1MyAwIDkwLjUgLTM3LjV0MzcuNSAtOTAuNXQtMzcuNSAtOTAuNXQtOTAuNSAtMzcuNXEtMzMgMCAtNjQgMTh2LTQwMnEwIC0xMDYgOTQgLTE4MXQyMjYgLTc1dDIyNiA3NXQ5NCAxODF2NDAycS0zMSAtMTggLTY0IC0xOHEtNTMgMCAtOTAuNSAzNy41dC0zNy41IDkwLjV0MzcuNSA5MC41dDkwLjUgMzcuNXEzNSAwIDY1IC0xOHQ0NyAtNDhxMTAgMiAxNiAycTI2IDAgNDUgLTE5dDE5IC00NXYtNTEycTAgLTE0NCAtMTEwIC0yNTINCnQtMjc0IC0xMjh2LTEzMnEwIC0xMDYgOTQgLTE4MXQyMjYgLTc1dDIyNiA3NXQ5NCAxODF2Mzk1cS01NyAyMSAtOTIuNSA3MHQtMzUuNSAxMTFxMCA4MCA1NiAxMzZ0MTM2IDU2dDEzNiAtNTZ0NTYgLTEzNnoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InN1aXRjYXNlIiB1bmljb2RlPSImI3hmMGYyOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTY0MCAxMTUyaDUxMnYxMjhoLTUxMnYtMTI4ek0yODggMTE1MnYtMTI4MGgtNjRxLTkyIDAgLTE1OCA2NnQtNjYgMTU4djgzMnEwIDkyIDY2IDE1OHQxNTggNjZoNjR6TTE0MDggMTE1MnYtMTI4MGgtMTAyNHYxMjgwaDEyOHYxNjBxMCA0MCAyOCA2OHQ2OCAyOGg1NzZxNDAgMCA2OCAtMjh0MjggLTY4di0xNjBoMTI4ek0xNzkyIDkyOHYtODMycTAgLTkyIC02NiAtMTU4dC0xNTggLTY2aC02NHYxMjgwaDY0cTkyIDAgMTU4IC02Ng0KdDY2IC0xNTh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJiZWxsX2FsdCIgdW5pY29kZT0iJiN4ZjBmMzsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik05MTIgLTE2MHEwIDE2IC0xNiAxNnEtNTkgMCAtMTAxLjUgNDIuNXQtNDIuNSAxMDEuNXEwIDE2IC0xNiAxNnQtMTYgLTE2cTAgLTczIDUxLjUgLTEyNC41dDEyNC41IC01MS41cTE2IDAgMTYgMTZ6TTE3MjggMTI4cTAgLTUyIC0zOCAtOTB0LTkwIC0zOGgtNDQ4cTAgLTEwNiAtNzUgLTE4MXQtMTgxIC03NXQtMTgxIDc1dC03NSAxODFoLTQ0OHEtNTIgMCAtOTAgMzh0LTM4IDkwcTUwIDQyIDkxIDg4dDg1IDExOS41dDc0LjUgMTU4LjUNCnQ1MCAyMDZ0MTkuNSAyNjBxMCAxNTIgMTE3IDI4Mi41dDMwNyAxNTguNXEtOCAxOSAtOCAzOXEwIDQwIDI4IDY4dDY4IDI4dDY4IC0yOHQyOCAtNjhxMCAtMjAgLTggLTM5cTE5MCAtMjggMzA3IC0xNTguNXQxMTcgLTI4Mi41cTAgLTEzOSAxOS41IC0yNjB0NTAgLTIwNnQ3NC41IC0xNTguNXQ4NSAtMTE5LjV0OTEgLTg4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY29mZmVlIiB1bmljb2RlPSImI3hmMGY0OyIgaG9yaXotYWR2LXg9IjE5MjAiIA0KZD0iTTE2NjQgODk2cTAgODAgLTU2IDEzNnQtMTM2IDU2aC02NHYtMzg0aDY0cTgwIDAgMTM2IDU2dDU2IDEzNnpNMCAxMjhoMTc5MnEwIC0xMDYgLTc1IC0xODF0LTE4MSAtNzVoLTEyODBxLTEwNiAwIC0xODEgNzV0LTc1IDE4MXpNMTg1NiA4OTZxMCAtMTU5IC0xMTIuNSAtMjcxLjV0LTI3MS41IC0xMTIuNWgtNjR2LTMycTAgLTkyIC02NiAtMTU4dC0xNTggLTY2aC03MDRxLTkyIDAgLTE1OCA2NnQtNjYgMTU4djczNnEwIDI2IDE5IDQ1DQp0NDUgMTloMTE1MnExNTkgMCAyNzEuNSAtMTEyLjV0MTEyLjUgLTI3MS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZm9vZCIgdW5pY29kZT0iJiN4ZjBmNTsiIGhvcml6LWFkdi14PSIxNDA4IiANCmQ9Ik02NDAgMTQ3MnYtNjQwcTAgLTYxIC0zNS41IC0xMTF0LTkyLjUgLTcwdi03NzlxMCAtNTIgLTM4IC05MHQtOTAgLTM4aC0xMjhxLTUyIDAgLTkwIDM4dC0zOCA5MHY3NzlxLTU3IDIwIC05Mi41IDcwdC0zNS41IDExMXY2NDBxMCAyNiAxOSA0NXQ0NSAxOXQ0NSAtMTl0MTkgLTQ1di00MTZxMCAtMjYgMTkgLTQ1dDQ1IC0xOXQ0NSAxOXQxOSA0NXY0MTZxMCAyNiAxOSA0NXQ0NSAxOXQ0NSAtMTl0MTkgLTQ1di00MTZxMCAtMjYgMTkgLTQ1DQp0NDUgLTE5dDQ1IDE5dDE5IDQ1djQxNnEwIDI2IDE5IDQ1dDQ1IDE5dDQ1IC0xOXQxOSAtNDV6TTE0MDggMTQ3MnYtMTYwMHEwIC01MiAtMzggLTkwdC05MCAtMzhoLTEyOHEtNTIgMCAtOTAgMzh0LTM4IDkwdjUxMmgtMjI0cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2ODAwcTAgMTMyIDk0IDIyNnQyMjYgOTRoMjU2cTI2IDAgNDUgLTE5dDE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImZpbGVfdGV4dF9hbHQiIHVuaWNvZGU9IiYjeGYwZjY7IiANCmQ9Ik0xNDY4IDExNTZxMjggLTI4IDQ4IC03NnQyMCAtODh2LTExNTJxMCAtNDAgLTI4IC02OHQtNjggLTI4aC0xMzQ0cS00MCAwIC02OCAyOHQtMjggNjh2MTYwMHEwIDQwIDI4IDY4dDY4IDI4aDg5NnE0MCAwIDg4IC0yMHQ3NiAtNDh6TTEwMjQgMTQwMHYtMzc2aDM3NnEtMTAgMjkgLTIyIDQxbC0zMTMgMzEzcS0xMiAxMiAtNDEgMjJ6TTE0MDggLTEyOHYxMDI0aC00MTZxLTQwIDAgLTY4IDI4dC0yOCA2OHY0MTZoLTc2OHYtMTUzNmgxMjgweg0KTTM4NCA3MzZxMCAxNCA5IDIzdDIzIDloNzA0cTE0IDAgMjMgLTl0OSAtMjN2LTY0cTAgLTE0IC05IC0yM3QtMjMgLTloLTcwNHEtMTQgMCAtMjMgOXQtOSAyM3Y2NHpNMTEyMCA1MTJxMTQgMCAyMyAtOXQ5IC0yM3YtNjRxMCAtMTQgLTkgLTIzdC0yMyAtOWgtNzA0cS0xNCAwIC0yMyA5dC05IDIzdjY0cTAgMTQgOSAyM3QyMyA5aDcwNHpNMTEyMCAyNTZxMTQgMCAyMyAtOXQ5IC0yM3YtNjRxMCAtMTQgLTkgLTIzdC0yMyAtOWgtNzA0DQpxLTE0IDAgLTIzIDl0LTkgMjN2NjRxMCAxNCA5IDIzdDIzIDloNzA0eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iYnVpbGRpbmciIHVuaWNvZGU9IiYjeGYwZjc7IiBob3Jpei1hZHYteD0iMTQwOCIgDQpkPSJNMzg0IDIyNHYtNjRxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC02NHEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djY0cTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoNjRxMTMgMCAyMi41IC05LjV0OS41IC0yMi41ek0zODQgNDgwdi02NHEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTY0cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2NjRxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWg2NHExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6DQpNNjQwIDQ4MHYtNjRxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC02NHEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djY0cTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoNjRxMTMgMCAyMi41IC05LjV0OS41IC0yMi41ek0zODQgNzM2di02NHEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTY0cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2NjRxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWg2NHExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6DQpNMTE1MiAyMjR2LTY0cTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtNjRxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXY2NHEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDY0cTEzIDAgMjIuNSAtOS41dDkuNSAtMjIuNXpNODk2IDQ4MHYtNjRxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC02NHEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djY0cTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoNjRxMTMgMCAyMi41IC05LjV0OS41IC0yMi41eg0KTTY0MCA3MzZ2LTY0cTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtNjRxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXY2NHEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDY0cTEzIDAgMjIuNSAtOS41dDkuNSAtMjIuNXpNMzg0IDk5MnYtNjRxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC02NHEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djY0cTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoNjRxMTMgMCAyMi41IC05LjV0OS41IC0yMi41eg0KTTExNTIgNDgwdi02NHEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTY0cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2NjRxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWg2NHExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6TTg5NiA3MzZ2LTY0cTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtNjRxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXY2NHEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDY0cTEzIDAgMjIuNSAtOS41dDkuNSAtMjIuNXoNCk02NDAgOTkydi02NHEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTY0cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2NjRxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWg2NHExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6TTM4NCAxMjQ4di02NHEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTY0cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2NjRxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWg2NHExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6DQpNMTE1MiA3MzZ2LTY0cTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtNjRxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXY2NHEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDY0cTEzIDAgMjIuNSAtOS41dDkuNSAtMjIuNXpNODk2IDk5MnYtNjRxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC02NHEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djY0cTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoNjRxMTMgMCAyMi41IC05LjV0OS41IC0yMi41eg0KTTY0MCAxMjQ4di02NHEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTY0cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2NjRxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWg2NHExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6TTExNTIgOTkydi02NHEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTY0cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2NjRxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWg2NHExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6DQpNODk2IDEyNDh2LTY0cTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtNjRxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXY2NHEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDY0cTEzIDAgMjIuNSAtOS41dDkuNSAtMjIuNXpNMTE1MiAxMjQ4di02NHEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTY0cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2NjRxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWg2NHExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6DQpNODk2IC0xMjhoMzg0djE1MzZoLTExNTJ2LTE1MzZoMzg0djIyNHEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDMyMHExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV2LTIyNHpNMTQwOCAxNDcydi0xNjY0cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTI4MHEtMjYgMCAtNDUgMTl0LTE5IDQ1djE2NjRxMCAyNiAxOSA0NXQ0NSAxOWgxMjgwcTI2IDAgNDUgLTE5dDE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Imhvc3BpdGFsIiB1bmljb2RlPSImI3hmMGY4OyIgaG9yaXotYWR2LXg9IjE0MDgiIA0KZD0iTTM4NCAyMjR2LTY0cTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtNjRxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXY2NHEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDY0cTEzIDAgMjIuNSAtOS41dDkuNSAtMjIuNXpNMzg0IDQ4MHYtNjRxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC02NHEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djY0cTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoNjRxMTMgMCAyMi41IC05LjV0OS41IC0yMi41eg0KTTY0MCA0ODB2LTY0cTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtNjRxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXY2NHEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDY0cTEzIDAgMjIuNSAtOS41dDkuNSAtMjIuNXpNMzg0IDczNnYtNjRxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC02NHEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djY0cTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoNjRxMTMgMCAyMi41IC05LjV0OS41IC0yMi41eg0KTTExNTIgMjI0di02NHEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTY0cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2NjRxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWg2NHExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6TTg5NiA0ODB2LTY0cTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtNjRxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXY2NHEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDY0cTEzIDAgMjIuNSAtOS41dDkuNSAtMjIuNXoNCk02NDAgNzM2di02NHEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTY0cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2NjRxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWg2NHExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6TTExNTIgNDgwdi02NHEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTY0cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2NjRxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWg2NHExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV6DQpNODk2IDczNnYtNjRxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC02NHEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djY0cTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoNjRxMTMgMCAyMi41IC05LjV0OS41IC0yMi41ek0xMTUyIDczNnYtNjRxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC02NHEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djY0cTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoNjRxMTMgMCAyMi41IC05LjV0OS41IC0yMi41eg0KTTg5NiAtMTI4aDM4NHYxMTUyaC0yNTZ2LTMycTAgLTQwIC0yOCAtNjh0LTY4IC0yOGgtNDQ4cS00MCAwIC02OCAyOHQtMjggNjh2MzJoLTI1NnYtMTE1MmgzODR2MjI0cTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoMzIwcTEzIDAgMjIuNSAtOS41dDkuNSAtMjIuNXYtMjI0ek04OTYgMTA1NnYzMjBxMCAxMyAtOS41IDIyLjV0LTIyLjUgOS41aC02NHEtMTMgMCAtMjIuNSAtOS41dC05LjUgLTIyLjV2LTk2aC0xMjh2OTZxMCAxMyAtOS41IDIyLjUNCnQtMjIuNSA5LjVoLTY0cS0xMyAwIC0yMi41IC05LjV0LTkuNSAtMjIuNXYtMzIwcTAgLTEzIDkuNSAtMjIuNXQyMi41IC05LjVoNjRxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXY5NmgxMjh2LTk2cTAgLTEzIDkuNSAtMjIuNXQyMi41IC05LjVoNjRxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXpNMTQwOCAxMDg4di0xMjgwcTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTI4MHEtMjYgMCAtNDUgMTl0LTE5IDQ1djEyODBxMCAyNiAxOSA0NXQ0NSAxOWgzMjANCnYyODhxMCA0MCAyOCA2OHQ2OCAyOGg0NDhxNDAgMCA2OCAtMjh0MjggLTY4di0yODhoMzIwcTI2IDAgNDUgLTE5dDE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImFtYnVsYW5jZSIgdW5pY29kZT0iJiN4ZjBmOTsiIGhvcml6LWFkdi14PSIxOTIwIiANCmQ9Ik02NDAgMTI4cTAgNTMgLTM3LjUgOTAuNXQtOTAuNSAzNy41dC05MC41IC0zNy41dC0zNy41IC05MC41dDM3LjUgLTkwLjV0OTAuNSAtMzcuNXQ5MC41IDM3LjV0MzcuNSA5MC41ek0yNTYgNjQwaDM4NHYyNTZoLTE1OHEtMTQgLTIgLTIyIC05bC0xOTUgLTE5NXEtNyAtMTIgLTkgLTIydi0zMHpNMTUzNiAxMjhxMCA1MyAtMzcuNSA5MC41dC05MC41IDM3LjV0LTkwLjUgLTM3LjV0LTM3LjUgLTkwLjV0MzcuNSAtOTAuNXQ5MC41IC0zNy41DQp0OTAuNSAzNy41dDM3LjUgOTAuNXpNMTY2NCA4MDB2MTkycTAgMTQgLTkgMjN0LTIzIDloLTIyNHYyMjRxMCAxNCAtOSAyM3QtMjMgOWgtMTkycS0xNCAwIC0yMyAtOXQtOSAtMjN2LTIyNGgtMjI0cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTE5MnEwIC0xNCA5IC0yM3QyMyAtOWgyMjR2LTIyNHEwIC0xNCA5IC0yM3QyMyAtOWgxOTJxMTQgMCAyMyA5dDkgMjN2MjI0aDIyNHExNCAwIDIzIDl0OSAyM3pNMTkyMCAxMzQ0di0xMTUyDQpxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC0xOTJxMCAtMTA2IC03NSAtMTgxdC0xODEgLTc1dC0xODEgNzV0LTc1IDE4MWgtMzg0cTAgLTEwNiAtNzUgLTE4MXQtMTgxIC03NXQtMTgxIDc1dC03NSAxODFoLTEyOHEtMjYgMCAtNDUgMTl0LTE5IDQ1dDE5IDQ1dDQ1IDE5djQxNnEwIDI2IDEzIDU4dDMyIDUxbDE5OCAxOThxMTkgMTkgNTEgMzJ0NTggMTNoMTYwdjMyMHEwIDI2IDE5IDQ1dDQ1IDE5aDExNTJxMjYgMCA0NSAtMTl0MTkgLTQ1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ibWVka2l0IiB1bmljb2RlPSImI3hmMGZhOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTEyODAgNDE2djE5MnEwIDE0IC05IDIzdC0yMyA5aC0yMjR2MjI0cTAgMTQgLTkgMjN0LTIzIDloLTE5MnEtMTQgMCAtMjMgLTl0LTkgLTIzdi0yMjRoLTIyNHEtMTQgMCAtMjMgLTl0LTkgLTIzdi0xOTJxMCAtMTQgOSAtMjN0MjMgLTloMjI0di0yMjRxMCAtMTQgOSAtMjN0MjMgLTloMTkycTE0IDAgMjMgOXQ5IDIzdjIyNGgyMjRxMTQgMCAyMyA5dDkgMjN6TTY0MCAxMTUyaDUxMnYxMjhoLTUxMnYtMTI4ek0yNTYgMTE1MnYtMTI4MGgtMzINCnEtOTIgMCAtMTU4IDY2dC02NiAxNTh2ODMycTAgOTIgNjYgMTU4dDE1OCA2NmgzMnpNMTQ0MCAxMTUydi0xMjgwaC0xMDg4djEyODBoMTYwdjE2MHEwIDQwIDI4IDY4dDY4IDI4aDU3NnE0MCAwIDY4IC0yOHQyOCAtNjh2LTE2MGgxNjB6TTE3OTIgOTI4di04MzJxMCAtOTIgLTY2IC0xNTh0LTE1OCAtNjZoLTMydjEyODBoMzJxOTIgMCAxNTggLTY2dDY2IC0xNTh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJmaWdodGVyX2pldCIgdW5pY29kZT0iJiN4ZjBmYjsiIGhvcml6LWFkdi14PSIxOTIwIiANCmQ9Ik0xOTIwIDU3NnEtMSAtMzIgLTI4OCAtOTZsLTM1MiAtMzJsLTIyNCAtNjRoLTY0bC0yOTMgLTM1Mmg2OXEyNiAwIDQ1IC00LjV0MTkgLTExLjV0LTE5IC0xMS41dC00NSAtNC41aC05NmgtMTYwaC02NHYzMmg2NHY0MTZoLTE2MGwtMTkyIC0yMjRoLTk2bC0zMiAzMnYxOTJoMzJ2MzJoMTI4djhsLTE5MiAyNHYxMjhsMTkyIDI0djhoLTEyOHYzMmgtMzJ2MTkybDMyIDMyaDk2bDE5MiAtMjI0aDE2MHY0MTZoLTY0djMyaDY0aDE2MGg5Ng0KcTI2IDAgNDUgLTQuNXQxOSAtMTEuNXQtMTkgLTExLjV0LTQ1IC00LjVoLTY5bDI5MyAtMzUyaDY0bDIyNCAtNjRsMzUyIC0zMnExMjggLTI4IDIwMCAtNTJ0ODAgLTM0eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iYmVlciIgdW5pY29kZT0iJiN4ZjBmYzsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik02NDAgNjQwdjM4NGgtMjU2di0yNTZxMCAtNTMgMzcuNSAtOTAuNXQ5MC41IC0zNy41aDEyOHpNMTY2NCAxOTJ2LTE5MmgtMTE1MnYxOTJsMTI4IDE5MmgtMTI4cS0xNTkgMCAtMjcxLjUgMTEyLjV0LTExMi41IDI3MS41djMyMGwtNjQgNjRsMzIgMTI4aDQ4MGwzMiAxMjhoOTYwbDMyIC0xOTJsLTY0IC0zMnYtODAweiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iaF9zaWduIiB1bmljb2RlPSImI3hmMGZkOyIgDQpkPSJNMTI4MCAxOTJ2ODk2cTAgMjYgLTE5IDQ1dC00NSAxOWgtMTI4cS0yNiAwIC00NSAtMTl0LTE5IC00NXYtMzIwaC01MTJ2MzIwcTAgMjYgLTE5IDQ1dC00NSAxOWgtMTI4cS0yNiAwIC00NSAtMTl0LTE5IC00NXYtODk2cTAgLTI2IDE5IC00NXQ0NSAtMTloMTI4cTI2IDAgNDUgMTl0MTkgNDV2MzIwaDUxMnYtMzIwcTAgLTI2IDE5IC00NXQ0NSAtMTloMTI4cTI2IDAgNDUgMTl0MTkgNDV6TTE1MzYgMTEyMHYtOTYwDQpxMCAtMTE5IC04NC41IC0yMDMuNXQtMjAzLjUgLTg0LjVoLTk2MHEtMTE5IDAgLTIwMy41IDg0LjV0LTg0LjUgMjAzLjV2OTYwcTAgMTE5IDg0LjUgMjAzLjV0MjAzLjUgODQuNWg5NjBxMTE5IDAgMjAzLjUgLTg0LjV0ODQuNSAtMjAzLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJmMGZlIiB1bmljb2RlPSImI3hmMGZlOyIgDQpkPSJNMTI4MCA1NzZ2MTI4cTAgMjYgLTE5IDQ1dC00NSAxOWgtMzIwdjMyMHEwIDI2IC0xOSA0NXQtNDUgMTloLTEyOHEtMjYgMCAtNDUgLTE5dC0xOSAtNDV2LTMyMGgtMzIwcS0yNiAwIC00NSAtMTl0LTE5IC00NXYtMTI4cTAgLTI2IDE5IC00NXQ0NSAtMTloMzIwdi0zMjBxMCAtMjYgMTkgLTQ1dDQ1IC0xOWgxMjhxMjYgMCA0NSAxOXQxOSA0NXYzMjBoMzIwcTI2IDAgNDUgMTl0MTkgNDV6TTE1MzYgMTEyMHYtOTYwDQpxMCAtMTE5IC04NC41IC0yMDMuNXQtMjAzLjUgLTg0LjVoLTk2MHEtMTE5IDAgLTIwMy41IDg0LjV0LTg0LjUgMjAzLjV2OTYwcTAgMTE5IDg0LjUgMjAzLjV0MjAzLjUgODQuNWg5NjBxMTE5IDAgMjAzLjUgLTg0LjV0ODQuNSAtMjAzLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJkb3VibGVfYW5nbGVfbGVmdCIgdW5pY29kZT0iJiN4ZjEwMDsiIGhvcml6LWFkdi14PSIxMDI0IiANCmQ9Ik02MjcgMTYwcTAgLTEzIC0xMCAtMjNsLTUwIC01MHEtMTAgLTEwIC0yMyAtMTB0LTIzIDEwbC00NjYgNDY2cS0xMCAxMCAtMTAgMjN0MTAgMjNsNDY2IDQ2NnExMCAxMCAyMyAxMHQyMyAtMTBsNTAgLTUwcTEwIC0xMCAxMCAtMjN0LTEwIC0yM2wtMzkzIC0zOTNsMzkzIC0zOTNxMTAgLTEwIDEwIC0yM3pNMTAxMSAxNjBxMCAtMTMgLTEwIC0yM2wtNTAgLTUwcS0xMCAtMTAgLTIzIC0xMHQtMjMgMTBsLTQ2NiA0NjZxLTEwIDEwIC0xMCAyMw0KdDEwIDIzbDQ2NiA0NjZxMTAgMTAgMjMgMTB0MjMgLTEwbDUwIC01MHExMCAtMTAgMTAgLTIzdC0xMCAtMjNsLTM5MyAtMzkzbDM5MyAtMzkzcTEwIC0xMCAxMCAtMjN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJkb3VibGVfYW5nbGVfcmlnaHQiIHVuaWNvZGU9IiYjeGYxMDE7IiBob3Jpei1hZHYteD0iMTAyNCIgDQpkPSJNNTk1IDU3NnEwIC0xMyAtMTAgLTIzbC00NjYgLTQ2NnEtMTAgLTEwIC0yMyAtMTB0LTIzIDEwbC01MCA1MHEtMTAgMTAgLTEwIDIzdDEwIDIzbDM5MyAzOTNsLTM5MyAzOTNxLTEwIDEwIC0xMCAyM3QxMCAyM2w1MCA1MHExMCAxMCAyMyAxMHQyMyAtMTBsNDY2IC00NjZxMTAgLTEwIDEwIC0yM3pNOTc5IDU3NnEwIC0xMyAtMTAgLTIzbC00NjYgLTQ2NnEtMTAgLTEwIC0yMyAtMTB0LTIzIDEwbC01MCA1MHEtMTAgMTAgLTEwIDIzdDEwIDIzDQpsMzkzIDM5M2wtMzkzIDM5M3EtMTAgMTAgLTEwIDIzdDEwIDIzbDUwIDUwcTEwIDEwIDIzIDEwdDIzIC0xMGw0NjYgLTQ2NnExMCAtMTAgMTAgLTIzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZG91YmxlX2FuZ2xlX3VwIiB1bmljb2RlPSImI3hmMTAyOyIgaG9yaXotYWR2LXg9IjExNTIiIA0KZD0iTTEwNzUgMjI0cTAgLTEzIC0xMCAtMjNsLTUwIC01MHEtMTAgLTEwIC0yMyAtMTB0LTIzIDEwbC0zOTMgMzkzbC0zOTMgLTM5M3EtMTAgLTEwIC0yMyAtMTB0LTIzIDEwbC01MCA1MHEtMTAgMTAgLTEwIDIzdDEwIDIzbDQ2NiA0NjZxMTAgMTAgMjMgMTB0MjMgLTEwbDQ2NiAtNDY2cTEwIC0xMCAxMCAtMjN6TTEwNzUgNjA4cTAgLTEzIC0xMCAtMjNsLTUwIC01MHEtMTAgLTEwIC0yMyAtMTB0LTIzIDEwbC0zOTMgMzkzbC0zOTMgLTM5Mw0KcS0xMCAtMTAgLTIzIC0xMHQtMjMgMTBsLTUwIDUwcS0xMCAxMCAtMTAgMjN0MTAgMjNsNDY2IDQ2NnExMCAxMCAyMyAxMHQyMyAtMTBsNDY2IC00NjZxMTAgLTEwIDEwIC0yM3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImRvdWJsZV9hbmdsZV9kb3duIiB1bmljb2RlPSImI3hmMTAzOyIgaG9yaXotYWR2LXg9IjExNTIiIA0KZD0iTTEwNzUgNjcycTAgLTEzIC0xMCAtMjNsLTQ2NiAtNDY2cS0xMCAtMTAgLTIzIC0xMHQtMjMgMTBsLTQ2NiA0NjZxLTEwIDEwIC0xMCAyM3QxMCAyM2w1MCA1MHExMCAxMCAyMyAxMHQyMyAtMTBsMzkzIC0zOTNsMzkzIDM5M3ExMCAxMCAyMyAxMHQyMyAtMTBsNTAgLTUwcTEwIC0xMCAxMCAtMjN6TTEwNzUgMTA1NnEwIC0xMyAtMTAgLTIzbC00NjYgLTQ2NnEtMTAgLTEwIC0yMyAtMTB0LTIzIDEwbC00NjYgNDY2cS0xMCAxMCAtMTAgMjMNCnQxMCAyM2w1MCA1MHExMCAxMCAyMyAxMHQyMyAtMTBsMzkzIC0zOTNsMzkzIDM5M3ExMCAxMCAyMyAxMHQyMyAtMTBsNTAgLTUwcTEwIC0xMCAxMCAtMjN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJhbmdsZV9sZWZ0IiB1bmljb2RlPSImI3hmMTA0OyIgaG9yaXotYWR2LXg9IjY0MCIgDQpkPSJNNjI3IDk5MnEwIC0xMyAtMTAgLTIzbC0zOTMgLTM5M2wzOTMgLTM5M3ExMCAtMTAgMTAgLTIzdC0xMCAtMjNsLTUwIC01MHEtMTAgLTEwIC0yMyAtMTB0LTIzIDEwbC00NjYgNDY2cS0xMCAxMCAtMTAgMjN0MTAgMjNsNDY2IDQ2NnExMCAxMCAyMyAxMHQyMyAtMTBsNTAgLTUwcTEwIC0xMCAxMCAtMjN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJhbmdsZV9yaWdodCIgdW5pY29kZT0iJiN4ZjEwNTsiIGhvcml6LWFkdi14PSI2NDAiIA0KZD0iTTU5NSA1NzZxMCAtMTMgLTEwIC0yM2wtNDY2IC00NjZxLTEwIC0xMCAtMjMgLTEwdC0yMyAxMGwtNTAgNTBxLTEwIDEwIC0xMCAyM3QxMCAyM2wzOTMgMzkzbC0zOTMgMzkzcS0xMCAxMCAtMTAgMjN0MTAgMjNsNTAgNTBxMTAgMTAgMjMgMTB0MjMgLTEwbDQ2NiAtNDY2cTEwIC0xMCAxMCAtMjN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJhbmdsZV91cCIgdW5pY29kZT0iJiN4ZjEwNjsiIGhvcml6LWFkdi14PSIxMTUyIiANCmQ9Ik0xMDc1IDM1MnEwIC0xMyAtMTAgLTIzbC01MCAtNTBxLTEwIC0xMCAtMjMgLTEwdC0yMyAxMGwtMzkzIDM5M2wtMzkzIC0zOTNxLTEwIC0xMCAtMjMgLTEwdC0yMyAxMGwtNTAgNTBxLTEwIDEwIC0xMCAyM3QxMCAyM2w0NjYgNDY2cTEwIDEwIDIzIDEwdDIzIC0xMGw0NjYgLTQ2NnExMCAtMTAgMTAgLTIzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iYW5nbGVfZG93biIgdW5pY29kZT0iJiN4ZjEwNzsiIGhvcml6LWFkdi14PSIxMTUyIiANCmQ9Ik0xMDc1IDgwMHEwIC0xMyAtMTAgLTIzbC00NjYgLTQ2NnEtMTAgLTEwIC0yMyAtMTB0LTIzIDEwbC00NjYgNDY2cS0xMCAxMCAtMTAgMjN0MTAgMjNsNTAgNTBxMTAgMTAgMjMgMTB0MjMgLTEwbDM5MyAtMzkzbDM5MyAzOTNxMTAgMTAgMjMgMTB0MjMgLTEwbDUwIC01MHExMCAtMTAgMTAgLTIzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZGVza3RvcCIgdW5pY29kZT0iJiN4ZjEwODsiIGhvcml6LWFkdi14PSIxOTIwIiANCmQ9Ik0xNzkyIDU0NHY4MzJxMCAxMyAtOS41IDIyLjV0LTIyLjUgOS41aC0xNjAwcS0xMyAwIC0yMi41IC05LjV0LTkuNSAtMjIuNXYtODMycTAgLTEzIDkuNSAtMjIuNXQyMi41IC05LjVoMTYwMHExMyAwIDIyLjUgOS41dDkuNSAyMi41ek0xOTIwIDEzNzZ2LTEwODhxMCAtNjYgLTQ3IC0xMTN0LTExMyAtNDdoLTU0NHEwIC0zNyAxNiAtNzcuNXQzMiAtNzF0MTYgLTQzLjVxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC01MTJxLTI2IDAgLTQ1IDE5DQp0LTE5IDQ1cTAgMTQgMTYgNDR0MzIgNzB0MTYgNzhoLTU0NHEtNjYgMCAtMTEzIDQ3dC00NyAxMTN2MTA4OHEwIDY2IDQ3IDExM3QxMTMgNDdoMTYwMHE2NiAwIDExMyAtNDd0NDcgLTExM3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImxhcHRvcCIgdW5pY29kZT0iJiN4ZjEwOTsiIGhvcml6LWFkdi14PSIxOTIwIiANCmQ9Ik00MTYgMjU2cS02NiAwIC0xMTMgNDd0LTQ3IDExM3Y3MDRxMCA2NiA0NyAxMTN0MTEzIDQ3aDEwODhxNjYgMCAxMTMgLTQ3dDQ3IC0xMTN2LTcwNHEwIC02NiAtNDcgLTExM3QtMTEzIC00N2gtMTA4OHpNMzg0IDExMjB2LTcwNHEwIC0xMyA5LjUgLTIyLjV0MjIuNSAtOS41aDEwODhxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXY3MDRxMCAxMyAtOS41IDIyLjV0LTIyLjUgOS41aC0xMDg4cS0xMyAwIC0yMi41IC05LjV0LTkuNSAtMjIuNXoNCk0xNzYwIDE5MmgxNjB2LTk2cTAgLTQwIC00NyAtNjh0LTExMyAtMjhoLTE2MDBxLTY2IDAgLTExMyAyOHQtNDcgNjh2OTZoMTYwaDE2MDB6TTEwNDAgOTZxMTYgMCAxNiAxNnQtMTYgMTZoLTE2MHEtMTYgMCAtMTYgLTE2dDE2IC0xNmgxNjB6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ0YWJsZXQiIHVuaWNvZGU9IiYjeGYxMGE7IiBob3Jpei1hZHYteD0iMTE1MiIgDQpkPSJNNjQwIDEyOHEwIDI2IC0xOSA0NXQtNDUgMTl0LTQ1IC0xOXQtMTkgLTQ1dDE5IC00NXQ0NSAtMTl0NDUgMTl0MTkgNDV6TTEwMjQgMjg4djk2MHEwIDEzIC05LjUgMjIuNXQtMjIuNSA5LjVoLTgzMnEtMTMgMCAtMjIuNSAtOS41dC05LjUgLTIyLjV2LTk2MHEwIC0xMyA5LjUgLTIyLjV0MjIuNSAtOS41aDgzMnExMyAwIDIyLjUgOS41dDkuNSAyMi41ek0xMTUyIDEyNDh2LTEwODhxMCAtNjYgLTQ3IC0xMTN0LTExMyAtNDdoLTgzMg0KcS02NiAwIC0xMTMgNDd0LTQ3IDExM3YxMDg4cTAgNjYgNDcgMTEzdDExMyA0N2g4MzJxNjYgMCAxMTMgLTQ3dDQ3IC0xMTN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJtb2JpbGVfcGhvbmUiIHVuaWNvZGU9IiYjeGYxMGI7IiBob3Jpei1hZHYteD0iNzY4IiANCmQ9Ik00NjQgMTI4cTAgMzMgLTIzLjUgNTYuNXQtNTYuNSAyMy41dC01Ni41IC0yMy41dC0yMy41IC01Ni41dDIzLjUgLTU2LjV0NTYuNSAtMjMuNXQ1Ni41IDIzLjV0MjMuNSA1Ni41ek02NzIgMjg4djcwNHEwIDEzIC05LjUgMjIuNXQtMjIuNSA5LjVoLTUxMnEtMTMgMCAtMjIuNSAtOS41dC05LjUgLTIyLjV2LTcwNHEwIC0xMyA5LjUgLTIyLjV0MjIuNSAtOS41aDUxMnExMyAwIDIyLjUgOS41dDkuNSAyMi41ek00ODAgMTEzNg0KcTAgMTYgLTE2IDE2aC0xNjBxLTE2IDAgLTE2IC0xNnQxNiAtMTZoMTYwcTE2IDAgMTYgMTZ6TTc2OCAxMTUydi0xMDI0cTAgLTUyIC0zOCAtOTB0LTkwIC0zOGgtNTEycS01MiAwIC05MCAzOHQtMzggOTB2MTAyNHEwIDUyIDM4IDkwdDkwIDM4aDUxMnE1MiAwIDkwIC0zOHQzOCAtOTB6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJjaXJjbGVfYmxhbmsiIHVuaWNvZGU9IiYjeGYxMGM7IiANCmQ9Ik03NjggMTE4NHEtMTQ4IDAgLTI3MyAtNzN0LTE5OCAtMTk4dC03MyAtMjczdDczIC0yNzN0MTk4IC0xOTh0MjczIC03M3QyNzMgNzN0MTk4IDE5OHQ3MyAyNzN0LTczIDI3M3QtMTk4IDE5OHQtMjczIDczek0xNTM2IDY0MHEwIC0yMDkgLTEwMyAtMzg1LjV0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzdC0yNzkuNSAyNzkuNXQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN0Mzg1LjUgLTEwMw0KdDI3OS41IC0yNzkuNXQxMDMgLTM4NS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0icXVvdGVfbGVmdCIgdW5pY29kZT0iJiN4ZjEwZDsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik03NjggNTc2di0zODRxMCAtODAgLTU2IC0xMzZ0LTEzNiAtNTZoLTM4NHEtODAgMCAtMTM2IDU2dC01NiAxMzZ2NzA0cTAgMTA0IDQwLjUgMTk4LjV0MTA5LjUgMTYzLjV0MTYzLjUgMTA5LjV0MTk4LjUgNDAuNWg2NHEyNiAwIDQ1IC0xOXQxOSAtNDV2LTEyOHEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTY0cS0xMDYgMCAtMTgxIC03NXQtNzUgLTE4MXYtMzJxMCAtNDAgMjggLTY4dDY4IC0yOGgyMjRxODAgMCAxMzYgLTU2dDU2IC0xMzZ6DQpNMTY2NCA1NzZ2LTM4NHEwIC04MCAtNTYgLTEzNnQtMTM2IC01NmgtMzg0cS04MCAwIC0xMzYgNTZ0LTU2IDEzNnY3MDRxMCAxMDQgNDAuNSAxOTguNXQxMDkuNSAxNjMuNXQxNjMuNSAxMDkuNXQxOTguNSA0MC41aDY0cTI2IDAgNDUgLTE5dDE5IC00NXYtMTI4cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtNjRxLTEwNiAwIC0xODEgLTc1dC03NSAtMTgxdi0zMnEwIC00MCAyOCAtNjh0NjggLTI4aDIyNHE4MCAwIDEzNiAtNTZ0NTYgLTEzNnoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InF1b3RlX3JpZ2h0IiB1bmljb2RlPSImI3hmMTBlOyIgaG9yaXotYWR2LXg9IjE2NjQiIA0KZD0iTTc2OCAxMjE2di03MDRxMCAtMTA0IC00MC41IC0xOTguNXQtMTA5LjUgLTE2My41dC0xNjMuNSAtMTA5LjV0LTE5OC41IC00MC41aC02NHEtMjYgMCAtNDUgMTl0LTE5IDQ1djEyOHEwIDI2IDE5IDQ1dDQ1IDE5aDY0cTEwNiAwIDE4MSA3NXQ3NSAxODF2MzJxMCA0MCAtMjggNjh0LTY4IDI4aC0yMjRxLTgwIDAgLTEzNiA1NnQtNTYgMTM2djM4NHEwIDgwIDU2IDEzNnQxMzYgNTZoMzg0cTgwIDAgMTM2IC01NnQ1NiAtMTM2ek0xNjY0IDEyMTYNCnYtNzA0cTAgLTEwNCAtNDAuNSAtMTk4LjV0LTEwOS41IC0xNjMuNXQtMTYzLjUgLTEwOS41dC0xOTguNSAtNDAuNWgtNjRxLTI2IDAgLTQ1IDE5dC0xOSA0NXYxMjhxMCAyNiAxOSA0NXQ0NSAxOWg2NHExMDYgMCAxODEgNzV0NzUgMTgxdjMycTAgNDAgLTI4IDY4dC02OCAyOGgtMjI0cS04MCAwIC0xMzYgNTZ0LTU2IDEzNnYzODRxMCA4MCA1NiAxMzZ0MTM2IDU2aDM4NHE4MCAwIDEzNiAtNTZ0NTYgLTEzNnoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InNwaW5uZXIiIHVuaWNvZGU9IiYjeGYxMTA7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNNTI2IDE0MnEwIC01MyAtMzcuNSAtOTAuNXQtOTAuNSAtMzcuNXEtNTIgMCAtOTAgMzh0LTM4IDkwcTAgNTMgMzcuNSA5MC41dDkwLjUgMzcuNXQ5MC41IC0zNy41dDM3LjUgLTkwLjV6TTEwMjQgLTY0cTAgLTUzIC0zNy41IC05MC41dC05MC41IC0zNy41dC05MC41IDM3LjV0LTM3LjUgOTAuNXQzNy41IDkwLjV0OTAuNSAzNy41dDkwLjUgLTM3LjV0MzcuNSAtOTAuNXpNMzIwIDY0MHEwIC01MyAtMzcuNSAtOTAuNXQtOTAuNSAtMzcuNQ0KdC05MC41IDM3LjV0LTM3LjUgOTAuNXQzNy41IDkwLjV0OTAuNSAzNy41dDkwLjUgLTM3LjV0MzcuNSAtOTAuNXpNMTUyMiAxNDJxMCAtNTIgLTM4IC05MHQtOTAgLTM4cS01MyAwIC05MC41IDM3LjV0LTM3LjUgOTAuNXQzNy41IDkwLjV0OTAuNSAzNy41dDkwLjUgLTM3LjV0MzcuNSAtOTAuNXpNNTU4IDExMzhxMCAtNjYgLTQ3IC0xMTN0LTExMyAtNDd0LTExMyA0N3QtNDcgMTEzdDQ3IDExM3QxMTMgNDd0MTEzIC00N3Q0NyAtMTEzeg0KTTE3MjggNjQwcTAgLTUzIC0zNy41IC05MC41dC05MC41IC0zNy41dC05MC41IDM3LjV0LTM3LjUgOTAuNXQzNy41IDkwLjV0OTAuNSAzNy41dDkwLjUgLTM3LjV0MzcuNSAtOTAuNXpNMTA4OCAxMzQ0cTAgLTgwIC01NiAtMTM2dC0xMzYgLTU2dC0xMzYgNTZ0LTU2IDEzNnQ1NiAxMzZ0MTM2IDU2dDEzNiAtNTZ0NTYgLTEzNnpNMTYxOCAxMTM4cTAgLTkzIC02NiAtMTU4LjV0LTE1OCAtNjUuNXEtOTMgMCAtMTU4LjUgNjUuNXQtNjUuNSAxNTguNQ0KcTAgOTIgNjUuNSAxNTh0MTU4LjUgNjZxOTIgMCAxNTggLTY2dDY2IC0xNTh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJjaXJjbGUiIHVuaWNvZGU9IiYjeGYxMTE7IiANCmQ9Ik0xNTM2IDY0MHEwIC0yMDkgLTEwMyAtMzg1LjV0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzdC0yNzkuNSAyNzkuNXQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN0Mzg1LjUgLTEwM3QyNzkuNSAtMjc5LjV0MTAzIC0zODUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InJlcGx5IiB1bmljb2RlPSImI3hmMTEyOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE3OTIgNDE2cTAgLTE2NiAtMTI3IC00NTFxLTMgLTcgLTEwLjUgLTI0dC0xMy41IC0zMHQtMTMgLTIycS0xMiAtMTcgLTI4IC0xN3EtMTUgMCAtMjMuNSAxMHQtOC41IDI1cTAgOSAyLjUgMjYuNXQyLjUgMjMuNXE1IDY4IDUgMTIzcTAgMTAxIC0xNy41IDE4MXQtNDguNSAxMzguNXQtODAgMTAxdC0xMDUuNSA2OS41dC0xMzMgNDIuNXQtMTU0IDIxLjV0LTE3NS41IDZoLTIyNHYtMjU2cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOXQtNDUgMTkNCmwtNTEyIDUxMnEtMTkgMTkgLTE5IDQ1dDE5IDQ1bDUxMiA1MTJxMTkgMTkgNDUgMTl0NDUgLTE5dDE5IC00NXYtMjU2aDIyNHE3MTMgMCA4NzUgLTQwM3E1MyAtMTM0IDUzIC0zMzN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJnaXRodWJfYWx0IiB1bmljb2RlPSImI3hmMTEzOyIgaG9yaXotYWR2LXg9IjE2NjQiIA0KZD0iTTY0MCAzMjBxMCAtNDAgLTEyLjUgLTgydC00MyAtNzZ0LTcyLjUgLTM0dC03Mi41IDM0dC00MyA3NnQtMTIuNSA4MnQxMi41IDgydDQzIDc2dDcyLjUgMzR0NzIuNSAtMzR0NDMgLTc2dDEyLjUgLTgyek0xMjgwIDMyMHEwIC00MCAtMTIuNSAtODJ0LTQzIC03NnQtNzIuNSAtMzR0LTcyLjUgMzR0LTQzIDc2dC0xMi41IDgydDEyLjUgODJ0NDMgNzZ0NzIuNSAzNHQ3Mi41IC0zNHQ0MyAtNzZ0MTIuNSAtODJ6TTE0NDAgMzIwDQpxMCAxMjAgLTY5IDIwNHQtMTg3IDg0cS00MSAwIC0xOTUgLTIxcS03MSAtMTEgLTE1NyAtMTF0LTE1NyAxMXEtMTUyIDIxIC0xOTUgMjFxLTExOCAwIC0xODcgLTg0dC02OSAtMjA0cTAgLTg4IDMyIC0xNTMuNXQ4MSAtMTAzdDEyMiAtNjB0MTQwIC0yOS41dDE0OSAtN2gxNjhxODIgMCAxNDkgN3QxNDAgMjkuNXQxMjIgNjB0ODEgMTAzdDMyIDE1My41ek0xNjY0IDQ5NnEwIC0yMDcgLTYxIC0zMzFxLTM4IC03NyAtMTA1LjUgLTEzM3QtMTQxIC04Ng0KdC0xNzAgLTQ3LjV0LTE3MS41IC0yMnQtMTY3IC00LjVxLTc4IDAgLTE0MiAzdC0xNDcuNSAxMi41dC0xNTIuNSAzMHQtMTM3IDUxLjV0LTEyMSA4MXQtODYgMTE1cS02MiAxMjMgLTYyIDMzMXEwIDIzNyAxMzYgMzk2cS0yNyA4MiAtMjcgMTcwcTAgMTE2IDUxIDIxOHExMDggMCAxOTAgLTM5LjV0MTg5IC0xMjMuNXExNDcgMzUgMzA5IDM1cTE0OCAwIDI4MCAtMzJxMTA1IDgyIDE4NyAxMjF0MTg5IDM5cTUxIC0xMDIgNTEgLTIxOA0KcTAgLTg3IC0yNyAtMTY4cTEzNiAtMTYwIDEzNiAtMzk4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZm9sZGVyX2Nsb3NlX2FsdCIgdW5pY29kZT0iJiN4ZjExNDsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik0xNTM2IDIyNHY3MDRxMCA0MCAtMjggNjh0LTY4IDI4aC03MDRxLTQwIDAgLTY4IDI4dC0yOCA2OHY2NHEwIDQwIC0yOCA2OHQtNjggMjhoLTMyMHEtNDAgMCAtNjggLTI4dC0yOCAtNjh2LTk2MHEwIC00MCAyOCAtNjh0NjggLTI4aDEyMTZxNDAgMCA2OCAyOHQyOCA2OHpNMTY2NCA5Mjh2LTcwNHEwIC05MiAtNjYgLTE1OHQtMTU4IC02NmgtMTIxNnEtOTIgMCAtMTU4IDY2dC02NiAxNTh2OTYwcTAgOTIgNjYgMTU4dDE1OCA2NmgzMjANCnE5MiAwIDE1OCAtNjZ0NjYgLTE1OHYtMzJoNjcycTkyIDAgMTU4IC02NnQ2NiAtMTU4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZm9sZGVyX29wZW5fYWx0IiB1bmljb2RlPSImI3hmMTE1OyIgaG9yaXotYWR2LXg9IjE5MjAiIA0KZD0iTTE3ODEgNjA1cTAgMzUgLTUzIDM1aC0xMDg4cS00MCAwIC04NS41IC0yMS41dC03MS41IC01Mi41bC0yOTQgLTM2M3EtMTggLTI0IC0xOCAtNDBxMCAtMzUgNTMgLTM1aDEwODhxNDAgMCA4NiAyMnQ3MSA1M2wyOTQgMzYzcTE4IDIyIDE4IDM5ek02NDAgNzY4aDc2OHYxNjBxMCA0MCAtMjggNjh0LTY4IDI4aC01NzZxLTQwIDAgLTY4IDI4dC0yOCA2OHY2NHEwIDQwIC0yOCA2OHQtNjggMjhoLTMyMHEtNDAgMCAtNjggLTI4dC0yOCAtNjgNCnYtODUzbDI1NiAzMTVxNDQgNTMgMTE2IDg3LjV0MTQwIDM0LjV6TTE5MDkgNjA1cTAgLTYyIC00NiAtMTIwbC0yOTUgLTM2M3EtNDMgLTUzIC0xMTYgLTg3LjV0LTE0MCAtMzQuNWgtMTA4OHEtOTIgMCAtMTU4IDY2dC02NiAxNTh2OTYwcTAgOTIgNjYgMTU4dDE1OCA2NmgzMjBxOTIgMCAxNTggLTY2dDY2IC0xNTh2LTMyaDU0NHE5MiAwIDE1OCAtNjZ0NjYgLTE1OHYtMTYwaDE5MnE1NCAwIDk5IC0yNC41dDY3IC03MC41cTE1IC0zMiAxNSAtNjh6DQoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImV4cGFuZF9hbHQiIHVuaWNvZGU9IiYjeGYxMTY7IiBob3Jpei1hZHYteD0iMTc5MiIgDQogLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY29sbGFwc2VfYWx0IiB1bmljb2RlPSImI3hmMTE3OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InNtaWxlIiB1bmljb2RlPSImI3hmMTE4OyIgDQpkPSJNMTEzNCA0NjFxLTM3IC0xMjEgLTEzOCAtMTk1dC0yMjggLTc0dC0yMjggNzR0LTEzOCAxOTVxLTggMjUgNCA0OC41dDM4IDMxLjVxMjUgOCA0OC41IC00dDMxLjUgLTM4cTI1IC04MCA5Mi41IC0xMjkuNXQxNTEuNSAtNDkuNXQxNTEuNSA0OS41dDkyLjUgMTI5LjVxOCAyNiAzMiAzOHQ0OSA0dDM3IC0zMS41dDQgLTQ4LjV6TTY0MCA4OTZxMCAtNTMgLTM3LjUgLTkwLjV0LTkwLjUgLTM3LjV0LTkwLjUgMzcuNXQtMzcuNSA5MC41DQp0MzcuNSA5MC41dDkwLjUgMzcuNXQ5MC41IC0zNy41dDM3LjUgLTkwLjV6TTExNTIgODk2cTAgLTUzIC0zNy41IC05MC41dC05MC41IC0zNy41dC05MC41IDM3LjV0LTM3LjUgOTAuNXQzNy41IDkwLjV0OTAuNSAzNy41dDkwLjUgLTM3LjV0MzcuNSAtOTAuNXpNMTQwOCA2NDBxMCAxMzAgLTUxIDI0OC41dC0xMzYuNSAyMDR0LTIwNCAxMzYuNXQtMjQ4LjUgNTF0LTI0OC41IC01MXQtMjA0IC0xMzYuNXQtMTM2LjUgLTIwNHQtNTEgLTI0OC41DQp0NTEgLTI0OC41dDEzNi41IC0yMDR0MjA0IC0xMzYuNXQyNDguNSAtNTF0MjQ4LjUgNTF0MjA0IDEzNi41dDEzNi41IDIwNHQ1MSAyNDguNXpNMTUzNiA2NDBxMCAtMjA5IC0xMDMgLTM4NS41dC0yNzkuNSAtMjc5LjV0LTM4NS41IC0xMDN0LTM4NS41IDEwM3QtMjc5LjUgMjc5LjV0LTEwMyAzODUuNXQxMDMgMzg1LjV0Mjc5LjUgMjc5LjV0Mzg1LjUgMTAzdDM4NS41IC0xMDN0Mjc5LjUgLTI3OS41dDEwMyAtMzg1LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJmcm93biIgdW5pY29kZT0iJiN4ZjExOTsiIA0KZD0iTTExMzQgMzA3cTggLTI1IC00IC00OC41dC0zNyAtMzEuNXQtNDkgNHQtMzIgMzhxLTI1IDgwIC05Mi41IDEyOS41dC0xNTEuNSA0OS41dC0xNTEuNSAtNDkuNXQtOTIuNSAtMTI5LjVxLTggLTI2IC0zMS41IC0zOHQtNDguNSAtNHEtMjYgOCAtMzggMzEuNXQtNCA0OC41cTM3IDEyMSAxMzggMTk1dDIyOCA3NHQyMjggLTc0dDEzOCAtMTk1ek02NDAgODk2cTAgLTUzIC0zNy41IC05MC41dC05MC41IC0zNy41dC05MC41IDM3LjUNCnQtMzcuNSA5MC41dDM3LjUgOTAuNXQ5MC41IDM3LjV0OTAuNSAtMzcuNXQzNy41IC05MC41ek0xMTUyIDg5NnEwIC01MyAtMzcuNSAtOTAuNXQtOTAuNSAtMzcuNXQtOTAuNSAzNy41dC0zNy41IDkwLjV0MzcuNSA5MC41dDkwLjUgMzcuNXQ5MC41IC0zNy41dDM3LjUgLTkwLjV6TTE0MDggNjQwcTAgMTMwIC01MSAyNDguNXQtMTM2LjUgMjA0dC0yMDQgMTM2LjV0LTI0OC41IDUxdC0yNDguNSAtNTF0LTIwNCAtMTM2LjV0LTEzNi41IC0yMDQNCnQtNTEgLTI0OC41dDUxIC0yNDguNXQxMzYuNSAtMjA0dDIwNCAtMTM2LjV0MjQ4LjUgLTUxdDI0OC41IDUxdDIwNCAxMzYuNXQxMzYuNSAyMDR0NTEgMjQ4LjV6TTE1MzYgNjQwcTAgLTIwOSAtMTAzIC0zODUuNXQtMjc5LjUgLTI3OS41dC0zODUuNSAtMTAzdC0zODUuNSAxMDN0LTI3OS41IDI3OS41dC0xMDMgMzg1LjV0MTAzIDM4NS41dDI3OS41IDI3OS41dDM4NS41IDEwM3QzODUuNSAtMTAzdDI3OS41IC0yNzkuNXQxMDMgLTM4NS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ibWVoIiB1bmljb2RlPSImI3hmMTFhOyIgDQpkPSJNMTE1MiA0NDhxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC02NDBxLTI2IDAgLTQ1IDE5dC0xOSA0NXQxOSA0NXQ0NSAxOWg2NDBxMjYgMCA0NSAtMTl0MTkgLTQ1ek02NDAgODk2cTAgLTUzIC0zNy41IC05MC41dC05MC41IC0zNy41dC05MC41IDM3LjV0LTM3LjUgOTAuNXQzNy41IDkwLjV0OTAuNSAzNy41dDkwLjUgLTM3LjV0MzcuNSAtOTAuNXpNMTE1MiA4OTZxMCAtNTMgLTM3LjUgLTkwLjV0LTkwLjUgLTM3LjV0LTkwLjUgMzcuNQ0KdC0zNy41IDkwLjV0MzcuNSA5MC41dDkwLjUgMzcuNXQ5MC41IC0zNy41dDM3LjUgLTkwLjV6TTE0MDggNjQwcTAgMTMwIC01MSAyNDguNXQtMTM2LjUgMjA0dC0yMDQgMTM2LjV0LTI0OC41IDUxdC0yNDguNSAtNTF0LTIwNCAtMTM2LjV0LTEzNi41IC0yMDR0LTUxIC0yNDguNXQ1MSAtMjQ4LjV0MTM2LjUgLTIwNHQyMDQgLTEzNi41dDI0OC41IC01MXQyNDguNSA1MXQyMDQgMTM2LjV0MTM2LjUgMjA0dDUxIDI0OC41ek0xNTM2IDY0MA0KcTAgLTIwOSAtMTAzIC0zODUuNXQtMjc5LjUgLTI3OS41dC0zODUuNSAtMTAzdC0zODUuNSAxMDN0LTI3OS41IDI3OS41dC0xMDMgMzg1LjV0MTAzIDM4NS41dDI3OS41IDI3OS41dDM4NS41IDEwM3QzODUuNSAtMTAzdDI3OS41IC0yNzkuNXQxMDMgLTM4NS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZ2FtZXBhZCIgdW5pY29kZT0iJiN4ZjExYjsiIGhvcml6LWFkdi14PSIxOTIwIiANCmQ9Ik04MzIgNDQ4djEyOHEwIDE0IC05IDIzdC0yMyA5aC0xOTJ2MTkycTAgMTQgLTkgMjN0LTIzIDloLTEyOHEtMTQgMCAtMjMgLTl0LTkgLTIzdi0xOTJoLTE5MnEtMTQgMCAtMjMgLTl0LTkgLTIzdi0xMjhxMCAtMTQgOSAtMjN0MjMgLTloMTkydi0xOTJxMCAtMTQgOSAtMjN0MjMgLTloMTI4cTE0IDAgMjMgOXQ5IDIzdjE5MmgxOTJxMTQgMCAyMyA5dDkgMjN6TTE0MDggMzg0cTAgNTMgLTM3LjUgOTAuNXQtOTAuNSAzNy41dC05MC41IC0zNy41DQp0LTM3LjUgLTkwLjV0MzcuNSAtOTAuNXQ5MC41IC0zNy41dDkwLjUgMzcuNXQzNy41IDkwLjV6TTE2NjQgNjQwcTAgNTMgLTM3LjUgOTAuNXQtOTAuNSAzNy41dC05MC41IC0zNy41dC0zNy41IC05MC41dDM3LjUgLTkwLjV0OTAuNSAtMzcuNXQ5MC41IDM3LjV0MzcuNSA5MC41ek0xOTIwIDUxMnEwIC0yMTIgLTE1MCAtMzYydC0zNjIgLTE1MHEtMTkyIDAgLTMzOCAxMjhoLTIyMHEtMTQ2IC0xMjggLTMzOCAtMTI4cS0yMTIgMCAtMzYyIDE1MA0KdC0xNTAgMzYydDE1MCAzNjJ0MzYyIDE1MGg4OTZxMjEyIDAgMzYyIC0xNTB0MTUwIC0zNjJ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJrZXlib2FyZCIgdW5pY29kZT0iJiN4ZjExYzsiIGhvcml6LWFkdi14PSIxOTIwIiANCmQ9Ik0zODQgMzY4di05NnEwIC0xNiAtMTYgLTE2aC05NnEtMTYgMCAtMTYgMTZ2OTZxMCAxNiAxNiAxNmg5NnExNiAwIDE2IC0xNnpNNTEyIDYyNHYtOTZxMCAtMTYgLTE2IC0xNmgtMjI0cS0xNiAwIC0xNiAxNnY5NnEwIDE2IDE2IDE2aDIyNHExNiAwIDE2IC0xNnpNMzg0IDg4MHYtOTZxMCAtMTYgLTE2IC0xNmgtOTZxLTE2IDAgLTE2IDE2djk2cTAgMTYgMTYgMTZoOTZxMTYgMCAxNiAtMTZ6TTE0MDggMzY4di05NnEwIC0xNiAtMTYgLTE2DQpoLTg2NHEtMTYgMCAtMTYgMTZ2OTZxMCAxNiAxNiAxNmg4NjRxMTYgMCAxNiAtMTZ6TTc2OCA2MjR2LTk2cTAgLTE2IC0xNiAtMTZoLTk2cS0xNiAwIC0xNiAxNnY5NnEwIDE2IDE2IDE2aDk2cTE2IDAgMTYgLTE2ek02NDAgODgwdi05NnEwIC0xNiAtMTYgLTE2aC05NnEtMTYgMCAtMTYgMTZ2OTZxMCAxNiAxNiAxNmg5NnExNiAwIDE2IC0xNnpNMTAyNCA2MjR2LTk2cTAgLTE2IC0xNiAtMTZoLTk2cS0xNiAwIC0xNiAxNnY5NnEwIDE2IDE2IDE2DQpoOTZxMTYgMCAxNiAtMTZ6TTg5NiA4ODB2LTk2cTAgLTE2IC0xNiAtMTZoLTk2cS0xNiAwIC0xNiAxNnY5NnEwIDE2IDE2IDE2aDk2cTE2IDAgMTYgLTE2ek0xMjgwIDYyNHYtOTZxMCAtMTYgLTE2IC0xNmgtOTZxLTE2IDAgLTE2IDE2djk2cTAgMTYgMTYgMTZoOTZxMTYgMCAxNiAtMTZ6TTE2NjQgMzY4di05NnEwIC0xNiAtMTYgLTE2aC05NnEtMTYgMCAtMTYgMTZ2OTZxMCAxNiAxNiAxNmg5NnExNiAwIDE2IC0xNnpNMTE1MiA4ODB2LTk2DQpxMCAtMTYgLTE2IC0xNmgtOTZxLTE2IDAgLTE2IDE2djk2cTAgMTYgMTYgMTZoOTZxMTYgMCAxNiAtMTZ6TTE0MDggODgwdi05NnEwIC0xNiAtMTYgLTE2aC05NnEtMTYgMCAtMTYgMTZ2OTZxMCAxNiAxNiAxNmg5NnExNiAwIDE2IC0xNnpNMTY2NCA4ODB2LTM1MnEwIC0xNiAtMTYgLTE2aC0yMjRxLTE2IDAgLTE2IDE2djk2cTAgMTYgMTYgMTZoMTEydjI0MHEwIDE2IDE2IDE2aDk2cTE2IDAgMTYgLTE2ek0xNzkyIDEyOHY4OTZoLTE2NjR2LTg5Ng0KaDE2NjR6TTE5MjAgMTAyNHYtODk2cTAgLTUzIC0zNy41IC05MC41dC05MC41IC0zNy41aC0xNjY0cS01MyAwIC05MC41IDM3LjV0LTM3LjUgOTAuNXY4OTZxMCA1MyAzNy41IDkwLjV0OTAuNSAzNy41aDE2NjRxNTMgMCA5MC41IC0zNy41dDM3LjUgLTkwLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJmbGFnX2FsdCIgdW5pY29kZT0iJiN4ZjExZDsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNjY0IDQ5MXY2MTZxLTE2OSAtOTEgLTMwNiAtOTFxLTgyIDAgLTE0NSAzMnEtMTAwIDQ5IC0xODQgNzYuNXQtMTc4IDI3LjVxLTE3MyAwIC00MDMgLTEyN3YtNTk5cTI0NSAxMTMgNDMzIDExM3E1NSAwIDEwMy41IC03LjV0OTggLTI2dDc3IC0zMXQ4Mi41IC0zOS41bDI4IC0xNHE0NCAtMjIgMTAxIC0yMnExMjAgMCAyOTMgOTJ6TTMyMCAxMjgwcTAgLTM1IC0xNy41IC02NHQtNDYuNSAtNDZ2LTEyNjZxMCAtMTQgLTkgLTIzdC0yMyAtOQ0KaC02NHEtMTQgMCAtMjMgOXQtOSAyM3YxMjY2cS0yOSAxNyAtNDYuNSA0NnQtMTcuNSA2NHEwIDUzIDM3LjUgOTAuNXQ5MC41IDM3LjV0OTAuNSAtMzcuNXQzNy41IC05MC41ek0xNzkyIDEyMTZ2LTc2M3EwIC0zOSAtMzUgLTU3cS0xMCAtNSAtMTcgLTlxLTIxOCAtMTE2IC0zNjkgLTExNnEtODggMCAtMTU4IDM1bC0yOCAxNHEtNjQgMzMgLTk5IDQ4dC05MSAyOXQtMTE0IDE0cS0xMDIgMCAtMjM1LjUgLTQ0dC0yMjguNSAtMTAyDQpxLTE1IC05IC0zMyAtOXEtMTYgMCAtMzIgOHEtMzIgMTkgLTMyIDU2djc0MnEwIDM1IDMxIDU1cTM1IDIxIDc4LjUgNDIuNXQxMTQgNTJ0MTUyLjUgNDkuNXQxNTUgMTlxMTEyIDAgMjA5IC0zMXQyMDkgLTg2cTM4IC0xOSA4OSAtMTlxMTIyIDAgMzEwIDExMnEyMiAxMiAzMSAxN3EzMSAxNiA2MiAtMnEzMSAtMjAgMzEgLTU1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZmxhZ19jaGVja2VyZWQiIHVuaWNvZGU9IiYjeGYxMWU7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNODMyIDUzNnYxOTJxLTE4MSAtMTYgLTM4NCAtMTE3di0xODVxMjA1IDk2IDM4NCAxMTB6TTgzMiA5NTR2MTk3cS0xNzIgLTggLTM4NCAtMTI2di0xODlxMjE1IDExMSAzODQgMTE4ek0xNjY0IDQ5MXYxODRxLTIzNSAtMTE2IC0zODQgLTcxdjIyNHEtMjAgNiAtMzkgMTVxLTUgMyAtMzMgMTd0LTM0LjUgMTd0LTMxLjUgMTV0LTM0LjUgMTUuNXQtMzIuNSAxM3QtMzYgMTIuNXQtMzUgOC41dC0zOS41IDcuNXQtMzkuNSA0dC00NCAyDQpxLTIzIDAgLTQ5IC0zdi0yMjJoMTlxMTAyIDAgMTkyLjUgLTI5dDE5Ny41IC04MnExOSAtOSAzOSAtMTV2LTE4OHE0MiAtMTcgOTEgLTE3cTEyMCAwIDI5MyA5MnpNMTY2NCA5MTh2MTg5cS0xNjkgLTkxIC0zMDYgLTkxcS00NSAwIC03OCA4di0xOTZxMTQ4IC00MiAzODQgOTB6TTMyMCAxMjgwcTAgLTM1IC0xNy41IC02NHQtNDYuNSAtNDZ2LTEyNjZxMCAtMTQgLTkgLTIzdC0yMyAtOWgtNjRxLTE0IDAgLTIzIDl0LTkgMjN2MTI2Ng0KcS0yOSAxNyAtNDYuNSA0NnQtMTcuNSA2NHEwIDUzIDM3LjUgOTAuNXQ5MC41IDM3LjV0OTAuNSAtMzcuNXQzNy41IC05MC41ek0xNzkyIDEyMTZ2LTc2M3EwIC0zOSAtMzUgLTU3cS0xMCAtNSAtMTcgLTlxLTIxOCAtMTE2IC0zNjkgLTExNnEtODggMCAtMTU4IDM1bC0yOCAxNHEtNjQgMzMgLTk5IDQ4dC05MSAyOXQtMTE0IDE0cS0xMDIgMCAtMjM1LjUgLTQ0dC0yMjguNSAtMTAycS0xNSAtOSAtMzMgLTlxLTE2IDAgLTMyIDgNCnEtMzIgMTkgLTMyIDU2djc0MnEwIDM1IDMxIDU1cTM1IDIxIDc4LjUgNDIuNXQxMTQgNTJ0MTUyLjUgNDkuNXQxNTUgMTlxMTEyIDAgMjA5IC0zMXQyMDkgLTg2cTM4IC0xOSA4OSAtMTlxMTIyIDAgMzEwIDExMnEyMiAxMiAzMSAxN3EzMSAxNiA2MiAtMnEzMSAtMjAgMzEgLTU1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idGVybWluYWwiIHVuaWNvZGU9IiYjeGYxMjA7IiBob3Jpei1hZHYteD0iMTY2NCIgDQpkPSJNNTg1IDU1M2wtNDY2IC00NjZxLTEwIC0xMCAtMjMgLTEwdC0yMyAxMGwtNTAgNTBxLTEwIDEwIC0xMCAyM3QxMCAyM2wzOTMgMzkzbC0zOTMgMzkzcS0xMCAxMCAtMTAgMjN0MTAgMjNsNTAgNTBxMTAgMTAgMjMgMTB0MjMgLTEwbDQ2NiAtNDY2cTEwIC0xMCAxMCAtMjN0LTEwIC0yM3pNMTY2NCA5NnYtNjRxMCAtMTQgLTkgLTIzdC0yMyAtOWgtOTYwcS0xNCAwIC0yMyA5dC05IDIzdjY0cTAgMTQgOSAyM3QyMyA5aDk2MHExNCAwIDIzIC05DQp0OSAtMjN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJjb2RlIiB1bmljb2RlPSImI3hmMTIxOyIgaG9yaXotYWR2LXg9IjE5MjAiIA0KZD0iTTYxNyAxMzdsLTUwIC01MHEtMTAgLTEwIC0yMyAtMTB0LTIzIDEwbC00NjYgNDY2cS0xMCAxMCAtMTAgMjN0MTAgMjNsNDY2IDQ2NnExMCAxMCAyMyAxMHQyMyAtMTBsNTAgLTUwcTEwIC0xMCAxMCAtMjN0LTEwIC0yM2wtMzkzIC0zOTNsMzkzIC0zOTNxMTAgLTEwIDEwIC0yM3QtMTAgLTIzek0xMjA4IDEyMDRsLTM3MyAtMTI5MXEtNCAtMTMgLTE1LjUgLTE5LjV0LTIzLjUgLTIuNWwtNjIgMTdxLTEzIDQgLTE5LjUgMTUuNXQtMi41IDI0LjUNCmwzNzMgMTI5MXE0IDEzIDE1LjUgMTkuNXQyMy41IDIuNWw2MiAtMTdxMTMgLTQgMTkuNSAtMTUuNXQyLjUgLTI0LjV6TTE4NjUgNTUzbC00NjYgLTQ2NnEtMTAgLTEwIC0yMyAtMTB0LTIzIDEwbC01MCA1MHEtMTAgMTAgLTEwIDIzdDEwIDIzbDM5MyAzOTNsLTM5MyAzOTNxLTEwIDEwIC0xMCAyM3QxMCAyM2w1MCA1MHExMCAxMCAyMyAxMHQyMyAtMTBsNDY2IC00NjZxMTAgLTEwIDEwIC0yM3QtMTAgLTIzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0icmVwbHlfYWxsIiB1bmljb2RlPSImI3hmMTIyOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTY0MCA0NTR2LTcwcTAgLTQyIC0zOSAtNTlxLTEzIC01IC0yNSAtNXEtMjcgMCAtNDUgMTlsLTUxMiA1MTJxLTE5IDE5IC0xOSA0NXQxOSA0NWw1MTIgNTEycTI5IDMxIDcwIDE0cTM5IC0xNyAzOSAtNTl2LTY5bC0zOTcgLTM5OHEtMTkgLTE5IC0xOSAtNDV0MTkgLTQ1ek0xNzkyIDQxNnEwIC01OCAtMTcgLTEzMy41dC0zOC41IC0xMzh0LTQ4IC0xMjV0LTQwLjUgLTkwLjVsLTIwIC00MHEtOCAtMTcgLTI4IC0xN3EtNiAwIC05IDENCnEtMjUgOCAtMjMgMzRxNDMgNDAwIC0xMDYgNTY1cS02NCA3MSAtMTcwLjUgMTEwLjV0LTI2Ny41IDUyLjV2LTI1MXEwIC00MiAtMzkgLTU5cS0xMyAtNSAtMjUgLTVxLTI3IDAgLTQ1IDE5bC01MTIgNTEycS0xOSAxOSAtMTkgNDV0MTkgNDVsNTEyIDUxMnEyOSAzMSA3MCAxNHEzOSAtMTcgMzkgLTU5di0yNjJxNDExIC0yOCA1OTkgLTIyMXExNjkgLTE3MyAxNjkgLTUwOXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InN0YXJfaGFsZl9lbXB0eSIgdW5pY29kZT0iJiN4ZjEyMzsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik0xMTg2IDU3OWwyNTcgMjUwbC0zNTYgNTJsLTY2IDEwbC0zMCA2MGwtMTU5IDMyMnYtOTYzbDU5IC0zMWwzMTggLTE2OGwtNjAgMzU1bC0xMiA2NnpNMTYzOCA4NDFsLTM2MyAtMzU0bDg2IC01MDBxNSAtMzMgLTYgLTUxLjV0LTM0IC0xOC41cS0xNyAwIC00MCAxMmwtNDQ5IDIzNmwtNDQ5IC0yMzZxLTIzIC0xMiAtNDAgLTEycS0yMyAwIC0zNCAxOC41dC02IDUxLjVsODYgNTAwbC0zNjQgMzU0cS0zMiAzMiAtMjMgNTkuNXQ1NCAzNC41DQpsNTAyIDczbDIyNSA0NTVxMjAgNDEgNDkgNDFxMjggMCA0OSAtNDFsMjI1IC00NTVsNTAyIC03M3E0NSAtNyA1NCAtMzQuNXQtMjQgLTU5LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJsb2NhdGlvbl9hcnJvdyIgdW5pY29kZT0iJiN4ZjEyNDsiIGhvcml6LWFkdi14PSIxNDA4IiANCmQ9Ik0xNDAxIDExODdsLTY0MCAtMTI4MHEtMTcgLTM1IC01NyAtMzVxLTUgMCAtMTUgMnEtMjIgNSAtMzUuNSAyMi41dC0xMy41IDM5LjV2NTc2aC01NzZxLTIyIDAgLTM5LjUgMTMuNXQtMjIuNSAzNS41dDQgNDJ0MjkgMzBsMTI4MCA2NDBxMTMgNyAyOSA3cTI3IDAgNDUgLTE5cTE1IC0xNCAxOC41IC0zNC41dC02LjUgLTM5LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJjcm9wIiB1bmljb2RlPSImI3hmMTI1OyIgaG9yaXotYWR2LXg9IjE2NjQiIA0KZD0iTTU1NyAyNTZoNTk1djU5NXpNNTEyIDMwMWw1OTUgNTk1aC01OTV2LTU5NXpNMTY2NCAyMjR2LTE5MnEwIC0xNCAtOSAtMjN0LTIzIC05aC0yMjR2LTIyNHEwIC0xNCAtOSAtMjN0LTIzIC05aC0xOTJxLTE0IDAgLTIzIDl0LTkgMjN2MjI0aC04NjRxLTE0IDAgLTIzIDl0LTkgMjN2ODY0aC0yMjRxLTE0IDAgLTIzIDl0LTkgMjN2MTkycTAgMTQgOSAyM3QyMyA5aDIyNHYyMjRxMCAxNCA5IDIzdDIzIDloMTkycTE0IDAgMjMgLTl0OSAtMjMNCnYtMjI0aDg1MWwyNDYgMjQ3cTEwIDkgMjMgOXQyMyAtOXE5IC0xMCA5IC0yM3QtOSAtMjNsLTI0NyAtMjQ2di04NTFoMjI0cTE0IDAgMjMgLTl0OSAtMjN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJjb2RlX2ZvcmsiIHVuaWNvZGU9IiYjeGYxMjY7IiBob3Jpei1hZHYteD0iMTAyNCIgDQpkPSJNMjg4IDY0cTAgNDAgLTI4IDY4dC02OCAyOHQtNjggLTI4dC0yOCAtNjh0MjggLTY4dDY4IC0yOHQ2OCAyOHQyOCA2OHpNMjg4IDEyMTZxMCA0MCAtMjggNjh0LTY4IDI4dC02OCAtMjh0LTI4IC02OHQyOCAtNjh0NjggLTI4dDY4IDI4dDI4IDY4ek05MjggMTA4OHEwIDQwIC0yOCA2OHQtNjggMjh0LTY4IC0yOHQtMjggLTY4dDI4IC02OHQ2OCAtMjh0NjggMjh0MjggNjh6TTEwMjQgMTA4OHEwIC01MiAtMjYgLTk2LjV0LTcwIC02OS41DQpxLTIgLTI4NyAtMjI2IC00MTRxLTY3IC0zOCAtMjAzIC04MXEtMTI4IC00MCAtMTY5LjUgLTcxdC00MS41IC0xMDB2LTI2cTQ0IC0yNSA3MCAtNjkuNXQyNiAtOTYuNXEwIC04MCAtNTYgLTEzNnQtMTM2IC01NnQtMTM2IDU2dC01NiAxMzZxMCA1MiAyNiA5Ni41dDcwIDY5LjV2ODIwcS00NCAyNSAtNzAgNjkuNXQtMjYgOTYuNXEwIDgwIDU2IDEzNnQxMzYgNTZ0MTM2IC01NnQ1NiAtMTM2cTAgLTUyIC0yNiAtOTYuNXQtNzAgLTY5LjV2LTQ5Nw0KcTU0IDI2IDE1NCA1N3E1NSAxNyA4Ny41IDI5LjV0NzAuNSAzMXQ1OSAzOS41dDQwLjUgNTF0MjggNjkuNXQ4LjUgOTEuNXEtNDQgMjUgLTcwIDY5LjV0LTI2IDk2LjVxMCA4MCA1NiAxMzZ0MTM2IDU2dDEzNiAtNTZ0NTYgLTEzNnoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVubGluayIgdW5pY29kZT0iJiN4ZjEyNzsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik00MzkgMjY1bC0yNTYgLTI1NnEtMTEgLTkgLTIzIC05dC0yMyA5cS05IDEwIC05IDIzdDkgMjNsMjU2IDI1NnExMCA5IDIzIDl0MjMgLTlxOSAtMTAgOSAtMjN0LTkgLTIzek02MDggMjI0di0zMjBxMCAtMTQgLTkgLTIzdC0yMyAtOXQtMjMgOXQtOSAyM3YzMjBxMCAxNCA5IDIzdDIzIDl0MjMgLTl0OSAtMjN6TTM4NCA0NDhxMCAtMTQgLTkgLTIzdC0yMyAtOWgtMzIwcS0xNCAwIC0yMyA5dC05IDIzdDkgMjN0MjMgOWgzMjANCnExNCAwIDIzIC05dDkgLTIzek0xNjQ4IDMyMHEwIC0xMjAgLTg1IC0yMDNsLTE0NyAtMTQ2cS04MyAtODMgLTIwMyAtODNxLTEyMSAwIC0yMDQgODVsLTMzNCAzMzVxLTIxIDIxIC00MiA1NmwyMzkgMThsMjczIC0yNzRxMjcgLTI3IDY4IC0yNy41dDY4IDI2LjVsMTQ3IDE0NnEyOCAyOCAyOCA2N3EwIDQwIC0yOCA2OGwtMjc0IDI3NWwxOCAyMzlxMzUgLTIxIDU2IC00MmwzMzYgLTMzNnE4NCAtODYgODQgLTIwNHpNMTAzMSAxMDQ0bC0yMzkgLTE4DQpsLTI3MyAyNzRxLTI4IDI4IC02OCAyOHEtMzkgMCAtNjggLTI3bC0xNDcgLTE0NnEtMjggLTI4IC0yOCAtNjdxMCAtNDAgMjggLTY4bDI3NCAtMjc0bC0xOCAtMjQwcS0zNSAyMSAtNTYgNDJsLTMzNiAzMzZxLTg0IDg2IC04NCAyMDRxMCAxMjAgODUgMjAzbDE0NyAxNDZxODMgODMgMjAzIDgzcTEyMSAwIDIwNCAtODVsMzM0IC0zMzVxMjEgLTIxIDQyIC01NnpNMTY2NCA5NjBxMCAtMTQgLTkgLTIzdC0yMyAtOWgtMzIwcS0xNCAwIC0yMyA5DQp0LTkgMjN0OSAyM3QyMyA5aDMyMHExNCAwIDIzIC05dDkgLTIzek0xMTIwIDE1MDR2LTMyMHEwIC0xNCAtOSAtMjN0LTIzIC05dC0yMyA5dC05IDIzdjMyMHEwIDE0IDkgMjN0MjMgOXQyMyAtOXQ5IC0yM3pNMTUyNyAxMzUzbC0yNTYgLTI1NnEtMTEgLTkgLTIzIC05dC0yMyA5cS05IDEwIC05IDIzdDkgMjNsMjU2IDI1NnExMCA5IDIzIDl0MjMgLTlxOSAtMTAgOSAtMjN0LTkgLTIzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0icXVlc3Rpb24iIHVuaWNvZGU9IiYjeGYxMjg7IiBob3Jpei1hZHYteD0iMTAyNCIgDQpkPSJNNzA0IDI4MHYtMjQwcTAgLTE2IC0xMiAtMjh0LTI4IC0xMmgtMjQwcS0xNiAwIC0yOCAxMnQtMTIgMjh2MjQwcTAgMTYgMTIgMjh0MjggMTJoMjQwcTE2IDAgMjggLTEydDEyIC0yOHpNMTAyMCA4ODBxMCAtNTQgLTE1LjUgLTEwMXQtMzUgLTc2LjV0LTU1IC01OS41dC01Ny41IC00My41dC02MSAtMzUuNXEtNDEgLTIzIC02OC41IC02NXQtMjcuNSAtNjdxMCAtMTcgLTEyIC0zMi41dC0yOCAtMTUuNWgtMjQwcS0xNSAwIC0yNS41IDE4LjUNCnQtMTAuNSAzNy41djQ1cTAgODMgNjUgMTU2LjV0MTQzIDEwOC41cTU5IDI3IDg0IDU2dDI1IDc2cTAgNDIgLTQ2LjUgNzR0LTEwNy41IDMycS02NSAwIC0xMDggLTI5cS0zNSAtMjUgLTEwNyAtMTE1cS0xMyAtMTYgLTMxIC0xNnEtMTIgMCAtMjUgOGwtMTY0IDEyNXEtMTMgMTAgLTE1LjUgMjV0NS41IDI4cTE2MCAyNjYgNDY0IDI2NnE4MCAwIDE2MSAtMzF0MTQ2IC04M3QxMDYgLTEyNy41dDQxIC0xNTguNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il8yNzkiIHVuaWNvZGU9IiYjeGYxMjk7IiBob3Jpei1hZHYteD0iNjQwIiANCmQ9Ik02NDAgMTkydi0xMjhxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC01MTJxLTI2IDAgLTQ1IDE5dC0xOSA0NXYxMjhxMCAyNiAxOSA0NXQ0NSAxOWg2NHYzODRoLTY0cS0yNiAwIC00NSAxOXQtMTkgNDV2MTI4cTAgMjYgMTkgNDV0NDUgMTloMzg0cTI2IDAgNDUgLTE5dDE5IC00NXYtNTc2aDY0cTI2IDAgNDUgLTE5dDE5IC00NXpNNTEyIDEzNDR2LTE5MnEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTI1NnEtMjYgMCAtNDUgMTl0LTE5IDQ1djE5Mg0KcTAgMjYgMTkgNDV0NDUgMTloMjU2cTI2IDAgNDUgLTE5dDE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImV4Y2xhbWF0aW9uIiB1bmljb2RlPSImI3hmMTJhOyIgaG9yaXotYWR2LXg9IjY0MCIgDQpkPSJNNTEyIDI4OHYtMjI0cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMjU2cS0yNiAwIC00NSAxOXQtMTkgNDV2MjI0cTAgMjYgMTkgNDV0NDUgMTloMjU2cTI2IDAgNDUgLTE5dDE5IC00NXpNNTQyIDEzNDRsLTI4IC03NjhxLTEgLTI2IC0yMC41IC00NXQtNDUuNSAtMTloLTI1NnEtMjYgMCAtNDUuNSAxOXQtMjAuNSA0NWwtMjggNzY4cS0xIDI2IDE3LjUgNDV0NDQuNSAxOWgzMjBxMjYgMCA0NC41IC0xOXQxNy41IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InN1cGVyc2NyaXB0IiB1bmljb2RlPSImI3hmMTJiOyIgDQpkPSJNODk3IDE2N3YtMTY3aC0yNDhsLTE1OSAyNTJsLTI0IDQycS04IDkgLTExIDIxaC0zcS0xIC0zIC0yLjUgLTYuNXQtMy41IC04dC0zIC02LjVxLTEwIC0yMCAtMjUgLTQ0bC0xNTUgLTI1MGgtMjU4djE2N2gxMjhsMTk3IDI5MWwtMTg1IDI3MmgtMTM3djE2OGgyNzZsMTM5IC0yMjhxMiAtNCAyMyAtNDJxOCAtOSAxMSAtMjFoM3EzIDkgMTEgMjFsMjUgNDJsMTQwIDIyOGgyNTd2LTE2OGgtMTI1bC0xODQgLTI2N2wyMDQgLTI5NmgxMDl6DQpNMTUzNCA4NDZ2LTIwNmgtNTE0bC0zIDI3cS00IDI4IC00IDQ2cTAgNjQgMjYgMTE3dDY1IDg2LjV0ODQgNjV0ODQgNTQuNXQ2NSA1NHQyNiA2NHEwIDM4IC0yOS41IDYyLjV0LTcwLjUgMjQuNXEtNTEgMCAtOTcgLTM5cS0xNCAtMTEgLTM2IC0zOGwtMTA1IDkycTI2IDM3IDYzIDY2cTgzIDY1IDE4OCA2NXExMTAgMCAxNzggLTU5LjV0NjggLTE1OC41cTAgLTU2IC0yNC41IC0xMDN0LTYyIC03Ni41dC04MS41IC01OC41dC04MiAtNTAuNQ0KdC02NS41IC01MS41dC0zMC41IC02M2gyMzJ2ODBoMTI2eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ic3Vic2NyaXB0IiB1bmljb2RlPSImI3hmMTJjOyIgDQpkPSJNODk3IDE2N3YtMTY3aC0yNDhsLTE1OSAyNTJsLTI0IDQycS04IDkgLTExIDIxaC0zcS0xIC0zIC0yLjUgLTYuNXQtMy41IC04dC0zIC02LjVxLTEwIC0yMCAtMjUgLTQ0bC0xNTUgLTI1MGgtMjU4djE2N2gxMjhsMTk3IDI5MWwtMTg1IDI3MmgtMTM3djE2OGgyNzZsMTM5IC0yMjhxMiAtNCAyMyAtNDJxOCAtOSAxMSAtMjFoM3EzIDkgMTEgMjFsMjUgNDJsMTQwIDIyOGgyNTd2LTE2OGgtMTI1bC0xODQgLTI2N2wyMDQgLTI5NmgxMDl6DQpNMTUzNiAtNTB2LTIwNmgtNTE0bC00IDI3cS0zIDQ1IC0zIDQ2cTAgNjQgMjYgMTE3dDY1IDg2LjV0ODQgNjV0ODQgNTQuNXQ2NSA1NHQyNiA2NHEwIDM4IC0yOS41IDYyLjV0LTcwLjUgMjQuNXEtNTEgMCAtOTcgLTM5cS0xNCAtMTEgLTM2IC0zOGwtMTA1IDkycTI2IDM3IDYzIDY2cTgwIDY1IDE4OCA2NXExMTAgMCAxNzggLTU5LjV0NjggLTE1OC41cTAgLTY2IC0zNC41IC0xMTguNXQtODQgLTg2dC05OS41IC02Mi41dC04NyAtNjN0LTQxIC03Mw0KaDIzMnY4MGgxMjZ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfMjgzIiB1bmljb2RlPSImI3hmMTJkOyIgaG9yaXotYWR2LXg9IjE5MjAiIA0KZD0iTTg5NiAxMjhsMzM2IDM4NGgtNzY4bC0zMzYgLTM4NGg3Njh6TTE5MDkgMTIwNXExNSAtMzQgOS41IC03MS41dC0zMC41IC02NS41bC04OTYgLTEwMjRxLTM4IC00NCAtOTYgLTQ0aC03NjhxLTM4IDAgLTY5LjUgMjAuNXQtNDcuNSA1NC41cS0xNSAzNCAtOS41IDcxLjV0MzAuNSA2NS41bDg5NiAxMDI0cTM4IDQ0IDk2IDQ0aDc2OHEzOCAwIDY5LjUgLTIwLjV0NDcuNSAtNTQuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InB1enpsZV9waWVjZSIgdW5pY29kZT0iJiN4ZjEyZTsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik0xNjY0IDQzOHEwIC04MSAtNDQuNSAtMTM1dC0xMjMuNSAtNTRxLTQxIDAgLTc3LjUgMTcuNXQtNTkgMzh0LTU2LjUgMzh0LTcxIDE3LjVxLTExMCAwIC0xMTAgLTEyNHEwIC0zOSAxNiAtMTE1dDE1IC0xMTV2LTVxLTIyIDAgLTMzIC0xcS0zNCAtMyAtOTcuNSAtMTEuNXQtMTE1LjUgLTEzLjV0LTk4IC01cS02MSAwIC0xMDMgMjYuNXQtNDIgODMuNXEwIDM3IDE3LjUgNzF0MzggNTYuNXQzOCA1OXQxNy41IDc3LjVxMCA3OSAtNTQgMTIzLjUNCnQtMTM1IDQ0LjVxLTg0IDAgLTE0MyAtNDUuNXQtNTkgLTEyNy41cTAgLTQzIDE1IC04M3QzMy41IC02NC41dDMzLjUgLTUzdDE1IC01MC41cTAgLTQ1IC00NiAtODlxLTM3IC0zNSAtMTE3IC0zNXEtOTUgMCAtMjQ1IDI0cS05IDIgLTI3LjUgNHQtMjcuNSA0bC0xMyAycS0xIDAgLTMgMXEtMiAwIC0yIDF2MTAyNHEyIC0xIDE3LjUgLTMuNXQzNCAtNXQyMS41IC0zLjVxMTUwIC0yNCAyNDUgLTI0cTgwIDAgMTE3IDM1cTQ2IDQ0IDQ2IDg5DQpxMCAyMiAtMTUgNTAuNXQtMzMuNSA1M3QtMzMuNSA2NC41dC0xNSA4M3EwIDgyIDU5IDEyNy41dDE0NCA0NS41cTgwIDAgMTM0IC00NC41dDU0IC0xMjMuNXEwIC00MSAtMTcuNSAtNzcuNXQtMzggLTU5dC0zOCAtNTYuNXQtMTcuNSAtNzFxMCAtNTcgNDIgLTgzLjV0MTAzIC0yNi41cTY0IDAgMTgwIDE1dDE2MyAxN3YtMnEtMSAtMiAtMy41IC0xNy41dC01IC0zNHQtMy41IC0yMS41cS0yNCAtMTUwIC0yNCAtMjQ1cTAgLTgwIDM1IC0xMTcNCnE0NCAtNDYgODkgLTQ2cTIyIDAgNTAuNSAxNXQ1MyAzMy41dDY0LjUgMzMuNXQ4MyAxNXE4MiAwIDEyNy41IC01OXQ0NS41IC0xNDN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJtaWNyb3Bob25lIiB1bmljb2RlPSImI3hmMTMwOyIgaG9yaXotYWR2LXg9IjExNTIiIA0KZD0iTTExNTIgODMydi0xMjhxMCAtMjIxIC0xNDcuNSAtMzg0LjV0LTM2NC41IC0xODcuNXYtMTMyaDI1NnEyNiAwIDQ1IC0xOXQxOSAtNDV0LTE5IC00NXQtNDUgLTE5aC02NDBxLTI2IDAgLTQ1IDE5dC0xOSA0NXQxOSA0NXQ0NSAxOWgyNTZ2MTMycS0yMTcgMjQgLTM2NC41IDE4Ny41dC0xNDcuNSAzODQuNXYxMjhxMCAyNiAxOSA0NXQ0NSAxOXQ0NSAtMTl0MTkgLTQ1di0xMjhxMCAtMTg1IDEzMS41IC0zMTYuNXQzMTYuNSAtMTMxLjUNCnQzMTYuNSAxMzEuNXQxMzEuNSAzMTYuNXYxMjhxMCAyNiAxOSA0NXQ0NSAxOXQ0NSAtMTl0MTkgLTQ1ek04OTYgMTIxNnYtNTEycTAgLTEzMiAtOTQgLTIyNnQtMjI2IC05NHQtMjI2IDk0dC05NCAyMjZ2NTEycTAgMTMyIDk0IDIyNnQyMjYgOTR0MjI2IC05NHQ5NCAtMjI2eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ibWljcm9waG9uZV9vZmYiIHVuaWNvZGU9IiYjeGYxMzE7IiBob3Jpei1hZHYteD0iMTQwOCIgDQpkPSJNMjcxIDU5MWwtMTAxIC0xMDFxLTQyIDEwMyAtNDIgMjE0djEyOHEwIDI2IDE5IDQ1dDQ1IDE5dDQ1IC0xOXQxOSAtNDV2LTEyOHEwIC01MyAxNSAtMTEzek0xMzg1IDExOTNsLTM2MSAtMzYxdi0xMjhxMCAtMTMyIC05NCAtMjI2dC0yMjYgLTk0cS01NSAwIC0xMDkgMTlsLTk2IC05NnE5NyAtNTEgMjA1IC01MXExODUgMCAzMTYuNSAxMzEuNXQxMzEuNSAzMTYuNXYxMjhxMCAyNiAxOSA0NXQ0NSAxOXQ0NSAtMTl0MTkgLTQ1di0xMjgNCnEwIC0yMjEgLTE0Ny41IC0zODQuNXQtMzY0LjUgLTE4Ny41di0xMzJoMjU2cTI2IDAgNDUgLTE5dDE5IC00NXQtMTkgLTQ1dC00NSAtMTloLTY0MHEtMjYgMCAtNDUgMTl0LTE5IDQ1dDE5IDQ1dDQ1IDE5aDI1NnYxMzJxLTEyNSAxMyAtMjM1IDgxbC0yNTQgLTI1NHEtMTAgLTEwIC0yMyAtMTB0LTIzIDEwbC04MiA4MnEtMTAgMTAgLTEwIDIzdDEwIDIzbDEyMzQgMTIzNHExMCAxMCAyMyAxMHQyMyAtMTBsODIgLTgycTEwIC0xMCAxMCAtMjMNCnQtMTAgLTIzek0xMDA1IDEzMjVsLTYyMSAtNjIxdjUxMnEwIDEzMiA5NCAyMjZ0MjI2IDk0cTEwMiAwIDE4NC41IC01OXQxMTYuNSAtMTUyeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ic2hpZWxkIiB1bmljb2RlPSImI3hmMTMyOyIgaG9yaXotYWR2LXg9IjEyODAiIA0KZD0iTTEwODggNTc2djY0MGgtNDQ4di0xMTM3cTExOSA2MyAyMTMgMTM3cTIzNSAxODQgMjM1IDM2MHpNMTI4MCAxMzQ0di03NjhxMCAtODYgLTMzLjUgLTE3MC41dC04MyAtMTUwdC0xMTggLTEyNy41dC0xMjYuNSAtMTAzdC0xMjEgLTc3LjV0LTg5LjUgLTQ5LjV0LTQyLjUgLTIwcS0xMiAtNiAtMjYgLTZ0LTI2IDZxLTE2IDcgLTQyLjUgMjB0LTg5LjUgNDkuNXQtMTIxIDc3LjV0LTEyNi41IDEwM3QtMTE4IDEyNy41dC04MyAxNTANCnQtMzMuNSAxNzAuNXY3NjhxMCAyNiAxOSA0NXQ0NSAxOWgxMTUycTI2IDAgNDUgLTE5dDE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImNhbGVuZGFyX2VtcHR5IiB1bmljb2RlPSImI3hmMTMzOyIgaG9yaXotYWR2LXg9IjE2NjQiIA0KZD0iTTEyOCAtMTI4aDE0MDh2MTAyNGgtMTQwOHYtMTAyNHpNNTEyIDEwODh2Mjg4cTAgMTQgLTkgMjN0LTIzIDloLTY0cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTI4OHEwIC0xNCA5IC0yM3QyMyAtOWg2NHExNCAwIDIzIDl0OSAyM3pNMTI4MCAxMDg4djI4OHEwIDE0IC05IDIzdC0yMyA5aC02NHEtMTQgMCAtMjMgLTl0LTkgLTIzdi0yODhxMCAtMTQgOSAtMjN0MjMgLTloNjRxMTQgMCAyMyA5dDkgMjN6TTE2NjQgMTE1MnYtMTI4MA0KcTAgLTUyIC0zOCAtOTB0LTkwIC0zOGgtMTQwOHEtNTIgMCAtOTAgMzh0LTM4IDkwdjEyODBxMCA1MiAzOCA5MHQ5MCAzOGgxMjh2OTZxMCA2NiA0NyAxMTN0MTEzIDQ3aDY0cTY2IDAgMTEzIC00N3Q0NyAtMTEzdi05NmgzODR2OTZxMCA2NiA0NyAxMTN0MTEzIDQ3aDY0cTY2IDAgMTEzIC00N3Q0NyAtMTEzdi05NmgxMjhxNTIgMCA5MCAtMzh0MzggLTkweiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZmlyZV9leHRpbmd1aXNoZXIiIHVuaWNvZGU9IiYjeGYxMzQ7IiBob3Jpei1hZHYteD0iMTQwOCIgDQpkPSJNNTEyIDEzNDRxMCAyNiAtMTkgNDV0LTQ1IDE5dC00NSAtMTl0LTE5IC00NXQxOSAtNDV0NDUgLTE5dDQ1IDE5dDE5IDQ1ek0xNDA4IDEzNzZ2LTMyMHEwIC0xNiAtMTIgLTI1cS04IC03IC0yMCAtN3EtNCAwIC03IDFsLTQ0OCA5NnEtMTEgMiAtMTggMTF0LTcgMjBoLTI1NnYtMTAycTExMSAtMjMgMTgzLjUgLTExMXQ3Mi41IC0yMDN2LTgwMHEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTUxMnEtMjYgMCAtNDUgMTl0LTE5IDQ1djgwMA0KcTAgMTA2IDYyLjUgMTkwLjV0MTYxLjUgMTE0LjV2MTExaC0zMnEtNTkgMCAtMTE1IC0yMy41dC05MS41IC01M3QtNjYgLTY2LjV0LTQwLjUgLTUzLjV0LTE0IC0yNC41cS0xNyAtMzUgLTU3IC0zNXEtMTYgMCAtMjkgN3EtMjMgMTIgLTMxLjUgMzd0My41IDQ5cTUgMTAgMTQuNSAyNnQzNy41IDUzLjV0NjAuNSA3MHQ4NSA2N3QxMDguNSA1Mi41cS0yNSA0MiAtMjUgODZxMCA2NiA0NyAxMTN0MTEzIDQ3dDExMyAtNDd0NDcgLTExMw0KcTAgLTMzIC0xNCAtNjRoMzAycTAgMTEgNyAyMHQxOCAxMWw0NDggOTZxMyAxIDcgMXExMiAwIDIwIC03cTEyIC05IDEyIC0yNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InJvY2tldCIgdW5pY29kZT0iJiN4ZjEzNTsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik0xNDQwIDEwODhxMCA0MCAtMjggNjh0LTY4IDI4dC02OCAtMjh0LTI4IC02OHQyOCAtNjh0NjggLTI4dDY4IDI4dDI4IDY4ek0xNjY0IDEzNzZxMCAtMjQ5IC03NS41IC00MzAuNXQtMjUzLjUgLTM2MC41cS04MSAtODAgLTE5NSAtMTc2bC0yMCAtMzc5cS0yIC0xNiAtMTYgLTI2bC0zODQgLTIyNHEtNyAtNCAtMTYgLTRxLTEyIDAgLTIzIDlsLTY0IDY0cS0xMyAxNCAtOCAzMmw4NSAyNzZsLTI4MSAyODFsLTI3NiAtODVxLTMgLTEgLTkgLTENCnEtMTQgMCAtMjMgOWwtNjQgNjRxLTE3IDE5IC01IDM5bDIyNCAzODRxMTAgMTQgMjYgMTZsMzc5IDIwcTk2IDExNCAxNzYgMTk1cTE4OCAxODcgMzU4IDI1OHQ0MzEgNzFxMTQgMCAyNCAtOS41dDEwIC0yMi41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ibWF4Y2RuIiB1bmljb2RlPSImI3hmMTM2OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE3NDUgNzYzbC0xNjQgLTc2M2gtMzM0bDE3OCA4MzJxMTMgNTYgLTE1IDg4cS0yNyAzMyAtODMgMzNoLTE2OWwtMjA0IC05NTNoLTMzNGwyMDQgOTUzaC0yODZsLTIwNCAtOTUzaC0zMzRsMjA0IDk1M2wtMTUzIDMyN2gxMjc2cTEwMSAwIDE4OS41IC00MC41dDE0Ny41IC0xMTMuNXE2MCAtNzMgODEgLTE2OC41dDAgLTE5NC41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY2hldnJvbl9zaWduX2xlZnQiIHVuaWNvZGU9IiYjeGYxMzc7IiANCmQ9Ik05MDkgMTQxbDEwMiAxMDJxMTkgMTkgMTkgNDV0LTE5IDQ1bC0zMDcgMzA3bDMwNyAzMDdxMTkgMTkgMTkgNDV0LTE5IDQ1bC0xMDIgMTAycS0xOSAxOSAtNDUgMTl0LTQ1IC0xOWwtNDU0IC00NTRxLTE5IC0xOSAtMTkgLTQ1dDE5IC00NWw0NTQgLTQ1NHExOSAtMTkgNDUgLTE5dDQ1IDE5ek0xNTM2IDY0MHEwIC0yMDkgLTEwMyAtMzg1LjV0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzdC0yNzkuNSAyNzkuNQ0KdC0xMDMgMzg1LjV0MTAzIDM4NS41dDI3OS41IDI3OS41dDM4NS41IDEwM3QzODUuNSAtMTAzdDI3OS41IC0yNzkuNXQxMDMgLTM4NS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY2hldnJvbl9zaWduX3JpZ2h0IiB1bmljb2RlPSImI3hmMTM4OyIgDQpkPSJNNzE3IDE0MWw0NTQgNDU0cTE5IDE5IDE5IDQ1dC0xOSA0NWwtNDU0IDQ1NHEtMTkgMTkgLTQ1IDE5dC00NSAtMTlsLTEwMiAtMTAycS0xOSAtMTkgLTE5IC00NXQxOSAtNDVsMzA3IC0zMDdsLTMwNyAtMzA3cS0xOSAtMTkgLTE5IC00NXQxOSAtNDVsMTAyIC0xMDJxMTkgLTE5IDQ1IC0xOXQ0NSAxOXpNMTUzNiA2NDBxMCAtMjA5IC0xMDMgLTM4NS41dC0yNzkuNSAtMjc5LjV0LTM4NS41IC0xMDN0LTM4NS41IDEwM3QtMjc5LjUgMjc5LjUNCnQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN0Mzg1LjUgLTEwM3QyNzkuNSAtMjc5LjV0MTAzIC0zODUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImNoZXZyb25fc2lnbl91cCIgdW5pY29kZT0iJiN4ZjEzOTsiIA0KZD0iTTExNjUgMzk3bDEwMiAxMDJxMTkgMTkgMTkgNDV0LTE5IDQ1bC00NTQgNDU0cS0xOSAxOSAtNDUgMTl0LTQ1IC0xOWwtNDU0IC00NTRxLTE5IC0xOSAtMTkgLTQ1dDE5IC00NWwxMDIgLTEwMnExOSAtMTkgNDUgLTE5dDQ1IDE5bDMwNyAzMDdsMzA3IC0zMDdxMTkgLTE5IDQ1IC0xOXQ0NSAxOXpNMTUzNiA2NDBxMCAtMjA5IC0xMDMgLTM4NS41dC0yNzkuNSAtMjc5LjV0LTM4NS41IC0xMDN0LTM4NS41IDEwM3QtMjc5LjUgMjc5LjUNCnQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN0Mzg1LjUgLTEwM3QyNzkuNSAtMjc5LjV0MTAzIC0zODUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImNoZXZyb25fc2lnbl9kb3duIiB1bmljb2RlPSImI3hmMTNhOyIgDQpkPSJNODEzIDIzN2w0NTQgNDU0cTE5IDE5IDE5IDQ1dC0xOSA0NWwtMTAyIDEwMnEtMTkgMTkgLTQ1IDE5dC00NSAtMTlsLTMwNyAtMzA3bC0zMDcgMzA3cS0xOSAxOSAtNDUgMTl0LTQ1IC0xOWwtMTAyIC0xMDJxLTE5IC0xOSAtMTkgLTQ1dDE5IC00NWw0NTQgLTQ1NHExOSAtMTkgNDUgLTE5dDQ1IDE5ek0xNTM2IDY0MHEwIC0yMDkgLTEwMyAtMzg1LjV0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzdC0yNzkuNSAyNzkuNQ0KdC0xMDMgMzg1LjV0MTAzIDM4NS41dDI3OS41IDI3OS41dDM4NS41IDEwM3QzODUuNSAtMTAzdDI3OS41IC0yNzkuNXQxMDMgLTM4NS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iaHRtbDUiIHVuaWNvZGU9IiYjeGYxM2I7IiBob3Jpei1hZHYteD0iMTQwOCIgDQpkPSJNMTEzMCA5MzlsMTYgMTc1aC04ODRsNDcgLTUzNGg2MTJsLTIyIC0yMjhsLTE5NyAtNTNsLTE5NiA1M2wtMTMgMTQwaC0xNzVsMjIgLTI3OGwzNjIgLTEwMGg0djFsMzU5IDk5bDUwIDU0NGgtNjQ0bC0xNSAxODFoNjc0ek0wIDE0MDhoMTQwOGwtMTI4IC0xNDM4bC01NzggLTE2MmwtNTc0IDE2MnoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImNzczMiIHVuaWNvZGU9IiYjeGYxM2M7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMjc1IDE0MDhoMTUwNWwtMjY2IC0xMzMzbC04MDQgLTI2N2wtNjk4IDI2N2w3MSAzNTZoMjk3bC0yOSAtMTQ3bDQyMiAtMTYxbDQ4NiAxNjFsNjggMzM5aC0xMjA4bDU4IDI5N2gxMjA5bDM4IDE5MWgtMTIwOHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImFuY2hvciIgdW5pY29kZT0iJiN4ZjEzZDsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik05NjAgMTI4MHEwIDI2IC0xOSA0NXQtNDUgMTl0LTQ1IC0xOXQtMTkgLTQ1dDE5IC00NXQ0NSAtMTl0NDUgMTl0MTkgNDV6TTE3OTIgMzUydi0zNTJxMCAtMjIgLTIwIC0zMHEtOCAtMiAtMTIgLTJxLTEyIDAgLTIzIDlsLTkzIDkzcS0xMTkgLTE0MyAtMzE4LjUgLTIyNi41dC00MjkuNSAtODMuNXQtNDI5LjUgODMuNXQtMzE4LjUgMjI2LjVsLTkzIC05M3EtOSAtOSAtMjMgLTlxLTQgMCAtMTIgMnEtMjAgOCAtMjAgMzB2MzUyDQpxMCAxNCA5IDIzdDIzIDloMzUycTIyIDAgMzAgLTIwcTggLTE5IC03IC0zNWwtMTAwIC0xMDBxNjcgLTkxIDE4OS41IC0xNTMuNXQyNzEuNSAtODIuNXY2NDdoLTE5MnEtMjYgMCAtNDUgMTl0LTE5IDQ1djEyOHEwIDI2IDE5IDQ1dDQ1IDE5aDE5MnYxNjNxLTU4IDM0IC05MyA5Mi41dC0zNSAxMjguNXEwIDEwNiA3NSAxODF0MTgxIDc1dDE4MSAtNzV0NzUgLTE4MXEwIC03MCAtMzUgLTEyOC41dC05MyAtOTIuNXYtMTYzaDE5MnEyNiAwIDQ1IC0xOQ0KdDE5IC00NXYtMTI4cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTkydi02NDdxMTQ5IDIwIDI3MS41IDgyLjV0MTg5LjUgMTUzLjVsLTEwMCAxMDBxLTE1IDE2IC03IDM1cTggMjAgMzAgMjBoMzUycTE0IDAgMjMgLTl0OSAtMjN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmxvY2tfYWx0IiB1bmljb2RlPSImI3hmMTNlOyIgaG9yaXotYWR2LXg9IjExNTIiIA0KZD0iTTEwNTYgNzY4cTQwIDAgNjggLTI4dDI4IC02OHYtNTc2cTAgLTQwIC0yOCAtNjh0LTY4IC0yOGgtOTYwcS00MCAwIC02OCAyOHQtMjggNjh2NTc2cTAgNDAgMjggNjh0NjggMjhoMzJ2MzIwcTAgMTg1IDEzMS41IDMxNi41dDMxNi41IDEzMS41dDMxNi41IC0xMzEuNXQxMzEuNSAtMzE2LjVxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC02NHEtMjYgMCAtNDUgMTl0LTE5IDQ1cTAgMTA2IC03NSAxODF0LTE4MSA3NXQtMTgxIC03NXQtNzUgLTE4MQ0Kdi0zMjBoNzM2eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iYnVsbHNleWUiIHVuaWNvZGU9IiYjeGYxNDA7IiANCmQ9Ik0xMDI0IDY0MHEwIC0xMDYgLTc1IC0xODF0LTE4MSAtNzV0LTE4MSA3NXQtNzUgMTgxdDc1IDE4MXQxODEgNzV0MTgxIC03NXQ3NSAtMTgxek0xMTUyIDY0MHEwIDE1OSAtMTEyLjUgMjcxLjV0LTI3MS41IDExMi41dC0yNzEuNSAtMTEyLjV0LTExMi41IC0yNzEuNXQxMTIuNSAtMjcxLjV0MjcxLjUgLTExMi41dDI3MS41IDExMi41dDExMi41IDI3MS41ek0xMjgwIDY0MHEwIC0yMTIgLTE1MCAtMzYydC0zNjIgLTE1MHQtMzYyIDE1MA0KdC0xNTAgMzYydDE1MCAzNjJ0MzYyIDE1MHQzNjIgLTE1MHQxNTAgLTM2MnpNMTQwOCA2NDBxMCAxMzAgLTUxIDI0OC41dC0xMzYuNSAyMDR0LTIwNCAxMzYuNXQtMjQ4LjUgNTF0LTI0OC41IC01MXQtMjA0IC0xMzYuNXQtMTM2LjUgLTIwNHQtNTEgLTI0OC41dDUxIC0yNDguNXQxMzYuNSAtMjA0dDIwNCAtMTM2LjV0MjQ4LjUgLTUxdDI0OC41IDUxdDIwNCAxMzYuNXQxMzYuNSAyMDR0NTEgMjQ4LjV6TTE1MzYgNjQwDQpxMCAtMjA5IC0xMDMgLTM4NS41dC0yNzkuNSAtMjc5LjV0LTM4NS41IC0xMDN0LTM4NS41IDEwM3QtMjc5LjUgMjc5LjV0LTEwMyAzODUuNXQxMDMgMzg1LjV0Mjc5LjUgMjc5LjV0Mzg1LjUgMTAzdDM4NS41IC0xMDN0Mjc5LjUgLTI3OS41dDEwMyAtMzg1LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJlbGxpcHNpc19ob3Jpem9udGFsIiB1bmljb2RlPSImI3hmMTQxOyIgaG9yaXotYWR2LXg9IjE0MDgiIA0KZD0iTTM4NCA4MDB2LTE5MnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTE5MnEtNDAgMCAtNjggMjh0LTI4IDY4djE5MnEwIDQwIDI4IDY4dDY4IDI4aDE5MnE0MCAwIDY4IC0yOHQyOCAtNjh6TTg5NiA4MDB2LTE5MnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTE5MnEtNDAgMCAtNjggMjh0LTI4IDY4djE5MnEwIDQwIDI4IDY4dDY4IDI4aDE5MnE0MCAwIDY4IC0yOHQyOCAtNjh6TTE0MDggODAwdi0xOTJxMCAtNDAgLTI4IC02OHQtNjggLTI4aC0xOTINCnEtNDAgMCAtNjggMjh0LTI4IDY4djE5MnEwIDQwIDI4IDY4dDY4IDI4aDE5MnE0MCAwIDY4IC0yOHQyOCAtNjh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJlbGxpcHNpc192ZXJ0aWNhbCIgdW5pY29kZT0iJiN4ZjE0MjsiIGhvcml6LWFkdi14PSIzODQiIA0KZD0iTTM4NCAyODh2LTE5MnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTE5MnEtNDAgMCAtNjggMjh0LTI4IDY4djE5MnEwIDQwIDI4IDY4dDY4IDI4aDE5MnE0MCAwIDY4IC0yOHQyOCAtNjh6TTM4NCA4MDB2LTE5MnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTE5MnEtNDAgMCAtNjggMjh0LTI4IDY4djE5MnEwIDQwIDI4IDY4dDY4IDI4aDE5MnE0MCAwIDY4IC0yOHQyOCAtNjh6TTM4NCAxMzEydi0xOTJxMCAtNDAgLTI4IC02OHQtNjggLTI4aC0xOTINCnEtNDAgMCAtNjggMjh0LTI4IDY4djE5MnEwIDQwIDI4IDY4dDY4IDI4aDE5MnE0MCAwIDY4IC0yOHQyOCAtNjh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfMzAzIiB1bmljb2RlPSImI3hmMTQzOyIgDQpkPSJNNTEyIDI1NnEwIDUzIC0zNy41IDkwLjV0LTkwLjUgMzcuNXQtOTAuNSAtMzcuNXQtMzcuNSAtOTAuNXQzNy41IC05MC41dDkwLjUgLTM3LjV0OTAuNSAzNy41dDM3LjUgOTAuNXpNODYzIDE2MnEtMTMgMjMzIC0xNzYuNSAzOTYuNXQtMzk2LjUgMTc2LjVxLTE0IDEgLTI0IC05dC0xMCAtMjN2LTEyOHEwIC0xMyA4LjUgLTIydDIxLjUgLTEwcTE1NCAtMTEgMjY0IC0xMjF0MTIxIC0yNjRxMSAtMTMgMTAgLTIxLjV0MjIgLTguNWgxMjgNCnExMyAwIDIzIDEwdDkgMjR6TTEyNDcgMTYxcS01IDE1NCAtNTYgMjk3LjV0LTEzOS41IDI2MHQtMjA1IDIwNXQtMjYwIDEzOS41dC0yOTcuNSA1NnEtMTQgMSAtMjMgLTlxLTEwIC0xMCAtMTAgLTIzdi0xMjhxMCAtMTMgOSAtMjJ0MjIgLTEwcTIwNCAtNyAzNzggLTExMS41dDI3OC41IC0yNzguNXQxMTEuNSAtMzc4cTEgLTEzIDEwIC0yMnQyMiAtOWgxMjhxMTMgMCAyMyAxMHExMSA5IDkgMjN6TTE1MzYgMTEyMHYtOTYwDQpxMCAtMTE5IC04NC41IC0yMDMuNXQtMjAzLjUgLTg0LjVoLTk2MHEtMTE5IDAgLTIwMy41IDg0LjV0LTg0LjUgMjAzLjV2OTYwcTAgMTE5IDg0LjUgMjAzLjV0MjAzLjUgODQuNWg5NjBxMTE5IDAgMjAzLjUgLTg0LjV0ODQuNSAtMjAzLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJwbGF5X3NpZ24iIHVuaWNvZGU9IiYjeGYxNDQ7IiANCmQ9Ik03NjggMTQwOHEyMDkgMCAzODUuNSAtMTAzdDI3OS41IC0yNzkuNXQxMDMgLTM4NS41dC0xMDMgLTM4NS41dC0yNzkuNSAtMjc5LjV0LTM4NS41IC0xMDN0LTM4NS41IDEwM3QtMjc5LjUgMjc5LjV0LTEwMyAzODUuNXQxMDMgMzg1LjV0Mjc5LjUgMjc5LjV0Mzg1LjUgMTAzek0xMTUyIDU4NXEzMiAxOCAzMiA1NXQtMzIgNTVsLTU0NCAzMjBxLTMxIDE5IC02NCAxcS0zMiAtMTkgLTMyIC01NnYtNjQwcTAgLTM3IDMyIC01Ng0KcTE2IC04IDMyIC04cTE3IDAgMzIgOXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InRpY2tldCIgdW5pY29kZT0iJiN4ZjE0NTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xMDI0IDEwODRsMzE2IC0zMTZsLTU3MiAtNTcybC0zMTYgMzE2ek04MTMgMTA1bDYxOCA2MThxMTkgMTkgMTkgNDV0LTE5IDQ1bC0zNjIgMzYycS0xOCAxOCAtNDUgMTh0LTQ1IC0xOGwtNjE4IC02MThxLTE5IC0xOSAtMTkgLTQ1dDE5IC00NWwzNjIgLTM2MnExOCAtMTggNDUgLTE4dDQ1IDE4ek0xNzAyIDc0MmwtOTA3IC05MDhxLTM3IC0zNyAtOTAuNSAtMzd0LTkwLjUgMzdsLTEyNiAxMjZxNTYgNTYgNTYgMTM2dC01NiAxMzYNCnQtMTM2IDU2dC0xMzYgLTU2bC0xMjUgMTI2cS0zNyAzNyAtMzcgOTAuNXQzNyA5MC41bDkwNyA5MDZxMzcgMzcgOTAuNSAzN3Q5MC41IC0zN2wxMjUgLTEyNXEtNTYgLTU2IC01NiAtMTM2dDU2IC0xMzZ0MTM2IC01NnQxMzYgNTZsMTI2IC0xMjVxMzcgLTM3IDM3IC05MC41dC0zNyAtOTAuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Im1pbnVzX3NpZ25fYWx0IiB1bmljb2RlPSImI3hmMTQ2OyIgDQpkPSJNMTI4MCA1NzZ2MTI4cTAgMjYgLTE5IDQ1dC00NSAxOWgtODk2cS0yNiAwIC00NSAtMTl0LTE5IC00NXYtMTI4cTAgLTI2IDE5IC00NXQ0NSAtMTloODk2cTI2IDAgNDUgMTl0MTkgNDV6TTE1MzYgMTEyMHYtOTYwcTAgLTExOSAtODQuNSAtMjAzLjV0LTIwMy41IC04NC41aC05NjBxLTExOSAwIC0yMDMuNSA4NC41dC04NC41IDIwMy41djk2MHEwIDExOSA4NC41IDIwMy41dDIwMy41IDg0LjVoOTYwcTExOSAwIDIwMy41IC04NC41DQp0ODQuNSAtMjAzLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJjaGVja19taW51cyIgdW5pY29kZT0iJiN4ZjE0NzsiIGhvcml6LWFkdi14PSIxNDA4IiANCmQ9Ik0xMTUyIDczNnYtNjRxMCAtMTQgLTkgLTIzdC0yMyAtOWgtODMycS0xNCAwIC0yMyA5dC05IDIzdjY0cTAgMTQgOSAyM3QyMyA5aDgzMnExNCAwIDIzIC05dDkgLTIzek0xMjgwIDI4OHY4MzJxMCA2NiAtNDcgMTEzdC0xMTMgNDdoLTgzMnEtNjYgMCAtMTEzIC00N3QtNDcgLTExM3YtODMycTAgLTY2IDQ3IC0xMTN0MTEzIC00N2g4MzJxNjYgMCAxMTMgNDd0NDcgMTEzek0xNDA4IDExMjB2LTgzMnEwIC0xMTkgLTg0LjUgLTIwMy41DQp0LTIwMy41IC04NC41aC04MzJxLTExOSAwIC0yMDMuNSA4NC41dC04NC41IDIwMy41djgzMnEwIDExOSA4NC41IDIwMy41dDIwMy41IDg0LjVoODMycTExOSAwIDIwMy41IC04NC41dDg0LjUgLTIwMy41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ibGV2ZWxfdXAiIHVuaWNvZGU9IiYjeGYxNDg7IiBob3Jpei1hZHYteD0iMTAyNCIgDQpkPSJNMTAxOCA5MzNxLTE4IC0zNyAtNTggLTM3aC0xOTJ2LTg2NHEwIC0xNCAtOSAtMjN0LTIzIC05aC03MDRxLTIxIDAgLTI5IDE4cS04IDIwIDQgMzVsMTYwIDE5MnE5IDExIDI1IDExaDMyMHY2NDBoLTE5MnEtNDAgMCAtNTggMzdxLTE3IDM3IDkgNjhsMzIwIDM4NHExOCAyMiA0OSAyMnQ0OSAtMjJsMzIwIC0zODRxMjcgLTMyIDkgLTY4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ibGV2ZWxfZG93biIgdW5pY29kZT0iJiN4ZjE0OTsiIGhvcml6LWFkdi14PSIxMDI0IiANCmQ9Ik0zMiAxMjgwaDcwNHExMyAwIDIyLjUgLTkuNXQ5LjUgLTIzLjV2LTg2M2gxOTJxNDAgMCA1OCAtMzd0LTkgLTY5bC0zMjAgLTM4NHEtMTggLTIyIC00OSAtMjJ0LTQ5IDIybC0zMjAgMzg0cS0yNiAzMSAtOSA2OXExOCAzNyA1OCAzN2gxOTJ2NjQwaC0zMjBxLTE0IDAgLTI1IDExbC0xNjAgMTkycS0xMyAxNCAtNCAzNHE5IDE5IDI5IDE5eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY2hlY2tfc2lnbiIgdW5pY29kZT0iJiN4ZjE0YTsiIA0KZD0iTTY4NSAyMzdsNjE0IDYxNHExOSAxOSAxOSA0NXQtMTkgNDVsLTEwMiAxMDJxLTE5IDE5IC00NSAxOXQtNDUgLTE5bC00NjcgLTQ2N2wtMjExIDIxMXEtMTkgMTkgLTQ1IDE5dC00NSAtMTlsLTEwMiAtMTAycS0xOSAtMTkgLTE5IC00NXQxOSAtNDVsMzU4IC0zNThxMTkgLTE5IDQ1IC0xOXQ0NSAxOXpNMTUzNiAxMTIwdi05NjBxMCAtMTE5IC04NC41IC0yMDMuNXQtMjAzLjUgLTg0LjVoLTk2MHEtMTE5IDAgLTIwMy41IDg0LjUNCnQtODQuNSAyMDMuNXY5NjBxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDk2MHExMTkgMCAyMDMuNSAtODQuNXQ4NC41IC0yMDMuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImVkaXRfc2lnbiIgdW5pY29kZT0iJiN4ZjE0YjsiIA0KZD0iTTQwNCA0MjhsMTUyIC0xNTJsLTUyIC01MmgtNTZ2OTZoLTk2djU2ek04MTggODE4cTE0IC0xMyAtMyAtMzBsLTI5MSAtMjkxcS0xNyAtMTcgLTMwIC0zcS0xNCAxMyAzIDMwbDI5MSAyOTFxMTcgMTcgMzAgM3pNNTQ0IDEyOGw1NDQgNTQ0bC0yODggMjg4bC01NDQgLTU0NHYtMjg4aDI4OHpNMTE1MiA3MzZsOTIgOTJxMjggMjggMjggNjh0LTI4IDY4bC0xNTIgMTUycS0yOCAyOCAtNjggMjh0LTY4IC0yOGwtOTIgLTkyek0xNTM2IDExMjANCnYtOTYwcTAgLTExOSAtODQuNSAtMjAzLjV0LTIwMy41IC04NC41aC05NjBxLTExOSAwIC0yMDMuNSA4NC41dC04NC41IDIwMy41djk2MHEwIDExOSA4NC41IDIwMy41dDIwMy41IDg0LjVoOTYwcTExOSAwIDIwMy41IC04NC41dDg0LjUgLTIwMy41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzMxMiIgdW5pY29kZT0iJiN4ZjE0YzsiIA0KZD0iTTEyODAgNjA4djQ4MHEwIDI2IC0xOSA0NXQtNDUgMTloLTQ4MHEtNDIgMCAtNTkgLTM5cS0xNyAtNDEgMTQgLTcwbDE0NCAtMTQ0bC01MzQgLTUzNHEtMTkgLTE5IC0xOSAtNDV0MTkgLTQ1bDEwMiAtMTAycTE5IC0xOSA0NSAtMTl0NDUgMTlsNTM0IDUzNGwxNDQgLTE0NHExOCAtMTkgNDUgLTE5cTEyIDAgMjUgNXEzOSAxNyAzOSA1OXpNMTUzNiAxMTIwdi05NjBxMCAtMTE5IC04NC41IC0yMDMuNXQtMjAzLjUgLTg0LjVoLTk2MA0KcS0xMTkgMCAtMjAzLjUgODQuNXQtODQuNSAyMDMuNXY5NjBxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDk2MHExMTkgMCAyMDMuNSAtODQuNXQ4NC41IC0yMDMuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InNoYXJlX3NpZ24iIHVuaWNvZGU9IiYjeGYxNGQ7IiANCmQ9Ik0xMDA1IDQzNWwzNTIgMzUycTE5IDE5IDE5IDQ1dC0xOSA0NWwtMzUyIDM1MnEtMzAgMzEgLTY5IDE0cS00MCAtMTcgLTQwIC01OXYtMTYwcS0xMTkgMCAtMjE2IC0xOS41dC0xNjIuNSAtNTF0LTExNCAtNzl0LTc2LjUgLTk1LjV0LTQ0LjUgLTEwOXQtMjEuNSAtMTExLjV0LTUgLTExMC41cTAgLTE4MSAxNjcgLTQwNHExMSAtMTIgMjUgLTEycTcgMCAxMyAzcTIyIDkgMTkgMzNxLTQ0IDM1NCA2MiA0NzNxNDYgNTIgMTMwIDc1LjUNCnQyMjQgMjMuNXYtMTYwcTAgLTQyIDQwIC01OXExMiAtNSAyNCAtNXEyNiAwIDQ1IDE5ek0xNTM2IDExMjB2LTk2MHEwIC0xMTkgLTg0LjUgLTIwMy41dC0yMDMuNSAtODQuNWgtOTYwcS0xMTkgMCAtMjAzLjUgODQuNXQtODQuNSAyMDMuNXY5NjBxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDk2MHExMTkgMCAyMDMuNSAtODQuNXQ4NC41IC0yMDMuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImNvbXBhc3MiIHVuaWNvZGU9IiYjeGYxNGU7IiANCmQ9Ik02NDAgNDQ4bDI1NiAxMjhsLTI1NiAxMjh2LTI1NnpNMTAyNCAxMDM5di01NDJsLTUxMiAtMjU2djU0MnpNMTMxMiA2NDBxMCAxNDggLTczIDI3M3QtMTk4IDE5OHQtMjczIDczdC0yNzMgLTczdC0xOTggLTE5OHQtNzMgLTI3M3Q3MyAtMjczdDE5OCAtMTk4dDI3MyAtNzN0MjczIDczdDE5OCAxOTh0NzMgMjczek0xNTM2IDY0MHEwIC0yMDkgLTEwMyAtMzg1LjV0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzDQp0LTI3OS41IDI3OS41dC0xMDMgMzg1LjV0MTAzIDM4NS41dDI3OS41IDI3OS41dDM4NS41IDEwM3QzODUuNSAtMTAzdDI3OS41IC0yNzkuNXQxMDMgLTM4NS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY29sbGFwc2UiIHVuaWNvZGU9IiYjeGYxNTA7IiANCmQ9Ik0xMTQ1IDg2MXExOCAtMzUgLTUgLTY2bC0zMjAgLTQ0OHEtMTkgLTI3IC01MiAtMjd0LTUyIDI3bC0zMjAgNDQ4cS0yMyAzMSAtNSA2NnExNyAzNSA1NyAzNWg2NDBxNDAgMCA1NyAtMzV6TTEyODAgMTYwdjk2MHEwIDEzIC05LjUgMjIuNXQtMjIuNSA5LjVoLTk2MHEtMTMgMCAtMjIuNSAtOS41dC05LjUgLTIyLjV2LTk2MHEwIC0xMyA5LjUgLTIyLjV0MjIuNSAtOS41aDk2MHExMyAwIDIyLjUgOS41dDkuNSAyMi41ek0xNTM2IDExMjANCnYtOTYwcTAgLTExOSAtODQuNSAtMjAzLjV0LTIwMy41IC04NC41aC05NjBxLTExOSAwIC0yMDMuNSA4NC41dC04NC41IDIwMy41djk2MHEwIDExOSA4NC41IDIwMy41dDIwMy41IDg0LjVoOTYwcTExOSAwIDIwMy41IC04NC41dDg0LjUgLTIwMy41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iY29sbGFwc2VfdG9wIiB1bmljb2RlPSImI3hmMTUxOyIgDQpkPSJNMTE0NSA0MTlxLTE3IC0zNSAtNTcgLTM1aC02NDBxLTQwIDAgLTU3IDM1cS0xOCAzNSA1IDY2bDMyMCA0NDhxMTkgMjcgNTIgMjd0NTIgLTI3bDMyMCAtNDQ4cTIzIC0zMSA1IC02NnpNMTI4MCAxNjB2OTYwcTAgMTMgLTkuNSAyMi41dC0yMi41IDkuNWgtOTYwcS0xMyAwIC0yMi41IC05LjV0LTkuNSAtMjIuNXYtOTYwcTAgLTEzIDkuNSAtMjIuNXQyMi41IC05LjVoOTYwcTEzIDAgMjIuNSA5LjV0OS41IDIyLjV6TTE1MzYgMTEyMHYtOTYwDQpxMCAtMTE5IC04NC41IC0yMDMuNXQtMjAzLjUgLTg0LjVoLTk2MHEtMTE5IDAgLTIwMy41IDg0LjV0LTg0LjUgMjAzLjV2OTYwcTAgMTE5IDg0LjUgMjAzLjV0MjAzLjUgODQuNWg5NjBxMTE5IDAgMjAzLjUgLTg0LjV0ODQuNSAtMjAzLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfMzE3IiB1bmljb2RlPSImI3hmMTUyOyIgDQpkPSJNMTA4OCA2NDBxMCAtMzMgLTI3IC01MmwtNDQ4IC0zMjBxLTMxIC0yMyAtNjYgLTVxLTM1IDE3IC0zNSA1N3Y2NDBxMCA0MCAzNSA1N3EzNSAxOCA2NiAtNWw0NDggLTMyMHEyNyAtMTkgMjcgLTUyek0xMjgwIDE2MHY5NjBxMCAxNCAtOSAyM3QtMjMgOWgtOTYwcS0xNCAwIC0yMyAtOXQtOSAtMjN2LTk2MHEwIC0xNCA5IC0yM3QyMyAtOWg5NjBxMTQgMCAyMyA5dDkgMjN6TTE1MzYgMTEyMHYtOTYwcTAgLTExOSAtODQuNSAtMjAzLjUNCnQtMjAzLjUgLTg0LjVoLTk2MHEtMTE5IDAgLTIwMy41IDg0LjV0LTg0LjUgMjAzLjV2OTYwcTAgMTE5IDg0LjUgMjAzLjV0MjAzLjUgODQuNWg5NjBxMTE5IDAgMjAzLjUgLTg0LjV0ODQuNSAtMjAzLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJldXIiIHVuaWNvZGU9IiYjeGYxNTM7IiBob3Jpei1hZHYteD0iMTAyNCIgDQpkPSJNOTc2IDIyOWwzNSAtMTU5cTMgLTEyIC0zIC0yMi41dC0xNyAtMTQuNWwtNSAtMXEtNCAtMiAtMTAuNSAtMy41dC0xNiAtNC41dC0yMS41IC01LjV0LTI1LjUgLTV0LTMwIC01dC0zMy41IC00LjV0LTM2LjUgLTN0LTM4LjUgLTFxLTIzNCAwIC00MDkgMTMwLjV0LTIzOCAzNTEuNWgtOTVxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXYxMTNxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWg2NnEtMiA1NyAxIDEwNWgtNjdxLTE0IDAgLTIzIDkNCnQtOSAyM3YxMTRxMCAxNCA5IDIzdDIzIDloOThxNjcgMjEwIDI0My41IDMzOHQ0MDAuNSAxMjhxMTAyIDAgMTk0IC0yM3ExMSAtMyAyMCAtMTVxNiAtMTEgMyAtMjRsLTQzIC0xNTlxLTMgLTEzIC0xNCAtMTkuNXQtMjQgLTIuNWwtNCAxcS00IDEgLTExLjUgMi41bC0xNy41IDMuNXQtMjIuNSAzLjV0LTI2IDN0LTI5IDIuNXQtMjkuNSAxcS0xMjYgMCAtMjI2IC02NHQtMTUwIC0xNzZoNDY4cTE2IDAgMjUgLTEycTEwIC0xMiA3IC0yNg0KbC0yNCAtMTE0cS01IC0yNiAtMzIgLTI2aC00ODhxLTMgLTM3IDAgLTEwNWg0NTlxMTUgMCAyNSAtMTJxOSAtMTIgNiAtMjdsLTI0IC0xMTJxLTIgLTExIC0xMSAtMTguNXQtMjAgLTcuNWgtMzg3cTQ4IC0xMTcgMTQ5LjUgLTE4NS41dDIyOC41IC02OC41cTE4IDAgMzYgMS41dDMzLjUgMy41dDI5LjUgNC41dDI0LjUgNXQxOC41IDQuNWwxMiAzbDUgMnExMyA1IDI2IC0ycTEyIC03IDE1IC0yMXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImdicCIgdW5pY29kZT0iJiN4ZjE1NDsiIGhvcml6LWFkdi14PSIxMDI0IiANCmQ9Ik0xMDIwIDM5OXYtMzY3cTAgLTE0IC05IC0yM3QtMjMgLTloLTk1NnEtMTQgMCAtMjMgOXQtOSAyM3YxNTBxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWg5N3YzODNoLTk1cS0xNCAwIC0yMyA5LjV0LTkgMjIuNXYxMzFxMCAxNCA5IDIzdDIzIDloOTV2MjIzcTAgMTcxIDEyMy41IDI4MnQzMTQuNSAxMTFxMTg1IDAgMzM1IC0xMjVxOSAtOCAxMCAtMjAuNXQtNyAtMjIuNWwtMTAzIC0xMjdxLTkgLTExIC0yMiAtMTJxLTEzIC0yIC0yMyA3DQpxLTUgNSAtMjYgMTl0LTY5IDMydC05MyAxOHEtODUgMCAtMTM3IC00N3QtNTIgLTEyM3YtMjE1aDMwNXExMyAwIDIyLjUgLTl0OS41IC0yM3YtMTMxcTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtMzA1di0zNzloNDE0djE4MXEwIDEzIDkgMjIuNXQyMyA5LjVoMTYycTE0IDAgMjMgLTkuNXQ5IC0yMi41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idXNkIiB1bmljb2RlPSImI3hmMTU1OyIgaG9yaXotYWR2LXg9IjEwMjQiIA0KZD0iTTk3OCAzNTFxMCAtMTUzIC05OS41IC0yNjMuNXQtMjU4LjUgLTEzNi41di0xNzVxMCAtMTQgLTkgLTIzdC0yMyAtOWgtMTM1cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2MTc1cS02NiA5IC0xMjcuNSAzMXQtMTAxLjUgNDQuNXQtNzQgNDh0LTQ2LjUgMzcuNXQtMTcuNSAxOHEtMTcgMjEgLTIgNDFsMTAzIDEzNXE3IDEwIDIzIDEycTE1IDIgMjQgLTlsMiAtMnExMTMgLTk5IDI0MyAtMTI1cTM3IC04IDc0IC04cTgxIDAgMTQyLjUgNDMNCnQ2MS41IDEyMnEwIDI4IC0xNSA1M3QtMzMuNSA0MnQtNTguNSAzNy41dC02NiAzMnQtODAgMzIuNXEtMzkgMTYgLTYxLjUgMjV0LTYxLjUgMjYuNXQtNjIuNSAzMXQtNTYuNSAzNS41dC01My41IDQyLjV0LTQzLjUgNDl0LTM1LjUgNTh0LTIxIDY2LjV0LTguNSA3OHEwIDEzOCA5OCAyNDJ0MjU1IDEzNHYxODBxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWgxMzVxMTQgMCAyMyAtOXQ5IC0yM3YtMTc2cTU3IC02IDExMC41IC0yM3Q4NyAtMzMuNQ0KdDYzLjUgLTM3LjV0MzkgLTI5dDE1IC0xNHExNyAtMTggNSAtMzhsLTgxIC0xNDZxLTggLTE1IC0yMyAtMTZxLTE0IC0zIC0yNyA3cS0zIDMgLTE0LjUgMTJ0LTM5IDI2LjV0LTU4LjUgMzJ0LTc0LjUgMjZ0LTg1LjUgMTEuNXEtOTUgMCAtMTU1IC00M3QtNjAgLTExMXEwIC0yNiA4LjUgLTQ4dDI5LjUgLTQxLjV0MzkuNSAtMzN0NTYgLTMxdDYwLjUgLTI3dDcwIC0yNy41cTUzIC0yMCA4MSAtMzEuNXQ3NiAtMzV0NzUuNSAtNDIuNXQ2MiAtNTANCnQ1MyAtNjMuNXQzMS41IC03Ni41dDEzIC05NHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImluciIgdW5pY29kZT0iJiN4ZjE1NjsiIGhvcml6LWFkdi14PSI4OTgiIA0KZD0iTTg5OCAxMDY2di0xMDJxMCAtMTQgLTkgLTIzdC0yMyAtOWgtMTY4cS0yMyAtMTQ0IC0xMjkgLTIzNHQtMjc2IC0xMTBxMTY3IC0xNzggNDU5IC01MzZxMTQgLTE2IDQgLTM0cS04IC0xOCAtMjkgLTE4aC0xOTVxLTE2IDAgLTI1IDEycS0zMDYgMzY3IC00OTggNTcxcS05IDkgLTkgMjJ2MTI3cTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoMTEycTEzMiAwIDIxMi41IDQzdDEwMi41IDEyNWgtNDI3cS0xNCAwIC0yMyA5dC05IDIzdjEwMg0KcTAgMTQgOSAyM3QyMyA5aDQxM3EtNTcgMTEzIC0yNjggMTEzaC0xNDVxLTEzIDAgLTIyLjUgOS41dC05LjUgMjIuNXYxMzNxMCAxNCA5IDIzdDIzIDloODMycTE0IDAgMjMgLTl0OSAtMjN2LTEwMnEwIC0xNCAtOSAtMjN0LTIzIC05aC0yMzNxNDcgLTYxIDY0IC0xNDRoMTcxcTE0IDAgMjMgLTl0OSAtMjN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJqcHkiIHVuaWNvZGU9IiYjeGYxNTc7IiBob3Jpei1hZHYteD0iMTAyNyIgDQpkPSJNNjAzIDBoLTE3MnEtMTMgMCAtMjIuNSA5dC05LjUgMjN2MzMwaC0yODhxLTEzIDAgLTIyLjUgOXQtOS41IDIzdjEwM3EwIDEzIDkuNSAyMi41dDIyLjUgOS41aDI4OHY4NWgtMjg4cS0xMyAwIC0yMi41IDl0LTkuNSAyM3YxMDRxMCAxMyA5LjUgMjIuNXQyMi41IDkuNWgyMTRsLTMyMSA1NzhxLTggMTYgMCAzMnExMCAxNiAyOCAxNmgxOTRxMTkgMCAyOSAtMThsMjE1IC00MjVxMTkgLTM4IDU2IC0xMjVxMTAgMjQgMzAuNSA2OHQyNy41IDYxDQpsMTkxIDQyMHE4IDE5IDI5IDE5aDE5MXExNyAwIDI3IC0xNnE5IC0xNCAxIC0zMWwtMzEzIC01NzloMjE1cTEzIDAgMjIuNSAtOS41dDkuNSAtMjIuNXYtMTA0cTAgLTE0IC05LjUgLTIzdC0yMi41IC05aC0yOTB2LTg1aDI5MHExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV2LTEwM3EwIC0xNCAtOS41IC0yM3QtMjIuNSAtOWgtMjkwdi0zMzBxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0icnViIiB1bmljb2RlPSImI3hmMTU4OyIgaG9yaXotYWR2LXg9IjEyODAiIA0KZD0iTTEwNDMgOTcxcTAgMTAwIC02NSAxNjJ0LTE3MSA2MmgtMzIwdi00NDhoMzIwcTEwNiAwIDE3MSA2MnQ2NSAxNjJ6TTEyODAgOTcxcTAgLTE5MyAtMTI2LjUgLTMxNXQtMzI2LjUgLTEyMmgtMzQwdi0xMThoNTA1cTE0IDAgMjMgLTl0OSAtMjN2LTEyOHEwIC0xNCAtOSAtMjN0LTIzIC05aC01MDV2LTE5MnEwIC0xNCAtOS41IC0yM3QtMjIuNSAtOWgtMTY3cS0xNCAwIC0yMyA5dC05IDIzdjE5MmgtMjI0cS0xNCAwIC0yMyA5dC05IDIzdjEyOA0KcTAgMTQgOSAyM3QyMyA5aDIyNHYxMThoLTIyNHEtMTQgMCAtMjMgOXQtOSAyM3YxNDlxMCAxMyA5IDIyLjV0MjMgOS41aDIyNHY2MjlxMCAxNCA5IDIzdDIzIDloNTM5cTIwMCAwIDMyNi41IC0xMjJ0MTI2LjUgLTMxNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImtydyIgdW5pY29kZT0iJiN4ZjE1OTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik01MTQgMzQxbDgxIDI5OWgtMTU5bDc1IC0zMDBxMSAtMSAxIC0zdDEgLTNxMCAxIDAuNSAzLjV0MC41IDMuNXpNNjMwIDc2OGwzNSAxMjhoLTI5MmwzMiAtMTI4aDIyNXpNODIyIDc2OGgxMzlsLTM1IDEyOGgtNzB6TTEyNzEgMzQwbDc4IDMwMGgtMTYybDgxIC0yOTlxMCAtMSAwLjUgLTMuNXQxLjUgLTMuNXEwIDEgMC41IDN0MC41IDN6TTEzODIgNzY4bDMzIDEyOGgtMjk3bDM0IC0xMjhoMjMwek0xNzkyIDczNnYtNjRxMCAtMTQgLTkgLTIzDQp0LTIzIC05aC0yMTNsLTE2NCAtNjE2cS03IC0yNCAtMzEgLTI0aC0xNTlxLTI0IDAgLTMxIDI0bC0xNjYgNjE2aC0yMDlsLTE2NyAtNjE2cS03IC0yNCAtMzEgLTI0aC0xNTlxLTExIDAgLTE5LjUgN3QtMTAuNSAxN2wtMTYwIDYxNmgtMjA4cS0xNCAwIC0yMyA5dC05IDIzdjY0cTAgMTQgOSAyM3QyMyA5aDE3NWwtMzMgMTI4aC0xNDJxLTE0IDAgLTIzIDl0LTkgMjN2NjRxMCAxNCA5IDIzdDIzIDloMTA5bC04OSAzNDRxLTUgMTUgNSAyOA0KcTEwIDEyIDI2IDEyaDEzN3EyNiAwIDMxIC0yNGw5MCAtMzYwaDM1OWw5NyAzNjBxNyAyNCAzMSAyNGgxMjZxMjQgMCAzMSAtMjRsOTggLTM2MGgzNjVsOTMgMzYwcTUgMjQgMzEgMjRoMTM3cTE2IDAgMjYgLTEycTEwIC0xMyA1IC0yOGwtOTEgLTM0NGgxMTFxMTQgMCAyMyAtOXQ5IC0yM3YtNjRxMCAtMTQgLTkgLTIzdC0yMyAtOWgtMTQ1bC0zNCAtMTI4aDE3OXExNCAwIDIzIC05dDkgLTIzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iYnRjIiB1bmljb2RlPSImI3hmMTVhOyIgaG9yaXotYWR2LXg9IjEyODAiIA0KZD0iTTExNjcgODk2cTE4IC0xODIgLTEzMSAtMjU4cTExNyAtMjggMTc1IC0xMDN0NDUgLTIxNHEtNyAtNzEgLTMyLjUgLTEyNXQtNjQuNSAtODl0LTk3IC01OC41dC0xMjEuNSAtMzQuNXQtMTQ1LjUgLTE1di0yNTVoLTE1NHYyNTFxLTgwIDAgLTEyMiAxdi0yNTJoLTE1NHYyNTVxLTE4IDAgLTU0IDAuNXQtNTUgMC41aC0yMDBsMzEgMTgzaDExMXE1MCAwIDU4IDUxdjQwMmgxNnEtNiAxIC0xNiAxdjI4N3EtMTMgNjggLTg5IDY4aC0xMTF2MTY0DQpsMjEyIC0xcTY0IDAgOTcgMXYyNTJoMTU0di0yNDdxODIgMiAxMjIgMnYyNDVoMTU0di0yNTJxNzkgLTcgMTQwIC0yMi41dDExMyAtNDV0ODIuNSAtNzh0MzYuNSAtMTE0LjV6TTk1MiAzNTFxMCAzNiAtMTUgNjR0LTM3IDQ2dC01Ny41IDMwLjV0LTY1LjUgMTguNXQtNzQgOXQtNjkgM3QtNjQuNSAtMXQtNDcuNSAtMXYtMzM4cTggMCAzNyAtMC41dDQ4IC0wLjV0NTMgMS41dDU4LjUgNHQ1NyA4LjV0NTUuNSAxNHQ0Ny41IDIxdDM5LjUgMzANCnQyNC41IDQwdDkuNSA1MXpNODgxIDgyN3EwIDMzIC0xMi41IDU4LjV0LTMwLjUgNDJ0LTQ4IDI4dC01NSAxNi41dC02MS41IDh0LTU4IDIuNXQtNTQgLTF0LTM5LjUgLTAuNXYtMzA3cTUgMCAzNC41IC0wLjV0NDYuNSAwdDUwIDJ0NTUgNS41dDUxLjUgMTF0NDguNSAxOC41dDM3IDI3dDI3IDM4LjV0OSA1MXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImZpbGUiIHVuaWNvZGU9IiYjeGYxNWI7IiANCmQ9Ik0xMDI0IDEwMjR2NDcycTIyIC0xNCAzNiAtMjhsNDA4IC00MDhxMTQgLTE0IDI4IC0zNmgtNDcyek04OTYgOTkycTAgLTQwIDI4IC02OHQ2OCAtMjhoNTQ0di0xMDU2cTAgLTQwIC0yOCAtNjh0LTY4IC0yOGgtMTM0NHEtNDAgMCAtNjggMjh0LTI4IDY4djE2MDBxMCA0MCAyOCA2OHQ2OCAyOGg4MDB2LTU0NHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImZpbGVfdGV4dCIgdW5pY29kZT0iJiN4ZjE1YzsiIA0KZD0iTTE0NjggMTA2MHExNCAtMTQgMjggLTM2aC00NzJ2NDcycTIyIC0xNCAzNiAtMjh6TTk5MiA4OTZoNTQ0di0xMDU2cTAgLTQwIC0yOCAtNjh0LTY4IC0yOGgtMTM0NHEtNDAgMCAtNjggMjh0LTI4IDY4djE2MDBxMCA0MCAyOCA2OHQ2OCAyOGg4MDB2LTU0NHEwIC00MCAyOCAtNjh0NjggLTI4ek0xMTUyIDE2MHY2NHEwIDE0IC05IDIzdC0yMyA5aC03MDRxLTE0IDAgLTIzIC05dC05IC0yM3YtNjRxMCAtMTQgOSAtMjN0MjMgLTloNzA0DQpxMTQgMCAyMyA5dDkgMjN6TTExNTIgNDE2djY0cTAgMTQgLTkgMjN0LTIzIDloLTcwNHEtMTQgMCAtMjMgLTl0LTkgLTIzdi02NHEwIC0xNCA5IC0yM3QyMyAtOWg3MDRxMTQgMCAyMyA5dDkgMjN6TTExNTIgNjcydjY0cTAgMTQgLTkgMjN0LTIzIDloLTcwNHEtMTQgMCAtMjMgLTl0LTkgLTIzdi02NHEwIC0xNCA5IC0yM3QyMyAtOWg3MDRxMTQgMCAyMyA5dDkgMjN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJzb3J0X2J5X2FscGhhYmV0IiB1bmljb2RlPSImI3hmMTVkOyIgaG9yaXotYWR2LXg9IjE2NjQiIA0KZD0iTTExOTEgMTEyOGgxNzdsLTcyIDIxOGwtMTIgNDdxLTIgMTYgLTIgMjBoLTRsLTMgLTIwcTAgLTEgLTMuNSAtMTh0LTcuNSAtMjl6TTczNiA5NnEwIC0xMiAtMTAgLTI0bC0zMTkgLTMxOXEtMTAgLTkgLTIzIC05cS0xMiAwIC0yMyA5bC0zMjAgMzIwcS0xNSAxNiAtNyAzNXE4IDIwIDMwIDIwaDE5MnYxMzc2cTAgMTQgOSAyM3QyMyA5aDE5MnExNCAwIDIzIC05dDkgLTIzdi0xMzc2aDE5MnExNCAwIDIzIC05dDkgLTIzek0xNTcyIC0yMw0Kdi0yMzNoLTU4NHY5MGwzNjkgNTI5cTEyIDE4IDIxIDI3bDExIDl2M3EtMiAwIC02LjUgLTAuNXQtNy41IC0wLjVxLTEyIC0zIC0zMCAtM2gtMjMydi0xMTVoLTEyMHYyMjloNTY3di04OWwtMzY5IC01MzBxLTYgLTggLTIxIC0yNmwtMTEgLTExdi0ybDE0IDJxOSAyIDMwIDJoMjQ4djExOWgxMjF6TTE2NjEgODc0di0xMDZoLTI4OHYxMDZoNzVsLTQ3IDE0NGgtMjQzbC00NyAtMTQ0aDc1di0xMDZoLTI4N3YxMDZoNzBsMjMwIDY2MmgxNjINCmwyMzAgLTY2Mmg3MHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il8zMjkiIHVuaWNvZGU9IiYjeGYxNWU7IiBob3Jpei1hZHYteD0iMTY2NCIgDQpkPSJNMTE5MSAxMDRoMTc3bC03MiAyMThsLTEyIDQ3cS0yIDE2IC0yIDIwaC00bC0zIC0yMHEwIC0xIC0zLjUgLTE4dC03LjUgLTI5ek03MzYgOTZxMCAtMTIgLTEwIC0yNGwtMzE5IC0zMTlxLTEwIC05IC0yMyAtOXEtMTIgMCAtMjMgOWwtMzIwIDMyMHEtMTUgMTYgLTcgMzVxOCAyMCAzMCAyMGgxOTJ2MTM3NnEwIDE0IDkgMjN0MjMgOWgxOTJxMTQgMCAyMyAtOXQ5IC0yM3YtMTM3NmgxOTJxMTQgMCAyMyAtOXQ5IC0yM3pNMTY2MSAtMTUwDQp2LTEwNmgtMjg4djEwNmg3NWwtNDcgMTQ0aC0yNDNsLTQ3IC0xNDRoNzV2LTEwNmgtMjg3djEwNmg3MGwyMzAgNjYyaDE2MmwyMzAgLTY2Mmg3MHpNMTU3MiAxMDAxdi0yMzNoLTU4NHY5MGwzNjkgNTI5cTEyIDE4IDIxIDI3bDExIDl2M3EtMiAwIC02LjUgLTAuNXQtNy41IC0wLjVxLTEyIC0zIC0zMCAtM2gtMjMydi0xMTVoLTEyMHYyMjloNTY3di04OWwtMzY5IC01MzBxLTYgLTggLTIxIC0yNmwtMTEgLTEwdi0zbDE0IDNxOSAxIDMwIDFoMjQ4DQp2MTE5aDEyMXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InNvcnRfYnlfYXR0cmlidXRlcyIgdW5pY29kZT0iJiN4ZjE2MDsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik03MzYgOTZxMCAtMTIgLTEwIC0yNGwtMzE5IC0zMTlxLTEwIC05IC0yMyAtOXEtMTIgMCAtMjMgOWwtMzIwIDMyMHEtMTUgMTYgLTcgMzVxOCAyMCAzMCAyMGgxOTJ2MTM3NnEwIDE0IDkgMjN0MjMgOWgxOTJxMTQgMCAyMyAtOXQ5IC0yM3YtMTM3NmgxOTJxMTQgMCAyMyAtOXQ5IC0yM3pNMTc5MiAtMzJ2LTE5MnEwIC0xNCAtOSAtMjN0LTIzIC05aC04MzJxLTE0IDAgLTIzIDl0LTkgMjN2MTkycTAgMTQgOSAyM3QyMyA5aDgzMg0KcTE0IDAgMjMgLTl0OSAtMjN6TTE2MDAgNDgwdi0xOTJxMCAtMTQgLTkgLTIzdC0yMyAtOWgtNjQwcS0xNCAwIC0yMyA5dC05IDIzdjE5MnEwIDE0IDkgMjN0MjMgOWg2NDBxMTQgMCAyMyAtOXQ5IC0yM3pNMTQwOCA5OTJ2LTE5MnEwIC0xNCAtOSAtMjN0LTIzIC05aC00NDhxLTE0IDAgLTIzIDl0LTkgMjN2MTkycTAgMTQgOSAyM3QyMyA5aDQ0OHExNCAwIDIzIC05dDkgLTIzek0xMjE2IDE1MDR2LTE5MnEwIC0xNCAtOSAtMjN0LTIzIC05aC0yNTYNCnEtMTQgMCAtMjMgOXQtOSAyM3YxOTJxMCAxNCA5IDIzdDIzIDloMjU2cTE0IDAgMjMgLTl0OSAtMjN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJzb3J0X2J5X2F0dHJpYnV0ZXNfYWx0IiB1bmljb2RlPSImI3hmMTYxOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTEyMTYgLTMydi0xOTJxMCAtMTQgLTkgLTIzdC0yMyAtOWgtMjU2cS0xNCAwIC0yMyA5dC05IDIzdjE5MnEwIDE0IDkgMjN0MjMgOWgyNTZxMTQgMCAyMyAtOXQ5IC0yM3pNNzM2IDk2cTAgLTEyIC0xMCAtMjRsLTMxOSAtMzE5cS0xMCAtOSAtMjMgLTlxLTEyIDAgLTIzIDlsLTMyMCAzMjBxLTE1IDE2IC03IDM1cTggMjAgMzAgMjBoMTkydjEzNzZxMCAxNCA5IDIzdDIzIDloMTkycTE0IDAgMjMgLTl0OSAtMjN2LTEzNzZoMTkyDQpxMTQgMCAyMyAtOXQ5IC0yM3pNMTQwOCA0ODB2LTE5MnEwIC0xNCAtOSAtMjN0LTIzIC05aC00NDhxLTE0IDAgLTIzIDl0LTkgMjN2MTkycTAgMTQgOSAyM3QyMyA5aDQ0OHExNCAwIDIzIC05dDkgLTIzek0xNjAwIDk5MnYtMTkycTAgLTE0IC05IC0yM3QtMjMgLTloLTY0MHEtMTQgMCAtMjMgOXQtOSAyM3YxOTJxMCAxNCA5IDIzdDIzIDloNjQwcTE0IDAgMjMgLTl0OSAtMjN6TTE3OTIgMTUwNHYtMTkycTAgLTE0IC05IC0yM3QtMjMgLTloLTgzMg0KcS0xNCAwIC0yMyA5dC05IDIzdjE5MnEwIDE0IDkgMjN0MjMgOWg4MzJxMTQgMCAyMyAtOXQ5IC0yM3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InNvcnRfYnlfb3JkZXIiIHVuaWNvZGU9IiYjeGYxNjI7IiANCmQ9Ik0xMzQ2IDIyM3EwIDYzIC00NCAxMTZ0LTEwMyA1M3EtNTIgMCAtODMgLTM3dC0zMSAtOTR0MzYuNSAtOTV0MTA0LjUgLTM4cTUwIDAgODUgMjd0MzUgNjh6TTczNiA5NnEwIC0xMiAtMTAgLTI0bC0zMTkgLTMxOXEtMTAgLTkgLTIzIC05cS0xMiAwIC0yMyA5bC0zMjAgMzIwcS0xNSAxNiAtNyAzNXE4IDIwIDMwIDIwaDE5MnYxMzc2cTAgMTQgOSAyM3QyMyA5aDE5MnExNCAwIDIzIC05dDkgLTIzdi0xMzc2aDE5MnExNCAwIDIzIC05dDkgLTIzDQp6TTE0ODYgMTY1cTAgLTYyIC0xMyAtMTIxLjV0LTQxIC0xMTR0LTY4IC05NS41dC05OC41IC02NS41dC0xMjcuNSAtMjQuNXEtNjIgMCAtMTA4IDE2cS0yNCA4IC00MiAxNWwzOSAxMTNxMTUgLTcgMzEgLTExcTM3IC0xMyA3NSAtMTNxODQgMCAxMzQuNSA1OC41dDY2LjUgMTQ1LjVoLTJxLTIxIC0yMyAtNjEuNSAtMzd0LTg0LjUgLTE0cS0xMDYgMCAtMTczIDcxLjV0LTY3IDE3Mi41cTAgMTA1IDcyIDE3OHQxODEgNzNxMTIzIDAgMjA1IC05NC41DQp0ODIgLTI1Mi41ek0xNDU2IDg4MnYtMTE0aC00Njl2MTE0aDE2N3Y0MzJxMCA3IDAuNSAxOXQwLjUgMTd2MTZoLTJsLTcgLTEycS04IC0xMyAtMjYgLTMxbC02MiAtNThsLTgyIDg2bDE5MiAxODVoMTIzdi02NTRoMTY1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ic29ydF9ieV9vcmRlcl9hbHQiIHVuaWNvZGU9IiYjeGYxNjM7IiANCmQ9Ik0xMzQ2IDEyNDdxMCA2MyAtNDQgMTE2dC0xMDMgNTNxLTUyIDAgLTgzIC0zN3QtMzEgLTk0dDM2LjUgLTk1dDEwNC41IC0zOHE1MCAwIDg1IDI3dDM1IDY4ek03MzYgOTZxMCAtMTIgLTEwIC0yNGwtMzE5IC0zMTlxLTEwIC05IC0yMyAtOXEtMTIgMCAtMjMgOWwtMzIwIDMyMHEtMTUgMTYgLTcgMzVxOCAyMCAzMCAyMGgxOTJ2MTM3NnEwIDE0IDkgMjN0MjMgOWgxOTJxMTQgMCAyMyAtOXQ5IC0yM3YtMTM3NmgxOTJxMTQgMCAyMyAtOQ0KdDkgLTIzek0xNDU2IC0xNDJ2LTExNGgtNDY5djExNGgxNjd2NDMycTAgNyAwLjUgMTl0MC41IDE3djE2aC0ybC03IC0xMnEtOCAtMTMgLTI2IC0zMWwtNjIgLTU4bC04MiA4NmwxOTIgMTg1aDEyM3YtNjU0aDE2NXpNMTQ4NiAxMTg5cTAgLTYyIC0xMyAtMTIxLjV0LTQxIC0xMTR0LTY4IC05NS41dC05OC41IC02NS41dC0xMjcuNSAtMjQuNXEtNjIgMCAtMTA4IDE2cS0yNCA4IC00MiAxNWwzOSAxMTNxMTUgLTcgMzEgLTExcTM3IC0xMyA3NSAtMTMNCnE4NCAwIDEzNC41IDU4LjV0NjYuNSAxNDUuNWgtMnEtMjEgLTIzIC02MS41IC0zN3QtODQuNSAtMTRxLTEwNiAwIC0xNzMgNzEuNXQtNjcgMTcyLjVxMCAxMDUgNzIgMTc4dDE4MSA3M3ExMjMgMCAyMDUgLTk0LjV0ODIgLTI1Mi41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzMzNCIgdW5pY29kZT0iJiN4ZjE2NDsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik0yNTYgMTkycTAgMjYgLTE5IDQ1dC00NSAxOXEtMjcgMCAtNDUuNSAtMTl0LTE4LjUgLTQ1cTAgLTI3IDE4LjUgLTQ1LjV0NDUuNSAtMTguNXEyNiAwIDQ1IDE4LjV0MTkgNDUuNXpNNDE2IDcwNHYtNjQwcTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMjg4cS0yNiAwIC00NSAxOXQtMTkgNDV2NjQwcTAgMjYgMTkgNDV0NDUgMTloMjg4cTI2IDAgNDUgLTE5dDE5IC00NXpNMTYwMCA3MDRxMCAtODYgLTU1IC0xNDlxMTUgLTQ0IDE1IC03Ng0KcTMgLTc2IC00MyAtMTM3cTE3IC01NiAwIC0xMTdxLTE1IC01NyAtNTQgLTk0cTkgLTExMiAtNDkgLTE4MXEtNjQgLTc2IC0xOTcgLTc4aC0zNmgtNzZoLTE3cS02NiAwIC0xNDQgMTUuNXQtMTIxLjUgMjl0LTEyMC41IDM5LjVxLTEyMyA0MyAtMTU4IDQ0cS0yNiAxIC00NSAxOS41dC0xOSA0NC41djY0MXEwIDI1IDE4IDQzLjV0NDMgMjAuNXEyNCAyIDc2IDU5dDEwMSAxMjFxNjggODcgMTAxIDEyMHExOCAxOCAzMSA0OHQxNy41IDQ4LjUNCnQxMy41IDYwLjVxNyAzOSAxMi41IDYxdDE5LjUgNTJ0MzQgNTBxMTkgMTkgNDUgMTlxNDYgMCA4Mi41IC0xMC41dDYwIC0yNnQ0MCAtNDAuNXQyNCAtNDV0MTIgLTUwdDUgLTQ1dDAuNSAtMzlxMCAtMzggLTkuNSAtNzZ0LTE5IC02MHQtMjcuNSAtNTZxLTMgLTYgLTEwIC0xOHQtMTEgLTIydC04IC0yNGgyNzdxNzggMCAxMzUgLTU3dDU3IC0xMzV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfMzM1IiB1bmljb2RlPSImI3hmMTY1OyIgaG9yaXotYWR2LXg9IjE2NjQiIA0KZD0iTTI1NiA5NjBxMCAtMjYgLTE5IC00NXQtNDUgLTE5cS0yNyAwIC00NS41IDE5dC0xOC41IDQ1cTAgMjcgMTguNSA0NS41dDQ1LjUgMTguNXEyNiAwIDQ1IC0xOC41dDE5IC00NS41ek00MTYgNDQ4djY0MHEwIDI2IC0xOSA0NXQtNDUgMTloLTI4OHEtMjYgMCAtNDUgLTE5dC0xOSAtNDV2LTY0MHEwIC0yNiAxOSAtNDV0NDUgLTE5aDI4OHEyNiAwIDQ1IDE5dDE5IDQ1ek0xNTQ1IDU5N3E1NSAtNjEgNTUgLTE0OXEtMSAtNzggLTU3LjUgLTEzNQ0KdC0xMzQuNSAtNTdoLTI3N3E0IC0xNCA4IC0yNHQxMSAtMjJ0MTAgLTE4cTE4IC0zNyAyNyAtNTd0MTkgLTU4LjV0MTAgLTc2LjVxMCAtMjQgLTAuNSAtMzl0LTUgLTQ1dC0xMiAtNTB0LTI0IC00NXQtNDAgLTQwLjV0LTYwIC0yNnQtODIuNSAtMTAuNXEtMjYgMCAtNDUgMTlxLTIwIDIwIC0zNCA1MHQtMTkuNSA1MnQtMTIuNSA2MXEtOSA0MiAtMTMuNSA2MC41dC0xNy41IDQ4LjV0LTMxIDQ4cS0zMyAzMyAtMTAxIDEyMHEtNDkgNjQgLTEwMSAxMjENCnQtNzYgNTlxLTI1IDIgLTQzIDIwLjV0LTE4IDQzLjV2NjQxcTAgMjYgMTkgNDQuNXQ0NSAxOS41cTM1IDEgMTU4IDQ0cTc3IDI2IDEyMC41IDM5LjV0MTIxLjUgMjl0MTQ0IDE1LjVoMTdoNzZoMzZxMTMzIC0yIDE5NyAtNzhxNTggLTY5IDQ5IC0xODFxMzkgLTM3IDU0IC05NHExNyAtNjEgMCAtMTE3cTQ2IC02MSA0MyAtMTM3cTAgLTMyIC0xNSAtNzZ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ5b3V0dWJlX3NpZ24iIHVuaWNvZGU9IiYjeGYxNjY7IiANCmQ9Ik05MTkgMjMzdjE1N3EwIDUwIC0yOSA1MHEtMTcgMCAtMzMgLTE2di0yMjRxMTYgLTE2IDMzIC0xNnEyOSAwIDI5IDQ5ek0xMTAzIDM1NWg2NnYzNHEwIDUxIC0zMyA1MXQtMzMgLTUxdi0zNHpNNTMyIDYyMXYtNzBoLTgwdi00MjNoLTc0djQyM2gtNzh2NzBoMjMyek03MzMgNDk1di0zNjdoLTY3djQwcS0zOSAtNDUgLTc2IC00NXEtMzMgMCAtNDIgMjhxLTYgMTcgLTYgNTR2MjkwaDY2di0yNzBxMCAtMjQgMSAtMjZxMSAtMTUgMTUgLTE1DQpxMjAgMCA0MiAzMXYyODBoNjd6TTk4NSAzODR2LTE0NnEwIC01MiAtNyAtNzNxLTEyIC00MiAtNTMgLTQycS0zNSAwIC02OCA0MXYtMzZoLTY3djQ5M2g2N3YtMTYxcTMyIDQwIDY4IDQwcTQxIDAgNTMgLTQycTcgLTIxIDcgLTc0ek0xMjM2IDI1NXYtOXEwIC0yOSAtMiAtNDNxLTMgLTIyIC0xNSAtNDBxLTI3IC00MCAtODAgLTQwcS01MiAwIC04MSAzOHEtMjEgMjcgLTIxIDg2djEyOXEwIDU5IDIwIDg2cTI5IDM4IDgwIDM4dDc4IC0zOA0KcTIxIC0yOSAyMSAtODZ2LTc2aC0xMzN2LTY1cTAgLTUxIDM0IC01MXEyNCAwIDMwIDI2cTAgMSAwLjUgN3QwLjUgMTYuNXYyMS41aDY4ek03ODUgMTA3OXYtMTU2cTAgLTUxIC0zMiAtNTF0LTMyIDUxdjE1NnEwIDUyIDMyIDUydDMyIC01MnpNMTMxOCAzNjZxMCAxNzcgLTE5IDI2MHEtMTAgNDQgLTQzIDczLjV0LTc2IDM0LjVxLTEzNiAxNSAtNDEyIDE1cS0yNzUgMCAtNDExIC0xNXEtNDQgLTUgLTc2LjUgLTM0LjV0LTQyLjUgLTczLjUNCnEtMjAgLTg3IC0yMCAtMjYwcTAgLTE3NiAyMCAtMjYwcTEwIC00MyA0Mi41IC03M3Q3NS41IC0zNXExMzcgLTE1IDQxMiAtMTV0NDEyIDE1cTQzIDUgNzUuNSAzNXQ0Mi41IDczcTIwIDg0IDIwIDI2MHpNNTYzIDEwMTdsOTAgMjk2aC03NWwtNTEgLTE5NWwtNTMgMTk1aC03OHE3IC0yMyAyMyAtNjlsMjQgLTY5cTM1IC0xMDMgNDYgLTE1OHYtMjAxaDc0djIwMXpNODUyIDkzNnYxMzBxMCA1OCAtMjEgODdxLTI5IDM4IC03OCAzOA0KcS01MSAwIC03OCAtMzhxLTIxIC0yOSAtMjEgLTg3di0xMzBxMCAtNTggMjEgLTg3cTI3IC0zOCA3OCAtMzhxNDkgMCA3OCAzOHEyMSAyNyAyMSA4N3pNMTAzMyA4MTZoNjd2MzcwaC02N3YtMjgzcS0yMiAtMzEgLTQyIC0zMXEtMTUgMCAtMTYgMTZxLTEgMiAtMSAyNnYyNzJoLTY3di0yOTNxMCAtMzcgNiAtNTVxMTEgLTI3IDQzIC0yN3EzNiAwIDc3IDQ1di00MHpNMTUzNiAxMTIwdi05NjBxMCAtMTE5IC04NC41IC0yMDMuNXQtMjAzLjUgLTg0LjUNCmgtOTYwcS0xMTkgMCAtMjAzLjUgODQuNXQtODQuNSAyMDMuNXY5NjBxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDk2MHExMTkgMCAyMDMuNSAtODQuNXQ4NC41IC0yMDMuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InlvdXR1YmUiIHVuaWNvZGU9IiYjeGYxNjc7IiANCmQ9Ik05NzEgMjkydi0yMTFxMCAtNjcgLTM5IC02N3EtMjMgMCAtNDUgMjJ2MzAxcTIyIDIyIDQ1IDIycTM5IDAgMzkgLTY3ek0xMzA5IDI5MXYtNDZoLTkwdjQ2cTAgNjggNDUgNjh0NDUgLTY4ek0zNDMgNTA5aDEwN3Y5NGgtMzEydi05NGgxMDV2LTU2OWgxMDB2NTY5ek02MzEgLTYwaDg5djQ5NGgtODl2LTM3OHEtMzAgLTQyIC01NyAtNDJxLTE4IDAgLTIxIDIxcS0xIDMgLTEgMzV2MzY0aC04OXYtMzkxcTAgLTQ5IDggLTczDQpxMTIgLTM3IDU4IC0zN3E0OCAwIDEwMiA2MXYtNTR6TTEwNjAgODh2MTk3cTAgNzMgLTkgOTlxLTE3IDU2IC03MSA1NnEtNTAgMCAtOTMgLTU0djIxN2gtODl2LTY2M2g4OXY0OHE0NSAtNTUgOTMgLTU1cTU0IDAgNzEgNTVxOSAyNyA5IDEwMHpNMTM5OCA5OHYxM2gtOTFxMCAtNTEgLTIgLTYxcS03IC0zNiAtNDAgLTM2cS00NiAwIC00NiA2OXY4N2gxNzl2MTAzcTAgNzkgLTI3IDExNnEtMzkgNTEgLTEwNiA1MXEtNjggMCAtMTA3IC01MQ0KcS0yOCAtMzcgLTI4IC0xMTZ2LTE3M3EwIC03OSAyOSAtMTE2cTM5IC01MSAxMDggLTUxcTcyIDAgMTA4IDUzcTE4IDI3IDIxIDU0cTIgOSAyIDU4ek03OTAgMTAxMXYyMTBxMCA2OSAtNDMgNjl0LTQzIC02OXYtMjEwcTAgLTcwIDQzIC03MHQ0MyA3MHpNMTUwOSAyNjBxMCAtMjM0IC0yNiAtMzUwcS0xNCAtNTkgLTU4IC05OXQtMTAyIC00NnEtMTg0IC0yMSAtNTU1IC0yMXQtNTU1IDIxcS01OCA2IC0xMDIuNSA0NnQtNTcuNSA5OQ0KcS0yNiAxMTIgLTI2IDM1MHEwIDIzNCAyNiAzNTBxMTQgNTkgNTggOTl0MTAzIDQ3cTE4MyAyMCA1NTQgMjB0NTU1IC0yMHE1OCAtNyAxMDIuNSAtNDd0NTcuNSAtOTlxMjYgLTExMiAyNiAtMzUwek01MTEgMTUzNmgxMDJsLTEyMSAtMzk5di0yNzFoLTEwMHYyNzFxLTE0IDc0IC02MSAyMTJxLTM3IDEwMyAtNjUgMTg3aDEwNmw3MSAtMjYzek04ODEgMTIwM3YtMTc1cTAgLTgxIC0yOCAtMTE4cS0zOCAtNTEgLTEwNiAtNTFxLTY3IDAgLTEwNSA1MQ0KcS0yOCAzOCAtMjggMTE4djE3NXEwIDgwIDI4IDExN3EzOCA1MSAxMDUgNTFxNjggMCAxMDYgLTUxcTI4IC0zNyAyOCAtMTE3ek0xMjE2IDEzNjV2LTQ5OWgtOTF2NTVxLTUzIC02MiAtMTAzIC02MnEtNDYgMCAtNTkgMzdxLTggMjQgLTggNzV2Mzk0aDkxdi0zNjdxMCAtMzMgMSAtMzVxMyAtMjIgMjEgLTIycTI3IDAgNTcgNDN2MzgxaDkxeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ieGluZyIgdW5pY29kZT0iJiN4ZjE2ODsiIGhvcml6LWFkdi14PSIxNDA4IiANCmQ9Ik01OTcgODY5cS0xMCAtMTggLTI1NyAtNDU2cS0yNyAtNDYgLTY1IC00NmgtMjM5cS0yMSAwIC0zMSAxN3QwIDM2bDI1MyA0NDhxMSAwIDAgMWwtMTYxIDI3OXEtMTIgMjIgLTEgMzdxOSAxNSAzMiAxNWgyMzlxNDAgMCA2NiAtNDV6TTE0MDMgMTUxMXExMSAtMTYgMCAtMzdsLTUyOCAtOTM0di0xbDMzNiAtNjE1cTExIC0yMCAxIC0zN3EtMTAgLTE1IC0zMiAtMTVoLTIzOXEtNDIgMCAtNjYgNDVsLTMzOSA2MjJxMTggMzIgNTMxIDk0Mg0KcTI1IDQ1IDY0IDQ1aDI0MXEyMiAwIDMxIC0xNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Inhpbmdfc2lnbiIgdW5pY29kZT0iJiN4ZjE2OTsiIA0KZD0iTTY4NSA3NzFxMCAxIC0xMjYgMjIycS0yMSAzNCAtNTIgMzRoLTE4NHEtMTggMCAtMjYgLTExcS03IC0xMiAxIC0yOWwxMjUgLTIxNnYtMWwtMTk2IC0zNDZxLTkgLTE0IDAgLTI4cTggLTEzIDI0IC0xM2gxODVxMzEgMCA1MCAzNnpNMTMwOSAxMjY4cS03IDEyIC0yNCAxMmgtMTg3cS0zMCAwIC00OSAtMzVsLTQxMSAtNzI5cTEgLTIgMjYyIC00ODFxMjAgLTM1IDUyIC0zNWgxODRxMTggMCAyNSAxMnE4IDEzIC0xIDI4bC0yNjAgNDc2djENCmw0MDkgNzIzcTggMTYgMCAyOHpNMTUzNiAxMTIwdi05NjBxMCAtMTE5IC04NC41IC0yMDMuNXQtMjAzLjUgLTg0LjVoLTk2MHEtMTE5IDAgLTIwMy41IDg0LjV0LTg0LjUgMjAzLjV2OTYwcTAgMTE5IDg0LjUgMjAzLjV0MjAzLjUgODQuNWg5NjBxMTE5IDAgMjAzLjUgLTg0LjV0ODQuNSAtMjAzLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ5b3V0dWJlX3BsYXkiIHVuaWNvZGU9IiYjeGYxNmE7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNNzExIDQwOGw0ODQgMjUwbC00ODQgMjUzdi01MDN6TTg5NiAxMjcwcTE2OCAwIDMyNC41IC00LjV0MjI5LjUgLTkuNWw3MyAtNHExIDAgMTcgLTEuNXQyMyAtM3QyMy41IC00LjV0MjguNSAtOHQyOCAtMTN0MzEgLTE5LjV0MjkgLTI2LjVxNiAtNiAxNS41IC0xOC41dDI5IC01OC41dDI2LjUgLTEwMXE4IC02NCAxMi41IC0xMzYuNXQ1LjUgLTExMy41di00MHYtMTM2cTEgLTE0NSAtMTggLTI5MHEtNyAtNTUgLTI1IC05OS41dC0zMiAtNjEuNQ0KbC0xNCAtMTdxLTE0IC0xNSAtMjkgLTI2LjV0LTMxIC0xOXQtMjggLTEyLjV0LTI4LjUgLTh0LTI0IC00LjV0LTIzIC0zdC0xNi41IC0xLjVxLTI1MSAtMTkgLTYyNyAtMTlxLTIwNyAyIC0zNTkuNSA2LjV0LTIwMC41IDcuNWwtNDkgNGwtMzYgNHEtMzYgNSAtNTQuNSAxMHQtNTEgMjF0LTU2LjUgNDFxLTYgNiAtMTUuNSAxOC41dC0yOSA1OC41dC0yNi41IDEwMXEtOCA2NCAtMTIuNSAxMzYuNXQtNS41IDExMy41djQwdjEzNg0KcS0xIDE0NSAxOCAyOTBxNyA1NSAyNSA5OS41dDMyIDYxLjVsMTQgMTdxMTQgMTUgMjkgMjYuNXQzMSAxOS41dDI4IDEzdDI4LjUgOHQyMy41IDQuNXQyMyAzdDE3IDEuNXEyNTEgMTggNjI3IDE4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZHJvcGJveCIgdW5pY29kZT0iJiN4ZjE2YjsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik00MDIgODI5bDQ5NCAtMzA1bC0zNDIgLTI4NWwtNDkwIDMxOXpNMTM4OCAyNzR2LTEwOGwtNDkwIC0yOTN2LTFsLTEgMWwtMSAtMXYxbC00ODkgMjkzdjEwOGwxNDcgLTk2bDM0MiAyODR2MmwxIC0xbDEgMXYtMmwzNDMgLTI4NHpNNTU0IDE0MThsMzQyIC0yODVsLTQ5NCAtMzA0bC0zMzggMjcwek0xMzkwIDgyOWwzMzggLTI3MWwtNDg5IC0zMTlsLTM0MyAyODV6TTEyMzkgMTQxOGw0ODkgLTMxOWwtMzM4IC0yNzBsLTQ5NCAzMDR6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJzdGFja2V4Y2hhbmdlIiB1bmljb2RlPSImI3hmMTZjOyIgDQpkPSJNMTI4OSAtOTZoLTExMTh2NDgwaC0xNjB2LTY0MGgxNDM4djY0MGgtMTYwdi00ODB6TTM0NyA0MjhsMzMgMTU3bDc4MyAtMTY1bC0zMyAtMTU2ek00NTAgODAybDY3IDE0Nmw3MjUgLTMzOWwtNjcgLTE0NXpNNjUxIDExNThsMTAyIDEyM2w2MTQgLTUxM2wtMTAyIC0xMjN6TTEwNDggMTUzNmw0NzcgLTY0MWwtMTI4IC05NmwtNDc3IDY0MXpNMzMwIDY1djE1OWg4MDB2LTE1OWgtODAweiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iaW5zdGFncmFtIiB1bmljb2RlPSImI3hmMTZkOyIgDQpkPSJNMTAyNCA2NDBxMCAxMDYgLTc1IDE4MXQtMTgxIDc1dC0xODEgLTc1dC03NSAtMTgxdDc1IC0xODF0MTgxIC03NXQxODEgNzV0NzUgMTgxek0xMTYyIDY0MHEwIC0xNjQgLTExNSAtMjc5dC0yNzkgLTExNXQtMjc5IDExNXQtMTE1IDI3OXQxMTUgMjc5dDI3OSAxMTV0Mjc5IC0xMTV0MTE1IC0yNzl6TTEyNzAgMTA1MHEwIC0zOCAtMjcgLTY1dC02NSAtMjd0LTY1IDI3dC0yNyA2NXQyNyA2NXQ2NSAyN3Q2NSAtMjd0MjcgLTY1ek03NjggMTI3MA0KcS03IDAgLTc2LjUgMC41dC0xMDUuNSAwdC05Ni41IC0zdC0xMDMgLTEwdC03MS41IC0xOC41cS01MCAtMjAgLTg4IC01OHQtNTggLTg4cS0xMSAtMjkgLTE4LjUgLTcxLjV0LTEwIC0xMDN0LTMgLTk2LjV0MCAtMTA1LjV0MC41IC03Ni41dC0wLjUgLTc2LjV0MCAtMTA1LjV0MyAtOTYuNXQxMCAtMTAzdDE4LjUgLTcxLjVxMjAgLTUwIDU4IC04OHQ4OCAtNThxMjkgLTExIDcxLjUgLTE4LjV0MTAzIC0xMHQ5Ni41IC0zdDEwNS41IDB0NzYuNSAwLjUNCnQ3Ni41IC0wLjV0MTA1LjUgMHQ5Ni41IDN0MTAzIDEwdDcxLjUgMTguNXE1MCAyMCA4OCA1OHQ1OCA4OHExMSAyOSAxOC41IDcxLjV0MTAgMTAzdDMgOTYuNXQwIDEwNS41dC0wLjUgNzYuNXQwLjUgNzYuNXQwIDEwNS41dC0zIDk2LjV0LTEwIDEwM3QtMTguNSA3MS41cS0yMCA1MCAtNTggODh0LTg4IDU4cS0yOSAxMSAtNzEuNSAxOC41dC0xMDMgMTB0LTk2LjUgM3QtMTA1LjUgMHQtNzYuNSAtMC41ek0xNTM2IDY0MHEwIC0yMjkgLTUgLTMxNw0KcS0xMCAtMjA4IC0xMjQgLTMyMnQtMzIyIC0xMjRxLTg4IC01IC0zMTcgLTV0LTMxNyA1cS0yMDggMTAgLTMyMiAxMjR0LTEyNCAzMjJxLTUgODggLTUgMzE3dDUgMzE3cTEwIDIwOCAxMjQgMzIydDMyMiAxMjRxODggNSAzMTcgNXQzMTcgLTVxMjA4IC0xMCAzMjIgLTEyNHQxMjQgLTMyMnE1IC04OCA1IC0zMTd6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJmbGlja3IiIHVuaWNvZGU9IiYjeGYxNmU7IiANCmQ9Ik0xMjQ4IDE0MDhxMTE5IDAgMjAzLjUgLTg0LjV0ODQuNSAtMjAzLjV2LTk2MHEwIC0xMTkgLTg0LjUgLTIwMy41dC0yMDMuNSAtODQuNWgtOTYwcS0xMTkgMCAtMjAzLjUgODQuNXQtODQuNSAyMDMuNXY5NjBxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDk2MHpNNjk4IDY0MHEwIDg4IC02MiAxNTB0LTE1MCA2MnQtMTUwIC02MnQtNjIgLTE1MHQ2MiAtMTUwdDE1MCAtNjJ0MTUwIDYydDYyIDE1MHpNMTI2MiA2NDBxMCA4OCAtNjIgMTUwDQp0LTE1MCA2MnQtMTUwIC02MnQtNjIgLTE1MHQ2MiAtMTUwdDE1MCAtNjJ0MTUwIDYydDYyIDE1MHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImFkbiIgdW5pY29kZT0iJiN4ZjE3MDsiIA0KZD0iTTc2OCA5MTRsMjAxIC0zMDZoLTQwMnpNMTEzMyAzODRoOTRsLTQ1OSA2OTFsLTQ1OSAtNjkxaDk0bDEwNCAxNjBoNTIyek0xNTM2IDY0MHEwIC0yMDkgLTEwMyAtMzg1LjV0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzdC0yNzkuNSAyNzkuNXQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN0Mzg1LjUgLTEwM3QyNzkuNSAtMjc5LjV0MTAzIC0zODUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImYxNzEiIHVuaWNvZGU9IiYjeGYxNzE7IiBob3Jpei1hZHYteD0iMTQwOCIgDQpkPSJNODE1IDY3N3E4IC02MyAtNTAuNSAtMTAxdC0xMTEuNSAtNnEtMzkgMTcgLTUzLjUgNTh0LTAuNSA4MnQ1MiA1OHEzNiAxOCA3Mi41IDEydDY0IC0zNS41dDI3LjUgLTY3LjV6TTkyNiA2OThxLTE0IDEwNyAtMTEzIDE2NHQtMTk3IDEzcS02MyAtMjggLTEwMC41IC04OC41dC0zNC41IC0xMjkuNXE0IC05MSA3Ny41IC0xNTV0MTY1LjUgLTU2cTkxIDggMTUyIDg0dDUwIDE2OHpNMTE2NSAxMjQwcS0yMCAyNyAtNTYgNDQuNXQtNTggMjINCnQtNzEgMTIuNXEtMjkxIDQ3IC01NjYgLTJxLTQzIC03IC02NiAtMTJ0LTU1IC0yMnQtNTAgLTQzcTMwIC0yOCA3NiAtNDUuNXQ3My41IC0yMnQ4Ny41IC0xMS41cTIyOCAtMjkgNDQ4IC0xcTYzIDggODkuNSAxMnQ3Mi41IDIxLjV0NzUgNDYuNXpNMTIyMiAyMDVxLTggLTI2IC0xNS41IC03Ni41dC0xNCAtODR0LTI4LjUgLTcwdC01OCAtNTYuNXEtODYgLTQ4IC0xODkuNSAtNzEuNXQtMjAyIC0yMnQtMjAxLjUgMTguNXEtNDYgOCAtODEuNSAxOA0KdC03Ni41IDI3dC03MyA0My41dC01MiA2MS41cS0yNSA5NiAtNTcgMjkybDYgMTZsMTggOXEyMjMgLTE0OCA1MDYuNSAtMTQ4dDUwNy41IDE0OHEyMSAtNiAyNCAtMjN0LTUgLTQ1dC04IC0zN3pNMTQwMyAxMTY2cS0yNiAtMTY3IC0xMTEgLTY1NXEtNSAtMzAgLTI3IC01NnQtNDMuNSAtNDB0LTU0LjUgLTMxcS0yNTIgLTEyNiAtNjEwIC04OHEtMjQ4IDI3IC0zOTQgMTM5cS0xNSAxMiAtMjUuNSAyNi41dC0xNyAzNXQtOSAzNHQtNiAzOS41DQp0LTUuNSAzNXEtOSA1MCAtMjYuNSAxNTB0LTI4IDE2MS41dC0yMy41IDE0Ny41dC0yMiAxNThxMyAyNiAxNy41IDQ4LjV0MzEuNSAzNy41dDQ1IDMwdDQ2IDIyLjV0NDggMTguNXExMjUgNDYgMzEzIDY0cTM3OSAzNyA2NzYgLTUwcTE1NSAtNDYgMjE1IC0xMjJxMTYgLTIwIDE2LjUgLTUxdC01LjUgLTU0eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iYml0YnVja2V0X3NpZ24iIHVuaWNvZGU9IiYjeGYxNzI7IiANCmQ9Ik04NDggNjY2cTAgNDMgLTQxIDY2dC03NyAxcS00MyAtMjAgLTQyLjUgLTcyLjV0NDMuNSAtNzAuNXEzOSAtMjMgODEgNHQzNiA3MnpNOTI4IDY4MnE4IC02NiAtMzYgLTEyMXQtMTEwIC02MXQtMTE5IDQwdC01NiAxMTNxLTIgNDkgMjUuNSA5M3Q3Mi41IDY0cTcwIDMxIDE0MS41IC0xMHQ4MS41IC0xMTh6TTExMDAgMTA3M3EtMjAgLTIxIC01My41IC0zNHQtNTMgLTE2dC02My41IC04cS0xNTUgLTIwIC0zMjQgMHEtNDQgNiAtNjMgOS41DQp0LTUyLjUgMTZ0LTU0LjUgMzIuNXExMyAxOSAzNiAzMXQ0MCAxNS41dDQ3IDguNXExOTggMzUgNDA4IDFxMzMgLTUgNTEgLTguNXQ0MyAtMTZ0MzkgLTMxLjV6TTExNDIgMzI3cTAgNyA1LjUgMjYuNXQzIDMydC0xNy41IDE2LjVxLTE2MSAtMTA2IC0zNjUgLTEwNnQtMzY2IDEwNmwtMTIgLTZsLTUgLTEycTI2IC0xNTQgNDEgLTIxMHE0NyAtODEgMjA0IC0xMDhxMjQ5IC00NiA0MjggNTNxMzQgMTkgNDkgNTEuNXQyMi41IDg1LjV0MTIuNSA3MXoNCk0xMjcyIDEwMjBxOSA1MyAtOCA3NXEtNDMgNTUgLTE1NSA4OHEtMjE2IDYzIC00ODcgMzZxLTEzMiAtMTIgLTIyNiAtNDZxLTM4IC0xNSAtNTkuNSAtMjV0LTQ3IC0zNHQtMjkuNSAtNTRxOCAtNjggMTkgLTEzOHQyOSAtMTcxdDI0IC0xMzdxMSAtNSA1IC0zMXQ3IC0zNnQxMiAtMjd0MjIgLTI4cTEwNSAtODAgMjg0IC0xMDBxMjU5IC0yOCA0NDAgNjNxMjQgMTMgMzkuNSAyM3QzMSAyOXQxOS41IDQwcTQ4IDI2NyA4MCA0NzN6TTE1MzYgMTEyMA0Kdi05NjBxMCAtMTE5IC04NC41IC0yMDMuNXQtMjAzLjUgLTg0LjVoLTk2MHEtMTE5IDAgLTIwMy41IDg0LjV0LTg0LjUgMjAzLjV2OTYwcTAgMTE5IDg0LjUgMjAzLjV0MjAzLjUgODQuNWg5NjBxMTE5IDAgMjAzLjUgLTg0LjV0ODQuNSAtMjAzLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ0dW1ibHIiIHVuaWNvZGU9IiYjeGYxNzM7IiBob3Jpei1hZHYteD0iMTAyNCIgDQpkPSJNOTQ0IDIwN2w4MCAtMjM3cS0yMyAtMzUgLTExMSAtNjZ0LTE3NyAtMzJxLTEwNCAtMiAtMTkwLjUgMjZ0LTE0Mi41IDc0dC05NSAxMDZ0LTU1LjUgMTIwdC0xNi41IDExOHY1NDRoLTE2OHYyMTVxNzIgMjYgMTI5IDY5LjV0OTEgOTB0NTggMTAydDM0IDk5dDE1IDg4LjVxMSA1IDQuNSA4LjV0Ny41IDMuNWgyNDR2LTQyNGgzMzN2LTI1MmgtMzM0di01MThxMCAtMzAgNi41IC01NnQyMi41IC01Mi41dDQ5LjUgLTQxLjV0ODEuNSAtMTQNCnE3OCAyIDEzNCAyOXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InR1bWJscl9zaWduIiB1bmljb2RlPSImI3hmMTc0OyIgDQpkPSJNMTEzNiA3NWwtNjIgMTgzcS00NCAtMjIgLTEwMyAtMjJxLTM2IC0xIC02MiAxMC41dC0zOC41IDMxLjV0LTE3LjUgNDAuNXQtNSA0My41djM5OGgyNTd2MTk0aC0yNTZ2MzI2aC0xODhxLTggMCAtOSAtMTBxLTUgLTQ0IC0xNy41IC04N3QtMzkgLTk1dC03NyAtOTV0LTExOC41IC02OHYtMTY1aDEzMHYtNDE4cTAgLTU3IDIxLjUgLTExNXQ2NSAtMTExdDEyMSAtODUuNXQxNzYuNSAtMzAuNXE2OSAxIDEzNi41IDI1dDg1LjUgNTB6DQpNMTUzNiAxMTIwdi05NjBxMCAtMTE5IC04NC41IC0yMDMuNXQtMjAzLjUgLTg0LjVoLTk2MHEtMTE5IDAgLTIwMy41IDg0LjV0LTg0LjUgMjAzLjV2OTYwcTAgMTE5IDg0LjUgMjAzLjV0MjAzLjUgODQuNWg5NjBxMTE5IDAgMjAzLjUgLTg0LjV0ODQuNSAtMjAzLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJsb25nX2Fycm93X2Rvd24iIHVuaWNvZGU9IiYjeGYxNzU7IiBob3Jpei1hZHYteD0iNzY4IiANCmQ9Ik03NjUgMjM3cTggLTE5IC01IC0zNWwtMzUwIC0zODRxLTEwIC0xMCAtMjMgLTEwcS0xNCAwIC0yNCAxMGwtMzU1IDM4NHEtMTMgMTYgLTUgMzVxOSAxOSAyOSAxOWgyMjR2MTI0OHEwIDE0IDkgMjN0MjMgOWgxOTJxMTQgMCAyMyAtOXQ5IC0yM3YtMTI0OGgyMjRxMjEgMCAyOSAtMTl6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJsb25nX2Fycm93X3VwIiB1bmljb2RlPSImI3hmMTc2OyIgaG9yaXotYWR2LXg9Ijc2OCIgDQpkPSJNNzY1IDEwNDNxLTkgLTE5IC0yOSAtMTloLTIyNHYtMTI0OHEwIC0xNCAtOSAtMjN0LTIzIC05aC0xOTJxLTE0IDAgLTIzIDl0LTkgMjN2MTI0OGgtMjI0cS0yMSAwIC0yOSAxOXQ1IDM1bDM1MCAzODRxMTAgMTAgMjMgMTBxMTQgMCAyNCAtMTBsMzU1IC0zODRxMTMgLTE2IDUgLTM1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ibG9uZ19hcnJvd19sZWZ0IiB1bmljb2RlPSImI3hmMTc3OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE3OTIgNzM2di0xOTJxMCAtMTQgLTkgLTIzdC0yMyAtOWgtMTI0OHYtMjI0cTAgLTIxIC0xOSAtMjl0LTM1IDVsLTM4NCAzNTBxLTEwIDEwIC0xMCAyM3EwIDE0IDEwIDI0bDM4NCAzNTRxMTYgMTQgMzUgNnExOSAtOSAxOSAtMjl2LTIyNGgxMjQ4cTE0IDAgMjMgLTl0OSAtMjN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJsb25nX2Fycm93X3JpZ2h0IiB1bmljb2RlPSImI3hmMTc4OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE3MjggNjQzcTAgLTE0IC0xMCAtMjRsLTM4NCAtMzU0cS0xNiAtMTQgLTM1IC02cS0xOSA5IC0xOSAyOXYyMjRoLTEyNDhxLTE0IDAgLTIzIDl0LTkgMjN2MTkycTAgMTQgOSAyM3QyMyA5aDEyNDh2MjI0cTAgMjEgMTkgMjl0MzUgLTVsMzg0IC0zNTBxMTAgLTEwIDEwIC0yM3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImFwcGxlIiB1bmljb2RlPSImI3hmMTc5OyIgaG9yaXotYWR2LXg9IjE0MDgiIA0KZD0iTTEzOTMgMzIxcS0zOSAtMTI1IC0xMjMgLTI1MHEtMTI5IC0xOTYgLTI1NyAtMTk2cS00OSAwIC0xNDAgMzJxLTg2IDMyIC0xNTEgMzJxLTYxIDAgLTE0MiAtMzNxLTgxIC0zNCAtMTMyIC0zNHEtMTUyIDAgLTMwMSAyNTlxLTE0NyAyNjEgLTE0NyA1MDNxMCAyMjggMTEzIDM3NHExMTMgMTQ0IDI4NCAxNDRxNzIgMCAxNzcgLTMwcTEwNCAtMzAgMTM4IC0zMHE0NSAwIDE0MyAzNHExMDIgMzQgMTczIDM0cTExOSAwIDIxMyAtNjUNCnE1MiAtMzYgMTA0IC0xMDBxLTc5IC02NyAtMTE0IC0xMThxLTY1IC05NCAtNjUgLTIwN3EwIC0xMjQgNjkgLTIyM3QxNTggLTEyNnpNMTAxNyAxNDk0cTAgLTYxIC0yOSAtMTM2cS0zMCAtNzUgLTkzIC0xMzhxLTU0IC01NCAtMTA4IC03MnEtMzcgLTExIC0xMDQgLTE3cTMgMTQ5IDc4IDI1N3E3NCAxMDcgMjUwIDE0OHExIC0zIDIuNSAtMTF0Mi41IC0xMXEwIC00IDAuNSAtMTB0MC41IC0xMHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9IndpbmRvd3MiIHVuaWNvZGU9IiYjeGYxN2E7IiBob3Jpei1hZHYteD0iMTY2NCIgDQpkPSJNNjgyIDUzMHYtNjUxbC02ODIgOTR2NTU3aDY4MnpNNjgyIDEyNzN2LTY1OWgtNjgydjU2NXpNMTY2NCA1MzB2LTc4NmwtOTA3IDEyNXY2NjFoOTA3ek0xNjY0IDE0MDh2LTc5NGgtOTA3djY2OXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImFuZHJvaWQiIHVuaWNvZGU9IiYjeGYxN2I7IiBob3Jpei1hZHYteD0iMTQwOCIgDQpkPSJNNDkzIDEwNTNxMTYgMCAyNy41IDExLjV0MTEuNSAyNy41dC0xMS41IDI3LjV0LTI3LjUgMTEuNXQtMjcgLTExLjV0LTExIC0yNy41dDExIC0yNy41dDI3IC0xMS41ek05MTUgMTA1M3ExNiAwIDI3IDExLjV0MTEgMjcuNXQtMTEgMjcuNXQtMjcgMTEuNXQtMjcuNSAtMTEuNXQtMTEuNSAtMjcuNXQxMS41IC0yNy41dDI3LjUgLTExLjV6TTEwMyA4NjlxNDIgMCA3MiAtMzB0MzAgLTcydi00MzBxMCAtNDMgLTI5LjUgLTczdC03Mi41IC0zMA0KdC03MyAzMHQtMzAgNzN2NDMwcTAgNDIgMzAgNzJ0NzMgMzB6TTExNjMgODUwdi02NjZxMCAtNDYgLTMyIC03OHQtNzcgLTMyaC03NXYtMjI3cTAgLTQzIC0zMCAtNzN0LTczIC0zMHQtNzMgMzB0LTMwIDczdjIyN2gtMTM4di0yMjdxMCAtNDMgLTMwIC03M3QtNzMgLTMwcS00MiAwIC03MiAzMHQtMzAgNzNsLTEgMjI3aC03NHEtNDYgMCAtNzggMzJ0LTMyIDc4djY2Nmg5MTh6TTkzMSAxMjU1cTEwNyAtNTUgMTcxIC0xNTMuNXQ2NCAtMjE1LjUNCmgtOTI1cTAgMTE3IDY0IDIxNS41dDE3MiAxNTMuNWwtNzEgMTMxcS03IDEzIDUgMjBxMTMgNiAyMCAtNmw3MiAtMTMycTk1IDQyIDIwMSA0MnQyMDEgLTQybDcyIDEzMnE3IDEyIDIwIDZxMTIgLTcgNSAtMjB6TTE0MDggNzY3di00MzBxMCAtNDMgLTMwIC03M3QtNzMgLTMwcS00MiAwIC03MiAzMHQtMzAgNzN2NDMwcTAgNDMgMzAgNzIuNXQ3MiAyOS41cTQzIDAgNzMgLTI5LjV0MzAgLTcyLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJsaW51eCIgdW5pY29kZT0iJiN4ZjE3YzsiIA0KZD0iTTY2MyAxMTI1cS0xMSAtMSAtMTUuNSAtMTAuNXQtOC41IC05LjVxLTUgLTEgLTUgNXEwIDEyIDE5IDE1aDEwek03NTAgMTExMXEtNCAtMSAtMTEuNSA2LjV0LTE3LjUgNC41cTI0IDExIDMyIC0ycTMgLTYgLTMgLTl6TTM5OSA2ODRxLTQgMSAtNiAtM3QtNC41IC0xMi41dC01LjUgLTEzLjV0LTEwIC0xM3EtMTAgLTExIC0xIC0xMnE0IC0xIDEyLjUgN3QxMi41IDE4cTEgMyAyIDd0MiA2dDEuNSA0LjV0MC41IDR2M3QtMSAyLjV0LTMgMnoNCk0xMjU0IDMyNXEwIDE4IC01NSA0MnE0IDE1IDcuNSAyNy41dDUgMjZ0MyAyMS41dDAuNSAyMi41dC0xIDE5LjV0LTMuNSAyMnQtNCAyMC41dC01IDI1dC01LjUgMjYuNXEtMTAgNDggLTQ3IDEwM3QtNzIgNzVxMjQgLTIwIDU3IC04M3E4NyAtMTYyIDU0IC0yNzhxLTExIC00MCAtNTAgLTQycS0zMSAtNCAtMzguNSAxOC41dC04IDgzLjV0LTExLjUgMTA3cS05IDM5IC0xOS41IDY5dC0xOS41IDQ1LjV0LTE1LjUgMjQuNXQtMTMgMTV0LTcuNSA3DQpxLTE0IDYyIC0zMSAxMDN0LTI5LjUgNTZ0LTIzLjUgMzN0LTE1IDQwcS00IDIxIDYgNTMuNXQ0LjUgNDkuNXQtNDQuNSAyNXEtMTUgMyAtNDQuNSAxOHQtMzUuNSAxNnEtOCAxIC0xMSAyNnQ4IDUxdDM2IDI3cTM3IDMgNTEgLTMwdDQgLTU4cS0xMSAtMTkgLTIgLTI2LjV0MzAgLTAuNXExMyA0IDEzIDM2djM3cS01IDMwIC0xMy41IDUwdC0yMSAzMC41dC0yMy41IDE1dC0yNyA3LjVxLTEwNyAtOCAtODkgLTEzNHEwIC0xNSAtMSAtMTUNCnEtOSA5IC0yOS41IDEwLjV0LTMzIC0wLjV0LTE1LjUgNXExIDU3IC0xNiA5MHQtNDUgMzRxLTI3IDEgLTQxLjUgLTI3LjV0LTE2LjUgLTU5LjVxLTEgLTE1IDMuNSAtMzd0MTMgLTM3LjV0MTUuNSAtMTMuNXExMCAzIDE2IDE0cTQgOSAtNyA4cS03IDAgLTE1LjUgMTQuNXQtOS41IDMzLjVxLTEgMjIgOSAzN3QzNCAxNHExNyAwIDI3IC0yMXQ5LjUgLTM5dC0xLjUgLTIycS0yMiAtMTUgLTMxIC0yOXEtOCAtMTIgLTI3LjUgLTIzLjUNCnQtMjAuNSAtMTIuNXEtMTMgLTE0IC0xNS41IC0yN3Q3LjUgLTE4cTE0IC04IDI1IC0xOS41dDE2IC0xOXQxOC41IC0xM3QzNS41IC02LjVxNDcgLTIgMTAyIDE1cTIgMSAyMyA3dDM0LjUgMTAuNXQyOS41IDEzdDIxIDE3LjVxOSAxNCAyMCA4cTUgLTMgNi41IC04LjV0LTMgLTEydC0xNi41IC05LjVxLTIwIC02IC01Ni41IC0yMS41dC00NS41IC0xOS41cS00NCAtMTkgLTcwIC0yM3EtMjUgLTUgLTc5IDJxLTEwIDIgLTkgLTJ0MTcgLTE5DQpxMjUgLTIzIDY3IC0yMnExNyAxIDM2IDd0MzYgMTR0MzMuNSAxNy41dDMwIDE3dDI0LjUgMTJ0MTcuNSAyLjV0OC41IC0xMXEwIC0yIC0xIC00LjV0LTQgLTV0LTYgLTQuNXQtOC41IC01dC05IC00LjV0LTEwIC01dC05LjUgLTQuNXEtMjggLTE0IC02Ny41IC00NHQtNjYuNSAtNDN0LTQ5IC0xcS0yMSAxMSAtNjMgNzNxLTIyIDMxIC0yNSAyMnEtMSAtMyAtMSAtMTBxMCAtMjUgLTE1IC01Ni41dC0yOS41IC01NS41dC0yMSAtNTh0MTEuNSAtNjMNCnEtMjMgLTYgLTYyLjUgLTkwdC00Ny41IC0xNDFxLTIgLTE4IC0xLjUgLTY5dC01LjUgLTU5cS04IC0yNCAtMjkgLTNxLTMyIDMxIC0zNiA5NHEtMiAyOCA0IDU2cTQgMTkgLTEgMThxLTIgLTEgLTQgLTVxLTM2IC02NSAxMCAtMTY2cTUgLTEyIDI1IC0yOHQyNCAtMjBxMjAgLTIzIDEwNCAtOTAuNXQ5MyAtNzYuNXExNiAtMTUgMTcuNSAtMzh0LTE0IC00M3QtNDUuNSAtMjNxOCAtMTUgMjkgLTQ0LjV0MjggLTU0dDcgLTcwLjVxNDYgMjQgNyA5Mg0KcS00IDggLTEwLjUgMTZ0LTkuNSAxMnQtMiA2cTMgNSAxMyA5LjV0MjAgLTIuNXE0NiAtNTIgMTY2IC0zNnExMzMgMTUgMTc3IDg3cTIzIDM4IDM0IDMwcTEyIC02IDEwIC01MnEtMSAtMjUgLTIzIC05MnEtOSAtMjMgLTYgLTM3LjV0MjQgLTE1LjVxMyAxOSAxNC41IDc3dDEzLjUgOTBxMiAyMSAtNi41IDczLjV0LTcuNSA5N3QyMyA3MC41cTE1IDE4IDUxIDE4cTEgMzcgMzQuNSA1M3Q3Mi41IDEwLjV0NjAgLTIyLjV6TTYyNiAxMTUyDQpxMyAxNyAtMi41IDMwdC0xMS41IDE1cS05IDIgLTkgLTdxMiAtNSA1IC02cTEwIDAgNyAtMTVxLTMgLTIwIDggLTIwcTMgMCAzIDN6TTEwNDUgOTU1cS0yIDggLTYuNSAxMS41dC0xMyA1dC0xNC41IDUuNXEtNSAzIC05LjUgOHQtNyA4dC01LjUgNi41dC00IDR0LTQgLTEuNXEtMTQgLTE2IDcgLTQzLjV0MzkgLTMxLjVxOSAtMSAxNC41IDh0My41IDIwek04NjcgMTE2OHEwIDExIC01IDE5LjV0LTExIDEyLjV0LTkgM3EtNiAwIC04IC0ydDAgLTQNCnQ1IC0zcTE0IC00IDE4IC0zMXEwIC0zIDggMnEyIDIgMiAzek05MjEgMTQwMXEwIDIgLTIuNSA1dC05IDd0LTkuNSA2cS0xNSAxNSAtMjQgMTVxLTkgLTEgLTExLjUgLTcuNXQtMSAtMTN0LTAuNSAtMTIuNXEtMSAtNCAtNiAtMTAuNXQtNiAtOXQzIC04LjVxNCAtMyA4IDB0MTEgOXQxNSA5cTEgMSA5IDF0MTUgMnQ5IDd6TTE0ODYgNjBxMjAgLTEyIDMxIC0yNC41dDEyIC0yNHQtMi41IC0yMi41dC0xNS41IC0yMnQtMjMuNSAtMTkuNQ0KdC0zMCAtMTguNXQtMzEuNSAtMTYuNXQtMzIgLTE1LjV0LTI3IC0xM3EtMzggLTE5IC04NS41IC01NnQtNzUuNSAtNjRxLTE3IC0xNiAtNjggLTE5LjV0LTg5IDE0LjVxLTE4IDkgLTI5LjUgMjMuNXQtMTYuNSAyNS41dC0yMiAxOS41dC00NyA5LjVxLTQ0IDEgLTEzMCAxcS0xOSAwIC01NyAtMS41dC01OCAtMi41cS00NCAtMSAtNzkuNSAtMTV0LTUzLjUgLTMwdC00My41IC0yOC41dC01My41IC0xMS41cS0yOSAxIC0xMTEgMzF0LTE0NiA0Mw0KcS0xOSA0IC01MSA5LjV0LTUwIDl0LTM5LjUgOS41dC0zMy41IDE0LjV0LTE3IDE5LjVxLTEwIDIzIDcgNjYuNXQxOCA1NC41cTEgMTYgLTQgNDB0LTEwIDQyLjV0LTQuNSAzNi41dDEwLjUgMjdxMTQgMTIgNTcgMTR0NjAgMTJxMzAgMTggNDIgMzV0MTIgNTFxMjEgLTczIC0zMiAtMTA2cS0zMiAtMjAgLTgzIC0xNXEtMzQgMyAtNDMgLTEwcS0xMyAtMTUgNSAtNTdxMiAtNiA4IC0xOHQ4LjUgLTE4dDQuNSAtMTd0MSAtMjJxMCAtMTUgLTE3IC00OQ0KdC0xNCAtNDhxMyAtMTcgMzcgLTI2cTIwIC02IDg0LjUgLTE4LjV0OTkuNSAtMjAuNXEyNCAtNiA3NCAtMjJ0ODIuNSAtMjN0NTUuNSAtNHE0MyA2IDY0LjUgMjh0MjMgNDh0LTcuNSA1OC41dC0xOSA1MnQtMjAgMzYuNXEtMTIxIDE5MCAtMTY5IDI0MnEtNjggNzQgLTExMyA0MHEtMTEgLTkgLTE1IDE1cS0zIDE2IC0yIDM4cTEgMjkgMTAgNTJ0MjQgNDd0MjIgNDJxOCAyMSAyNi41IDcydDI5LjUgNzh0MzAgNjF0MzkgNTQNCnExMTAgMTQzIDEyNCAxOTVxLTEyIDExMiAtMTYgMzEwcS0yIDkwIDI0IDE1MS41dDEwNiAxMDQuNXEzOSAyMSAxMDQgMjFxNTMgMSAxMDYgLTEzLjV0ODkgLTQxLjVxNTcgLTQyIDkxLjUgLTEyMS41dDI5LjUgLTE0Ny41cS01IC05NSAzMCAtMjE0cTM0IC0xMTMgMTMzIC0yMThxNTUgLTU5IDk5LjUgLTE2M3Q1OS41IC0xOTFxOCAtNDkgNSAtODQuNXQtMTIgLTU1LjV0LTIwIC0yMnEtMTAgLTIgLTIzLjUgLTE5dC0yNyAtMzUuNQ0KdC00MC41IC0zMy41dC02MSAtMTRxLTE4IDEgLTMxLjUgNXQtMjIuNSAxMy41dC0xMy41IDE1LjV0LTExLjUgMjAuNXQtOSAxOS41cS0yMiAzNyAtNDEgMzB0LTI4IC00OXQ3IC05N3EyMCAtNzAgMSAtMTk1cS0xMCAtNjUgMTggLTEwMC41dDczIC0zM3Q4NSAzNS41cTU5IDQ5IDg5LjUgNjYuNXQxMDMuNSA0Mi41cTUzIDE4IDc3IDM2LjV0MTguNSAzNC41dC0yNSAyOC41dC01MS41IDIzLjVxLTMzIDExIC00OS41IDQ4dC0xNSA3Mi41DQp0MTUuNSA0Ny41cTEgLTMxIDggLTU2LjV0MTQuNSAtNDAuNXQyMC41IC0yOC41dDIxIC0xOXQyMS41IC0xM3QxNi41IC05LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJkcmliYmxlIiB1bmljb2RlPSImI3hmMTdkOyIgDQpkPSJNMTAyNCAzNnEtNDIgMjQxIC0xNDAgNDk4aC0ybC0yIC0xcS0xNiAtNiAtNDMgLTE2LjV0LTEwMSAtNDl0LTEzNyAtODJ0LTEzMSAtMTE0LjV0LTEwMyAtMTQ4bC0xNSAxMXExODQgLTE1MCA0MTggLTE1MHExMzIgMCAyNTYgNTJ6TTgzOSA2NDNxLTIxIDQ5IC01MyAxMTFxLTMxMSAtOTMgLTY3MyAtOTNxLTEgLTcgLTEgLTIxcTAgLTEyNCA0NCAtMjM2LjV0MTI0IC0yMDEuNXE1MCA4OSAxMjMuNSAxNjYuNXQxNDIuNSAxMjQuNXQxMzAuNSA4MQ0KdDk5LjUgNDhsMzcgMTNxNCAxIDEzIDMuNXQxMyA0LjV6TTczMiA4NTVxLTEyMCAyMTMgLTI0NCAzNzhxLTEzOCAtNjUgLTIzNCAtMTg2dC0xMjggLTI3MnEzMDIgMCA2MDYgODB6TTE0MTYgNTM2cS0yMTAgNjAgLTQwOSAyOXE4NyAtMjM5IDEyOCAtNDY5cTExMSA3NSAxODUgMTg5LjV0OTYgMjUwLjV6TTYxMSAxMjc3cS0xIDAgLTIgLTFxMSAxIDIgMXpNMTIwMSAxMTMycS0xODUgMTY0IC00MzMgMTY0cS03NiAwIC0xNTUgLTE5DQpxMTMxIC0xNzAgMjQ2IC0zODJxNjkgMjYgMTMwIDYwLjV0OTYuNSA2MS41dDY1LjUgNTd0MzcuNSA0MC41ek0xNDI0IDY0N3EtMyAyMzIgLTE0OSA0MTBsLTEgLTFxLTkgLTEyIC0xOSAtMjQuNXQtNDMuNSAtNDQuNXQtNzEgLTYwLjV0LTEwMCAtNjV0LTEzMS41IC02NC41cTI1IC01MyA0NCAtOTVxMiAtNSA2LjUgLTE3dDcuNSAtMTdxMzYgNSA3NC41IDd0NzMuNSAydDY5IC0xLjV0NjQgLTR0NTYuNSAtNS41dDQ4IC02LjV0MzYuNSAtNg0KdDI1IC00LjV6TTE1MzYgNjQwcTAgLTIwOSAtMTAzIC0zODUuNXQtMjc5LjUgLTI3OS41dC0zODUuNSAtMTAzdC0zODUuNSAxMDN0LTI3OS41IDI3OS41dC0xMDMgMzg1LjV0MTAzIDM4NS41dDI3OS41IDI3OS41dDM4NS41IDEwM3QzODUuNSAtMTAzdDI3OS41IC0yNzkuNXQxMDMgLTM4NS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ic2t5cGUiIHVuaWNvZGU9IiYjeGYxN2U7IiANCmQ9Ik0xMTczIDQ3M3EwIDUwIC0xOS41IDkxLjV0LTQ4LjUgNjguNXQtNzMgNDl0LTgyLjUgMzR0LTg3LjUgMjNsLTEwNCAyNHEtMzAgNyAtNDQgMTAuNXQtMzUgMTEuNXQtMzAgMTZ0LTE2LjUgMjF0LTcuNSAzMHEwIDc3IDE0NCA3N3E0MyAwIDc3IC0xMnQ1NCAtMjguNXQzOCAtMzMuNXQ0MCAtMjl0NDggLTEycTQ3IDAgNzUuNSAzMnQyOC41IDc3cTAgNTUgLTU2IDk5LjV0LTE0MiA2Ny41dC0xODIgMjNxLTY4IDAgLTEzMiAtMTUuNQ0KdC0xMTkuNSAtNDd0LTg5IC04N3QtMzMuNSAtMTI4LjVxMCAtNjEgMTkgLTEwNi41dDU2IC03NS41dDgwIC00OC41dDEwMyAtMzIuNWwxNDYgLTM2cTkwIC0yMiAxMTIgLTM2cTMyIC0yMCAzMiAtNjBxMCAtMzkgLTQwIC02NC41dC0xMDUgLTI1LjVxLTUxIDAgLTkxLjUgMTZ0LTY1IDM4LjV0LTQ1LjUgNDV0LTQ2IDM4LjV0LTU0IDE2cS01MCAwIC03NS41IC0zMHQtMjUuNSAtNzVxMCAtOTIgMTIyIC0xNTcuNXQyOTEgLTY1LjUNCnE3MyAwIDE0MCAxOC41dDEyMi41IDUzLjV0ODguNSA5My41dDMzIDEzMS41ek0xNTM2IDI1NnEwIC0xNTkgLTExMi41IC0yNzEuNXQtMjcxLjUgLTExMi41cS0xMzAgMCAtMjM0IDgwcS03NyAtMTYgLTE1MCAtMTZxLTE0MyAwIC0yNzMuNSA1NS41dC0yMjUgMTUwdC0xNTAgMjI1dC01NS41IDI3My41cTAgNzMgMTYgMTUwcS04MCAxMDQgLTgwIDIzNHEwIDE1OSAxMTIuNSAyNzEuNXQyNzEuNSAxMTIuNXExMzAgMCAyMzQgLTgwDQpxNzcgMTYgMTUwIDE2cTE0MyAwIDI3My41IC01NS41dDIyNSAtMTUwdDE1MCAtMjI1dDU1LjUgLTI3My41cTAgLTczIC0xNiAtMTUwcTgwIC0xMDQgODAgLTIzNHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImZvdXJzcXVhcmUiIHVuaWNvZGU9IiYjeGYxODA7IiBob3Jpei1hZHYteD0iMTI4MCIgDQpkPSJNMTAwMCAxMTAybDM3IDE5NHE1IDIzIC05IDQwdC0zNSAxN2gtNzEycS0yMyAwIC0zOC41IC0xN3QtMTUuNSAtMzd2LTExMDFxMCAtNyA2IC0xbDI5MSAzNTJxMjMgMjYgMzggMzMuNXQ0OCA3LjVoMjM5cTIyIDAgMzcgMTQuNXQxOCAyOS41cTI0IDEzMCAzNyAxOTFxNCAyMSAtMTEuNSA0MHQtMzYuNSAxOWgtMjk0cS0yOSAwIC00OCAxOXQtMTkgNDh2NDJxMCAyOSAxOSA0Ny41dDQ4IDE4LjVoMzQ2cTE4IDAgMzUgMTMuNXQyMCAyOS41eg0KTTEyMjcgMTMyNHEtMTUgLTczIC01My41IC0yNjYuNXQtNjkuNSAtMzUwdC0zNSAtMTczLjVxLTYgLTIyIC05IC0zMi41dC0xNCAtMzIuNXQtMjQuNSAtMzN0LTM4LjUgLTIxdC01OCAtMTBoLTI3MXEtMTMgMCAtMjIgLTEwcS04IC05IC00MjYgLTQ5NHEtMjIgLTI1IC01OC41IC0yOC41dC00OC41IDUuNXEtNTUgMjIgLTU1IDk4djE0MTBxMCA1NSAzOCAxMDIuNXQxMjAgNDcuNWg4ODhxOTUgMCAxMjcgLTUzdDEwIC0xNTl6TTEyMjcgMTMyNA0KbC0xNTggLTc5MHE0IDE3IDM1IDE3My41dDY5LjUgMzUwdDUzLjUgMjY2LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ0cmVsbG8iIHVuaWNvZGU9IiYjeGYxODE7IiANCmQ9Ik03MDQgMTkydjEwMjRxMCAxNCAtOSAyM3QtMjMgOWgtNDgwcS0xNCAwIC0yMyAtOXQtOSAtMjN2LTEwMjRxMCAtMTQgOSAtMjN0MjMgLTloNDgwcTE0IDAgMjMgOXQ5IDIzek0xMzc2IDU3NnY2NDBxMCAxNCAtOSAyM3QtMjMgOWgtNDgwcS0xNCAwIC0yMyAtOXQtOSAtMjN2LTY0MHEwIC0xNCA5IC0yM3QyMyAtOWg0ODBxMTQgMCAyMyA5dDkgMjN6TTE1MzYgMTM0NHYtMTQwOHEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTE0MDgNCnEtMjYgMCAtNDUgMTl0LTE5IDQ1djE0MDhxMCAyNiAxOSA0NXQ0NSAxOWgxNDA4cTI2IDAgNDUgLTE5dDE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImZlbWFsZSIgdW5pY29kZT0iJiN4ZjE4MjsiIGhvcml6LWFkdi14PSIxMjgwIiANCmQ9Ik0xMjgwIDQ4MHEwIC00MCAtMjggLTY4dC02OCAtMjhxLTUxIDAgLTgwIDQzbC0yMjcgMzQxaC00NXYtMTMybDI0NyAtNDExcTkgLTE1IDkgLTMzcTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTkydi0yNzJxMCAtNDYgLTMzIC03OXQtNzkgLTMzaC0xNjBxLTQ2IDAgLTc5IDMzdC0zMyA3OXYyNzJoLTE5MnEtMjYgMCAtNDUgMTl0LTE5IDQ1cTAgMTggOSAzM2wyNDcgNDExdjEzMmgtNDVsLTIyNyAtMzQxcS0yOSAtNDMgLTgwIC00Mw0KcS00MCAwIC02OCAyOHQtMjggNjhxMCAyOSAxNiA1M2wyNTYgMzg0cTczIDEwNyAxNzYgMTA3aDM4NHExMDMgMCAxNzYgLTEwN2wyNTYgLTM4NHExNiAtMjQgMTYgLTUzek04NjQgMTI4MHEwIC05MyAtNjUuNSAtMTU4LjV0LTE1OC41IC02NS41dC0xNTguNSA2NS41dC02NS41IDE1OC41dDY1LjUgMTU4LjV0MTU4LjUgNjUuNXQxNTguNSAtNjUuNXQ2NS41IC0xNTguNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Im1hbGUiIHVuaWNvZGU9IiYjeGYxODM7IiBob3Jpei1hZHYteD0iMTAyNCIgDQpkPSJNMTAyNCA4MzJ2LTQxNnEwIC00MCAtMjggLTY4dC02OCAtMjh0LTY4IDI4dC0yOCA2OHYzNTJoLTY0di05MTJxMCAtNDYgLTMzIC03OXQtNzkgLTMzdC03OSAzM3QtMzMgNzl2NDY0aC02NHYtNDY0cTAgLTQ2IC0zMyAtNzl0LTc5IC0zM3QtNzkgMzN0LTMzIDc5djkxMmgtNjR2LTM1MnEwIC00MCAtMjggLTY4dC02OCAtMjh0LTY4IDI4dC0yOCA2OHY0MTZxMCA4MCA1NiAxMzZ0MTM2IDU2aDY0MHE4MCAwIDEzNiAtNTZ0NTYgLTEzNnoNCk03MzYgMTI4MHEwIC05MyAtNjUuNSAtMTU4LjV0LTE1OC41IC02NS41dC0xNTguNSA2NS41dC02NS41IDE1OC41dDY1LjUgMTU4LjV0MTU4LjUgNjUuNXQxNTguNSAtNjUuNXQ2NS41IC0xNTguNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImdpdHRpcCIgdW5pY29kZT0iJiN4ZjE4NDsiIA0KZD0iTTc3MyAyMzRsMzUwIDQ3M3ExNiAyMiAyNC41IDU5dC02IDg1dC02MS41IDc5cS00MCAyNiAtODMgMjUuNXQtNzMuNSAtMTcuNXQtNTQuNSAtNDVxLTM2IC00MCAtOTYgLTQwcS01OSAwIC05NSA0MHEtMjQgMjggLTU0LjUgNDV0LTczLjUgMTcuNXQtODQgLTI1LjVxLTQ2IC0zMSAtNjAuNSAtNzl0LTYgLTg1dDI0LjUgLTU5ek0xNTM2IDY0MHEwIC0yMDkgLTEwMyAtMzg1LjV0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzDQp0LTI3OS41IDI3OS41dC0xMDMgMzg1LjV0MTAzIDM4NS41dDI3OS41IDI3OS41dDM4NS41IDEwM3QzODUuNSAtMTAzdDI3OS41IC0yNzkuNXQxMDMgLTM4NS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ic3VuIiB1bmljb2RlPSImI3hmMTg1OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE0NzIgNjQwcTAgMTE3IC00NS41IDIyMy41dC0xMjMgMTg0dC0xODQgMTIzdC0yMjMuNSA0NS41dC0yMjMuNSAtNDUuNXQtMTg0IC0xMjN0LTEyMyAtMTg0dC00NS41IC0yMjMuNXQ0NS41IC0yMjMuNXQxMjMgLTE4NHQxODQgLTEyM3QyMjMuNSAtNDUuNXQyMjMuNSA0NS41dDE4NCAxMjN0MTIzIDE4NHQ0NS41IDIyMy41ek0xNzQ4IDM2M3EtNCAtMTUgLTIwIC0yMGwtMjkyIC05NnYtMzA2cTAgLTE2IC0xMyAtMjZxLTE1IC0xMCAtMjkgLTQNCmwtMjkyIDk0bC0xODAgLTI0OHEtMTAgLTEzIC0yNiAtMTN0LTI2IDEzbC0xODAgMjQ4bC0yOTIgLTk0cS0xNCAtNiAtMjkgNHEtMTMgMTAgLTEzIDI2djMwNmwtMjkyIDk2cS0xNiA1IC0yMCAyMHEtNSAxNyA0IDI5bDE4MCAyNDhsLTE4MCAyNDhxLTkgMTMgLTQgMjlxNCAxNSAyMCAyMGwyOTIgOTZ2MzA2cTAgMTYgMTMgMjZxMTUgMTAgMjkgNGwyOTIgLTk0bDE4MCAyNDhxOSAxMiAyNiAxMnQyNiAtMTJsMTgwIC0yNDhsMjkyIDk0DQpxMTQgNiAyOSAtNHExMyAtMTAgMTMgLTI2di0zMDZsMjkyIC05NnExNiAtNSAyMCAtMjBxNSAtMTYgLTQgLTI5bC0xODAgLTI0OGwxODAgLTI0OHE5IC0xMiA0IC0yOXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il8zNjYiIHVuaWNvZGU9IiYjeGYxODY7IiANCmQ9Ik0xMjYyIDIzM3EtNTQgLTkgLTExMCAtOXEtMTgyIDAgLTMzNyA5MHQtMjQ1IDI0NXQtOTAgMzM3cTAgMTkyIDEwNCAzNTdxLTIwMSAtNjAgLTMyOC41IC0yMjl0LTEyNy41IC0zODRxMCAtMTMwIDUxIC0yNDguNXQxMzYuNSAtMjA0dDIwNCAtMTM2LjV0MjQ4LjUgLTUxcTE0NCAwIDI3My41IDYxLjV0MjIwLjUgMTcxLjV6TTE0NjUgMzE4cS05NCAtMjAzIC0yODMuNSAtMzI0LjV0LTQxMy41IC0xMjEuNXEtMTU2IDAgLTI5OCA2MQ0KdC0yNDUgMTY0dC0xNjQgMjQ1dC02MSAyOThxMCAxNTMgNTcuNSAyOTIuNXQxNTYgMjQxLjV0MjM1LjUgMTY0LjV0MjkwIDY4LjVxNDQgMiA2MSAtMzlxMTggLTQxIC0xNSAtNzJxLTg2IC03OCAtMTMxLjUgLTE4MS41dC00NS41IC0yMTguNXEwIC0xNDggNzMgLTI3M3QxOTggLTE5OHQyNzMgLTczcTExOCAwIDIyOCA1MXE0MSAxOCA3MiAtMTNxMTQgLTE0IDE3LjUgLTM0dC00LjUgLTM4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iYXJjaGl2ZSIgdW5pY29kZT0iJiN4ZjE4NzsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xMDg4IDcwNHEwIDI2IC0xOSA0NXQtNDUgMTloLTI1NnEtMjYgMCAtNDUgLTE5dC0xOSAtNDV0MTkgLTQ1dDQ1IC0xOWgyNTZxMjYgMCA0NSAxOXQxOSA0NXpNMTY2NCA4OTZ2LTk2MHEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTE0MDhxLTI2IDAgLTQ1IDE5dC0xOSA0NXY5NjBxMCAyNiAxOSA0NXQ0NSAxOWgxNDA4cTI2IDAgNDUgLTE5dDE5IC00NXpNMTcyOCAxMzQ0di0yNTZxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC0xNTM2DQpxLTI2IDAgLTQ1IDE5dC0xOSA0NXYyNTZxMCAyNiAxOSA0NXQ0NSAxOWgxNTM2cTI2IDAgNDUgLTE5dDE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImJ1ZyIgdW5pY29kZT0iJiN4ZjE4ODsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik0xNjMyIDU3NnEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTIyNHEwIC0xNzEgLTY3IC0yOTBsMjA4IC0yMDlxMTkgLTE5IDE5IC00NXQtMTkgLTQ1cS0xOCAtMTkgLTQ1IC0xOXQtNDUgMTlsLTE5OCAxOTdxLTUgLTUgLTE1IC0xM3QtNDIgLTI4LjV0LTY1IC0zNi41dC04MiAtMjl0LTk3IC0xM3Y4OTZoLTEyOHYtODk2cS01MSAwIC0xMDEuNSAxMy41dC04NyAzM3QtNjYgMzl0LTQzLjUgMzIuNWwtMTUgMTRsLTE4MyAtMjA3DQpxLTIwIC0yMSAtNDggLTIxcS0yNCAwIC00MyAxNnEtMTkgMTggLTIwLjUgNDQuNXQxNS41IDQ2LjVsMjAyIDIyN3EtNTggMTE0IC01OCAyNzRoLTIyNHEtMjYgMCAtNDUgMTl0LTE5IDQ1dDE5IDQ1dDQ1IDE5aDIyNHYyOTRsLTE3MyAxNzNxLTE5IDE5IC0xOSA0NXQxOSA0NXQ0NSAxOXQ0NSAtMTlsMTczIC0xNzNoODQ0bDE3MyAxNzNxMTkgMTkgNDUgMTl0NDUgLTE5dDE5IC00NXQtMTkgLTQ1bC0xNzMgLTE3M3YtMjk0aDIyNHEyNiAwIDQ1IC0xOQ0KdDE5IC00NXpNMTE1MiAxMTUyaC02NDBxMCAxMzMgOTMuNSAyMjYuNXQyMjYuNSA5My41dDIyNi41IC05My41dDkzLjUgLTIyNi41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idmsiIHVuaWNvZGU9IiYjeGYxODk7IiBob3Jpei1hZHYteD0iMTkyMCIgDQpkPSJNMTkxNyAxMDE2cTIzIC02NCAtMTUwIC0yOTRxLTI0IC0zMiAtNjUgLTg1cS00MCAtNTEgLTU1IC03MnQtMzAuNSAtNDkuNXQtMTIgLTQydDEzIC0zNC41dDMyLjUgLTQzdDU3IC01M3E0IC0yIDUgLTRxMTQxIC0xMzEgMTkxIC0yMjFxMyAtNSA2LjUgLTEyLjV0NyAtMjYuNXQtMC41IC0zNHQtMjUgLTI3LjV0LTU5IC0xMi41bC0yNTYgLTRxLTI0IC01IC01NiA1dC01MiAyMmwtMjAgMTJxLTMwIDIxIC03MCA2NHQtNjguNSA3Ny41dC02MSA1OA0KdC01Ni41IDE1LjVxLTMgLTEgLTggLTMuNXQtMTcgLTE0LjV0LTIxLjUgLTI5LjV0LTE3IC01MnQtNi41IC03Ny41cTAgLTE1IC0zLjUgLTI3LjV0LTcuNSAtMTguNWwtNCAtNXEtMTggLTE5IC01MyAtMjJoLTExNXEtNzEgLTQgLTE0NiAxNi41dC0xMzEuNSA1M3QtMTAzIDY2dC03MC41IDU3LjVsLTI1IDI0cS0xMCAxMCAtMjcuNSAzMHQtNzEuNSA5MXQtMTA2IDE1MXQtMTIyLjUgMjExdC0xMzAuNSAyNzJxLTYgMTYgLTYgMjd0MyAxNmw0IDYNCnExNSAxOSA1NyAxOWwyNzQgMnExMiAtMiAyMyAtNi41dDE2IC04LjVsNSAtM3ExNiAtMTEgMjQgLTMycTIwIC01MCA0NiAtMTAzLjV0NDEgLTgxLjVsMTYgLTI5cTI5IC02MCA1NiAtMTA0dDQ4LjUgLTY4LjV0NDEuNSAtMzguNXQzNCAtMTR0MjcgNXEyIDEgNSA1dDEyIDIydDEzLjUgNDd0OS41IDgxdDAgMTI1cS0yIDQwIC05IDczdC0xNCA0NmwtNiAxMnEtMjUgMzQgLTg1IDQzcS0xMyAyIDUgMjRxMTYgMTkgMzggMzBxNTMgMjYgMjM5IDI0DQpxODIgLTEgMTM1IC0xM3EyMCAtNSAzMy41IC0xMy41dDIwLjUgLTI0dDEwLjUgLTMydDMuNSAtNDUuNXQtMSAtNTV0LTIuNSAtNzAuNXQtMS41IC04Mi41cTAgLTExIC0xIC00MnQtMC41IC00OHQzLjUgLTQwLjV0MTEuNSAtMzl0MjIuNSAtMjQuNXE4IC0yIDE3IC00dDI2IDExdDM4IDM0LjV0NTIgNjd0NjggMTA3LjVxNjAgMTA0IDEwNyAyMjVxNCAxMCAxMCAxNy41dDExIDEwLjVsNCAzbDUgMi41dDEzIDN0MjAgMC41bDI4OCAyDQpxMzkgNSA2NCAtMi41dDMxIC0xNi41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0id2VpYm8iIHVuaWNvZGU9IiYjeGYxOGE7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNNjc1IDI1MnEyMSAzNCAxMSA2OXQtNDUgNTBxLTM0IDE0IC03MyAxdC02MCAtNDZxLTIyIC0zNCAtMTMgLTY4LjV0NDMgLTUwLjV0NzQuNSAtMi41dDYyLjUgNDcuNXpNNzY5IDM3M3E4IDEzIDMuNSAyNi41dC0xNy41IDE4LjVxLTE0IDUgLTI4LjUgLTAuNXQtMjEuNSAtMTguNXEtMTcgLTMxIDEzIC00NXExNCAtNSAyOSAwLjV0MjIgMTguNXpNOTQzIDI2NnEtNDUgLTEwMiAtMTU4IC0xNTB0LTIyNCAtMTINCnEtMTA3IDM0IC0xNDcuNSAxMjYuNXQ2LjUgMTg3LjVxNDcgOTMgMTUxLjUgMTM5dDIxMC41IDE5cTExMSAtMjkgMTU4LjUgLTExOS41dDIuNSAtMTkwLjV6TTEyNTUgNDI2cS05IDk2IC04OSAxNzB0LTIwOC41IDEwOXQtMjc0LjUgMjFxLTIyMyAtMjMgLTM2OS41IC0xNDEuNXQtMTMyLjUgLTI2NC41cTkgLTk2IDg5IC0xNzB0MjA4LjUgLTEwOXQyNzQuNSAtMjFxMjIzIDIzIDM2OS41IDE0MS41dDEzMi41IDI2NC41ek0xNTYzIDQyMg0KcTAgLTY4IC0zNyAtMTM5LjV0LTEwOSAtMTM3dC0xNjguNSAtMTE3LjV0LTIyNiAtODN0LTI3MC41IC0zMXQtMjc1IDMzLjV0LTI0MC41IDkzdC0xNzEuNSAxNTF0LTY1IDE5OS41cTAgMTE1IDY5LjUgMjQ1dDE5Ny41IDI1OHExNjkgMTY5IDM0MS41IDIzNnQyNDYuNSAtN3E2NSAtNjQgMjAgLTIwOXEtNCAtMTQgLTEgLTIwdDEwIC03dDE0LjUgMC41dDEzLjUgMy41bDYgMnExMzkgNTkgMjQ2IDU5dDE1MyAtNjFxNDUgLTYzIDAgLTE3OA0KcS0yIC0xMyAtNC41IC0yMHQ0LjUgLTEyLjV0MTIgLTcuNXQxNyAtNnE1NyAtMTggMTAzIC00N3Q4MCAtODEuNXQzNCAtMTE2LjV6TTE0ODkgMTA0NnE0MiAtNDcgNTQuNSAtMTA4LjV0LTYuNSAtMTE3LjVxLTggLTIzIC0yOS41IC0zNHQtNDQuNSAtNHEtMjMgOCAtMzQgMjkuNXQtNCA0NC41cTIwIDYzIC0yNCAxMTF0LTEwNyAzNXEtMjQgLTUgLTQ1IDh0LTI1IDM3cS01IDI0IDggNDQuNXQzNyAyNS41cTYwIDEzIDExOSAtNS41dDEwMSAtNjUuNXoNCk0xNjcwIDEyMDlxODcgLTk2IDExMi41IC0yMjIuNXQtMTMuNSAtMjQxLjVxLTkgLTI3IC0zNCAtNDB0LTUyIC00dC00MCAzNHQtNSA1MnEyOCA4MiAxMCAxNzJ0LTgwIDE1OHEtNjIgNjkgLTE0OCA5NS41dC0xNzMgOC41cS0yOCAtNiAtNTIgOS41dC0zMCA0My41dDkuNSA1MS41dDQzLjUgMjkuNXExMjMgMjYgMjQ0IC0xMS41dDIwOCAtMTM0LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJyZW5yZW4iIHVuaWNvZGU9IiYjeGYxOGI7IiANCmQ9Ik0xMTMzIC0zNHEtMTcxIC05NCAtMzY4IC05NHEtMTk2IDAgLTM2NyA5NHExMzggODcgMjM1LjUgMjExdDEzMS41IDI2OHEzNSAtMTQ0IDEzMi41IC0yNjh0MjM1LjUgLTIxMXpNNjM4IDEzOTR2LTQ4NXEwIC0yNTIgLTEyNi41IC00NTkuNXQtMzMwLjUgLTMwNi41cS0xODEgMjE1IC0xODEgNDk1cTAgMTg3IDgzLjUgMzQ5LjV0MjI5LjUgMjY5LjV0MzI1IDEzN3pNMTUzNiA2MzhxMCAtMjgwIC0xODEgLTQ5NQ0KcS0yMDQgOTkgLTMzMC41IDMwNi41dC0xMjYuNSA0NTkuNXY0ODVxMTc5IC0zMCAzMjUgLTEzN3QyMjkuNSAtMjY5LjV0ODMuNSAtMzQ5LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfMzcyIiB1bmljb2RlPSImI3hmMThjOyIgaG9yaXotYWR2LXg9IjE0MDgiIA0KZD0iTTE0MDIgNDMzcS0zMiAtODAgLTc2IC0xMzh0LTkxIC04OC41dC05OSAtNDYuNXQtMTAxLjUgLTE0LjV0LTk2LjUgOC41dC04Ni41IDIydC02OS41IDI3LjV0LTQ2IDIyLjVsLTE3IDEwcS0xMTMgLTIyOCAtMjg5LjUgLTM1OS41dC0zODQuNSAtMTMyLjVxLTE5IDAgLTMyIDEzdC0xMyAzMnQxMyAzMS41dDMyIDEyLjVxMTczIDEgMzIyLjUgMTA3LjV0MjUxLjUgMjk0LjVxLTM2IC0xNCAtNzIgLTIzdC04MyAtMTN0LTkxIDIuNXQtOTMgMjguNQ0KdC05MiA1OXQtODQuNSAxMDB0LTc0LjUgMTQ2cTExNCA0NyAyMTQgNTd0MTY3LjUgLTcuNXQxMjQuNSAtNTYuNXQ4OC41IC03N3Q1Ni41IC04MnE1MyAxMzEgNzkgMjkxcS03IC0xIC0xOCAtMi41dC00Ni41IC0yLjV0LTY5LjUgMC41dC04MS41IDEwdC04OC41IDIzdC04NCA0Mi41dC03NSA2NXQtNTQuNSA5NC41dC0yOC41IDEyNy41cTcwIDI4IDEzMy41IDM2LjV0MTEyLjUgLTF0OTIgLTMwdDczLjUgLTUwdDU2IC02MXQ0MiAtNjN0MjcuNSAtNTYNCnQxNiAtMzkuNWw0IC0xNnExMiAxMjIgMTIgMTk1cS04IDYgLTIxLjUgMTZ0LTQ5IDQ0LjV0LTYzLjUgNzEuNXQtNTQgOTN0LTMzIDExMi41dDEyIDEyN3Q3MCAxMzguNXE3MyAtMjUgMTI3LjUgLTYxLjV0ODQuNSAtNzYuNXQ0OCAtODV0MjAuNSAtODl0LTAuNSAtODUuNXQtMTMgLTc2LjV0LTE5IC02MnQtMTcgLTQybC03IC0xNXExIC00IDEgLTUwdC0xIC03MnEzIDcgMTAgMTguNXQzMC41IDQzdDUwLjUgNTh0NzEgNTUuNXQ5MS41IDQ0LjUNCnQxMTIgMTQuNXQxMzIuNSAtMjRxLTIgLTc4IC0yMS41IC0xNDEuNXQtNTAgLTEwNC41dC02OS41IC03MS41dC04MS41IC00NS41dC04NC41IC0yNHQtODAgLTkuNXQtNjcuNSAxdC00Ni41IDQuNWwtMTcgM3EtMjMgLTE0NyAtNzMgLTI4M3E2IDcgMTggMTguNXQ0OS41IDQxdDc3LjUgNTIuNXQ5OS41IDQydDExNy41IDIwdDEyOSAtMjMuNXQxMzcgLTc3LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJzdGFja19leGNoYW5nZSIgdW5pY29kZT0iJiN4ZjE4ZDsiIGhvcml6LWFkdi14PSIxMjgwIiANCmQ9Ik0xMjU5IDI4M3YtNjZxMCAtODUgLTU3LjUgLTE0NC41dC0xMzguNSAtNTkuNWgtNTdsLTI2MCAtMjY5djI2OWgtNTI5cS04MSAwIC0xMzguNSA1OS41dC01Ny41IDE0NC41djY2aDEyMzh6TTEyNTkgNjA5di0yNTVoLTEyMzh2MjU1aDEyMzh6TTEyNTkgOTM3di0yNTVoLTEyMzh2MjU1aDEyMzh6TTEyNTkgMTA3N3YtNjdoLTEyMzh2NjdxMCA4NCA1Ny41IDE0My41dDEzOC41IDU5LjVoODQ2cTgxIDAgMTM4LjUgLTU5LjV0NTcuNSAtMTQzLjV6DQoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il8zNzQiIHVuaWNvZGU9IiYjeGYxOGU7IiANCmQ9Ik0xMTUyIDY0MHEwIC0xNCAtOSAtMjNsLTMyMCAtMzIwcS05IC05IC0yMyAtOXEtMTMgMCAtMjIuNSA5LjV0LTkuNSAyMi41djE5MmgtMzUycS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2MTkycTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoMzUydjE5MnEwIDE0IDkgMjN0MjMgOXExMiAwIDI0IC0xMGwzMTkgLTMxOXE5IC05IDkgLTIzek0xMzEyIDY0MHEwIDE0OCAtNzMgMjczdC0xOTggMTk4dC0yNzMgNzN0LTI3MyAtNzN0LTE5OCAtMTk4DQp0LTczIC0yNzN0NzMgLTI3M3QxOTggLTE5OHQyNzMgLTczdDI3MyA3M3QxOTggMTk4dDczIDI3M3pNMTUzNiA2NDBxMCAtMjA5IC0xMDMgLTM4NS41dC0yNzkuNSAtMjc5LjV0LTM4NS41IC0xMDN0LTM4NS41IDEwM3QtMjc5LjUgMjc5LjV0LTEwMyAzODUuNXQxMDMgMzg1LjV0Mjc5LjUgMjc5LjV0Mzg1LjUgMTAzdDM4NS41IC0xMDN0Mjc5LjUgLTI3OS41dDEwMyAtMzg1LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJhcnJvd19jaXJjbGVfYWx0X2xlZnQiIHVuaWNvZGU9IiYjeGYxOTA7IiANCmQ9Ik0xMTUyIDczNnYtMTkycTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtMzUydi0xOTJxMCAtMTQgLTkgLTIzdC0yMyAtOXEtMTIgMCAtMjQgMTBsLTMxOSAzMTlxLTkgOSAtOSAyM3Q5IDIzbDMyMCAzMjBxOSA5IDIzIDlxMTMgMCAyMi41IC05LjV0OS41IC0yMi41di0xOTJoMzUycTEzIDAgMjIuNSAtOS41dDkuNSAtMjIuNXpNMTMxMiA2NDBxMCAxNDggLTczIDI3M3QtMTk4IDE5OHQtMjczIDczdC0yNzMgLTczdC0xOTggLTE5OA0KdC03MyAtMjczdDczIC0yNzN0MTk4IC0xOTh0MjczIC03M3QyNzMgNzN0MTk4IDE5OHQ3MyAyNzN6TTE1MzYgNjQwcTAgLTIwOSAtMTAzIC0zODUuNXQtMjc5LjUgLTI3OS41dC0zODUuNSAtMTAzdC0zODUuNSAxMDN0LTI3OS41IDI3OS41dC0xMDMgMzg1LjV0MTAzIDM4NS41dDI3OS41IDI3OS41dDM4NS41IDEwM3QzODUuNSAtMTAzdDI3OS41IC0yNzkuNXQxMDMgLTM4NS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzM3NiIgdW5pY29kZT0iJiN4ZjE5MTsiIA0KZD0iTTEwMjQgOTYwdi02NDBxMCAtMjYgLTE5IC00NXQtNDUgLTE5cS0yMCAwIC0zNyAxMmwtNDQ4IDMyMHEtMjcgMTkgLTI3IDUydDI3IDUybDQ0OCAzMjBxMTcgMTIgMzcgMTJxMjYgMCA0NSAtMTl0MTkgLTQ1ek0xMjgwIDE2MHY5NjBxMCAxMyAtOS41IDIyLjV0LTIyLjUgOS41aC05NjBxLTEzIDAgLTIyLjUgLTkuNXQtOS41IC0yMi41di05NjBxMCAtMTMgOS41IC0yMi41dDIyLjUgLTkuNWg5NjBxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXoNCk0xNTM2IDExMjB2LTk2MHEwIC0xMTkgLTg0LjUgLTIwMy41dC0yMDMuNSAtODQuNWgtOTYwcS0xMTkgMCAtMjAzLjUgODQuNXQtODQuNSAyMDMuNXY5NjBxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDk2MHExMTkgMCAyMDMuNSAtODQuNXQ4NC41IC0yMDMuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImRvdF9jaXJjbGVfYWx0IiB1bmljb2RlPSImI3hmMTkyOyIgDQpkPSJNMTAyNCA2NDBxMCAtMTA2IC03NSAtMTgxdC0xODEgLTc1dC0xODEgNzV0LTc1IDE4MXQ3NSAxODF0MTgxIDc1dDE4MSAtNzV0NzUgLTE4MXpNNzY4IDExODRxLTE0OCAwIC0yNzMgLTczdC0xOTggLTE5OHQtNzMgLTI3M3Q3MyAtMjczdDE5OCAtMTk4dDI3MyAtNzN0MjczIDczdDE5OCAxOTh0NzMgMjczdC03MyAyNzN0LTE5OCAxOTh0LTI3MyA3M3pNMTUzNiA2NDBxMCAtMjA5IC0xMDMgLTM4NS41dC0yNzkuNSAtMjc5LjUNCnQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzdC0yNzkuNSAyNzkuNXQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN0Mzg1LjUgLTEwM3QyNzkuNSAtMjc5LjV0MTAzIC0zODUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il8zNzgiIHVuaWNvZGU9IiYjeGYxOTM7IiBob3Jpei1hZHYteD0iMTY2NCIgDQpkPSJNMTAyMyAzNDlsMTAyIC0yMDRxLTU4IC0xNzkgLTIxMCAtMjkwdC0zMzkgLTExMXEtMTU2IDAgLTI4OC41IDc3LjV0LTIxMCAyMTB0LTc3LjUgMjg4LjVxMCAxODEgMTA0LjUgMzMwdDI3NC41IDIxMWwxNyAtMTMxcS0xMjIgLTU0IC0xOTUgLTE2NS41dC03MyAtMjQ0LjVxMCAtMTg1IDEzMS41IC0zMTYuNXQzMTYuNSAtMTMxLjVxMTI2IDAgMjMyLjUgNjV0MTY1IDE3NS41dDQ5LjUgMjM2LjV6TTE1NzEgMjQ5bDU4IC0xMTRsLTI1NiAtMTI4DQpxLTEzIC03IC0yOSAtN3EtNDAgMCAtNTcgMzVsLTIzOSA0NzdoLTQ3MnEtMjQgMCAtNDIuNSAxNi41dC0yMS41IDQwLjVsLTk2IDc3OXEtMiAxNyA2IDQycTE0IDUxIDU3IDgyLjV0OTcgMzEuNXE2NiAwIDExMyAtNDd0NDcgLTExM3EwIC02OSAtNTIgLTExNy41dC0xMjAgLTQxLjVsMzcgLTI4OWg0MjN2LTEyOGgtNDA3bDE2IC0xMjhoNDU1cTQwIDAgNTcgLTM1bDIyOCAtNDU1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idmltZW9fc3F1YXJlIiB1bmljb2RlPSImI3hmMTk0OyIgDQpkPSJNMTI5MiA4OThxMTAgMjE2IC0xNjEgMjIycS0yMzEgOCAtMzEyIC0yNjFxNDQgMTkgODIgMTlxODUgMCA3NCAtOTZxLTQgLTU3IC03NCAtMTY3dC0xMDUgLTExMHEtNDMgMCAtODIgMTY5cS0xMyA1NCAtNDUgMjU1cS0zMCAxODkgLTE2MCAxNzdxLTU5IC03IC0xNjQgLTEwMGwtODEgLTcybC04MSAtNzJsNTIgLTY3cTc2IDUyIDg3IDUycTU3IDAgMTA3IC0xNzlxMTUgLTU1IDQ1IC0xNjQuNXQ0NSAtMTY0LjVxNjggLTE3OSAxNjQgLTE3OQ0KcTE1NyAwIDM4MyAyOTRxMjIwIDI4MyAyMjYgNDQ0ek0xNTM2IDExMjB2LTk2MHEwIC0xMTkgLTg0LjUgLTIwMy41dC0yMDMuNSAtODQuNWgtOTYwcS0xMTkgMCAtMjAzLjUgODQuNXQtODQuNSAyMDMuNXY5NjBxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDk2MHExMTkgMCAyMDMuNSAtODQuNXQ4NC41IC0yMDMuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il8zODAiIHVuaWNvZGU9IiYjeGYxOTU7IiBob3Jpei1hZHYteD0iMTE1MiIgDQpkPSJNMTE1MiA3MDRxMCAtMTkxIC05NC41IC0zNTN0LTI1Ni41IC0yNTYuNXQtMzUzIC05NC41aC0xNjBxLTE0IDAgLTIzIDl0LTkgMjN2NjExbC0yMTUgLTY2cS0zIC0xIC05IC0xcS0xMCAwIC0xOSA2cS0xMyAxMCAtMTMgMjZ2MTI4cTAgMjMgMjMgMzFsMjMzIDcxdjkzbC0yMTUgLTY2cS0zIC0xIC05IC0xcS0xMCAwIC0xOSA2cS0xMyAxMCAtMTMgMjZ2MTI4cTAgMjMgMjMgMzFsMjMzIDcxdjI1MHEwIDE0IDkgMjN0MjMgOWgxNjANCnExNCAwIDIzIC05dDkgLTIzdi0xODFsMzc1IDExNnExNSA1IDI4IC01dDEzIC0yNnYtMTI4cTAgLTIzIC0yMyAtMzFsLTM5MyAtMTIxdi05M2wzNzUgMTE2cTE1IDUgMjggLTV0MTMgLTI2di0xMjhxMCAtMjMgLTIzIC0zMWwtMzkzIC0xMjF2LTQ4N3ExODggMTMgMzE4IDE1MXQxMzAgMzI4cTAgMTQgOSAyM3QyMyA5aDE2MHExNCAwIDIzIC05dDkgLTIzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0icGx1c19zcXVhcmVfbyIgdW5pY29kZT0iJiN4ZjE5NjsiIGhvcml6LWFkdi14PSIxNDA4IiANCmQ9Ik0xMTUyIDczNnYtNjRxMCAtMTQgLTkgLTIzdC0yMyAtOWgtMzUydi0zNTJxMCAtMTQgLTkgLTIzdC0yMyAtOWgtNjRxLTE0IDAgLTIzIDl0LTkgMjN2MzUyaC0zNTJxLTE0IDAgLTIzIDl0LTkgMjN2NjRxMCAxNCA5IDIzdDIzIDloMzUydjM1MnEwIDE0IDkgMjN0MjMgOWg2NHExNCAwIDIzIC05dDkgLTIzdi0zNTJoMzUycTE0IDAgMjMgLTl0OSAtMjN6TTEyODAgMjg4djgzMnEwIDY2IC00NyAxMTN0LTExMyA0N2gtODMyDQpxLTY2IDAgLTExMyAtNDd0LTQ3IC0xMTN2LTgzMnEwIC02NiA0NyAtMTEzdDExMyAtNDdoODMycTY2IDAgMTEzIDQ3dDQ3IDExM3pNMTQwOCAxMTIwdi04MzJxMCAtMTE5IC04NC41IC0yMDMuNXQtMjAzLjUgLTg0LjVoLTgzMnEtMTE5IDAgLTIwMy41IDg0LjV0LTg0LjUgMjAzLjV2ODMycTAgMTE5IDg0LjUgMjAzLjV0MjAzLjUgODQuNWg4MzJxMTE5IDAgMjAzLjUgLTg0LjV0ODQuNSAtMjAzLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfMzgyIiB1bmljb2RlPSImI3hmMTk3OyIgaG9yaXotYWR2LXg9IjIxNzYiIA0KZD0iTTYyMCA0MTZxLTExMCAtNjQgLTI2OCAtNjRoLTEyOHY2NGgtNjRxLTEzIDAgLTIyLjUgMjMuNXQtOS41IDU2LjVxMCAyNCA3IDQ5cS01OCAyIC05Ni41IDEwLjV0LTM4LjUgMjAuNXQzOC41IDIwLjV0OTYuNSAxMC41cS03IDI1IC03IDQ5cTAgMzMgOS41IDU2LjV0MjIuNSAyMy41aDY0djY0aDEyOHExNTggMCAyNjggLTY0aDExMTNxNDIgLTcgMTA2LjUgLTE4dDgwLjUgLTE0cTg5IC0xNSAxNTAgLTQwLjV0ODMuNSAtNDcuNXQyMi41IC00MA0KdC0yMi41IC00MHQtODMuNSAtNDcuNXQtMTUwIC00MC41cS0xNiAtMyAtODAuNSAtMTR0LTEwNi41IC0xOGgtMTExM3pNMTczOSA2NjhxNTMgLTM2IDUzIC05MnQtNTMgLTkybDgxIC0zMHE2OCA0OCA2OCAxMjJ0LTY4IDEyMnpNNjI1IDQwMGgxMDE1cS0yMTcgLTM4IC00NTYgLTgwcS01NyAwIC0xMTMgLTI0dC04MyAtNDhsLTI4IC0yNGwtMjg4IC0yODhxLTI2IC0yNiAtNzAuNSAtNDV0LTg5LjUgLTE5aC05NmwtOTMgNDY0aDI5DQpxMTU3IDAgMjczIDY0ek0zNTIgODE2aC0yOWw5MyA0NjRoOTZxNDYgMCA5MCAtMTl0NzAgLTQ1bDI4OCAtMjg4cTQgLTQgMTEgLTEwLjV0MzAuNSAtMjN0NDguNSAtMjl0NjEuNSAtMjN0NzIuNSAtMTAuNWw0NTYgLTgwaC0xMDE1cS0xMTYgNjQgLTI3MyA2NHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il8zODMiIHVuaWNvZGU9IiYjeGYxOTg7IiBob3Jpei1hZHYteD0iMTY2NCIgDQpkPSJNMTUxOSA3NjBxNjIgMCAxMDMuNSAtNDAuNXQ0MS41IC0xMDEuNXEwIC05NyAtOTMgLTEzMGwtMTcyIC01OWw1NiAtMTY3cTcgLTIxIDcgLTQ3cTAgLTU5IC00MiAtMTAydC0xMDEgLTQzcS00NyAwIC04NS41IDI3dC01My41IDcybC01NSAxNjVsLTMxMCAtMTA2bDU1IC0xNjRxOCAtMjQgOCAtNDdxMCAtNTkgLTQyIC0xMDJ0LTEwMiAtNDNxLTQ3IDAgLTg1IDI3dC01MyA3MmwtNTUgMTYzbC0xNTMgLTUzcS0yOSAtOSAtNTAgLTkNCnEtNjEgMCAtMTAxLjUgNDB0LTQwLjUgMTAxcTAgNDcgMjcuNSA4NXQ3MS41IDUzbDE1NiA1M2wtMTA1IDMxM2wtMTU2IC01NHEtMjYgLTggLTQ4IC04cS02MCAwIC0xMDEgNDAuNXQtNDEgMTAwLjVxMCA0NyAyNy41IDg1dDcxLjUgNTNsMTU3IDUzbC01MyAxNTlxLTggMjQgLTggNDdxMCA2MCA0MiAxMDIuNXQxMDIgNDIuNXE0NyAwIDg1IC0yN3Q1MyAtNzJsNTQgLTE2MGwzMTAgMTA1bC01NCAxNjBxLTggMjQgLTggNDdxMCA1OSA0Mi41IDEwMg0KdDEwMS41IDQzcTQ3IDAgODUuNSAtMjcuNXQ1My41IC03MS41bDUzIC0xNjFsMTYyIDU1cTIxIDYgNDMgNnE2MCAwIDEwMi41IC0zOS41dDQyLjUgLTk4LjVxMCAtNDUgLTMwIC04MS41dC03NCAtNTEuNWwtMTU3IC01NGwxMDUgLTMxNmwxNjQgNTZxMjQgOCA0NiA4ek03MjUgNDk4bDMxMCAxMDVsLTEwNSAzMTVsLTMxMCAtMTA3eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzM4NCIgdW5pY29kZT0iJiN4ZjE5OTsiIA0KZD0iTTEyNDggMTQwOHExMTkgMCAyMDMuNSAtODQuNXQ4NC41IC0yMDMuNXYtOTYwcTAgLTExOSAtODQuNSAtMjAzLjV0LTIwMy41IC04NC41aC05NjBxLTExOSAwIC0yMDMuNSA4NC41dC04NC41IDIwMy41djk2MHEwIDExOSA4NC41IDIwMy41dDIwMy41IDg0LjVoOTYwek0xMjgwIDM1MnY0MzZxLTMxIC0zNSAtNjQgLTU1cS0zNCAtMjIgLTEzMi41IC04NXQtMTUxLjUgLTk5cS05OCAtNjkgLTE2NCAtNjl2MHYwcS02NiAwIC0xNjQgNjkNCnEtNDcgMzIgLTE0MiA5Mi41dC0xNDIgOTIuNXEtMTIgOCAtMzMgMjd0LTMxIDI3di00MzZxMCAtNDAgMjggLTY4dDY4IC0yOGg4MzJxNDAgMCA2OCAyOHQyOCA2OHpNMTI4MCA5MjVxMCA0MSAtMjcuNSA3MHQtNjguNSAyOWgtODMycS00MCAwIC02OCAtMjh0LTI4IC02OHEwIC0zNyAzMC41IC03Ni41dDY3LjUgLTY0LjVxNDcgLTMyIDEzNy41IC04OXQxMjkuNSAtODNxMyAtMiAxNyAtMTEuNXQyMSAtMTR0MjEgLTEzdDIzLjUgLTEzDQp0MjEuNSAtOS41dDIyLjUgLTcuNXQyMC41IC0yLjV0MjAuNSAyLjV0MjIuNSA3LjV0MjEuNSA5LjV0MjMuNSAxM3QyMSAxM3QyMSAxNHQxNyAxMS41bDI2NyAxNzRxMzUgMjMgNjYuNSA2Mi41dDMxLjUgNzMuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il8zODUiIHVuaWNvZGU9IiYjeGYxOWE7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTI3IDY0MHEwIDE2MyA2NyAzMTNsMzY3IC0xMDA1cS0xOTYgOTUgLTMxNSAyODF0LTExOSA0MTF6TTE0MTUgNjc5cTAgLTE5IC0yLjUgLTM4LjV0LTEwIC00OS41dC0xMS41IC00NHQtMTcuNSAtNTl0LTE3LjUgLTU4bC03NiAtMjU2bC0yNzggODI2cTQ2IDMgODggOHExOSAyIDI2IDE4LjV0LTIuNSAzMXQtMjguNSAxMy41bC0yMDUgLTEwcS03NSAxIC0yMDIgMTBxLTEyIDEgLTIwLjUgLTV0LTExLjUgLTE1dC0xLjUgLTE4LjV0OSAtMTYuNQ0KdDE5LjUgLThsODAgLThsMTIwIC0zMjhsLTE2OCAtNTA0bC0yODAgODMycTQ2IDMgODggOHExOSAyIDI2IDE4LjV0LTIuNSAzMXQtMjguNSAxMy41bC0yMDUgLTEwcS03IDAgLTIzIDAuNXQtMjYgMC41cTEwNSAxNjAgMjc0LjUgMjUzLjV0MzY3LjUgOTMuNXExNDcgMCAyODAuNSAtNTN0MjM4LjUgLTE0OWgtMTBxLTU1IDAgLTkyIC00MC41dC0zNyAtOTUuNXEwIC0xMiAyIC0yNHQ0IC0yMS41dDggLTIzdDkgLTIxdDEyIC0yMi41dDEyLjUgLTIxDQp0MTQuNSAtMjR0MTQgLTIzcTYzIC0xMDcgNjMgLTIxMnpNOTA5IDU3M2wyMzcgLTY0N3ExIC02IDUgLTExcS0xMjYgLTQ0IC0yNTUgLTQ0cS0xMTIgMCAtMjE3IDMyek0xNTcwIDEwMDlxOTUgLTE3NCA5NSAtMzY5cTAgLTIwOSAtMTA0IC0zODUuNXQtMjc5IC0yNzguNWwyMzUgNjc4cTU5IDE2OSA1OSAyNzZxMCA0MiAtNiA3OXpNODk2IDE1MzZxMTgyIDAgMzQ4IC03MXQyODYgLTE5MXQxOTEgLTI4NnQ3MSAtMzQ4dC03MSAtMzQ4dC0xOTEgLTI4Ng0KdC0yODYgLTE5MXQtMzQ4IC03MXQtMzQ4IDcxdC0yODYgMTkxdC0xOTEgMjg2dC03MSAzNDh0NzEgMzQ4dDE5MSAyODZ0Mjg2IDE5MXQzNDggNzF6TTg5NiAtMjE1cTE3MyAwIDMzMS41IDY4dDI3MyAxODIuNXQxODIuNSAyNzN0NjggMzMxLjV0LTY4IDMzMS41dC0xODIuNSAyNzN0LTI3MyAxODIuNXQtMzMxLjUgNjh0LTMzMS41IC02OHQtMjczIC0xODIuNXQtMTgyLjUgLTI3M3QtNjggLTMzMS41dDY4IC0zMzEuNXQxODIuNSAtMjczDQp0MjczIC0xODIuNXQzMzEuNSAtNjh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfMzg2IiB1bmljb2RlPSImI3hmMTliOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTEwODYgMTUzNnYtMTUzNmwtMjcyIC0xMjhxLTIyOCAyMCAtNDE0IDEwMnQtMjkzIDIwOC41dC0xMDcgMjcyLjVxMCAxNDAgMTAwLjUgMjYzLjV0Mjc1IDIwNS41dDM5MS41IDEwOHYtMTcycS0yMTcgLTM4IC0zNTYuNSAtMTUwdC0xMzkuNSAtMjU1cTAgLTE1MiAxNTQuNSAtMjY3dDM4OC41IC0xNDV2MTM2MHpNMTc1NSA5NTRsMzcgLTM5MGwtNTI1IDExNGwxNDcgODNxLTExOSA3MCAtMjgwIDk5djE3MnEyNzcgLTMzIDQ4MSAtMTU3eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzM4NyIgdW5pY29kZT0iJiN4ZjE5YzsiIGhvcml6LWFkdi14PSIyMDQ4IiANCmQ9Ik05NjAgMTUzNmw5NjAgLTM4NHYtMTI4aC0xMjhxMCAtMjYgLTIwLjUgLTQ1dC00OC41IC0xOWgtMTUyNnEtMjggMCAtNDguNSAxOXQtMjAuNSA0NWgtMTI4djEyOHpNMjU2IDg5NmgyNTZ2LTc2OGgxMjh2NzY4aDI1NnYtNzY4aDEyOHY3NjhoMjU2di03NjhoMTI4djc2OGgyNTZ2LTc2OGg1OXEyOCAwIDQ4LjUgLTE5dDIwLjUgLTQ1di02NGgtMTY2NHY2NHEwIDI2IDIwLjUgNDV0NDguNSAxOWg1OXY3Njh6TTE4NTEgLTY0DQpxMjggMCA0OC41IC0xOXQyMC41IC00NXYtMTI4aC0xOTIwdjEyOHEwIDI2IDIwLjUgNDV0NDguNSAxOWgxNzgyeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzM4OCIgdW5pY29kZT0iJiN4ZjE5ZDsiIGhvcml6LWFkdi14PSIyMzA0IiANCmQ9Ik0xNzc0IDcwMGwxOCAtMzE2cTQgLTY5IC04MiAtMTI4dC0yMzUgLTkzLjV0LTMyMyAtMzQuNXQtMzIzIDM0LjV0LTIzNSA5My41dC04MiAxMjhsMTggMzE2bDU3NCAtMTgxcTIyIC03IDQ4IC03dDQ4IDd6TTIzMDQgMTAyNHEwIC0yMyAtMjIgLTMxbC0xMTIwIC0zNTJxLTQgLTEgLTEwIC0xdC0xMCAxbC02NTIgMjA2cS00MyAtMzQgLTcxIC0xMTEuNXQtMzQgLTE3OC41cTYzIC0zNiA2MyAtMTA5cTAgLTY5IC01OCAtMTA3bDU4IC00MzMNCnEyIC0xNCAtOCAtMjVxLTkgLTExIC0yNCAtMTFoLTE5MnEtMTUgMCAtMjQgMTFxLTEwIDExIC04IDI1bDU4IDQzM3EtNTggMzggLTU4IDEwN3EwIDczIDY1IDExMXExMSAyMDcgOTggMzMwbC0zMzMgMTA0cS0yMiA4IC0yMiAzMXQyMiAzMWwxMTIwIDM1MnE0IDEgMTAgMXQxMCAtMWwxMTIwIC0zNTJxMjIgLTggMjIgLTMxeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzM4OSIgdW5pY29kZT0iJiN4ZjE5ZTsiIA0KZD0iTTg1OSA1NzlsMTMgLTcwN3EtNjIgMTEgLTEwNSAxMXEtNDEgMCAtMTA1IC0xMWwxMyA3MDdxLTQwIDY5IC0xNjguNSAyOTUuNXQtMjE2LjUgMzc0LjV0LTE4MSAyODdxNTggLTE1IDEwOCAtMTVxNDQgMCAxMTEgMTVxNjMgLTExMSAxMzMuNSAtMjI5LjV0MTY3IC0yNzYuNXQxMzguNSAtMjI3cTM3IDYxIDEwOS41IDE3Ny41dDExNy41IDE5MHQxMDUgMTc2dDEwNyAxODkuNXE1NCAtMTQgMTA3IC0xNHE1NiAwIDExNCAxNHYwDQpxLTI4IC0zOSAtNjAgLTg4LjV0LTQ5LjUgLTc4LjV0LTU2LjUgLTk2dC00OSAtODRxLTE0NiAtMjQ4IC0zNTMgLTYxMHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYxQTAiIHVuaWNvZGU9IiYjeGYxYTA7IiANCmQ9Ik03NjggNzUwaDcyNXExMiAtNjcgMTIgLTEyOHEwIC0yMTcgLTkxIC0zODcuNXQtMjU5LjUgLTI2Ni41dC0zODYuNSAtOTZxLTE1NyAwIC0yOTkgNjAuNXQtMjQ1IDE2My41dC0xNjMuNSAyNDV0LTYwLjUgMjk5dDYwLjUgMjk5dDE2My41IDI0NXQyNDUgMTYzLjV0Mjk5IDYwLjVxMzAwIDAgNTE1IC0yMDFsLTIwOSAtMjAxcS0xMjMgMTE5IC0zMDYgMTE5cS0xMjkgMCAtMjM4LjUgLTY1dC0xNzMuNSAtMTc2LjV0LTY0IC0yNDMuNQ0KdDY0IC0yNDMuNXQxNzMuNSAtMTc2LjV0MjM4LjUgLTY1cTg3IDAgMTYwIDI0dDEyMCA2MHQ4MiA4MnQ1MS41IDg3dDIyLjUgNzhoLTQzNnYyNjR6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJmMWExIiB1bmljb2RlPSImI3hmMWExOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTEwOTUgMzY5cTE2IC0xNiAwIC0zMXEtNjIgLTYyIC0xOTkgLTYydC0xOTkgNjJxLTE2IDE1IDAgMzFxNiA2IDE1IDZ0MTUgLTZxNDggLTQ5IDE2OSAtNDlxMTIwIDAgMTY5IDQ5cTYgNiAxNSA2dDE1IC02ek03ODggNTUwcTAgLTM3IC0yNiAtNjN0LTYzIC0yNnQtNjMuNSAyNnQtMjYuNSA2M3EwIDM4IDI2LjUgNjR0NjMuNSAyNnQ2MyAtMjYuNXQyNiAtNjMuNXpNMTE4MyA1NTBxMCAtMzcgLTI2LjUgLTYzdC02My41IC0yNnQtNjMgMjYNCnQtMjYgNjN0MjYgNjMuNXQ2MyAyNi41dDYzLjUgLTI2dDI2LjUgLTY0ek0xNDM0IDY3MHEwIDQ5IC0zNSA4NHQtODUgMzV0LTg2IC0zNnEtMTMwIDkwIC0zMTEgOTZsNjMgMjgzbDIwMCAtNDVxMCAtMzcgMjYgLTYzdDYzIC0yNnQ2My41IDI2LjV0MjYuNSA2My41dC0yNi41IDYzLjV0LTYzLjUgMjYuNXEtNTQgMCAtODAgLTUwbC0yMjEgNDlxLTE5IDUgLTI1IC0xNmwtNjkgLTMxMnEtMTgwIC03IC0zMDkgLTk3cS0zNSAzNyAtODcgMzcNCnEtNTAgMCAtODUgLTM1dC0zNSAtODRxMCAtMzUgMTguNSAtNjR0NDkuNSAtNDRxLTYgLTI3IC02IC01NnEwIC0xNDIgMTQwIC0yNDN0MzM3IC0xMDFxMTk4IDAgMzM4IDEwMXQxNDAgMjQzcTAgMzIgLTcgNTdxMzAgMTUgNDggNDMuNXQxOCA2My41ek0xNzkyIDY0MHEwIC0xODIgLTcxIC0zNDh0LTE5MSAtMjg2dC0yODYgLTE5MXQtMzQ4IC03MXQtMzQ4IDcxdC0yODYgMTkxdC0xOTEgMjg2dC03MSAzNDh0NzEgMzQ4dDE5MSAyODZ0Mjg2IDE5MQ0KdDM0OCA3MXQzNDggLTcxdDI4NiAtMTkxdDE5MSAtMjg2dDcxIC0zNDh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfMzkyIiB1bmljb2RlPSImI3hmMWEyOyIgDQpkPSJNOTM5IDQwN3ExMyAtMTMgMCAtMjZxLTUzIC01MyAtMTcxIC01M3QtMTcxIDUzcS0xMyAxMyAwIDI2cTUgNiAxMyA2dDEzIC02cTQyIC00MiAxNDUgLTQydDE0NSA0MnE1IDYgMTMgNnQxMyAtNnpNNjc2IDU2M3EwIC0zMSAtMjMgLTU0dC01NCAtMjN0LTU0IDIzdC0yMyA1NHEwIDMyIDIyLjUgNTQuNXQ1NC41IDIyLjV0NTQuNSAtMjIuNXQyMi41IC01NC41ek0xMDE0IDU2M3EwIC0zMSAtMjMgLTU0dC01NCAtMjN0LTU0IDIzdC0yMyA1NA0KcTAgMzIgMjIuNSA1NC41dDU0LjUgMjIuNXQ1NC41IC0yMi41dDIyLjUgLTU0LjV6TTEyMjkgNjY2cTAgNDIgLTMwIDcydC03MyAzMHEtNDIgMCAtNzMgLTMxcS0xMTMgNzggLTI2NyA4Mmw1NCAyNDNsMTcxIC0zOXExIC0zMiAyMy41IC01NHQ1My41IC0yMnEzMiAwIDU0LjUgMjIuNXQyMi41IDU0LjV0LTIyLjUgNTQuNXQtNTQuNSAyMi41cS00OCAwIC02OSAtNDNsLTE4OSA0MnEtMTcgNSAtMjEgLTEzbC02MCAtMjY4cS0xNTQgLTYgLTI2NSAtODMNCnEtMzAgMzIgLTc0IDMycS00MyAwIC03MyAtMzB0LTMwIC03MnEwIC0zMCAxNiAtNTV0NDIgLTM4cS01IC0yNSAtNSAtNDhxMCAtMTIyIDEyMCAtMjA4LjV0Mjg5IC04Ni41cTE3MCAwIDI5MCA4Ni41dDEyMCAyMDguNXEwIDI1IC02IDQ5cTI1IDEzIDQwLjUgMzcuNXQxNS41IDU0LjV6TTE1MzYgMTEyMHYtOTYwcTAgLTExOSAtODQuNSAtMjAzLjV0LTIwMy41IC04NC41aC05NjBxLTExOSAwIC0yMDMuNSA4NC41dC04NC41IDIwMy41djk2MA0KcTAgMTE5IDg0LjUgMjAzLjV0MjAzLjUgODQuNWg5NjBxMTE5IDAgMjAzLjUgLTg0LjV0ODQuNSAtMjAzLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfMzkzIiB1bmljb2RlPSImI3hmMWEzOyIgDQpkPSJNODY2IDY5N2w5MCAyN3Y2MnEwIDc5IC01OCAxMzV0LTEzOCA1NnQtMTM4IC01NS41dC01OCAtMTM0LjV2LTI4M3EwIC0yMCAtMTQgLTMzLjV0LTMzIC0xMy41dC0zMi41IDEzLjV0LTEzLjUgMzMuNXYxMjBoLTE1MXYtMTIycTAgLTgyIDU3LjUgLTEzOXQxMzkuNSAtNTdxODEgMCAxMzguNSA1Ni41dDU3LjUgMTM2LjV2MjgwcTAgMTkgMTMuNSAzM3QzMy41IDE0cTE5IDAgMzIuNSAtMTR0MTMuNSAtMzN2LTU0ek0xMTk5IDUwMnYxMjJoLTE1MA0Kdi0xMjZxMCAtMjAgLTEzLjUgLTMzLjV0LTMzLjUgLTEzLjVxLTE5IDAgLTMyLjUgMTR0LTEzLjUgMzN2MTIzbC05MCAtMjZsLTYwIDI4di0xMjNxMCAtODAgNTggLTEzN3QxMzkgLTU3dDEzOC41IDU3dDU3LjUgMTM5ek0xNTM2IDY0MHEwIC0yMDkgLTEwMyAtMzg1LjV0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzdC0yNzkuNSAyNzkuNXQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDMNCnQzODUuNSAtMTAzdDI3OS41IC0yNzkuNXQxMDMgLTM4NS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZjFhNCIgdW5pY29kZT0iJiN4ZjFhNDsiIGhvcml6LWFkdi14PSIxOTIwIiANCmQ9Ik0xMDYyIDgyNHYxMThxMCA0MiAtMzAgNzJ0LTcyIDMwdC03MiAtMzB0LTMwIC03MnYtNjEycTAgLTE3NSAtMTI2IC0yOTl0LTMwMyAtMTI0cS0xNzggMCAtMzAzLjUgMTI1LjV0LTEyNS41IDMwMy41djI2NmgzMjh2LTI2MnEwIC00MyAzMCAtNzIuNXQ3MiAtMjkuNXQ3MiAyOS41dDMwIDcyLjV2NjIwcTAgMTcxIDEyNi41IDI5MnQzMDEuNSAxMjFxMTc2IDAgMzAyIC0xMjJ0MTI2IC0yOTR2LTEzNmwtMTk1IC01OHpNMTU5MiA2MDJoMzI4DQp2LTI2NnEwIC0xNzggLTEyNS41IC0zMDMuNXQtMzAzLjUgLTEyNS41cS0xNzcgMCAtMzAzIDEyNC41dC0xMjYgMzAwLjV2MjY4bDEzMSAtNjFsMTk1IDU4di0yNzBxMCAtNDIgMzAgLTcxLjV0NzIgLTI5LjV0NzIgMjkuNXQzMCA3MS41djI3NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il8zOTUiIHVuaWNvZGU9IiYjeGYxYTU7IiANCmQ9Ik0xNDcyIDE2MHY0ODBoLTcwNHY3MDRoLTQ4MHEtOTMgMCAtMTU4LjUgLTY1LjV0LTY1LjUgLTE1OC41di00ODBoNzA0di03MDRoNDgwcTkzIDAgMTU4LjUgNjUuNXQ2NS41IDE1OC41ek0xNTM2IDExMjB2LTk2MHEwIC0xMTkgLTg0LjUgLTIwMy41dC0yMDMuNSAtODQuNWgtOTYwcS0xMTkgMCAtMjAzLjUgODQuNXQtODQuNSAyMDMuNXY5NjBxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDk2MHExMTkgMCAyMDMuNSAtODQuNQ0KdDg0LjUgLTIwMy41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzM5NiIgdW5pY29kZT0iJiN4ZjFhNjsiIGhvcml6LWFkdi14PSIyMDQ4IiANCmQ9Ik0zMjggMTI1NGgyMDR2LTk4M2gtNTMydjY5N2gzMjh2Mjg2ek0zMjggNDM1djM2OWgtMTIzdi0zNjloMTIzek02MTQgOTY4di02OTdoMjA1djY5N2gtMjA1ek02MTQgMTI1NHYtMjA0aDIwNXYyMDRoLTIwNXpNOTAxIDk2OGg1MzN2LTk0MmgtNTMzdjE2M2gzMjh2ODJoLTMyOHY2OTd6TTEyMjkgNDM1djM2OWgtMTIzdi0zNjloMTIzek0xNTE2IDk2OGg1MzJ2LTk0MmgtNTMydjE2M2gzMjd2ODJoLTMyN3Y2OTd6TTE4NDMgNDM1djM2OWgtMTIzDQp2LTM2OWgxMjN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfMzk3IiB1bmljb2RlPSImI3hmMWE3OyIgDQpkPSJNMTA0NiA1MTZxMCAtNjQgLTM4IC0xMDl0LTkxIC00NXEtNDMgMCAtNzAgMTV2Mjc3cTI4IDE3IDcwIDE3cTUzIDAgOTEgLTQ1LjV0MzggLTEwOS41ek03MDMgOTQ0cTAgLTY0IC0zOCAtMTA5LjV0LTkxIC00NS41cS00MyAwIC03MCAxNXYyNzdxMjggMTcgNzAgMTdxNTMgMCA5MSAtNDV0MzggLTEwOXpNMTI2NSA1MTNxMCAxMzQgLTg4IDIyOXQtMjEzIDk1cS0yMCAwIC0zOSAtM3EtMjMgLTc4IC03OCAtMTM2cS04NyAtOTUgLTIxMSAtMTAxDQp2LTYzNmwyMTEgNDF2MjA2cTUxIC0xOSAxMTcgLTE5cTEyNSAwIDIxMyA5NXQ4OCAyMjl6TTkyMiA5NDBxMCAxMzQgLTg4LjUgMjI5dC0yMTMuNSA5NXEtNzQgMCAtMTQxIC0zNmgtMTg2di04NDBsMjExIDQxdjIwNnE1NSAtMTkgMTE2IC0xOXExMjUgMCAyMTMuNSA5NXQ4OC41IDIyOXpNMTUzNiAxMTIwdi05NjBxMCAtMTE5IC04NC41IC0yMDMuNXQtMjAzLjUgLTg0LjVoLTk2MHEtMTE5IDAgLTIwMy41IDg0LjV0LTg0LjUgMjAzLjV2OTYwDQpxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDk2MHExMTkgMCAyMDMuNSAtODQuNXQ4NC41IC0yMDMuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il8zOTgiIHVuaWNvZGU9IiYjeGYxYTg7IiBob3Jpei1hZHYteD0iMjAzOCIgDQpkPSJNMTIyMiA2MDdxNzUgMyAxNDMuNSAtMjAuNXQxMTggLTU4LjV0MTAxIC05NC41dDg0IC0xMDh0NzUuNSAtMTIwLjVxMzMgLTU2IDc4LjUgLTEwOXQ3NS41IC04MC41dDk5IC04OC41cS00OCAtMzAgLTEwOC41IC01Ny41dC0xMzguNSAtNTl0LTExNCAtNDcuNXEtNDQgMzcgLTc0IDExNXQtNDMuNSAxNjQuNXQtMzMgMTgwLjV0LTQyLjUgMTY4LjV0LTcyLjUgMTIzdC0xMjIuNSA0OC41bC0xMCAtMmwtNiAtNHE0IC01IDEzIC0xNA0KcTYgLTUgMjggLTIzLjV0MjUuNSAtMjJ0MTkgLTE4dDE4IC0yMC41dDExLjUgLTIxdDEwLjUgLTI3LjV0NC41IC0zMXQ0IC00MC41bDEgLTMzcTEgLTI2IC0yLjUgLTU3LjV0LTcuNSAtNTJ0LTEyLjUgLTU4LjV0LTExLjUgLTUzcS0zNSAxIC0xMDEgLTkuNXQtOTggLTEwLjVxLTM5IDAgLTcyIDEwcS0yIDE2IC0yIDQ3cTAgNzQgMyA5NnEyIDEzIDMxLjUgNDEuNXQ1NyA1OXQyNi41IDUxLjVxLTI0IDIgLTQzIC0yNA0KcS0zNiAtNTMgLTExMS41IC05OS41dC0xMzYuNSAtNDYuNXEtMjUgMCAtNzUuNSA2M3QtMTA2LjUgMTM5LjV0LTg0IDk2LjVxLTYgNCAtMjcgMzBxLTQ4MiAtMTEyIC01MTMgLTExMnEtMTYgMCAtMjggMTF0LTEyIDI3cTAgMTUgOC41IDI2LjV0MjIuNSAxNC41bDQ4NiAxMDZxLTggMTQgLTggMjV0NS41IDE3LjV0MTYgMTEuNXQyMCA3dDIzIDQuNXQxOC41IDQuNXE0IDEgMTUuNSA3LjV0MTcuNSA2LjVxMTUgMCAyOCAtMTZ0MjAgLTMzDQpxMTYzIDM3IDE3MiAzN3ExNyAwIDI5LjUgLTExdDEyLjUgLTI4cTAgLTE1IC04LjUgLTI2dC0yMy41IC0xNGwtMTgyIC00MGwtMSAtMTZxLTEgLTI2IDgxLjUgLTExNy41dDEwNC41IC05MS41cTQ3IDAgMTE5IDgwdDcyIDEyOXEwIDM2IC0yMy41IDUzdC01MSAxOC41dC01MSAxMS41dC0yMy41IDM0cTAgMTYgMTAgMzRsLTY4IDE5cTQzIDQ0IDQzIDExN3EwIDI2IC01IDU4cTgyIDE2IDE0NCAxNnE0NCAwIDcxLjUgLTEuNXQ0OC41IC04LjUNCnQzMSAtMTMuNXQyMC41IC0yNC41dDE1LjUgLTMzLjV0MTcgLTQ3LjV0MjQgLTYwbDUwIDI1cS0zIC00MCAtMjMgLTYwdC00Mi41IC0yMXQtNDAgLTYuNXQtMTYuNSAtMjAuNXpNMTI4MiA4NDJxLTUgNSAtMTMuNSAxNS41dC0xMiAxNC41dC0xMC41IDExLjV0LTEwIDEwLjVsLTggOHQtOC41IDcuNXQtOCA1dC04LjUgNC41cS03IDMgLTE0LjUgNXQtMjAuNSAyLjV0LTIyIDAuNWgtMzIuNWgtMzcuNXEtMTI2IDAgLTIxNyAtNDMNCnExNiAzMCAzNiA0Ni41dDU0IDI5LjV0NjUuNSAzNnQ0NiAzNi41dDUwIDU1dDQzLjUgNTAuNXExMiAtOSAyOCAtMzEuNXQzMiAtMzYuNXQzOCAtMTNsMTIgMXYtNzZsMjIgLTFxMjQ3IDk1IDM3MSAxOTBxMjggMjEgNTAgMzl0NDIuNSAzNy41dDMzIDMxdDI5LjUgMzR0MjQgMzF0MjQuNSAzN3QyMyAzOHQyNyA0Ny41dDI5LjUgNTNsNyA5cS0yIC01MyAtNDMgLTEzOXEtNzkgLTE2NSAtMjA1IC0yNjR0LTMwNiAtMTQycS0xNCAtMyAtNDIgLTcuNQ0KdC01MCAtOS41dC0zOSAtMTRxMyAtMTkgMjQuNSAtNDZ0MjEuNSAtMzRxMCAtMTEgLTI2IC0zMHpNMTA2MSAtNzlxMzkgMjYgMTMxLjUgNDcuNXQxNDYuNSAyMS41cTkgMCAyMi41IC0xNS41dDI4IC00Mi41dDI2IC01MHQyNCAtNTF0MTQuNSAtMzNxLTEyMSAtNDUgLTI0NCAtNDVxLTYxIDAgLTEyNSAxMXpNODIyIDU2OGw0OCAxMmwxMDkgLTE3N2wtNzMgLTQ4ek0xMzIzIDUxcTMgLTE1IDMgLTE2cTAgLTcgLTE3LjUgLTE0LjV0LTQ2IC0xMw0KdC01NCAtOS41dC01My41IC03LjV0LTMyIC00LjVsLTcgNDNxMjEgMiA2MC41IDguNXQ3MiAxMHQ2MC41IDMuNWgxNHpNODY2IDY3OWwtOTYgLTIwbC02IDE3cTEwIDEgMzIuNSA3dDM0LjUgNnExOSAwIDM1IC0xMHpNMTA2MSA0NWgzMWwxMCAtODNsLTQxIC0xMnY5NXpNMTk1MCAxNTM1djF2LTF6TTE5NTAgMTUzNWwtMSAtNWwtMiAtMmwxIDN6TTE5NTAgMTUzNWwxIDF6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfMzk5IiB1bmljb2RlPSImI3hmMWE5OyIgDQpkPSJNMTE2NyAtNTBxLTUgMTkgLTI0IDVxLTMwIC0yMiAtODcgLTM5dC0xMzEgLTE3cS0xMjkgMCAtMTkzIDQ5cS01IDQgLTEzIDRxLTExIDAgLTI2IC0xMnEtNyAtNiAtNy41IC0xNnQ3LjUgLTIwcTM0IC0zMiA4Ny41IC00NnQxMDIuNSAtMTIuNXQ5OSA0LjVxNDEgNCA4NC41IDIwLjV0NjUgMzB0MjguNSAyMC41cTEyIDEyIDcgMjl6TTExMjggNjVxLTE5IDQ3IC0zOSA2MXEtMjMgMTUgLTc2IDE1cS00NyAwIC03MSAtMTANCnEtMjkgLTEyIC03OCAtNTZxLTI2IC0yNCAtMTIgLTQ0cTkgLTggMTcuNSAtNC41dDMxLjUgMjMuNXEzIDIgMTAuNSA4LjV0MTAuNSA4LjV0MTAgN3QxMS41IDd0MTIuNSA1dDE1IDQuNXQxNi41IDIuNXQyMC41IDFxMjcgMCA0NC41IC03LjV0MjMgLTE0LjV0MTMuNSAtMjJxMTAgLTE3IDEyLjUgLTIwdDEyLjUgMXEyMyAxMiAxNCAzNHpNMTQ4MyAzNDZxMCAyMiAtNSA0NC41dC0xNi41IDQ1dC0zNCAzNi41dC01Mi41IDE0DQpxLTMzIDAgLTk3IC00MS41dC0xMjkgLTgzLjV0LTEwMSAtNDJxLTI3IC0xIC02My41IDE5dC03NiA0OXQtODMuNSA1OHQtMTAwIDQ5dC0xMTEgMTlxLTExNSAtMSAtMTk3IC03OC41dC04NCAtMTc4LjVxLTIgLTExMiA3NCAtMTY0cTI5IC0yMCA2Mi41IC0yOC41dDEwMy41IC04LjVxNTcgMCAxMzIgMzIuNXQxMzQgNzF0MTIwIDcwLjV0OTMgMzFxMjYgLTEgNjUgLTMxLjV0NzEuNSAtNjd0NjggLTY3LjV0NTUuNSAtMzJxMzUgLTMgNTguNSAxNA0KdDU1LjUgNjNxMjggNDEgNDIuNSAxMDF0MTQuNSAxMDZ6TTE1MzYgNTA2cTAgLTE2NCAtNjIgLTMwNC41dC0xNjYgLTIzNnQtMjQyLjUgLTE0OS41dC0yOTAuNSAtNTR0LTI5MyA1Ny41dC0yNDcuNSAxNTd0LTE3MC41IDI0MS41dC02NCAzMDJxMCA4OSAxOS41IDE3Mi41dDQ5IDE0NS41dDcwLjUgMTE4LjV0NzguNSA5NHQ3OC41IDY5LjV0NjQuNSA0Ni41dDQyLjUgMjQuNXExNCA4IDUxIDI2LjV0NTQuNSAyOC41dDQ4IDMwdDYwLjUgNDQNCnEzNiAyOCA1OCA3Mi41dDMwIDEyNS41cTEyOSAtMTU1IDE4NiAtMTkzcTQ0IC0yOSAxMzAgLTY4dDEyOSAtNjZxMjEgLTEzIDM5IC0yNXQ2MC41IC00Ni41dDc2IC03MC41dDc1IC05NXQ2OSAtMTIydDQ3IC0xNDguNXQxOS41IC0xNzcuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80MDAiIHVuaWNvZGU9IiYjeGYxYWE7IiANCmQ9Ik0xMDcwIDQ2M2wtMTYwIC0xNjBsLTE1MSAtMTUybC0zMCAtMzBxLTY1IC02NCAtMTUxLjUgLTg3dC0xNzEuNSAtMnEtMTYgLTcwIC03MiAtMTE1dC0xMjkgLTQ1cS04NSAwIC0xNDUgNjAuNXQtNjAgMTQ1LjVxMCA3MiA0NC41IDEyOHQxMTMuNSA3MnEtMjIgODYgMSAxNzN0ODggMTUybDEyIDEybDE1MSAtMTUybC0xMSAtMTFxLTM3IC0zNyAtMzcgLTg5dDM3IC05MHEzNyAtMzcgODkgLTM3dDg5IDM3bDMwIDMwbDE1MSAxNTJsMTYxIDE2MHoNCk03MjkgMTE0NWwxMiAtMTJsLTE1MiAtMTUybC0xMiAxMnEtMzcgMzcgLTg5IDM3dC04OSAtMzd0LTM3IC04OS41dDM3IC04OS41bDI5IC0yOWwxNTIgLTE1MmwxNjAgLTE2MGwtMTUxIC0xNTJsLTE2MSAxNjBsLTE1MSAxNTJsLTMwIDMwcS02OCA2NyAtOTAgMTU5LjV0NSAxNzkuNXEtNzAgMTUgLTExNSA3MXQtNDUgMTI5cTAgODUgNjAgMTQ1LjV0MTQ1IDYwLjVxNzYgMCAxMzMuNSAtNDl0NjkuNSAtMTIzcTg0IDIwIDE2OS41IC0zLjUNCnQxNDkuNSAtODcuNXpNMTUzNiA3OHEwIC04NSAtNjAgLTE0NS41dC0xNDUgLTYwLjVxLTc0IDAgLTEzMSA0N3QtNzEgMTE4cS04NiAtMjggLTE3OS41IC02dC0xNjEuNSA5MGwtMTEgMTJsMTUxIDE1MmwxMiAtMTJxMzcgLTM3IDg5IC0zN3Q4OSAzN3QzNyA4OXQtMzcgODlsLTMwIDMwbC0xNTIgMTUybC0xNjAgMTYwbDE1MiAxNTJsMTYwIC0xNjBsMTUyIC0xNTJsMjkgLTMwcTY0IC02NCA4Ny41IC0xNTAuNXQyLjUgLTE3MS41DQpxNzYgLTExIDEyNi41IC02OC41dDUwLjUgLTEzNC41ek0xNTM0IDEyMDJxMCAtNzcgLTUxIC0xMzV0LTEyNyAtNjlxMjYgLTg1IDMgLTE3Ni41dC05MCAtMTU4LjVsLTEyIC0xMmwtMTUxIDE1MmwxMiAxMnEzNyAzNyAzNyA4OXQtMzcgODl0LTg5IDM3dC04OSAtMzdsLTMwIC0zMGwtMTUyIC0xNTJsLTE2MCAtMTYwbC0xNTIgMTUybDE2MSAxNjBsMTUyIDE1MmwyOSAzMHE2NyA2NyAxNTkgODkuNXQxNzggLTMuNXExMSA3NSA2OC41IDEyNg0KdDEzNS41IDUxcTg1IDAgMTQ1IC02MC41dDYwIC0xNDUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImYxYWIiIHVuaWNvZGU9IiYjeGYxYWI7IiANCmQ9Ik02NTQgNDU4cS0xIC0zIC0xMi41IDAuNXQtMzEuNSAxMS41bC0yMCA5cS00NCAyMCAtODcgNDlxLTcgNSAtNDEgMzEuNXQtMzggMjguNXEtNjcgLTEwMyAtMTM0IC0xODFxLTgxIC05NSAtMTA1IC0xMTBxLTQgLTIgLTE5LjUgLTR0LTE4LjUgMHE2IDQgODIgOTJxMjEgMjQgODUuNSAxMTV0NzguNSAxMThxMTcgMzAgNTEgOTguNXQzNiA3Ny41cS04IDEgLTExMCAtMzNxLTggLTIgLTI3LjUgLTcuNXQtMzQuNSAtOS41dC0xNyAtNQ0KcS0yIC0yIC0yIC0xMC41dC0xIC05LjVxLTUgLTEwIC0zMSAtMTVxLTIzIC03IC00NyAwcS0xOCA0IC0yOCAyMXEtNCA2IC01IDIzcTYgMiAyNC41IDV0MjkuNSA2cTU4IDE2IDEwNSAzMnExMDAgMzUgMTAyIDM1cTEwIDIgNDMgMTkuNXQ0NCAyMS41cTkgMyAyMS41IDh0MTQuNSA1LjV0NiAtMC41cTIgLTEyIC0xIC0zM3EwIC0yIC0xMi41IC0yN3QtMjYuNSAtNTMuNXQtMTcgLTMzLjVxLTI1IC01MCAtNzcgLTEzMWw2NCAtMjgNCnExMiAtNiA3NC41IC0zMnQ2Ny41IC0yOHE0IC0xIDEwLjUgLTI1LjV0NC41IC0zMC41ek00NDkgOTQ0cTMgLTE1IC00IC0yOHEtMTIgLTIzIC01MCAtMzhxLTMwIC0xMiAtNjAgLTEycS0yNiAzIC00OSAyNnEtMTQgMTUgLTE4IDQxbDEgM3EzIC0zIDE5LjUgLTV0MjYuNSAwdDU4IDE2cTM2IDEyIDU1IDE0cTE3IDAgMjEgLTE3ek0xMTQ3IDgxNWw2MyAtMjI3bC0xMzkgNDJ6TTM5IDE1bDY5NCAyMzJ2MTAzMmwtNjk0IC0yMzN2LTEwMzF6DQpNMTI4MCAzMzJsMTAyIC0zMWwtMTgxIDY1N2wtMTAwIDMxbC0yMTYgLTUzNmwxMDIgLTMxbDQ1IDExMGwyMTEgLTY1ek03NzcgMTI5NGw1NzMgLTE4NHYzODB6TTEwODggLTI5bDE1OCAtMTNsLTU0IC0xNjBsLTQwIDY2cS0xMzAgLTgzIC0yNzYgLTEwOHEtNTggLTEyIC05MSAtMTJoLTg0cS03OSAwIC0xOTkuNSAzOXQtMTgzLjUgODVxLTggNyAtOCAxNnEwIDggNSAxMy41dDEzIDUuNXE0IDAgMTggLTcuNXQzMC41IC0xNi41dDIwLjUgLTExDQpxNzMgLTM3IDE1OS41IC02MS41dDE1Ny41IC0yNC41cTk1IDAgMTY3IDE0LjV0MTU3IDUwLjVxMTUgNyAzMC41IDE1LjV0MzQgMTl0MjguNSAxNi41ek0xNTM2IDEwNTB2LTEwNzlsLTc3NCAyNDZxLTE0IC02IC0zNzUgLTEyNy41dC0zNjggLTEyMS41cS0xMyAwIC0xOCAxM3EwIDEgLTEgM3YxMDc4cTMgOSA0IDEwcTUgNiAyMCAxMXExMDcgMzYgMTQ5IDUwdjM4NGw1NTggLTE5OHEyIDAgMTYwLjUgNTV0MzE2IDEwOC41dDE2MS41IDUzLjUNCnEyMCAwIDIwIC0yMXYtNDE4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQwMiIgdW5pY29kZT0iJiN4ZjFhYzsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0yODggMTE1MnE2NiAwIDExMyAtNDd0NDcgLTExM3YtMTA4OHEwIC02NiAtNDcgLTExM3QtMTEzIC00N2gtMTI4cS02NiAwIC0xMTMgNDd0LTQ3IDExM3YxMDg4cTAgNjYgNDcgMTEzdDExMyA0N2gxMjh6TTE2NjQgOTg5cTU4IC0zNCA5MyAtOTN0MzUgLTEyOHYtNzY4cTAgLTEwNiAtNzUgLTE4MXQtMTgxIC03NWgtODY0cS02NiAwIC0xMTMgNDd0LTQ3IDExM3YxNTM2cTAgNDAgMjggNjh0NjggMjhoNjcycTQwIDAgODggLTIwdDc2IC00OA0KbDE1MiAtMTUycTI4IC0yOCA0OCAtNzZ0MjAgLTg4di0xNjN6TTkyOCAwdjEyOHEwIDE0IC05IDIzdC0yMyA5aC0xMjhxLTE0IDAgLTIzIC05dC05IC0yM3YtMTI4cTAgLTE0IDkgLTIzdDIzIC05aDEyOHExNCAwIDIzIDl0OSAyM3pNOTI4IDI1NnYxMjhxMCAxNCAtOSAyM3QtMjMgOWgtMTI4cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTEyOHEwIC0xNCA5IC0yM3QyMyAtOWgxMjhxMTQgMCAyMyA5dDkgMjN6TTkyOCA1MTJ2MTI4cTAgMTQgLTkgMjMNCnQtMjMgOWgtMTI4cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTEyOHEwIC0xNCA5IC0yM3QyMyAtOWgxMjhxMTQgMCAyMyA5dDkgMjN6TTExODQgMHYxMjhxMCAxNCAtOSAyM3QtMjMgOWgtMTI4cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTEyOHEwIC0xNCA5IC0yM3QyMyAtOWgxMjhxMTQgMCAyMyA5dDkgMjN6TTExODQgMjU2djEyOHEwIDE0IC05IDIzdC0yMyA5aC0xMjhxLTE0IDAgLTIzIC05dC05IC0yM3YtMTI4cTAgLTE0IDkgLTIzdDIzIC05aDEyOA0KcTE0IDAgMjMgOXQ5IDIzek0xMTg0IDUxMnYxMjhxMCAxNCAtOSAyM3QtMjMgOWgtMTI4cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTEyOHEwIC0xNCA5IC0yM3QyMyAtOWgxMjhxMTQgMCAyMyA5dDkgMjN6TTE0NDAgMHYxMjhxMCAxNCAtOSAyM3QtMjMgOWgtMTI4cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTEyOHEwIC0xNCA5IC0yM3QyMyAtOWgxMjhxMTQgMCAyMyA5dDkgMjN6TTE0NDAgMjU2djEyOHEwIDE0IC05IDIzdC0yMyA5aC0xMjgNCnEtMTQgMCAtMjMgLTl0LTkgLTIzdi0xMjhxMCAtMTQgOSAtMjN0MjMgLTloMTI4cTE0IDAgMjMgOXQ5IDIzek0xNDQwIDUxMnYxMjhxMCAxNCAtOSAyM3QtMjMgOWgtMTI4cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTEyOHEwIC0xNCA5IC0yM3QyMyAtOWgxMjhxMTQgMCAyMyA5dDkgMjN6TTE1MzYgODk2djI1NmgtMTYwcS00MCAwIC02OCAyOHQtMjggNjh2MTYwaC02NDB2LTUxMmg4OTZ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDAzIiB1bmljb2RlPSImI3hmMWFkOyIgDQpkPSJNMTM0NCAxNTM2cTI2IDAgNDUgLTE5dDE5IC00NXYtMTY2NHEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTEyODBxLTI2IDAgLTQ1IDE5dC0xOSA0NXYxNjY0cTAgMjYgMTkgNDV0NDUgMTloMTI4MHpNNTEyIDEyNDh2LTY0cTAgLTE0IDkgLTIzdDIzIC05aDY0cTE0IDAgMjMgOXQ5IDIzdjY0cTAgMTQgLTkgMjN0LTIzIDloLTY0cS0xNCAwIC0yMyAtOXQtOSAtMjN6TTUxMiA5OTJ2LTY0cTAgLTE0IDkgLTIzdDIzIC05aDY0cTE0IDAgMjMgOQ0KdDkgMjN2NjRxMCAxNCAtOSAyM3QtMjMgOWgtNjRxLTE0IDAgLTIzIC05dC05IC0yM3pNNTEyIDczNnYtNjRxMCAtMTQgOSAtMjN0MjMgLTloNjRxMTQgMCAyMyA5dDkgMjN2NjRxMCAxNCAtOSAyM3QtMjMgOWgtNjRxLTE0IDAgLTIzIC05dC05IC0yM3pNNTEyIDQ4MHYtNjRxMCAtMTQgOSAtMjN0MjMgLTloNjRxMTQgMCAyMyA5dDkgMjN2NjRxMCAxNCAtOSAyM3QtMjMgOWgtNjRxLTE0IDAgLTIzIC05dC05IC0yM3pNMzg0IDE2MHY2NA0KcTAgMTQgLTkgMjN0LTIzIDloLTY0cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTY0cTAgLTE0IDkgLTIzdDIzIC05aDY0cTE0IDAgMjMgOXQ5IDIzek0zODQgNDE2djY0cTAgMTQgLTkgMjN0LTIzIDloLTY0cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTY0cTAgLTE0IDkgLTIzdDIzIC05aDY0cTE0IDAgMjMgOXQ5IDIzek0zODQgNjcydjY0cTAgMTQgLTkgMjN0LTIzIDloLTY0cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTY0cTAgLTE0IDkgLTIzdDIzIC05aDY0DQpxMTQgMCAyMyA5dDkgMjN6TTM4NCA5Mjh2NjRxMCAxNCAtOSAyM3QtMjMgOWgtNjRxLTE0IDAgLTIzIC05dC05IC0yM3YtNjRxMCAtMTQgOSAtMjN0MjMgLTloNjRxMTQgMCAyMyA5dDkgMjN6TTM4NCAxMTg0djY0cTAgMTQgLTkgMjN0LTIzIDloLTY0cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTY0cTAgLTE0IDkgLTIzdDIzIC05aDY0cTE0IDAgMjMgOXQ5IDIzek04OTYgLTk2djE5MnEwIDE0IC05IDIzdC0yMyA5aC0zMjBxLTE0IDAgLTIzIC05DQp0LTkgLTIzdi0xOTJxMCAtMTQgOSAtMjN0MjMgLTloMzIwcTE0IDAgMjMgOXQ5IDIzek04OTYgNDE2djY0cTAgMTQgLTkgMjN0LTIzIDloLTY0cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTY0cTAgLTE0IDkgLTIzdDIzIC05aDY0cTE0IDAgMjMgOXQ5IDIzek04OTYgNjcydjY0cTAgMTQgLTkgMjN0LTIzIDloLTY0cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTY0cTAgLTE0IDkgLTIzdDIzIC05aDY0cTE0IDAgMjMgOXQ5IDIzek04OTYgOTI4djY0DQpxMCAxNCAtOSAyM3QtMjMgOWgtNjRxLTE0IDAgLTIzIC05dC05IC0yM3YtNjRxMCAtMTQgOSAtMjN0MjMgLTloNjRxMTQgMCAyMyA5dDkgMjN6TTg5NiAxMTg0djY0cTAgMTQgLTkgMjN0LTIzIDloLTY0cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTY0cTAgLTE0IDkgLTIzdDIzIC05aDY0cTE0IDAgMjMgOXQ5IDIzek0xMTUyIDE2MHY2NHEwIDE0IC05IDIzdC0yMyA5aC02NHEtMTQgMCAtMjMgLTl0LTkgLTIzdi02NHEwIC0xNCA5IC0yM3QyMyAtOWg2NA0KcTE0IDAgMjMgOXQ5IDIzek0xMTUyIDQxNnY2NHEwIDE0IC05IDIzdC0yMyA5aC02NHEtMTQgMCAtMjMgLTl0LTkgLTIzdi02NHEwIC0xNCA5IC0yM3QyMyAtOWg2NHExNCAwIDIzIDl0OSAyM3pNMTE1MiA2NzJ2NjRxMCAxNCAtOSAyM3QtMjMgOWgtNjRxLTE0IDAgLTIzIC05dC05IC0yM3YtNjRxMCAtMTQgOSAtMjN0MjMgLTloNjRxMTQgMCAyMyA5dDkgMjN6TTExNTIgOTI4djY0cTAgMTQgLTkgMjN0LTIzIDloLTY0cS0xNCAwIC0yMyAtOQ0KdC05IC0yM3YtNjRxMCAtMTQgOSAtMjN0MjMgLTloNjRxMTQgMCAyMyA5dDkgMjN6TTExNTIgMTE4NHY2NHEwIDE0IC05IDIzdC0yMyA5aC02NHEtMTQgMCAtMjMgLTl0LTkgLTIzdi02NHEwIC0xNCA5IC0yM3QyMyAtOWg2NHExNCAwIDIzIDl0OSAyM3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80MDQiIHVuaWNvZGU9IiYjeGYxYWU7IiBob3Jpei1hZHYteD0iMTI4MCIgDQpkPSJNMTE4OCA5ODhsLTI5MiAtMjkydi04MjRxMCAtNDYgLTMzIC03OXQtNzkgLTMzdC03OSAzM3QtMzMgNzl2Mzg0aC02NHYtMzg0cTAgLTQ2IC0zMyAtNzl0LTc5IC0zM3QtNzkgMzN0LTMzIDc5djgyNGwtMjkyIDI5MnEtMjggMjggLTI4IDY4dDI4IDY4cTI5IDI4IDY4LjUgMjh0NjcuNSAtMjhsMjI4IC0yMjhoMzY4bDIyOCAyMjhxMjggMjggNjggMjh0NjggLTI4cTI4IC0yOSAyOCAtNjguNXQtMjggLTY3LjV6TTg2NCAxMTUyDQpxMCAtOTMgLTY1LjUgLTE1OC41dC0xNTguNSAtNjUuNXQtMTU4LjUgNjUuNXQtNjUuNSAxNTguNXQ2NS41IDE1OC41dDE1OC41IDY1LjV0MTU4LjUgLTY1LjV0NjUuNSAtMTU4LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMUIxIiB1bmljb2RlPSImI3hmMWIwOyIgaG9yaXotYWR2LXg9IjE2NjQiIA0KZD0iTTc4MCAxMDY0cTAgLTYwIC0xOSAtMTEzLjV0LTYzIC05Mi41dC0xMDUgLTM5cS03NiAwIC0xMzggNTcuNXQtOTIgMTM1LjV0LTMwIDE1MXEwIDYwIDE5IDExMy41dDYzIDkyLjV0MTA1IDM5cTc3IDAgMTM4LjUgLTU3LjV0OTEuNSAtMTM1dDMwIC0xNTEuNXpNNDM4IDU4MXEwIC04MCAtNDIgLTEzOXQtMTE5IC01OXEtNzYgMCAtMTQxLjUgNTUuNXQtMTAwLjUgMTMzLjV0LTM1IDE1MnEwIDgwIDQyIDEzOS41dDExOSA1OS41DQpxNzYgMCAxNDEuNSAtNTUuNXQxMDAuNSAtMTM0dDM1IC0xNTIuNXpNODMyIDYwOHExMTggMCAyNTUgLTk3LjV0MjI5IC0yMzd0OTIgLTI1NC41cTAgLTQ2IC0xNyAtNzYuNXQtNDguNSAtNDV0LTY0LjUgLTIwdC03NiAtNS41cS02OCAwIC0xODcuNSA0NXQtMTgyLjUgNDVxLTY2IDAgLTE5Mi41IC00NC41dC0yMDAuNSAtNDQuNXEtMTgzIDAgLTE4MyAxNDZxMCA4NiA1NiAxOTEuNXQxMzkuNSAxOTIuNXQxODcuNSAxNDZ0MTkzIDU5ek0xMDcxIDgxOQ0KcS02MSAwIC0xMDUgMzl0LTYzIDkyLjV0LTE5IDExMy41cTAgNzQgMzAgMTUxLjV0OTEuNSAxMzV0MTM4LjUgNTcuNXE2MSAwIDEwNSAtMzl0NjMgLTkyLjV0MTkgLTExMy41cTAgLTczIC0zMCAtMTUxdC05MiAtMTM1LjV0LTEzOCAtNTcuNXpNMTUwMyA5MjNxNzcgMCAxMTkgLTU5LjV0NDIgLTEzOS41cTAgLTc0IC0zNSAtMTUydC0xMDAuNSAtMTMzLjV0LTE0MS41IC01NS41cS03NyAwIC0xMTkgNTl0LTQyIDEzOXEwIDc0IDM1IDE1Mi41DQp0MTAwLjUgMTM0dDE0MS41IDU1LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDA2IiB1bmljb2RlPSImI3hmMWIxOyIgaG9yaXotYWR2LXg9Ijc2OCIgDQpkPSJNNzA0IDEwMDhxMCAtMTQ1IC01NyAtMjQzLjV0LTE1MiAtMTM1LjVsNDUgLTgyMXEyIC0yNiAtMTYgLTQ1dC00NCAtMTloLTE5MnEtMjYgMCAtNDQgMTl0LTE2IDQ1bDQ1IDgyMXEtOTUgMzcgLTE1MiAxMzUuNXQtNTcgMjQzLjVxMCAxMjggNDIuNSAyNDkuNXQxMTcuNSAyMDB0MTYwIDc4LjV0MTYwIC03OC41dDExNy41IC0yMDB0NDIuNSAtMjQ5LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDA3IiB1bmljb2RlPSImI3hmMWIyOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTg5NiAtOTNsNjQwIDM0OXY2MzZsLTY0MCAtMjMzdi03NTJ6TTgzMiA3NzJsNjk4IDI1NGwtNjk4IDI1NGwtNjk4IC0yNTR6TTE2NjQgMTAyNHYtNzY4cTAgLTM1IC0xOCAtNjV0LTQ5IC00N2wtNzA0IC0zODRxLTI4IC0xNiAtNjEgLTE2dC02MSAxNmwtNzA0IDM4NHEtMzEgMTcgLTQ5IDQ3dC0xOCA2NXY3NjhxMCA0MCAyMyA3M3Q2MSA0N2w3MDQgMjU2cTIyIDggNDQgOHQ0NCAtOGw3MDQgLTI1NnEzOCAtMTQgNjEgLTQ3dDIzIC03M3oNCiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQwOCIgdW5pY29kZT0iJiN4ZjFiMzsiIGhvcml6LWFkdi14PSIyMzA0IiANCmQ9Ik02NDAgLTk2bDM4NCAxOTJ2MzE0bC0zODQgLTE2NHYtMzQyek01NzYgMzU4bDQwNCAxNzNsLTQwNCAxNzNsLTQwNCAtMTczek0xNjY0IC05NmwzODQgMTkydjMxNGwtMzg0IC0xNjR2LTM0MnpNMTYwMCAzNThsNDA0IDE3M2wtNDA0IDE3M2wtNDA0IC0xNzN6TTExNTIgNjUxbDM4NCAxNjV2MjY2bC0zODQgLTE2NHYtMjY3ek0xMDg4IDEwMzBsNDQxIDE4OWwtNDQxIDE4OWwtNDQxIC0xODl6TTIxNzYgNTEydi00MTZxMCAtMzYgLTE5IC02Nw0KdC01MiAtNDdsLTQ0OCAtMjI0cS0yNSAtMTQgLTU3IC0xNHQtNTcgMTRsLTQ0OCAyMjRxLTQgMiAtNyA0cS0yIC0yIC03IC00bC00NDggLTIyNHEtMjUgLTE0IC01NyAtMTR0LTU3IDE0bC00NDggMjI0cS0zMyAxNiAtNTIgNDd0LTE5IDY3djQxNnEwIDM4IDIxLjUgNzB0NTYuNSA0OGw0MzQgMTg2djQwMHEwIDM4IDIxLjUgNzB0NTYuNSA0OGw0NDggMTkycTIzIDEwIDUwIDEwdDUwIC0xMGw0NDggLTE5MnEzNSAtMTYgNTYuNSAtNDh0MjEuNSAtNzANCnYtNDAwbDQzNCAtMTg2cTM2IC0xNiA1NyAtNDh0MjEgLTcweiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQwOSIgdW5pY29kZT0iJiN4ZjFiNDsiIGhvcml6LWFkdi14PSIyMDQ4IiANCmQ9Ik0xODQ4IDExOTdoLTUxMXYtMTI0aDUxMXYxMjR6TTE1OTYgNzcxcS05MCAwIC0xNDYgLTUyLjV0LTYyIC0xNDIuNWg0MDhxLTE4IDE5NSAtMjAwIDE5NXpNMTYxMiAxODZxNjMgMCAxMjIgMzJ0NzYgODdoMjIxcS0xMDAgLTMwNyAtNDI3IC0zMDdxLTIxNCAwIC0zNDAuNSAxMzJ0LTEyNi41IDM0N3EwIDIwOCAxMzAuNSAzNDUuNXQzMzYuNSAxMzcuNXExMzggMCAyNDAuNSAtNjh0MTUzIC0xNzl0NTAuNSAtMjQ4cTAgLTE3IC0yIC00N2gtNjU4DQpxMCAtMTExIDU3LjUgLTE3MS41dDE2Ni41IC02MC41ek0yNzcgMjM2aDI5NnEyMDUgMCAyMDUgMTY3cTAgMTgwIC0xOTkgMTgwaC0zMDJ2LTM0N3pNMjc3IDc3M2gyODFxNzggMCAxMjMuNSAzNi41dDQ1LjUgMTEzLjVxMCAxNDQgLTE5MCAxNDRoLTI2MHYtMjk0ek0wIDEyODJoNTk0cTg3IDAgMTU1IC0xNHQxMjYuNSAtNDcuNXQ5MCAtOTYuNXQzMS41IC0xNTRxMCAtMTgxIC0xNzIgLTI2M3ExMTQgLTMyIDE3MiAtMTE1dDU4IC0yMDQNCnEwIC03NSAtMjQuNSAtMTM2LjV0LTY2IC0xMDMuNXQtOTguNSAtNzF0LTEyMSAtNDJ0LTEzNCAtMTNoLTYxMXYxMjYweiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQxMCIgdW5pY29kZT0iJiN4ZjFiNTsiIA0KZD0iTTEyNDggMTQwOHExMTkgMCAyMDMuNSAtODQuNXQ4NC41IC0yMDMuNXYtOTYwcTAgLTExOSAtODQuNSAtMjAzLjV0LTIwMy41IC04NC41aC05NjBxLTExOSAwIC0yMDMuNSA4NC41dC04NC41IDIwMy41djk2MHEwIDExOSA4NC41IDIwMy41dDIwMy41IDg0LjVoOTYwek00OTkgMTA0MWgtMzcxdi03ODdoMzgycTExNyAwIDE5NyA1Ny41dDgwIDE3MC41cTAgMTU4IC0xNDMgMjAwcTEwNyA1MiAxMDcgMTY0cTAgNTcgLTE5LjUgOTYuNQ0KdC01Ni41IDYwLjV0LTc5IDI5LjV0LTk3IDguNXpNNDc3IDcyM2gtMTc2djE4NGgxNjNxMTE5IDAgMTE5IC05MHEwIC05NCAtMTA2IC05NHpNNDg2IDM4OGgtMTg1djIxN2gxODlxMTI0IDAgMTI0IC0xMTNxMCAtMTA0IC0xMjggLTEwNHpNMTEzNiAzNTZxLTY4IDAgLTEwNCAzOHQtMzYgMTA3aDQxMXExIDEwIDEgMzBxMCAxMzIgLTc0LjUgMjIwLjV0LTIwMy41IDg4LjVxLTEyOCAwIC0yMTAgLTg2dC04MiAtMjE2cTAgLTEzNSA3OSAtMjE3DQp0MjEzIC04MnEyMDUgMCAyNjcgMTkxaC0xMzhxLTExIC0zNCAtNDcuNSAtNTR0LTc1LjUgLTIwek0xMTI2IDcyMnExMTMgMCAxMjQgLTEyMmgtMjU0cTQgNTYgMzkgODl0OTEgMzN6TTk2NCA5ODhoMzE5di03N2gtMzE5djc3eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQxMSIgdW5pY29kZT0iJiN4ZjFiNjsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNTgyIDk1NHEwIC0xMDEgLTcxLjUgLTE3Mi41dC0xNzIuNSAtNzEuNXQtMTcyLjUgNzEuNXQtNzEuNSAxNzIuNXQ3MS41IDE3Mi41dDE3Mi41IDcxLjV0MTcyLjUgLTcxLjV0NzEuNSAtMTcyLjV6TTgxMiAyMTJxMCAxMDQgLTczIDE3N3QtMTc3IDczcS0yNyAwIC01NCAtNmwxMDQgLTQycTc3IC0zMSAxMDkuNSAtMTA2LjV0MS41IC0xNTEuNXEtMzEgLTc3IC0xMDcgLTEwOXQtMTUyIC0xcS0yMSA4IC02MiAyNC41dC02MSAyNC41DQpxMzIgLTYwIDkxIC05Ni41dDEzMCAtMzYuNXExMDQgMCAxNzcgNzN0NzMgMTc3ek0xNjQyIDk1M3EwIDEyNiAtODkuNSAyMTUuNXQtMjE1LjUgODkuNXEtMTI3IDAgLTIxNi41IC04OS41dC04OS41IC0yMTUuNXEwIC0xMjcgODkuNSAtMjE2dDIxNi41IC04OXExMjYgMCAyMTUuNSA4OXQ4OS41IDIxNnpNMTc5MiA5NTNxMCAtMTg5IC0xMzMuNSAtMzIydC0zMjEuNSAtMTMzbC00MzcgLTMxOXEtMTIgLTEyOSAtMTA5IC0yMTh0LTIyOSAtODkNCnEtMTIxIDAgLTIxNCA3NnQtMTE4IDE5MmwtMjMwIDkydjQyOWwzODkgLTE1N3E3OSA0OCAxNzMgNDhxMTMgMCAzNSAtMmwyODQgNDA3cTIgMTg3IDEzNS41IDMxOXQzMjAuNSAxMzJxMTg4IDAgMzIxLjUgLTEzMy41dDEzMy41IC0zMjEuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80MTIiIHVuaWNvZGU9IiYjeGYxYjc7IiANCmQ9Ik0xMjQyIDg4OXEwIDgwIC01NyAxMzYuNXQtMTM3IDU2LjV0LTEzNi41IC01N3QtNTYuNSAtMTM2cTAgLTgwIDU2LjUgLTEzNi41dDEzNi41IC01Ni41dDEzNyA1Ni41dDU3IDEzNi41ek02MzIgMzAxcTAgLTgzIC01OCAtMTQwLjV0LTE0MCAtNTcuNXEtNTYgMCAtMTAzIDI5dC03MiA3N3E1MiAtMjAgOTggLTQwcTYwIC0yNCAxMjAgMS41dDg1IDg2LjVxMjQgNjAgLTEuNSAxMjB0LTg2LjUgODRsLTgyIDMzcTIyIDUgNDIgNQ0KcTgyIDAgMTQwIC01Ny41dDU4IC0xNDAuNXpNMTUzNiAxMTIwdi05NjBxMCAtMTE5IC04NC41IC0yMDMuNXQtMjAzLjUgLTg0LjVoLTk2MHEtMTE5IDAgLTIwMy41IDg0LjV0LTg0LjUgMjAzLjV2MTUzbDE3MiAtNjlxMjAgLTkyIDkzLjUgLTE1MnQxNjguNSAtNjBxMTA0IDAgMTgxIDcwdDg3IDE3M2wzNDUgMjUycTE1MCAwIDI1NS41IDEwNS41dDEwNS41IDI1NC41cTAgMTUwIC0xMDUuNSAyNTUuNXQtMjU1LjUgMTA1LjUNCnEtMTQ4IDAgLTI1MyAtMTA0LjV0LTEwNyAtMjUyLjVsLTIyNSAtMzIycS05IDEgLTI4IDFxLTc1IDAgLTEzNyAtMzdsLTI5NyAxMTl2NDY4cTAgMTE5IDg0LjUgMjAzLjV0MjAzLjUgODQuNWg5NjBxMTE5IDAgMjAzLjUgLTg0LjV0ODQuNSAtMjAzLjV6TTEyODkgODg3cTAgLTEwMCAtNzEgLTE3MC41dC0xNzEgLTcwLjV0LTE3MC41IDcwLjV0LTcwLjUgMTcwLjV0NzAuNSAxNzF0MTcwLjUgNzFxMTAxIDAgMTcxLjUgLTcwLjV0NzAuNSAtMTcxLjV6DQoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80MTMiIHVuaWNvZGU9IiYjeGYxYjg7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNODM2IDM2N2wtMTUgLTM2OGwtMiAtMjJsLTQyMCAyOXEtMzYgMyAtNjcgMzEuNXQtNDcgNjUuNXEtMTEgMjcgLTE0LjUgNTV0NCA2NXQxMiA1NXQyMS41IDY0dDE5IDUzcTc4IC0xMiA1MDkgLTI4ek00NDkgOTUzbDE4MCAtMzc5bC0xNDcgOTJxLTYzIC03MiAtMTExLjUgLTE0NC41dC03Mi41IC0xMjV0LTM5LjUgLTk0LjV0LTE4LjUgLTYzbC00IC0yMWwtMTkwIDM1N3EtMTcgMjYgLTE4IDU2dDYgNDdsOCAxOHEzNSA2MyAxMTQgMTg4DQpsLTE0MCA4NnpNMTY4MCA0MzZsLTE4OCAtMzU5cS0xMiAtMjkgLTM2LjUgLTQ2LjV0LTQzLjUgLTIwLjVsLTE4IC00cS03MSAtNyAtMjE5IC0xMmw4IC0xNjRsLTIzMCAzNjdsMjExIDM2Mmw3IC0xNzNxMTcwIC0xNiAyODMgLTV0MTcwIDMzek04OTUgMTM2MHEtNDcgLTYzIC0yNjUgLTQzNWwtMzE3IDE4N2wtMTkgMTJsMjI1IDM1NnEyMCAzMSA2MCA0NXQ4MCAxMHEyNCAtMiA0OC41IC0xMnQ0MiAtMjF0NDEuNSAtMzN0MzYgLTM0LjUNCnQzNiAtMzkuNXQzMiAtMzV6TTE1NTAgMTA1M2wyMTIgLTM2M3ExOCAtMzcgMTIuNSAtNzZ0LTI3LjUgLTc0cS0xMyAtMjAgLTMzIC0zN3QtMzggLTI4dC00OC41IC0yMnQtNDcgLTE2dC01MS41IC0xNHQtNDYgLTEycS0zNCA3MiAtMjY1IDQzNmwzMTMgMTk1ek0xNDA3IDEyNzlsMTQyIDgzbC0yMjAgLTM3M2wtNDE5IDIwbDE1MSA4NnEtMzQgODkgLTc1IDE2NnQtNzUuNSAxMjMuNXQtNjQuNSA4MHQtNDcgNDYuNWwtMTcgMTNsNDA1IC0xDQpxMzEgMyA1OCAtMTAuNXQzOSAtMjguNWwxMSAtMTVxMzkgLTYxIDExMiAtMTkweiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQxNCIgdW5pY29kZT0iJiN4ZjFiOTsiIGhvcml6LWFkdi14PSIyMDQ4IiANCmQ9Ik00ODAgNDQ4cTAgNjYgLTQ3IDExM3QtMTEzIDQ3dC0xMTMgLTQ3dC00NyAtMTEzdDQ3IC0xMTN0MTEzIC00N3QxMTMgNDd0NDcgMTEzek01MTYgNzY4aDEwMTZsLTg5IDM1N3EtMiA4IC0xNCAxNy41dC0yMSA5LjVoLTc2OHEtOSAwIC0yMSAtOS41dC0xNCAtMTcuNXpNMTg4OCA0NDhxMCA2NiAtNDcgMTEzdC0xMTMgNDd0LTExMyAtNDd0LTQ3IC0xMTN0NDcgLTExM3QxMTMgLTQ3dDExMyA0N3Q0NyAxMTN6TTIwNDggNTQ0di0zODQNCnEwIC0xNCAtOSAtMjN0LTIzIC05aC05NnYtMTI4cTAgLTgwIC01NiAtMTM2dC0xMzYgLTU2dC0xMzYgNTZ0LTU2IDEzNnYxMjhoLTEwMjR2LTEyOHEwIC04MCAtNTYgLTEzNnQtMTM2IC01NnQtMTM2IDU2dC01NiAxMzZ2MTI4aC05NnEtMTQgMCAtMjMgOXQtOSAyM3YzODRxMCA5MyA2NS41IDE1OC41dDE1OC41IDY1LjVoMjhsMTA1IDQxOXEyMyA5NCAxMDQgMTU3LjV0MTc5IDYzLjVoNzY4cTk4IDAgMTc5IC02My41dDEwNCAtMTU3LjUNCmwxMDUgLTQxOWgyOHE5MyAwIDE1OC41IC02NS41dDY1LjUgLTE1OC41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQxNSIgdW5pY29kZT0iJiN4ZjFiYTsiIGhvcml6LWFkdi14PSIyMDQ4IiANCmQ9Ik0xODI0IDY0MHE5MyAwIDE1OC41IC02NS41dDY1LjUgLTE1OC41di0zODRxMCAtMTQgLTkgLTIzdC0yMyAtOWgtOTZ2LTY0cTAgLTgwIC01NiAtMTM2dC0xMzYgLTU2dC0xMzYgNTZ0LTU2IDEzNnY2NGgtMTAyNHYtNjRxMCAtODAgLTU2IC0xMzZ0LTEzNiAtNTZ0LTEzNiA1NnQtNTYgMTM2djY0aC05NnEtMTQgMCAtMjMgOXQtOSAyM3YzODRxMCA5MyA2NS41IDE1OC41dDE1OC41IDY1LjVoMjhsMTA1IDQxOXEyMyA5NCAxMDQgMTU3LjUNCnQxNzkgNjMuNWgxMjh2MjI0cTAgMTQgOSAyM3QyMyA5aDQ0OHExNCAwIDIzIC05dDkgLTIzdi0yMjRoMTI4cTk4IDAgMTc5IC02My41dDEwNCAtMTU3LjVsMTA1IC00MTloMjh6TTMyMCAxNjBxNjYgMCAxMTMgNDd0NDcgMTEzdC00NyAxMTN0LTExMyA0N3QtMTEzIC00N3QtNDcgLTExM3Q0NyAtMTEzdDExMyAtNDd6TTUxNiA2NDBoMTAxNmwtODkgMzU3cS0yIDggLTE0IDE3LjV0LTIxIDkuNWgtNzY4cS05IDAgLTIxIC05LjV0LTE0IC0xNy41eg0KTTE3MjggMTYwcTY2IDAgMTEzIDQ3dDQ3IDExM3QtNDcgMTEzdC0xMTMgNDd0LTExMyAtNDd0LTQ3IC0xMTN0NDcgLTExM3QxMTMgLTQ3eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQxNiIgdW5pY29kZT0iJiN4ZjFiYjsiIA0KZD0iTTE1MDQgNjRxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC00NjJxMSAtMTcgNiAtODcuNXQ1IC0xMDguNXEwIC0yNSAtMTggLTQyLjV0LTQzIC0xNy41aC0zMjBxLTI1IDAgLTQzIDE3LjV0LTE4IDQyLjVxMCAzOCA1IDEwOC41dDYgODcuNWgtNDYycS0yNiAwIC00NSAxOXQtMTkgNDV0MTkgNDVsNDAyIDQwM2gtMjI5cS0yNiAwIC00NSAxOXQtMTkgNDV0MTkgNDVsNDAyIDQwM2gtMTk3cS0yNiAwIC00NSAxOXQtMTkgNDV0MTkgNDVsMzg0IDM4NA0KcTE5IDE5IDQ1IDE5dDQ1IC0xOWwzODQgLTM4NHExOSAtMTkgMTkgLTQ1dC0xOSAtNDV0LTQ1IC0xOWgtMTk3bDQwMiAtNDAzcTE5IC0xOSAxOSAtNDV0LTE5IC00NXQtNDUgLTE5aC0yMjlsNDAyIC00MDNxMTkgLTE5IDE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80MTciIHVuaWNvZGU9IiYjeGYxYmM7IiANCmQ9Ik0xMTI3IDMyNnEwIDMyIC0zMCA1MXEtMTkzIDExNSAtNDQ3IDExNXEtMTMzIDAgLTI4NyAtMzRxLTQyIC05IC00MiAtNTJxMCAtMjAgMTMuNSAtMzQuNXQzNS41IC0xNC41cTUgMCAzNyA4cTEzMiAyNyAyNDMgMjdxMjI2IDAgMzk3IC0xMDNxMTkgLTExIDMzIC0xMXExOSAwIDMzIDEzLjV0MTQgMzQuNXpNMTIyMyA1NDFxMCA0MCAtMzUgNjFxLTIzNyAxNDEgLTU0OCAxNDFxLTE1MyAwIC0zMDMgLTQycS00OCAtMTMgLTQ4IC02NA0KcTAgLTI1IDE3LjUgLTQyLjV0NDIuNSAtMTcuNXE3IDAgMzcgOHExMjIgMzMgMjUxIDMzcTI3OSAwIDQ4OCAtMTI0cTI0IC0xMyAzOCAtMTNxMjUgMCA0Mi41IDE3LjV0MTcuNSA0Mi41ek0xMzMxIDc4OXEwIDQ3IC00MCA3MHEtMTI2IDczIC0yOTMgMTEwLjV0LTM0MyAzNy41cS0yMDQgMCAtMzY0IC00N3EtMjMgLTcgLTM4LjUgLTI1LjV0LTE1LjUgLTQ4LjVxMCAtMzEgMjAuNSAtNTJ0NTEuNSAtMjFxMTEgMCA0MCA4cTEzMyAzNyAzMDcgMzcNCnExNTkgMCAzMDkuNSAtMzR0MjUzLjUgLTk1cTIxIC0xMiA0MCAtMTJxMjkgMCA1MC41IDIwLjV0MjEuNSA1MS41ek0xNTM2IDY0MHEwIC0yMDkgLTEwMyAtMzg1LjV0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzdC0yNzkuNSAyNzkuNXQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN0Mzg1LjUgLTEwM3QyNzkuNSAtMjc5LjV0MTAzIC0zODUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80MTgiIHVuaWNvZGU9IiYjeGYxYmQ7IiBob3Jpei1hZHYteD0iMTAyNCIgDQpkPSJNMTAyNCAxMjMzbC0zMDMgLTU4MmwyNCAtMzFoMjc5di00MTVoLTUwN2wtNDQgLTMwbC0xNDIgLTI3M2wtMzAgLTMwaC0zMDF2MzAzbDMwMyA1ODNsLTI0IDMwaC0yNzl2NDE1aDUwN2w0NCAzMGwxNDIgMjczbDMwIDMwaDMwMXYtMzAzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQxOSIgdW5pY29kZT0iJiN4ZjFiZTsiIGhvcml6LWFkdi14PSIyMzA0IiANCmQ9Ik03ODQgMTY0bDE2IDI0MWwtMTYgNTIzcS0xIDEwIC03LjUgMTd0LTE2LjUgN3EtOSAwIC0xNiAtN3QtNyAtMTdsLTE0IC01MjNsMTQgLTI0MXExIC0xMCA3LjUgLTE2LjV0MTUuNSAtNi41cTIyIDAgMjQgMjN6TTEwODAgMTkzbDExIDIxMWwtMTIgNTg2cTAgMTYgLTEzIDI0cS04IDUgLTE2IDV0LTE2IC01cS0xMyAtOCAtMTMgLTI0bC0xIC02bC0xMCAtNTc5cTAgLTEgMTEgLTIzNnYtMXEwIC0xMCA2IC0xN3E5IC0xMSAyMyAtMTENCnExMSAwIDIwIDlxOSA3IDkgMjB6TTM1IDUzM2wyMCAtMTI4bC0yMCAtMTI2cS0yIC05IC05IC05dC05IDlsLTE3IDEyNmwxNyAxMjhxMiA5IDkgOXQ5IC05ek0xMjEgNjEybDI2IC0yMDdsLTI2IC0yMDNxLTIgLTkgLTEwIC05cS05IDAgLTkgMTBsLTIzIDIwMmwyMyAyMDdxMCA5IDkgOXE4IDAgMTAgLTl6TTQwMSAxNTl6TTIxMyA2NTBsMjUgLTI0NWwtMjUgLTIzN3EwIC0xMSAtMTEgLTExcS0xMCAwIC0xMiAxMWwtMjEgMjM3bDIxIDI0NQ0KcTIgMTIgMTIgMTJxMTEgMCAxMSAtMTJ6TTMwNyA2NTdsMjMgLTI1MmwtMjMgLTI0NHEtMiAtMTMgLTE0IC0xM3EtMTMgMCAtMTMgMTNsLTIxIDI0NGwyMSAyNTJxMCAxMyAxMyAxM3ExMiAwIDE0IC0xM3pNNDAxIDYzOWwyMSAtMjM0bC0yMSAtMjQ2cS0yIC0xNiAtMTYgLTE2cS02IDAgLTEwLjUgNC41dC00LjUgMTEuNWwtMjAgMjQ2bDIwIDIzNHEwIDYgNC41IDEwLjV0MTAuNSA0LjVxMTQgMCAxNiAtMTV6TTc4NCAxNjR6TTQ5NSA3ODUNCmwyMSAtMzgwbC0yMSAtMjQ2cTAgLTcgLTUgLTEyLjV0LTEyIC01LjVxLTE2IDAgLTE4IDE4bC0xOCAyNDZsMTggMzgwcTIgMTggMTggMThxNyAwIDEyIC01LjV0NSAtMTIuNXpNNTg5IDg3MWwxOSAtNDY4bC0xOSAtMjQ0cTAgLTggLTUuNSAtMTMuNXQtMTMuNSAtNS41cS0xOCAwIC0yMCAxOWwtMTYgMjQ0bDE2IDQ2OHEyIDE5IDIwIDE5cTggMCAxMy41IC01LjV0NS41IC0xMy41ek02ODcgOTExbDE4IC01MDZsLTE4IC0yNDINCnEtMiAtMjEgLTIyIC0yMXEtMTkgMCAtMjEgMjFsLTE2IDI0MmwxNiA1MDZxMCA5IDYuNSAxNS41dDE0LjUgNi41cTkgMCAxNSAtNi41dDcgLTE1LjV6TTEwNzkgMTY5djB2MHYwek04ODEgOTE1bDE1IC01MTBsLTE1IC0yMzlxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41dC0xNyA3dC04IDE4bC0xNCAyMzlsMTQgNTEwcTAgMTEgNy41IDE4dDE3LjUgN3QxNy41IC03dDcuNSAtMTh6TTk4MCA4OTZsMTQgLTQ5MmwtMTQgLTIzNg0KcTAgLTExIC04IC0xOXQtMTkgLTh0LTE5IDh0LTkgMTlsLTEyIDIzNmwxMiA0OTJxMSAxMiA5IDIwdDE5IDh0MTguNSAtOHQ4LjUgLTIwek0xMTkyIDQwNGwtMTQgLTIzMXYwcTAgLTEzIC05IC0yMnQtMjIgLTl0LTIyIDl0LTEwIDIybC02IDExNGwtNiAxMTdsMTIgNjM2djNxMiAxNSAxMiAyNHE5IDcgMjAgN3E4IDAgMTUgLTVxMTQgLTggMTYgLTI2ek0yMzA0IDQyM3EwIC0xMTcgLTgzIC0xOTkuNXQtMjAwIC04Mi41aC03ODYNCnEtMTMgMiAtMjIgMTF0LTkgMjJ2ODk5cTAgMjMgMjggMzNxODUgMzQgMTgxIDM0cTE5NSAwIDMzOCAtMTMxLjV0MTYwIC0zMjMuNXE1MyAyMiAxMTAgMjJxMTE3IDAgMjAwIC04M3Q4MyAtMjAxeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjFDMCIgdW5pY29kZT0iJiN4ZjFjMDsiIA0KZD0iTTc2OCA3NjhxMjM3IDAgNDQzIDQzdDMyNSAxMjd2LTE3MHEwIC02OSAtMTAzIC0xMjh0LTI4MCAtOTMuNXQtMzg1IC0zNC41dC0zODUgMzQuNXQtMjgwIDkzLjV0LTEwMyAxMjh2MTcwcTExOSAtODQgMzI1IC0xMjd0NDQzIC00M3pNNzY4IDBxMjM3IDAgNDQzIDQzdDMyNSAxMjd2LTE3MHEwIC02OSAtMTAzIC0xMjh0LTI4MCAtOTMuNXQtMzg1IC0zNC41dC0zODUgMzQuNXQtMjgwIDkzLjV0LTEwMyAxMjh2MTcwcTExOSAtODQgMzI1IC0xMjcNCnQ0NDMgLTQzek03NjggMzg0cTIzNyAwIDQ0MyA0M3QzMjUgMTI3di0xNzBxMCAtNjkgLTEwMyAtMTI4dC0yODAgLTkzLjV0LTM4NSAtMzQuNXQtMzg1IDM0LjV0LTI4MCA5My41dC0xMDMgMTI4djE3MHExMTkgLTg0IDMyNSAtMTI3dDQ0MyAtNDN6TTc2OCAxNTM2cTIwOCAwIDM4NSAtMzQuNXQyODAgLTkzLjV0MTAzIC0xMjh2LTEyOHEwIC02OSAtMTAzIC0xMjh0LTI4MCAtOTMuNXQtMzg1IC0zNC41dC0zODUgMzQuNXQtMjgwIDkzLjUNCnQtMTAzIDEyOHYxMjhxMCA2OSAxMDMgMTI4dDI4MCA5My41dDM4NSAzNC41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjFDMSIgdW5pY29kZT0iJiN4ZjFjMTsiIA0KZD0iTTE0NjggMTE1NnEyOCAtMjggNDggLTc2dDIwIC04OHYtMTE1MnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTEzNDRxLTQwIDAgLTY4IDI4dC0yOCA2OHYxNjAwcTAgNDAgMjggNjh0NjggMjhoODk2cTQwIDAgODggLTIwdDc2IC00OHpNMTAyNCAxNDAwdi0zNzZoMzc2cS0xMCAyOSAtMjIgNDFsLTMxMyAzMTNxLTEyIDEyIC00MSAyMnpNMTQwOCAtMTI4djEwMjRoLTQxNnEtNDAgMCAtNjggMjh0LTI4IDY4djQxNmgtNzY4di0xNTM2aDEyODB6DQpNODk0IDQ2NXEzMyAtMjYgODQgLTU2cTU5IDcgMTE3IDdxMTQ3IDAgMTc3IC00OXExNiAtMjIgMiAtNTJxMCAtMSAtMSAtMmwtMiAtMnYtMXEtNiAtMzggLTcxIC0zOHEtNDggMCAtMTE1IDIwdC0xMzAgNTNxLTIyMSAtMjQgLTM5MiAtODNxLTE1MyAtMjYyIC0yNDIgLTI2MnEtMTUgMCAtMjggN2wtMjQgMTJxLTEgMSAtNiA1cS0xMCAxMCAtNiAzNnE5IDQwIDU2IDkxLjV0MTMyIDk2LjVxMTQgOSAyMyAtNnEyIC0yIDIgLTRxNTIgODUgMTA3IDE5Nw0KcTY4IDEzNiAxMDQgMjYycS0yNCA4MiAtMzAuNSAxNTkuNXQ2LjUgMTI3LjVxMTEgNDAgNDIgNDBoMjFoMXEyMyAwIDM1IC0xNXExOCAtMjEgOSAtNjhxLTIgLTYgLTQgLThxMSAtMyAxIC04di0zMHEtMiAtMTIzIC0xNCAtMTkycTU1IC0xNjQgMTQ2IC0yMzh6TTMxOCA1NHE1MiAyNCAxMzcgMTU4cS01MSAtNDAgLTg3LjUgLTg0dC00OS41IC03NHpNNzE2IDk3NHEtMTUgLTQyIC0yIC0xMzJxMSA3IDcgNDRxMCAzIDcgNDNxMSA0IDQgOA0KcS0xIDEgLTEgMnEtMSAyIC0xIDNxLTEgMjIgLTEzIDM2cTAgLTEgLTEgLTJ2LTJ6TTU5MiAzMTNxMTM1IDU0IDI4NCA4MXEtMiAxIC0xMyA5LjV0LTE2IDEzLjVxLTc2IDY3IC0xMjcgMTc2cS0yNyAtODYgLTgzIC0xOTdxLTMwIC01NiAtNDUgLTgzek0xMjM4IDMyOXEtMjQgMjQgLTE0MCAyNHE3NiAtMjggMTI0IC0yOHExNCAwIDE4IDFxMCAxIC0yIDN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDIyIiB1bmljb2RlPSImI3hmMWMyOyIgDQpkPSJNMTQ2OCAxMTU2cTI4IC0yOCA0OCAtNzZ0MjAgLTg4di0xMTUycTAgLTQwIC0yOCAtNjh0LTY4IC0yOGgtMTM0NHEtNDAgMCAtNjggMjh0LTI4IDY4djE2MDBxMCA0MCAyOCA2OHQ2OCAyOGg4OTZxNDAgMCA4OCAtMjB0NzYgLTQ4ek0xMDI0IDE0MDB2LTM3NmgzNzZxLTEwIDI5IC0yMiA0MWwtMzEzIDMxM3EtMTIgMTIgLTQxIDIyek0xNDA4IC0xMjh2MTAyNGgtNDE2cS00MCAwIC02OCAyOHQtMjggNjh2NDE2aC03Njh2LTE1MzZoMTI4MHoNCk0yMzMgNzY4di0xMDdoNzBsMTY0IC02NjFoMTU5bDEyOCA0ODVxNyAyMCAxMCA0NnEyIDE2IDIgMjRoNGwzIC0yNHExIC0zIDMuNSAtMjB0NS41IC0yNmwxMjggLTQ4NWgxNTlsMTY0IDY2MWg3MHYxMDdoLTMwMHYtMTA3aDkwbC05OSAtNDM4cS01IC0yMCAtNyAtNDZsLTIgLTIxaC00cTAgMyAtMC41IDYuNXQtMS41IDh0LTEgNi41cS0xIDUgLTQgMjF0LTUgMjVsLTE0NCA1NDVoLTExNGwtMTQ0IC01NDVxLTIgLTkgLTQuNSAtMjQuNQ0KdC0zLjUgLTIxLjVsLTQgLTIxaC00bC0yIDIxcS0yIDI2IC03IDQ2bC05OSA0MzhoOTB2MTA3aC0zMDB6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDIzIiB1bmljb2RlPSImI3hmMWMzOyIgDQpkPSJNMTQ2OCAxMTU2cTI4IC0yOCA0OCAtNzZ0MjAgLTg4di0xMTUycTAgLTQwIC0yOCAtNjh0LTY4IC0yOGgtMTM0NHEtNDAgMCAtNjggMjh0LTI4IDY4djE2MDBxMCA0MCAyOCA2OHQ2OCAyOGg4OTZxNDAgMCA4OCAtMjB0NzYgLTQ4ek0xMDI0IDE0MDB2LTM3NmgzNzZxLTEwIDI5IC0yMiA0MWwtMzEzIDMxM3EtMTIgMTIgLTQxIDIyek0xNDA4IC0xMjh2MTAyNGgtNDE2cS00MCAwIC02OCAyOHQtMjggNjh2NDE2aC03Njh2LTE1MzZoMTI4MHoNCk00MjkgMTA2di0xMDZoMjgxdjEwNmgtNzVsMTAzIDE2MXE1IDcgMTAgMTYuNXQ3LjUgMTMuNXQzLjUgNGgycTEgLTQgNSAtMTBxMiAtNCA0LjUgLTcuNXQ2IC04dDYuNSAtOC41bDEwNyAtMTYxaC03NnYtMTA2aDI5MXYxMDZoLTY4bC0xOTIgMjczbDE5NSAyODJoNjd2MTA3aC0yNzl2LTEwN2g3NGwtMTAzIC0xNTlxLTQgLTcgLTEwIC0xNi41dC05IC0xMy41bC0yIC0zaC0ycS0xIDQgLTUgMTBxLTYgMTEgLTE3IDIzbC0xMDYgMTU5aDc2djEwNw0KaC0yOTB2LTEwN2g2OGwxODkgLTI3MmwtMTk0IC0yODNoLTY4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQyNCIgdW5pY29kZT0iJiN4ZjFjNDsiIA0KZD0iTTE0NjggMTE1NnEyOCAtMjggNDggLTc2dDIwIC04OHYtMTE1MnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTEzNDRxLTQwIDAgLTY4IDI4dC0yOCA2OHYxNjAwcTAgNDAgMjggNjh0NjggMjhoODk2cTQwIDAgODggLTIwdDc2IC00OHpNMTAyNCAxNDAwdi0zNzZoMzc2cS0xMCAyOSAtMjIgNDFsLTMxMyAzMTNxLTEyIDEyIC00MSAyMnpNMTQwOCAtMTI4djEwMjRoLTQxNnEtNDAgMCAtNjggMjh0LTI4IDY4djQxNmgtNzY4di0xNTM2aDEyODB6DQpNNDE2IDEwNnYtMTA2aDMyN3YxMDZoLTkzdjE2N2gxMzdxNzYgMCAxMTggMTVxNjcgMjMgMTA2LjUgODd0MzkuNSAxNDZxMCA4MSAtMzcgMTQxdC0xMDAgODdxLTQ4IDE5IC0xMzAgMTloLTM2OHYtMTA3aDkydi01NTVoLTkyek03NjkgMzg2aC0xMTl2MjY4aDEyMHE1MiAwIDgzIC0xOHE1NiAtMzMgNTYgLTExNXEwIC04OSAtNjIgLTEyMHEtMzEgLTE1IC03OCAtMTV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDI1IiB1bmljb2RlPSImI3hmMWM1OyIgDQpkPSJNMTQ2OCAxMTU2cTI4IC0yOCA0OCAtNzZ0MjAgLTg4di0xMTUycTAgLTQwIC0yOCAtNjh0LTY4IC0yOGgtMTM0NHEtNDAgMCAtNjggMjh0LTI4IDY4djE2MDBxMCA0MCAyOCA2OHQ2OCAyOGg4OTZxNDAgMCA4OCAtMjB0NzYgLTQ4ek0xMDI0IDE0MDB2LTM3NmgzNzZxLTEwIDI5IC0yMiA0MWwtMzEzIDMxM3EtMTIgMTIgLTQxIDIyek0xNDA4IC0xMjh2MTAyNGgtNDE2cS00MCAwIC02OCAyOHQtMjggNjh2NDE2aC03Njh2LTE1MzZoMTI4MHoNCk0xMjgwIDMyMHYtMzIwaC0xMDI0djE5MmwxOTIgMTkybDEyOCAtMTI4bDM4NCAzODR6TTQ0OCA1MTJxLTgwIDAgLTEzNiA1NnQtNTYgMTM2dDU2IDEzNnQxMzYgNTZ0MTM2IC01NnQ1NiAtMTM2dC01NiAtMTM2dC0xMzYgLTU2eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQyNiIgdW5pY29kZT0iJiN4ZjFjNjsiIA0KZD0iTTY0MCAxMTUydjEyOGgtMTI4di0xMjhoMTI4ek03NjggMTAyNHYxMjhoLTEyOHYtMTI4aDEyOHpNNjQwIDg5NnYxMjhoLTEyOHYtMTI4aDEyOHpNNzY4IDc2OHYxMjhoLTEyOHYtMTI4aDEyOHpNMTQ2OCAxMTU2cTI4IC0yOCA0OCAtNzZ0MjAgLTg4di0xMTUycTAgLTQwIC0yOCAtNjh0LTY4IC0yOGgtMTM0NHEtNDAgMCAtNjggMjh0LTI4IDY4djE2MDBxMCA0MCAyOCA2OHQ2OCAyOGg4OTZxNDAgMCA4OCAtMjB0NzYgLTQ4ek0xMDI0IDE0MDANCnYtMzc2aDM3NnEtMTAgMjkgLTIyIDQxbC0zMTMgMzEzcS0xMiAxMiAtNDEgMjJ6TTE0MDggLTEyOHYxMDI0aC00MTZxLTQwIDAgLTY4IDI4dC0yOCA2OHY0MTZoLTEyOHYtMTI4aC0xMjh2MTI4aC01MTJ2LTE1MzZoMTI4MHpNNzgxIDU5M2wxMDcgLTM0OXE4IC0yNyA4IC01MnEwIC04MyAtNzIuNSAtMTM3LjV0LTE4My41IC01NC41dC0xODMuNSA1NC41dC03Mi41IDEzNy41cTAgMjUgOCA1MnEyMSA2MyAxMjAgMzk2djEyOGgxMjh2LTEyOGg3OQ0KcTIyIDAgMzkgLTEzdDIzIC0zNHpNNjQwIDEyOHE1MyAwIDkwLjUgMTl0MzcuNSA0NXQtMzcuNSA0NXQtOTAuNSAxOXQtOTAuNSAtMTl0LTM3LjUgLTQ1dDM3LjUgLTQ1dDkwLjUgLTE5eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQyNyIgdW5pY29kZT0iJiN4ZjFjNzsiIA0KZD0iTTE0NjggMTE1NnEyOCAtMjggNDggLTc2dDIwIC04OHYtMTE1MnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTEzNDRxLTQwIDAgLTY4IDI4dC0yOCA2OHYxNjAwcTAgNDAgMjggNjh0NjggMjhoODk2cTQwIDAgODggLTIwdDc2IC00OHpNMTAyNCAxNDAwdi0zNzZoMzc2cS0xMCAyOSAtMjIgNDFsLTMxMyAzMTNxLTEyIDEyIC00MSAyMnpNMTQwOCAtMTI4djEwMjRoLTQxNnEtNDAgMCAtNjggMjh0LTI4IDY4djQxNmgtNzY4di0xNTM2aDEyODB6DQpNNjIwIDY4NnEyMCAtOCAyMCAtMzB2LTU0NHEwIC0yMiAtMjAgLTMwcS04IC0yIC0xMiAtMnEtMTIgMCAtMjMgOWwtMTY2IDE2N2gtMTMxcS0xNCAwIC0yMyA5dC05IDIzdjE5MnEwIDE0IDkgMjN0MjMgOWgxMzFsMTY2IDE2N3ExNiAxNSAzNSA3ek0xMDM3IC0zcTMxIDAgNTAgMjRxMTI5IDE1OSAxMjkgMzYzdC0xMjkgMzYzcS0xNiAyMSAtNDMgMjR0LTQ3IC0xNHEtMjEgLTE3IC0yMy41IC00My41dDE0LjUgLTQ3LjUNCnExMDAgLTEyMyAxMDAgLTI4MnQtMTAwIC0yODJxLTE3IC0yMSAtMTQuNSAtNDcuNXQyMy41IC00Mi41cTE4IC0xNSA0MCAtMTV6TTgyNiAxNDVxMjcgMCA0NyAyMHE4NyA5MyA4NyAyMTl0LTg3IDIxOXEtMTggMTkgLTQ1IDIwdC00NiAtMTd0LTIwIC00NC41dDE4IC00Ni41cTUyIC01NyA1MiAtMTMxdC01MiAtMTMxcS0xOSAtMjAgLTE4IC00Ni41dDIwIC00NC41cTIwIC0xNyA0NCAtMTd6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDI4IiB1bmljb2RlPSImI3hmMWM4OyIgDQpkPSJNMTQ2OCAxMTU2cTI4IC0yOCA0OCAtNzZ0MjAgLTg4di0xMTUycTAgLTQwIC0yOCAtNjh0LTY4IC0yOGgtMTM0NHEtNDAgMCAtNjggMjh0LTI4IDY4djE2MDBxMCA0MCAyOCA2OHQ2OCAyOGg4OTZxNDAgMCA4OCAtMjB0NzYgLTQ4ek0xMDI0IDE0MDB2LTM3NmgzNzZxLTEwIDI5IC0yMiA0MWwtMzEzIDMxM3EtMTIgMTIgLTQxIDIyek0xNDA4IC0xMjh2MTAyNGgtNDE2cS00MCAwIC02OCAyOHQtMjggNjh2NDE2aC03Njh2LTE1MzZoMTI4MHoNCk03NjggNzY4cTUyIDAgOTAgLTM4dDM4IC05MHYtMzg0cTAgLTUyIC0zOCAtOTB0LTkwIC0zOGgtMzg0cS01MiAwIC05MCAzOHQtMzggOTB2Mzg0cTAgNTIgMzggOTB0OTAgMzhoMzg0ek0xMjYwIDc2NnEyMCAtOCAyMCAtMzB2LTU3NnEwIC0yMiAtMjAgLTMwcS04IC0yIC0xMiAtMnEtMTQgMCAtMjMgOWwtMjY1IDI2NnY5MGwyNjUgMjY2cTkgOSAyMyA5cTQgMCAxMiAtMnoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80MjkiIHVuaWNvZGU9IiYjeGYxYzk7IiANCmQ9Ik0xNDY4IDExNTZxMjggLTI4IDQ4IC03NnQyMCAtODh2LTExNTJxMCAtNDAgLTI4IC02OHQtNjggLTI4aC0xMzQ0cS00MCAwIC02OCAyOHQtMjggNjh2MTYwMHEwIDQwIDI4IDY4dDY4IDI4aDg5NnE0MCAwIDg4IC0yMHQ3NiAtNDh6TTEwMjQgMTQwMHYtMzc2aDM3NnEtMTAgMjkgLTIyIDQxbC0zMTMgMzEzcS0xMiAxMiAtNDEgMjJ6TTE0MDggLTEyOHYxMDI0aC00MTZxLTQwIDAgLTY4IDI4dC0yOCA2OHY0MTZoLTc2OHYtMTUzNmgxMjgweg0KTTQ4MCA3NjhxOCAxMSAyMSAxMi41dDI0IC02LjVsNTEgLTM4cTExIC04IDEyLjUgLTIxdC02LjUgLTI0bC0xODIgLTI0M2wxODIgLTI0M3E4IC0xMSA2LjUgLTI0dC0xMi41IC0yMWwtNTEgLTM4cS0xMSAtOCAtMjQgLTYuNXQtMjEgMTIuNWwtMjI2IDMwMXEtMTQgMTkgMCAzOHpNMTI4MiA0NjdxMTQgLTE5IDAgLTM4bC0yMjYgLTMwMXEtOCAtMTEgLTIxIC0xMi41dC0yNCA2LjVsLTUxIDM4cS0xMSA4IC0xMi41IDIxdDYuNSAyNGwxODIgMjQzDQpsLTE4MiAyNDNxLTggMTEgLTYuNSAyNHQxMi41IDIxbDUxIDM4cTExIDggMjQgNi41dDIxIC0xMi41ek02NjIgNnEtMTMgMiAtMjAuNSAxM3QtNS41IDI0bDEzOCA4MzFxMiAxMyAxMyAyMC41dDI0IDUuNWw2MyAtMTBxMTMgLTIgMjAuNSAtMTN0NS41IC0yNGwtMTM4IC04MzFxLTIgLTEzIC0xMyAtMjAuNXQtMjQgLTUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80MzAiIHVuaWNvZGU9IiYjeGYxY2E7IiANCmQ9Ik0xNDk3IDcwOXYtMTk4cS0xMDEgLTIzIC0xOTggLTIzcS02NSAtMTM2IC0xNjUuNSAtMjcxdC0xODEuNSAtMjE1LjV0LTEyOCAtMTA2LjVxLTgwIC00NSAtMTYyIDNxLTI4IDE3IC02MC41IDQzLjV0LTg1IDgzLjV0LTEwMi41IDEyOC41dC0xMDcuNSAxODR0LTEwNS41IDI0NHQtOTEuNSAzMTQuNXQtNzAuNSAzOTBoMjgzcTI2IC0yMTggNzAgLTM5OC41dDEwNC41IC0zMTd0MTIxLjUgLTIzNS41dDE0MCAtMTk1cTE2OSAxNjkgMjg3IDQwNg0KcS0xNDIgNzIgLTIyMyAyMjB0LTgxIDMzM3EwIDE5MiAxMDQgMzE0LjV0Mjg0IDEyMi41cTE3OCAwIDI3MyAtMTA1LjV0OTUgLTI5Ny41cTAgLTE1OSAtNTggLTI4NnEtNyAtMSAtMTkuNSAtM3QtNDYgLTJ0LTYzIDZ0LTYyIDI1LjV0LTUwLjUgNTEuNXEzMSAxMDMgMzEgMTg0cTAgODcgLTI5IDEzMnQtNzkgNDVxLTUzIDAgLTg1IC00OS41dC0zMiAtMTQwLjVxMCAtMTg2IDEwNSAtMjkzLjV0MjY3IC0xMDcuNXE2MiAwIDEyMSAxNHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80MzEiIHVuaWNvZGU9IiYjeGYxY2I7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMjE2IDM2N2w2MDMgLTQwMnYzNTlsLTMzNCAyMjN6TTE1NCA1MTFsMTkzIDEyOWwtMTkzIDEyOXYtMjU4ek05NzMgLTM1bDYwMyA0MDJsLTI2OSAxODBsLTMzNCAtMjIzdi0zNTl6TTg5NiA0NThsMjcyIDE4MmwtMjcyIDE4MmwtMjcyIC0xODJ6TTQ4NSA3MzNsMzM0IDIyM3YzNTlsLTYwMyAtNDAyek0xNDQ1IDY0MGwxOTMgLTEyOXYyNTh6TTEzMDcgNzMzbDI2OSAxODBsLTYwMyA0MDJ2LTM1OXpNMTc5MiA5MTN2LTU0Ng0KcTAgLTQxIC0zNCAtNjRsLTgxOSAtNTQ2cS0yMSAtMTMgLTQzIC0xM3QtNDMgMTNsLTgxOSA1NDZxLTM0IDIzIC0zNCA2NHY1NDZxMCA0MSAzNCA2NGw4MTkgNTQ2cTIxIDEzIDQzIDEzdDQzIC0xM2w4MTkgLTU0NnEzNCAtMjMgMzQgLTY0eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQzMiIgdW5pY29kZT0iJiN4ZjFjYzsiIGhvcml6LWFkdi14PSIyMDQ4IiANCmQ9Ik0xODAwIDc2NHExMTEgLTQ2IDE3OS41IC0xNDUuNXQ2OC41IC0yMjEuNXEwIC0xNjQgLTExOCAtMjgwLjV0LTI4NSAtMTE2LjVxLTQgMCAtMTEuNSAwLjV0LTEwLjUgMC41aC0xMjA5aC0xaC0yaC01cS0xNzAgMTAgLTI4OCAxMjUuNXQtMTE4IDI4MC41cTAgMTEwIDU1IDIwM3QxNDcgMTQ3cS0xMiAzOSAtMTIgODJxMCAxMTUgODIgMTk2dDE5OSA4MXE5NSAwIDE3MiAtNThxNzUgMTU0IDIyMi41IDI0OHQzMjYuNSA5NA0KcTE2NiAwIDMwNiAtODAuNXQyMjEuNSAtMjE4LjV0ODEuNSAtMzAxcTAgLTYgLTAuNSAtMTh0LTAuNSAtMTh6TTQ2OCA0OThxMCAtMTIyIDg0IC0xOTN0MjA4IC03MXExMzcgMCAyNDAgOTlxLTE2IDIwIC00Ny41IDU2LjV0LTQzLjUgNTAuNXEtNjcgLTY1IC0xNDQgLTY1cS01NSAwIC05My41IDMzLjV0LTM4LjUgODcuNXEwIDUzIDM4LjUgODd0OTEuNSAzNHE0NCAwIDg0LjUgLTIxdDczIC01NXQ2NSAtNzV0NjkgLTgydDc3IC03NXQ5NyAtNTUNCnQxMjEuNSAtMjFxMTIxIDAgMjA0LjUgNzEuNXQ4My41IDE5MC41cTAgMTIxIC04NCAxOTJ0LTIwNyA3MXEtMTQzIDAgLTI0MSAtOTdsOTMgLTEwOHE2NiA2NCAxNDIgNjRxNTIgMCA5MiAtMzN0NDAgLTg0cTAgLTU3IC0zNyAtOTEuNXQtOTQgLTM0LjVxLTQzIDAgLTgyLjUgMjF0LTcyIDU1dC02NS41IDc1dC02OS41IDgydC03Ny41IDc1dC05Ni41IDU1dC0xMTguNSAyMXEtMTIyIDAgLTIwNyAtNzAuNXQtODUgLTE4OS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQzMyIgdW5pY29kZT0iJiN4ZjFjZDsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik04OTYgMTUzNnExODIgMCAzNDggLTcxdDI4NiAtMTkxdDE5MSAtMjg2dDcxIC0zNDh0LTcxIC0zNDh0LTE5MSAtMjg2dC0yODYgLTE5MXQtMzQ4IC03MXQtMzQ4IDcxdC0yODYgMTkxdC0xOTEgMjg2dC03MSAzNDh0NzEgMzQ4dDE5MSAyODZ0Mjg2IDE5MXQzNDggNzF6TTg5NiAxNDA4cS0xOTAgMCAtMzYxIC05MGwxOTQgLTE5NHE4MiAyOCAxNjcgMjh0MTY3IC0yOGwxOTQgMTk0cS0xNzEgOTAgLTM2MSA5MHpNMjE4IDI3OWwxOTQgMTk0DQpxLTI4IDgyIC0yOCAxNjd0MjggMTY3bC0xOTQgMTk0cS05MCAtMTcxIC05MCAtMzYxdDkwIC0zNjF6TTg5NiAtMTI4cTE5MCAwIDM2MSA5MGwtMTk0IDE5NHEtODIgLTI4IC0xNjcgLTI4dC0xNjcgMjhsLTE5NCAtMTk0cTE3MSAtOTAgMzYxIC05MHpNODk2IDI1NnExNTkgMCAyNzEuNSAxMTIuNXQxMTIuNSAyNzEuNXQtMTEyLjUgMjcxLjV0LTI3MS41IDExMi41dC0yNzEuNSAtMTEyLjV0LTExMi41IC0yNzEuNXQxMTIuNSAtMjcxLjUNCnQyNzEuNSAtMTEyLjV6TTEzODAgNDczbDE5NCAtMTk0cTkwIDE3MSA5MCAzNjF0LTkwIDM2MWwtMTk0IC0xOTRxMjggLTgyIDI4IC0xNjd0LTI4IC0xNjd6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDM0IiB1bmljb2RlPSImI3hmMWNlOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE3NjAgNjQwcTAgLTE3NiAtNjguNSAtMzM2dC0xODQgLTI3NS41dC0yNzUuNSAtMTg0dC0zMzYgLTY4LjV0LTMzNiA2OC41dC0yNzUuNSAxODR0LTE4NCAyNzUuNXQtNjguNSAzMzZxMCAyMTMgOTcgMzk4LjV0MjY1IDMwNS41dDM3NCAxNTF2LTIyOHEtMjIxIC00NSAtMzY2LjUgLTIyMXQtMTQ1LjUgLTQwNnEwIC0xMzAgNTEgLTI0OC41dDEzNi41IC0yMDR0MjA0IC0xMzYuNXQyNDguNSAtNTF0MjQ4LjUgNTF0MjA0IDEzNi41DQp0MTM2LjUgMjA0dDUxIDI0OC41cTAgMjMwIC0xNDUuNSA0MDZ0LTM2Ni41IDIyMXYyMjhxMjA2IC0zMSAzNzQgLTE1MXQyNjUgLTMwNS41dDk3IC0zOTguNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYxRDAiIHVuaWNvZGU9IiYjeGYxZDA7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTkgNjYycTggMjE3IDExNiA0MDZ0MzA1IDMxOGg1cTAgLTEgLTEgLTNxLTggLTggLTI4IC0zMy41dC01MiAtNzYuNXQtNjAgLTExMC41dC00NC41IC0xMzUuNXQtMTQgLTE1MC41dDM5IC0xNTcuNXQxMDguNSAtMTU0cTUwIC01MCAxMDIgLTY5LjV0OTAuNSAtMTEuNXQ2OS41IDIzLjV0NDcgMzIuNWwxNiAxNnEzOSA1MSA1MyAxMTYuNXQ2LjUgMTIyLjV0LTIxIDEwN3QtMjYuNSA4MGwtMTQgMjlxLTEwIDI1IC0zMC41IDQ5LjV0LTQzIDQxDQp0LTQzLjUgMjkuNXQtMzUgMTlsLTEzIDZsMTA0IDExNXEzOSAtMTcgNzggLTUydDU5IC02MWwxOSAtMjdxMSA0OCAtMTguNSAxMDMuNXQtNDAuNSA4Ny41bC0yMCAzMWwxNjEgMTgzbDE2MCAtMTgxcS0zMyAtNDYgLTUyLjUgLTEwMi41dC0yMi41IC05MC41bC00IC0zM3EyMiAzNyA2MS41IDcyLjV0NjcuNSA1Mi41bDI4IDE3bDEwMyAtMTE1cS00NCAtMTQgLTg1IC01MHQtNjAgLTY1bC0xOSAtMjlxLTMxIC01NiAtNDggLTEzMy41dC03IC0xNzANCnQ1NyAtMTU2LjVxMzMgLTQ1IDc3LjUgLTYwLjV0ODUgLTUuNXQ3NiAyNi41dDU3LjUgMzMuNWwyMSAxNnE2MCA1MyA5Ni41IDExNXQ0OC41IDEyMS41dDEwIDEyMS41dC0xOCAxMTh0LTM3IDEwNy41dC00NS41IDkzdC00NSA3MnQtMzQuNSA0Ny41bC0xMyAxN3EtMTQgMTMgLTcgMTNsMTAgLTNxNDAgLTI5IDYyLjUgLTQ2dDYyIC01MHQ2NCAtNTh0NTguNSAtNjV0NTUuNSAtNzd0NDUuNSAtODh0MzggLTEwM3QyMy41IC0xMTd0MTAuNSAtMTM2DQpxMyAtMjU5IC0xMDggLTQ2NXQtMzEyIC0zMjF0LTQ1NiAtMTE1cS0xODUgMCAtMzUxIDc0dC0yODMuNSAxOTh0LTE4NCAyOTN0LTYwLjUgMzUzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjFEMSIgdW5pY29kZT0iJiN4ZjFkMTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik04NzQgLTEwMnYtNjZxLTIwOCA2IC0zODUgMTA5LjV0LTI4MyAyNzUuNWw1OCAzNHEyOSAtNDkgNzMgLTk5bDY1IDU3cTE0OCAtMTY4IDM2OCAtMjEybC0xNyAtODZxNjUgLTEyIDEyMSAtMTN6TTI3NiA0MjhsLTgzIC0yOHEyMiAtNjAgNDkgLTExMmwtNTcgLTMzcS05OCAxODAgLTk4IDM4NXQ5OCAzODVsNTcgLTMzcS0zMCAtNTYgLTQ5IC0xMTJsODIgLTI4cS0zNSAtMTAwIC0zNSAtMjEycTAgLTEwOSAzNiAtMjEyek0xNTI4IDI1MQ0KbDU4IC0zNHEtMTA2IC0xNzIgLTI4MyAtMjc1LjV0LTM4NSAtMTA5LjV2NjZxNTYgMSAxMjEgMTNsLTE3IDg2cTIyMCA0NCAzNjggMjEybDY1IC01N3E0NCA1MCA3MyA5OXpNMTM3NyA4MDVsLTIzMyAtODBxMTQgLTQyIDE0IC04NXQtMTQgLTg1bDIzMiAtODBxLTMxIC05MiAtOTggLTE2OWwtMTg1IDE2MnEtNTcgLTY3IC0xNDcgLTg1bDQ4IC0yNDFxLTUyIC0xMCAtOTggLTEwdC05OCAxMGw0OCAyNDFxLTkwIDE4IC0xNDcgODVsLTE4NSAtMTYyDQpxLTY3IDc3IC05OCAxNjlsMjMyIDgwcS0xNCA0MiAtMTQgODV0MTQgODVsLTIzMyA4MHEzMyA5MyA5OSAxNjlsMTg1IC0xNjJxNTkgNjggMTQ3IDg2bC00OCAyNDBxNDQgMTAgOTggMTB0OTggLTEwbC00OCAtMjQwcTg4IC0xOCAxNDcgLTg2bDE4NSAxNjJxNjYgLTc2IDk5IC0xNjl6TTg3NCAxNDQ4di02NnEtNjUgLTIgLTEyMSAtMTNsMTcgLTg2cS0yMjAgLTQyIC0zNjggLTIxMWwtNjUgNTZxLTM4IC00MiAtNzMgLTk4bC01NyAzMw0KcTEwNiAxNzIgMjgyIDI3NS41dDM4NSAxMDkuNXpNMTcwNSA2NDBxMCAtMjA1IC05OCAtMzg1bC01NyAzM3EyNyA1MiA0OSAxMTJsLTgzIDI4cTM2IDEwMyAzNiAyMTJxMCAxMTIgLTM1IDIxMmw4MiAyOHEtMTkgNTYgLTQ5IDExMmw1NyAzM3E5OCAtMTgwIDk4IC0zODV6TTE1ODUgMTA2M2wtNTcgLTMzcS0zNSA1NiAtNzMgOThsLTY1IC01NnEtMTQ4IDE2OSAtMzY4IDIxMWwxNyA4NnEtNTYgMTEgLTEyMSAxM3Y2NnEyMDkgLTYgMzg1IC0xMDkuNQ0KdDI4MiAtMjc1LjV6TTE3NDggNjQwcTAgMTczIC02Ny41IDMzMXQtMTgxLjUgMjcydC0yNzIgMTgxLjV0LTMzMSA2Ny41dC0zMzEgLTY3LjV0LTI3MiAtMTgxLjV0LTE4MS41IC0yNzJ0LTY3LjUgLTMzMXQ2Ny41IC0zMzF0MTgxLjUgLTI3MnQyNzIgLTE4MS41dDMzMSAtNjcuNXQzMzEgNjcuNXQyNzIgMTgxLjV0MTgxLjUgMjcydDY3LjUgMzMxek0xNzkyIDY0MHEwIC0xODIgLTcxIC0zNDh0LTE5MSAtMjg2dC0yODYgLTE5MXQtMzQ4IC03MQ0KdC0zNDggNzF0LTI4NiAxOTF0LTE5MSAyODZ0LTcxIDM0OHQ3MSAzNDh0MTkxIDI4NnQyODYgMTkxdDM0OCA3MXQzNDggLTcxdDI4NiAtMTkxdDE5MSAtMjg2dDcxIC0zNDh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMUQyIiB1bmljb2RlPSImI3hmMWQyOyIgDQpkPSJNNTgyIDIyOHEwIC02NiAtOTMgLTY2cS0xMDcgMCAtMTA3IDYzcTAgNjQgOTggNjRxMTAyIDAgMTAyIC02MXpNNTQ2IDY5NHEwIC04NSAtNzQgLTg1cS03NyAwIC03NyA4NHEwIDkwIDc3IDkwcTM2IDAgNTUgLTI1LjV0MTkgLTYzLjV6TTcxMiA3Njl2MTI1cS03OCAtMjkgLTEzNSAtMjlxLTUwIDI5IC0xMTAgMjlxLTg2IDAgLTE0NSAtNTd0LTU5IC0xNDNxMCAtNTAgMjkuNSAtMTAydDczLjUgLTY3di0zcS0zOCAtMTcgLTM4IC04NQ0KcTAgLTUzIDQxIC03N3YtM3EtMTEzIC0zNyAtMTEzIC0xMzlxMCAtNDUgMjAgLTc4LjV0NTQgLTUxdDcyIC0yNS41dDgxIC04cTIyNCAwIDIyNCAxODhxMCA2NyAtNDggOTl0LTEyNiA0NnEtMjcgNSAtNTEuNSAyMC41dC0yNC41IDM5LjVxMCA0NCA0OSA1MnE3NyAxNSAxMjIgNzB0NDUgMTM0cTAgMjQgLTEwIDUycTM3IDkgNDkgMTN6TTc3MSAzNTBoMTM3cS0yIDI3IC0yIDgydjM4N3EwIDQ2IDIgNjloLTEzN3EzIC0yMyAzIC03MXYtMzkyDQpxMCAtNTAgLTMgLTc1ek0xMjgwIDM2NnYxMjFxLTMwIC0yMSAtNjggLTIxcS01MyAwIC01MyA4MnYyMjVoNTJxOSAwIDI2LjUgLTF0MjYuNSAtMXYxMTdoLTEwNXEwIDgyIDMgMTAyaC0xNDBxNCAtMjQgNCAtNTV2LTQ3aC02MHYtMTE3cTM2IDMgMzcgM3EzIDAgMTEgLTAuNXQxMiAtMC41di0yaC0ydi0yMTdxMCAtMzcgMi41IC02NHQxMS41IC01Ni41dDI0LjUgLTQ4LjV0NDMuNSAtMzF0NjYgLTEycTY0IDAgMTA4IDI0ek05MjQgMTA3Mg0KcTAgMzYgLTI0IDYzLjV0LTYwIDI3LjV0LTYwLjUgLTI3dC0yNC41IC02NHEwIC0zNiAyNSAtNjIuNXQ2MCAtMjYuNXQ1OS41IDI3dDI0LjUgNjJ6TTE1MzYgMTEyMHYtOTYwcTAgLTExOSAtODQuNSAtMjAzLjV0LTIwMy41IC04NC41aC05NjBxLTExOSAwIC0yMDMuNSA4NC41dC04NC41IDIwMy41djk2MHEwIDExOSA4NC41IDIwMy41dDIwMy41IDg0LjVoOTYwcTExOSAwIDIwMy41IC04NC41dDg0LjUgLTIwMy41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQzOCIgdW5pY29kZT0iJiN4ZjFkMzsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik01OTUgMjJxMCAxMDAgLTE2NSAxMDBxLTE1OCAwIC0xNTggLTEwNHEwIC0xMDEgMTcyIC0xMDFxMTUxIDAgMTUxIDEwNXpNNTM2IDc3N3EwIDYxIC0zMCAxMDJ0LTg5IDQxcS0xMjQgMCAtMTI0IC0xNDVxMCAtMTM1IDEyNCAtMTM1cTExOSAwIDExOSAxMzd6TTgwNSAxMTAxdi0yMDJxLTM2IC0xMiAtNzkgLTIycTE2IC00MyAxNiAtODRxMCAtMTI3IC03MyAtMjE2LjV0LTE5NyAtMTEyLjVxLTQwIC04IC01OS41IC0yN3QtMTkuNSAtNTgNCnEwIC0zMSAyMi41IC01MS41dDU4IC0zMnQ3OC41IC0yMnQ4NiAtMjUuNXQ3OC41IC0zNy41dDU4IC02NHQyMi41IC05OC41cTAgLTMwNCAtMzYzIC0zMDRxLTY5IDAgLTEzMCAxMi41dC0xMTYgNDF0LTg3LjUgODJ0LTMyLjUgMTI3LjVxMCAxNjUgMTgyIDIyNXY0cS02NyA0MSAtNjcgMTI2cTAgMTA5IDYzIDEzN3Y0cS03MiAyNCAtMTE5LjUgMTA4LjV0LTQ3LjUgMTY1LjVxMCAxMzkgOTUgMjMxLjV0MjM1IDkyLjVxOTYgMCAxNzggLTQ3DQpxOTggMCAyMTggNDd6TTExMjMgMjIwaC0yMjJxNCA0NSA0IDEzNHY2MDlxMCA5NCAtNCAxMjhoMjIycS00IC0zMyAtNCAtMTI0di02MTNxMCAtODkgNCAtMTM0ek0xNzI0IDQ0MnYtMTk2cS03MSAtMzkgLTE3NCAtMzlxLTYyIDAgLTEwNyAyMHQtNzAgNTB0LTM5LjUgNzh0LTE4LjUgOTJ0LTQgMTAzdjM1MWgydjRxLTcgMCAtMTkgMXQtMTggMXEtMjEgMCAtNTkgLTZ2MTkwaDk2djc2cTAgNTQgLTYgODloMjI3cS02IC00MSAtNiAtMTY1aDE3MQ0Kdi0xOTBxLTE1IDAgLTQzLjUgMnQtNDIuNSAyaC04NXYtMzY1cTAgLTEzMSA4NyAtMTMxcTYxIDAgMTA5IDMzek0xMTQ4IDEzODlxMCAtNTggLTM5IC0xMDEuNXQtOTYgLTQzLjVxLTU4IDAgLTk4IDQzLjV0LTQwIDEwMS41cTAgNTkgMzkuNSAxMDN0OTguNSA0NHE1OCAwIDk2LjUgLTQ0LjV0MzguNSAtMTAyLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDM5IiB1bmljb2RlPSImI3hmMWQ0OyIgDQpkPSJNODA5IDUzMmwyNjYgNDk5aC0xMTJsLTE1NyAtMzEycS0yNCAtNDggLTQ0IC05MmwtNDIgOTJsLTE1NSAzMTJoLTEyMGwyNjMgLTQ5M3YtMzI0aDEwMXYzMTh6TTE1MzYgMTEyMHYtOTYwcTAgLTExOSAtODQuNSAtMjAzLjV0LTIwMy41IC04NC41aC05NjBxLTExOSAwIC0yMDMuNSA4NC41dC04NC41IDIwMy41djk2MHEwIDExOSA4NC41IDIwMy41dDIwMy41IDg0LjVoOTYwcTExOSAwIDIwMy41IC04NC41dDg0LjUgLTIwMy41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjFENSIgdW5pY29kZT0iJiN4ZjFkNTsiIGhvcml6LWFkdi14PSIxMjgwIiANCmQ9Ik04NDIgOTY0cTAgLTgwIC01NyAtMTM2LjV0LTEzNiAtNTYuNXEtNjAgMCAtMTExIDM1cS02MiAtNjcgLTExNSAtMTQ2cS0yNDcgLTM3MSAtMjAyIC04NTlxMSAtMjIgLTEyLjUgLTM4LjV0LTM0LjUgLTE4LjVoLTVxLTIwIDAgLTM1IDEzLjV0LTE3IDMzLjVxLTE0IDEyNiAtMy41IDI0Ny41dDI5LjUgMjE3dDU0IDE4NnQ2OSAxNTUuNXQ3NCAxMjVxNjEgOTAgMTMyIDE2NXEtMTYgMzUgLTE2IDc3cTAgODAgNTYuNSAxMzYuNXQxMzYuNSA1Ni41DQp0MTM2LjUgLTU2LjV0NTYuNSAtMTM2LjV6TTEyMjMgOTUzcTAgLTE1OCAtNzggLTI5MnQtMjEyLjUgLTIxMnQtMjkyLjUgLTc4cS02NCAwIC0xMzEgMTRxLTIxIDUgLTMyLjUgMjMuNXQtNi41IDM5LjVxNSAyMCAyMyAzMS41dDM5IDcuNXE1MSAtMTMgMTA4IC0xM3E5NyAwIDE4NiAzOHQxNTMgMTAydDEwMiAxNTN0MzggMTg2dC0zOCAxODZ0LTEwMiAxNTN0LTE1MyAxMDJ0LTE4NiAzOHQtMTg2IC0zOHQtMTUzIC0xMDJ0LTEwMiAtMTUzDQp0LTM4IC0xODZxMCAtMTE0IDUyIC0yMThxMTAgLTIwIDMuNSAtNDB0LTI1LjUgLTMwdC0zOS41IC0zdC0zMC41IDI2cS02NCAxMjMgLTY0IDI2NXEwIDExOSA0Ni41IDIyN3QxMjQuNSAxODZ0MTg2IDEyNHQyMjYgNDZxMTU4IDAgMjkyLjUgLTc4dDIxMi41IC0yMTIuNXQ3OCAtMjkyLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMUQ2IiB1bmljb2RlPSImI3hmMWQ2OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTI3MCA3MzBxLTggMTkgLTggNTJxMCAyMCAxMSA0OXQyNCA0NXEtMSAyMiA3LjUgNTN0MjIuNSA0M3EwIDEzOSA5Mi41IDI4OC41dDIxNy41IDIwOS41cTEzOSA2NiAzMjQgNjZxMTMzIDAgMjY2IC01NXE0OSAtMjEgOTAgLTQ4dDcxIC01NnQ1NSAtNjh0NDIgLTc0dDMyLjUgLTg0LjV0MjUuNSAtODkuNXQyMiAtOThsMSAtNXE1NSAtODMgNTUgLTE1MHEwIC0xNCAtOSAtNDB0LTkgLTM4cTAgLTEgMS41IC0zLjV0My41IC01dDIgLTMuNQ0KcTc3IC0xMTQgMTIwLjUgLTIxNC41dDQzLjUgLTIwOC41cTAgLTQzIC0xOS41IC0xMDB0LTU1LjUgLTU3cS05IDAgLTE5LjUgNy41dC0xOSAxNy41dC0xOSAyNnQtMTYgMjYuNXQtMTMuNSAyNnQtOSAxNy41cS0xIDEgLTMgMWwtNSAtNHEtNTkgLTE1NCAtMTMyIC0yMjNxMjAgLTIwIDYxLjUgLTM4LjV0NjkgLTQxLjV0MzUuNSAtNjVxLTIgLTQgLTQgLTE2dC03IC0xOHEtNjQgLTk3IC0zMDIgLTk3cS01MyAwIC0xMTAuNSA5dC05OCAyMA0KdC0xMDQuNSAzMHEtMTUgNSAtMjMgN3EtMTQgNCAtNDYgNC41dC00MCAxLjVxLTQxIC00NSAtMTI3LjUgLTY1dC0xNjguNSAtMjBxLTM1IDAgLTY5IDEuNXQtOTMgOXQtMTAxIDIwLjV0LTc0LjUgNDB0LTMyLjUgNjRxMCA0MCAxMCA1OS41dDQxIDQ4LjVxMTEgMiA0MC41IDEzdDQ5LjUgMTJxNCAwIDE0IDJxMiAyIDIgNGwtMiAzcS00OCAxMSAtMTA4IDEwNS41dC03MyAxNTYuNWwtNSAzcS00IDAgLTEyIC0yMHEtMTggLTQxIC01NC41IC03NC41DQp0LTc3LjUgLTM3LjVoLTFxLTQgMCAtNiA0LjV0LTUgNS41cS0yMyA1NCAtMjMgMTAwcTAgMjc1IDI1MiA0NjZ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMUQ3IiB1bmljb2RlPSImI3hmMWQ3OyIgaG9yaXotYWR2LXg9IjIwNDgiIA0KZD0iTTU4MCAxMDc1cTAgNDEgLTI1IDY2dC02NiAyNXEtNDMgMCAtNzYgLTI1LjV0LTMzIC02NS41cTAgLTM5IDMzIC02NC41dDc2IC0yNS41cTQxIDAgNjYgMjQuNXQyNSA2NS41ek0xMzIzIDU2OHEwIDI4IC0yNS41IDUwdC02NS41IDIycS0yNyAwIC00OS41IC0yMi41dC0yMi41IC00OS41cTAgLTI4IDIyLjUgLTUwLjV0NDkuNSAtMjIuNXE0MCAwIDY1LjUgMjJ0MjUuNSA1MXpNMTA4NyAxMDc1cTAgNDEgLTI0LjUgNjZ0LTY1LjUgMjUNCnEtNDMgMCAtNzYgLTI1LjV0LTMzIC02NS41cTAgLTM5IDMzIC02NC41dDc2IC0yNS41cTQxIDAgNjUuNSAyNC41dDI0LjUgNjUuNXpNMTcyMiA1NjhxMCAyOCAtMjYgNTB0LTY1IDIycS0yNyAwIC00OS41IC0yMi41dC0yMi41IC00OS41cTAgLTI4IDIyLjUgLTUwLjV0NDkuNSAtMjIuNXEzOSAwIDY1IDIydDI2IDUxek0xNDU2IDk2NXEtMzEgNCAtNzAgNHEtMTY5IDAgLTMxMSAtNzd0LTIyMy41IC0yMDguNXQtODEuNSAtMjg3LjUNCnEwIC03OCAyMyAtMTUycS0zNSAtMyAtNjggLTNxLTI2IDAgLTUwIDEuNXQtNTUgNi41dC00NC41IDd0LTU0LjUgMTAuNXQtNTAgMTAuNWwtMjUzIC0xMjdsNzIgMjE4cS0yOTAgMjAzIC0yOTAgNDkwcTAgMTY5IDk3LjUgMzExdDI2NCAyMjMuNXQzNjMuNSA4MS41cTE3NiAwIDMzMi41IC02NnQyNjIgLTE4Mi41dDEzNi41IC0yNjAuNXpNMjA0OCA0MDRxMCAtMTE3IC02OC41IC0yMjMuNXQtMTg1LjUgLTE5My41bDU1IC0xODFsLTE5OSAxMDkNCnEtMTUwIC0zNyAtMjE4IC0zN3EtMTY5IDAgLTMxMSA3MC41dC0yMjMuNSAxOTEuNXQtODEuNSAyNjR0ODEuNSAyNjR0MjIzLjUgMTkxLjV0MzExIDcwLjVxMTYxIDAgMzAzIC03MC41dDIyNy41IC0xOTJ0ODUuNSAtMjYzLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDQzIiB1bmljb2RlPSImI3hmMWQ4OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE3NjQgMTUyNXEzMyAtMjQgMjcgLTY0bC0yNTYgLTE1MzZxLTUgLTI5IC0zMiAtNDVxLTE0IC04IC0zMSAtOHEtMTEgMCAtMjQgNWwtNDUzIDE4NWwtMjQyIC0yOTVxLTE4IC0yMyAtNDkgLTIzcS0xMyAwIC0yMiA0cS0xOSA3IC0zMC41IDIzLjV0LTExLjUgMzYuNXYzNDlsODY0IDEwNTlsLTEwNjkgLTkyNWwtMzk1IDE2MnEtMzcgMTQgLTQwIDU1cS0yIDQwIDMyIDU5bDE2NjQgOTYwcTE1IDkgMzIgOXEyMCAwIDM2IC0xMXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80NDQiIHVuaWNvZGU9IiYjeGYxZDk7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTc2NCAxNTI1cTMzIC0yNCAyNyAtNjRsLTI1NiAtMTUzNnEtNSAtMjkgLTMyIC00NXEtMTQgLTggLTMxIC04cS0xMSAwIC0yNCA1bC01MjcgMjE1bC0yOTggLTMyN3EtMTggLTIxIC00NyAtMjFxLTE0IDAgLTIzIDRxLTE5IDcgLTMwIDIzLjV0LTExIDM2LjV2NDUybC00NzIgMTkzcS0zNyAxNCAtNDAgNTVxLTMgMzkgMzIgNTlsMTY2NCA5NjBxMzUgMjEgNjggLTJ6TTE0MjIgMjZsMjIxIDEzMjNsLTE0MzQgLTgyN2wzMzYgLTEzNw0KbDg2MyA2MzlsLTQ3OCAtNzk3eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQ0NSIgdW5pY29kZT0iJiN4ZjFkYTsiIA0KZD0iTTE1MzYgNjQwcTAgLTE1NiAtNjEgLTI5OHQtMTY0IC0yNDV0LTI0NSAtMTY0dC0yOTggLTYxcS0xNzIgMCAtMzI3IDcyLjV0LTI2NCAyMDQuNXEtNyAxMCAtNi41IDIyLjV0OC41IDIwLjVsMTM3IDEzOHExMCA5IDI1IDlxMTYgLTIgMjMgLTEycTczIC05NSAxNzkgLTE0N3QyMjUgLTUycTEwNCAwIDE5OC41IDQwLjV0MTYzLjUgMTA5LjV0MTA5LjUgMTYzLjV0NDAuNSAxOTguNXQtNDAuNSAxOTguNXQtMTA5LjUgMTYzLjUNCnQtMTYzLjUgMTA5LjV0LTE5OC41IDQwLjVxLTk4IDAgLTE4OCAtMzUuNXQtMTYwIC0xMDEuNWwxMzcgLTEzOHEzMSAtMzAgMTQgLTY5cS0xNyAtNDAgLTU5IC00MGgtNDQ4cS0yNiAwIC00NSAxOXQtMTkgNDV2NDQ4cTAgNDIgNDAgNTlxMzkgMTcgNjkgLTE0bDEzMCAtMTI5cTEwNyAxMDEgMjQ0LjUgMTU2LjV0Mjg0LjUgNTUuNXExNTYgMCAyOTggLTYxdDI0NSAtMTY0dDE2NCAtMjQ1dDYxIC0yOTh6TTg5NiA5Mjh2LTQ0OHEwIC0xNCAtOSAtMjMNCnQtMjMgLTloLTMyMHEtMTQgMCAtMjMgOXQtOSAyM3Y2NHEwIDE0IDkgMjN0MjMgOWgyMjR2MzUycTAgMTQgOSAyM3QyMyA5aDY0cTE0IDAgMjMgLTl0OSAtMjN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDQ2IiB1bmljb2RlPSImI3hmMWRiOyIgDQpkPSJNNzY4IDEyODBxLTEzMCAwIC0yNDguNSAtNTF0LTIwNCAtMTM2LjV0LTEzNi41IC0yMDR0LTUxIC0yNDguNXQ1MSAtMjQ4LjV0MTM2LjUgLTIwNHQyMDQgLTEzNi41dDI0OC41IC01MXQyNDguNSA1MXQyMDQgMTM2LjV0MTM2LjUgMjA0dDUxIDI0OC41dC01MSAyNDguNXQtMTM2LjUgMjA0dC0yMDQgMTM2LjV0LTI0OC41IDUxek0xNTM2IDY0MHEwIC0yMDkgLTEwMyAtMzg1LjV0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzDQp0LTI3OS41IDI3OS41dC0xMDMgMzg1LjV0MTAzIDM4NS41dDI3OS41IDI3OS41dDM4NS41IDEwM3QzODUuNSAtMTAzdDI3OS41IC0yNzkuNXQxMDMgLTM4NS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQ0NyIgdW5pY29kZT0iJiN4ZjFkYzsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNjgyIC0xMjhxLTQ0IDAgLTEzMi41IDMuNXQtMTMzLjUgMy41cS00NCAwIC0xMzIgLTMuNXQtMTMyIC0zLjVxLTI0IDAgLTM3IDIwLjV0LTEzIDQ1LjVxMCAzMSAxNyA0NnQzOSAxN3Q1MSA3dDQ1IDE1cTMzIDIxIDMzIDE0MGwtMSAzOTFxMCAyMSAtMSAzMXEtMTMgNCAtNTAgNGgtNjc1cS0zOCAwIC01MSAtNHEtMSAtMTAgLTEgLTMxbC0xIC0zNzFxMCAtMTQyIDM3IC0xNjRxMTYgLTEwIDQ4IC0xM3Q1NyAtMy41dDQ1IC0xNQ0KdDIwIC00NS41cTAgLTI2IC0xMi41IC00OHQtMzYuNSAtMjJxLTQ3IDAgLTEzOS41IDMuNXQtMTM4LjUgMy41cS00MyAwIC0xMjggLTMuNXQtMTI3IC0zLjVxLTIzIDAgLTM1LjUgMjF0LTEyLjUgNDVxMCAzMCAxNS41IDQ1dDM2IDE3LjV0NDcuNSA3LjV0NDIgMTVxMzMgMjMgMzMgMTQzbC0xIDU3djgxM3EwIDMgMC41IDI2dDAgMzYuNXQtMS41IDM4LjV0LTMuNSA0MnQtNi41IDM2LjV0LTExIDMxLjV0LTE2IDE4cS0xNSAxMCAtNDUgMTJ0LTUzIDINCnQtNDEgMTR0LTE4IDQ1cTAgMjYgMTIgNDh0MzYgMjJxNDYgMCAxMzguNSAtMy41dDEzOC41IC0zLjVxNDIgMCAxMjYuNSAzLjV0MTI2LjUgMy41cTI1IDAgMzcuNSAtMjJ0MTIuNSAtNDhxMCAtMzAgLTE3IC00My41dC0zOC41IC0xNC41dC00OS41IC00dC00MyAtMTNxLTM1IC0yMSAtMzUgLTE2MGwxIC0zMjBxMCAtMjEgMSAtMzJxMTMgLTMgMzkgLTNoNjk5cTI1IDAgMzggM3ExIDExIDEgMzJsMSAzMjBxMCAxMzkgLTM1IDE2MA0KcS0xOCAxMSAtNTguNSAxMi41dC02NiAxM3QtMjUuNSA0OS41cTAgMjYgMTIuNSA0OHQzNy41IDIycTQ0IDAgMTMyIC0zLjV0MTMyIC0zLjVxNDMgMCAxMjkgMy41dDEyOSAzLjVxMjUgMCAzNy41IC0yMnQxMi41IC00OHEwIC0zMCAtMTcuNSAtNDR0LTQwIC0xNC41dC01MS41IC0zdC00NCAtMTIuNXEtMzUgLTIzIC0zNSAtMTYxbDEgLTk0M3EwIC0xMTkgMzQgLTE0MHExNiAtMTAgNDYgLTEzLjV0NTMuNSAtNC41dDQxLjUgLTE1LjV0MTggLTQ0LjUNCnEwIC0yNiAtMTIgLTQ4dC0zNiAtMjJ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDQ4IiB1bmljb2RlPSImI3hmMWRkOyIgaG9yaXotYWR2LXg9IjEyODAiIA0KZD0iTTEyNzggMTM0N3YtNzNxMCAtMjkgLTE4LjUgLTYxdC00Mi41IC0zMnEtNTAgMCAtNTQgLTFxLTI2IC02IC0zMiAtMzFxLTMgLTExIC0zIC02NHYtMTE1MnEwIC0yNSAtMTggLTQzdC00MyAtMThoLTEwOHEtMjUgMCAtNDMgMTh0LTE4IDQzdjEyMThoLTE0M3YtMTIxOHEwIC0yNSAtMTcuNSAtNDN0LTQzLjUgLTE4aC0xMDhxLTI2IDAgLTQzLjUgMTh0LTE3LjUgNDN2NDk2cS0xNDcgMTIgLTI0NSA1OXEtMTI2IDU4IC0xOTIgMTc5DQpxLTY0IDExNyAtNjQgMjU5cTAgMTY2IDg4IDI4NnE4OCAxMTggMjA5IDE1OXExMTEgMzcgNDE3IDM3aDQ3OXEyNSAwIDQzIC0xOHQxOCAtNDN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDQ5IiB1bmljb2RlPSImI3hmMWRlOyIgDQpkPSJNMzUyIDEyOHYtMTI4aC0zNTJ2MTI4aDM1MnpNNzA0IDI1NnEyNiAwIDQ1IC0xOXQxOSAtNDV2LTI1NnEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTI1NnEtMjYgMCAtNDUgMTl0LTE5IDQ1djI1NnEwIDI2IDE5IDQ1dDQ1IDE5aDI1NnpNODY0IDY0MHYtMTI4aC04NjR2MTI4aDg2NHpNMjI0IDExNTJ2LTEyOGgtMjI0djEyOGgyMjR6TTE1MzYgMTI4di0xMjhoLTczNnYxMjhoNzM2ek01NzYgMTI4MHEyNiAwIDQ1IC0xOXQxOSAtNDV2LTI1Ng0KcTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMjU2cS0yNiAwIC00NSAxOXQtMTkgNDV2MjU2cTAgMjYgMTkgNDV0NDUgMTloMjU2ek0xMjE2IDc2OHEyNiAwIDQ1IC0xOXQxOSAtNDV2LTI1NnEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTI1NnEtMjYgMCAtNDUgMTl0LTE5IDQ1djI1NnEwIDI2IDE5IDQ1dDQ1IDE5aDI1NnpNMTUzNiA2NDB2LTEyOGgtMjI0djEyOGgyMjR6TTE1MzYgMTE1MnYtMTI4aC04NjR2MTI4aDg2NHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYxRTAiIHVuaWNvZGU9IiYjeGYxZTA7IiANCmQ9Ik0xMjE2IDUxMnExMzMgMCAyMjYuNSAtOTMuNXQ5My41IC0yMjYuNXQtOTMuNSAtMjI2LjV0LTIyNi41IC05My41dC0yMjYuNSA5My41dC05My41IDIyNi41cTAgMTIgMiAzNGwtMzYwIDE4MHEtOTIgLTg2IC0yMTggLTg2cS0xMzMgMCAtMjI2LjUgOTMuNXQtOTMuNSAyMjYuNXQ5My41IDIyNi41dDIyNi41IDkzLjVxMTI2IDAgMjE4IC04NmwzNjAgMTgwcS0yIDIyIC0yIDM0cTAgMTMzIDkzLjUgMjI2LjV0MjI2LjUgOTMuNQ0KdDIyNi41IC05My41dDkzLjUgLTIyNi41dC05My41IC0yMjYuNXQtMjI2LjUgLTkzLjVxLTEyNiAwIC0yMTggODZsLTM2MCAtMTgwcTIgLTIyIDIgLTM0dC0yIC0zNGwzNjAgLTE4MHE5MiA4NiAyMTggODZ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDUxIiB1bmljb2RlPSImI3hmMWUxOyIgDQpkPSJNMTI4MCAzNDFxMCA4OCAtNjIuNSAxNTF0LTE1MC41IDYzcS04NCAwIC0xNDUgLTU4bC0yNDEgMTIwcTIgMTYgMiAyM3QtMiAyM2wyNDEgMTIwcTYxIC01OCAxNDUgLTU4cTg4IDAgMTUwLjUgNjN0NjIuNSAxNTF0LTYyLjUgMTUwLjV0LTE1MC41IDYyLjV0LTE1MSAtNjIuNXQtNjMgLTE1MC41cTAgLTcgMiAtMjNsLTI0MSAtMTIwcS02MiA1NyAtMTQ1IDU3cS04OCAwIC0xNTAuNSAtNjIuNXQtNjIuNSAtMTUwLjV0NjIuNSAtMTUwLjUNCnQxNTAuNSAtNjIuNXE4MyAwIDE0NSA1N2wyNDEgLTEyMHEtMiAtMTYgLTIgLTIzcTAgLTg4IDYzIC0xNTAuNXQxNTEgLTYyLjV0MTUwLjUgNjIuNXQ2Mi41IDE1MC41ek0xNTM2IDExMjB2LTk2MHEwIC0xMTkgLTg0LjUgLTIwMy41dC0yMDMuNSAtODQuNWgtOTYwcS0xMTkgMCAtMjAzLjUgODQuNXQtODQuNSAyMDMuNXY5NjBxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDk2MHExMTkgMCAyMDMuNSAtODQuNXQ4NC41IC0yMDMuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80NTIiIHVuaWNvZGU9IiYjeGYxZTI7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNNTcxIDk0N3EtMTAgMjUgLTM0IDM1dC00OSAwcS0xMDggLTQ0IC0xOTEgLTEyN3QtMTI3IC0xOTFxLTEwIC0yNSAwIC00OXQzNSAtMzRxMTMgLTUgMjQgLTVxNDIgMCA2MCA0MHEzNCA4NCA5OC41IDE0OC41dDE0OC41IDk4LjVxMjUgMTEgMzUgMzV0MCA0OXpNMTUxMyAxMzAzbDQ2IC00NmwtMjQ0IC0yNDNsNjggLTY4cTE5IC0xOSAxOSAtNDUuNXQtMTkgLTQ1LjVsLTY0IC02NHE4OSAtMTYxIDg5IC0zNDNxMCAtMTQzIC01NS41IC0yNzMuNQ0KdC0xNTAgLTIyNXQtMjI1IC0xNTB0LTI3My41IC01NS41dC0yNzMuNSA1NS41dC0yMjUgMTUwdC0xNTAgMjI1dC01NS41IDI3My41dDU1LjUgMjczLjV0MTUwIDIyNXQyMjUgMTUwdDI3My41IDU1LjVxMTgyIDAgMzQzIC04OWw2NCA2NHExOSAxOSA0NS41IDE5dDQ1LjUgLTE5bDY4IC02OHpNMTUyMSAxMzU5cS0xMCAtMTAgLTIyIC0xMHEtMTMgMCAtMjMgMTBsLTkxIDkwcS05IDEwIC05IDIzdDkgMjNxMTAgOSAyMyA5dDIzIC05bDkwIC05MQ0KcTEwIC05IDEwIC0yMi41dC0xMCAtMjIuNXpNMTc1MSAxMTI5cS0xMSAtOSAtMjMgLTl0LTIzIDlsLTkwIDkxcS0xMCA5IC0xMCAyMi41dDEwIDIyLjVxOSAxMCAyMi41IDEwdDIyLjUgLTEwbDkxIC05MHE5IC0xMCA5IC0yM3QtOSAtMjN6TTE3OTIgMTMxMnEwIC0xNCAtOSAtMjN0LTIzIC05aC05NnEtMTQgMCAtMjMgOXQtOSAyM3Q5IDIzdDIzIDloOTZxMTQgMCAyMyAtOXQ5IC0yM3pNMTYwMCAxNTA0di05NnEwIC0xNCAtOSAtMjN0LTIzIC05DQp0LTIzIDl0LTkgMjN2OTZxMCAxNCA5IDIzdDIzIDl0MjMgLTl0OSAtMjN6TTE3NTEgMTQ0OWwtOTEgLTkwcS0xMCAtMTAgLTIyIC0xMHEtMTMgMCAtMjMgMTBxLTEwIDkgLTEwIDIyLjV0MTAgMjIuNWw5MCA5MXExMCA5IDIzIDl0MjMgLTlxOSAtMTAgOSAtMjN0LTkgLTIzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQ1MyIgdW5pY29kZT0iJiN4ZjFlMzsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik02MDkgNzIwbDI4NyAyMDhsMjg3IC0yMDhsLTEwOSAtMzM2aC0zNTV6TTg5NiAxNTM2cTE4MiAwIDM0OCAtNzF0Mjg2IC0xOTF0MTkxIC0yODZ0NzEgLTM0OHQtNzEgLTM0OHQtMTkxIC0yODZ0LTI4NiAtMTkxdC0zNDggLTcxdC0zNDggNzF0LTI4NiAxOTF0LTE5MSAyODZ0LTcxIDM0OHQ3MSAzNDh0MTkxIDI4NnQyODYgMTkxdDM0OCA3MXpNMTUxNSAxODZxMTQ5IDIwMyAxNDkgNDU0djNsLTEwMiAtODlsLTI0MCAyMjRsNjMgMzIzDQpsMTM0IC0xMnEtMTUwIDIwNiAtMzg5IDI4Mmw1MyAtMTI0bC0yODcgLTE1OWwtMjg3IDE1OWw1MyAxMjRxLTIzOSAtNzYgLTM4OSAtMjgybDEzNSAxMmw2MiAtMzIzbC0yNDAgLTIyNGwtMTAyIDg5di0zcTAgLTI1MSAxNDkgLTQ1NGwzMCAxMzJsMzI2IC00MGwxMzkgLTI5OGwtMTE2IC02OXExMTcgLTM5IDI0MCAtMzl0MjQwIDM5bC0xMTYgNjlsMTM5IDI5OGwzMjYgNDB6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDU0IiB1bmljb2RlPSImI3hmMWU0OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTQ0OCAyMjR2LTE5MnEwIC0xNCAtOSAtMjN0LTIzIC05aC0xOTJxLTE0IDAgLTIzIDl0LTkgMjN2MTkycTAgMTQgOSAyM3QyMyA5aDE5MnExNCAwIDIzIC05dDkgLTIzek0yNTYgNjA4di0xOTJxMCAtMTQgLTkgLTIzdC0yMyAtOWgtMTkycS0xNCAwIC0yMyA5dC05IDIzdjE5MnEwIDE0IDkgMjN0MjMgOWgxOTJxMTQgMCAyMyAtOXQ5IC0yM3pNODMyIDIyNHYtMTkycTAgLTE0IC05IC0yM3QtMjMgLTloLTE5MnEtMTQgMCAtMjMgOXQtOSAyMw0KdjE5MnEwIDE0IDkgMjN0MjMgOWgxOTJxMTQgMCAyMyAtOXQ5IC0yM3pNNjQwIDYwOHYtMTkycTAgLTE0IC05IC0yM3QtMjMgLTloLTE5MnEtMTQgMCAtMjMgOXQtOSAyM3YxOTJxMCAxNCA5IDIzdDIzIDloMTkycTE0IDAgMjMgLTl0OSAtMjN6TTY2IDc2OHEtMjggMCAtNDcgMTl0LTE5IDQ2djEyOWg1MTR2LTEyOXEwIC0yNyAtMTkgLTQ2dC00NiAtMTloLTM4M3pNMTIxNiAyMjR2LTE5MnEwIC0xNCAtOSAtMjN0LTIzIC05aC0xOTINCnEtMTQgMCAtMjMgOXQtOSAyM3YxOTJxMCAxNCA5IDIzdDIzIDloMTkycTE0IDAgMjMgLTl0OSAtMjN6TTEwMjQgNjA4di0xOTJxMCAtMTQgLTkgLTIzdC0yMyAtOWgtMTkycS0xNCAwIC0yMyA5dC05IDIzdjE5MnEwIDE0IDkgMjN0MjMgOWgxOTJxMTQgMCAyMyAtOXQ5IC0yM3pNMTYwMCAyMjR2LTE5MnEwIC0xNCAtOSAtMjN0LTIzIC05aC0xOTJxLTE0IDAgLTIzIDl0LTkgMjN2MTkycTAgMTQgOSAyM3QyMyA5aDE5MnExNCAwIDIzIC05dDkgLTIzDQp6TTE0MDggNjA4di0xOTJxMCAtMTQgLTkgLTIzdC0yMyAtOWgtMTkycS0xNCAwIC0yMyA5dC05IDIzdjE5MnEwIDE0IDkgMjN0MjMgOWgxOTJxMTQgMCAyMyAtOXQ5IC0yM3pNMTc5MiAxMDE2di0xM2gtNTE0djEwcTAgMTA0IC0zODIgMTAycS0zODIgLTEgLTM4MiAtMTAydi0xMGgtNTE0djEzcTAgMTcgOC41IDQzdDM0IDY0dDY1LjUgNzUuNXQxMTAuNSA3NnQxNjAgNjcuNXQyMjQgNDcuNXQyOTMuNSAxOC41dDI5MyAtMTguNXQyMjQgLTQ3LjUNCnQxNjAuNSAtNjcuNXQxMTAuNSAtNzZ0NjUuNSAtNzUuNXQzNCAtNjR0OC41IC00M3pNMTc5MiA2MDh2LTE5MnEwIC0xNCAtOSAtMjN0LTIzIC05aC0xOTJxLTE0IDAgLTIzIDl0LTkgMjN2MTkycTAgMTQgOSAyM3QyMyA5aDE5MnExNCAwIDIzIC05dDkgLTIzek0xNzkyIDk2MnYtMTI5cTAgLTI3IC0xOSAtNDZ0LTQ2IC0xOWgtMzg0cS0yNyAwIC00NiAxOXQtMTkgNDZ2MTI5aDUxNHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80NTUiIHVuaWNvZGU9IiYjeGYxZTU7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNNzA0IDEyMTZ2LTc2OHEwIC0yNiAtMTkgLTQ1dC00NSAtMTl2LTU3NnEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTUxMnEtMjYgMCAtNDUgMTl0LTE5IDQ1djUxMmwyNDkgODczcTcgMjMgMzEgMjNoNDI0ek0xMDI0IDEyMTZ2LTcwNGgtMjU2djcwNGgyNTZ6TTE3OTIgMzIwdi01MTJxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC01MTJxLTI2IDAgLTQ1IDE5dC0xOSA0NXY1NzZxLTI2IDAgLTQ1IDE5dC0xOSA0NXY3NjhoNDI0cTI0IDAgMzEgLTIzeg0KTTczNiAxNTA0di0yMjRoLTM1MnYyMjRxMCAxNCA5IDIzdDIzIDloMjg4cTE0IDAgMjMgLTl0OSAtMjN6TTE0MDggMTUwNHYtMjI0aC0zNTJ2MjI0cTAgMTQgOSAyM3QyMyA5aDI4OHExNCAwIDIzIC05dDkgLTIzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQ1NiIgdW5pY29kZT0iJiN4ZjFlNjsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNzU1IDEwODNxMzcgLTM4IDM3IC05MC41dC0zNyAtOTAuNWwtNDAxIC00MDBsMTUwIC0xNTBsLTE2MCAtMTYwcS0xNjMgLTE2MyAtMzg5LjUgLTE4Ni41dC00MTEuNSAxMDAuNWwtMzYyIC0zNjJoLTE4MXYxODFsMzYyIDM2MnEtMTI0IDE4NSAtMTAwLjUgNDExLjV0MTg2LjUgMzg5LjVsMTYwIDE2MGwxNTAgLTE1MGw0MDAgNDAxcTM4IDM3IDkxIDM3dDkwIC0zN3QzNyAtOTAuNXQtMzcgLTkwLjVsLTQwMCAtNDAxbDIzNCAtMjM0DQpsNDAxIDQwMHEzOCAzNyA5MSAzN3Q5MCAtMzd6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDU3IiB1bmljb2RlPSImI3hmMWU3OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTg3MyA3OTZxMCAtODMgLTYzLjUgLTE0Mi41dC0xNTIuNSAtNTkuNXQtMTUyLjUgNTkuNXQtNjMuNSAxNDIuNXEwIDg0IDYzLjUgMTQzdDE1Mi41IDU5dDE1Mi41IC01OXQ2My41IC0xNDN6TTEzNzUgNzk2cTAgLTgzIC02MyAtMTQyLjV0LTE1MyAtNTkuNXEtODkgMCAtMTUyLjUgNTkuNXQtNjMuNSAxNDIuNXEwIDg0IDYzLjUgMTQzdDE1Mi41IDU5cTkwIDAgMTUzIC01OXQ2MyAtMTQzek0xNjAwIDYxNnY2NjdxMCA4NyAtMzIgMTIzLjUNCnQtMTExIDM2LjVoLTExMTJxLTgzIDAgLTExMi41IC0zNHQtMjkuNSAtMTI2di02NzNxNDMgLTIzIDg4LjUgLTQwdDgxIC0yOHQ4MSAtMTguNXQ3MSAtMTF0NzAgLTR0NTguNSAtMC41dDU2LjUgMnQ0NC41IDJxNjggMSA5NSAtMjdxNiAtNiAxMCAtOXEyNiAtMjUgNjEgLTUxcTcgOTEgMTE4IDg3cTUgMCAzNi41IC0xLjV0NDMgLTJ0NDUuNSAtMXQ1MyAxdDU0LjUgNC41dDYxIDguNXQ2MiAxMy41dDY3IDE5LjV0NjcuNSAyN3Q3MiAzNC41eg0KTTE3NjMgNjIxcS0xMjEgLTE0OSAtMzcyIC0yNTJxODQgLTI4NSAtMjMgLTQ2NXEtNjYgLTExMyAtMTgzIC0xNDhxLTEwNCAtMzIgLTE4MiAxNXEtODYgNTEgLTgyIDE2NGwtMSAzMjZ2MXEtOCAyIC0yNC41IDZ0LTIzLjUgNWwtMSAtMzM4cTQgLTExNCAtODMgLTE2NHEtNzkgLTQ3IC0xODMgLTE1cS0xMTcgMzYgLTE4MiAxNTBxLTEwNSAxODAgLTIyIDQ2M3EtMjUxIDEwMyAtMzcyIDI1MnEtMjUgMzcgLTQgNjN0NjAgLTFxNCAtMiAxMS41IC03DQp0MTAuNSAtOHY2OTRxMCA3MiA0NyAxMjN0MTE0IDUxaDEyNTdxNjcgMCAxMTQgLTUxdDQ3IC0xMjN2LTY5NGwyMSAxNXEzOSAyNyA2MCAxdC00IC02M3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80NTgiIHVuaWNvZGU9IiYjeGYxZTg7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNODk2IDExMDJ2LTQzNGgtMTQ1djQzNGgxNDV6TTEyOTQgMTEwMnYtNDM0aC0xNDV2NDM0aDE0NXpNMTI5NCAzNDJsMjUzIDI1NHY3OTVoLTExOTR2LTEwNDloMzI2di0yMTdsMjE3IDIxN2gzOTh6TTE2OTIgMTUzNnYtMTAxM2wtNDM0IC00MzRoLTMyNmwtMjE3IC0yMTdoLTIxN3YyMTdoLTM5OHYxMTU4bDEwOSAyODloMTQ4M3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80NTkiIHVuaWNvZGU9IiYjeGYxZTk7IiANCmQ9Ik03NzMgMjE3di0xMjdxLTEgLTI5MiAtNiAtMzA1cS0xMiAtMzIgLTUxIC00MHEtNTQgLTkgLTE4MS41IDM4dC0xNjIuNSA4OXEtMTMgMTUgLTE3IDM2cS0xIDEyIDQgMjZxNCAxMCAzNCA0N3QxODEgMjE2cTEgMCA2MCA3MHExNSAxOSAzOS41IDI0LjV0NDkuNSAtMy41cTI0IC0xMCAzNy41IC0yOXQxMi41IC00MnpNNjI0IDQ2OHEtMyAtNTUgLTUyIC03MGwtMTIwIC0zOXEtMjc1IC04OCAtMjkyIC04OHEtMzUgMiAtNTQgMzYNCnEtMTIgMjUgLTE3IDc1cS04IDc2IDEgMTY2LjV0MzAgMTI0LjV0NTYgMzJxMTMgMCAyMDIgLTc3cTcxIC0yOSAxMTUgLTQ3bDg0IC0zNHEyMyAtOSAzNS41IC0zMC41dDExLjUgLTQ4LjV6TTE0NTAgMTcxcS03IC01NCAtOTEuNSAtMTYxdC0xMzUuNSAtMTI3cS0zNyAtMTQgLTYzIDdxLTE0IDEwIC0xODQgMjg3bC00NyA3N3EtMTQgMjEgLTExLjUgNDZ0MTkuNSA0NnEzNSA0MyA4MyAyNnExIC0xIDExOSAtNDBxMjAzIC02NiAyNDIgLTc5LjUNCnQ0NyAtMjAuNXEyOCAtMjIgMjIgLTYxek03NzggODAzcTUgLTEwMiAtNTQgLTEyMnEtNTggLTE3IC0xMTQgNzFsLTM3OCA1OThxLTggMzUgMTkgNjJxNDEgNDMgMjA3LjUgODkuNXQyMjQuNSAzMS41cTQwIC0xMCA0OSAtNDVxMyAtMTggMjIgLTMwNS41dDI0IC0zNzkuNXpNMTQ0MCA2OTVxMyAtMzkgLTI2IC01OXEtMTUgLTEwIC0zMjkgLTg2cS02NyAtMTUgLTkxIC0yM2wxIDJxLTIzIC02IC00NiA0dC0zNyAzMnEtMzAgNDcgMCA4Nw0KcTEgMSA3NSAxMDJxMTI1IDE3MSAxNTAgMjA0dDM0IDM5cTI4IDE5IDY1IDJxNDggLTIzIDEyMyAtMTMzLjV0ODEgLTE2Ny41di0zeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQ2MCIgdW5pY29kZT0iJiN4ZjFlYTsiIGhvcml6LWFkdi14PSIyMDQ4IiANCmQ9Ik0xMDI0IDEwMjRoLTM4NHYtMzg0aDM4NHYzODR6TTExNTIgMzg0di0xMjhoLTY0MHYxMjhoNjQwek0xMTUyIDExNTJ2LTY0MGgtNjQwdjY0MGg2NDB6TTE3OTIgMzg0di0xMjhoLTUxMnYxMjhoNTEyek0xNzkyIDY0MHYtMTI4aC01MTJ2MTI4aDUxMnpNMTc5MiA4OTZ2LTEyOGgtNTEydjEyOGg1MTJ6TTE3OTIgMTE1MnYtMTI4aC01MTJ2MTI4aDUxMnpNMjU2IDE5MnY5NjBoLTEyOHYtOTYwcTAgLTI2IDE5IC00NXQ0NSAtMTl0NDUgMTkNCnQxOSA0NXpNMTkyMCAxOTJ2MTA4OGgtMTUzNnYtMTA4OHEwIC0zMyAtMTEgLTY0aDE0ODNxMjYgMCA0NSAxOXQxOSA0NXpNMjA0OCAxNDA4di0xMjE2cTAgLTgwIC01NiAtMTM2dC0xMzYgLTU2aC0xNjY0cS04MCAwIC0xMzYgNTZ0LTU2IDEzNnYxMDg4aDI1NnYxMjhoMTc5MnoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80NjEiIHVuaWNvZGU9IiYjeGYxZWI7IiBob3Jpei1hZHYteD0iMjA0OCIgDQpkPSJNMTAyNCAxM3EtMjAgMCAtOTMgNzMuNXQtNzMgOTMuNXEwIDMyIDYyLjUgNTR0MTAzLjUgMjJ0MTAzLjUgLTIydDYyLjUgLTU0cTAgLTIwIC03MyAtOTMuNXQtOTMgLTczLjV6TTEyOTQgMjg0cS0yIDAgLTQwIDI1dC0xMDEuNSA1MHQtMTI4LjUgMjV0LTEyOC41IC0yNXQtMTAxIC01MHQtNDAuNSAtMjVxLTE4IDAgLTkzLjUgNzV0LTc1LjUgOTNxMCAxMyAxMCAyM3E3OCA3NyAxOTYgMTIxdDIzMyA0NHQyMzMgLTQ0dDE5NiAtMTIxDQpxMTAgLTEwIDEwIC0yM3EwIC0xOCAtNzUuNSAtOTN0LTkzLjUgLTc1ek0xNTY3IDU1NnEtMTEgMCAtMjMgOHEtMTM2IDEwNSAtMjUyIDE1NC41dC0yNjggNDkuNXEtODUgMCAtMTcwLjUgLTIydC0xNDkgLTUzdC0xMTMuNSAtNjJ0LTc5IC01M3QtMzEgLTIycS0xNyAwIC05MiA3NXQtNzUgOTNxMCAxMiAxMCAyMnExMzIgMTMyIDMyMCAyMDV0MzgwIDczdDM4MCAtNzN0MzIwIC0yMDVxMTAgLTEwIDEwIC0yMnEwIC0xOCAtNzUgLTkzdC05MiAtNzV6DQpNMTgzOCA4MjdxLTExIDAgLTIyIDlxLTE3OSAxNTcgLTM3MS41IDIzNi41dC00MjAuNSA3OS41dC00MjAuNSAtNzkuNXQtMzcxLjUgLTIzNi41cS0xMSAtOSAtMjIgLTlxLTE3IDAgLTkyLjUgNzV0LTc1LjUgOTNxMCAxMyAxMCAyM3ExODcgMTg2IDQ0NSAyODh0NTI3IDEwMnQ1MjcgLTEwMnQ0NDUgLTI4OHExMCAtMTAgMTAgLTIzcTAgLTE4IC03NS41IC05M3QtOTIuNSAtNzV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDYyIiB1bmljb2RlPSImI3hmMWVjOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTM4NCAwcTAgNTMgLTM3LjUgOTAuNXQtOTAuNSAzNy41dC05MC41IC0zNy41dC0zNy41IC05MC41dDM3LjUgLTkwLjV0OTAuNSAtMzcuNXQ5MC41IDM3LjV0MzcuNSA5MC41ek03NjggMHEwIDUzIC0zNy41IDkwLjV0LTkwLjUgMzcuNXQtOTAuNSAtMzcuNXQtMzcuNSAtOTAuNXQzNy41IC05MC41dDkwLjUgLTM3LjV0OTAuNSAzNy41dDM3LjUgOTAuNXpNMzg0IDM4NHEwIDUzIC0zNy41IDkwLjV0LTkwLjUgMzcuNXQtOTAuNSAtMzcuNQ0KdC0zNy41IC05MC41dDM3LjUgLTkwLjV0OTAuNSAtMzcuNXQ5MC41IDM3LjV0MzcuNSA5MC41ek0xMTUyIDBxMCA1MyAtMzcuNSA5MC41dC05MC41IDM3LjV0LTkwLjUgLTM3LjV0LTM3LjUgLTkwLjV0MzcuNSAtOTAuNXQ5MC41IC0zNy41dDkwLjUgMzcuNXQzNy41IDkwLjV6TTc2OCAzODRxMCA1MyAtMzcuNSA5MC41dC05MC41IDM3LjV0LTkwLjUgLTM3LjV0LTM3LjUgLTkwLjV0MzcuNSAtOTAuNXQ5MC41IC0zNy41dDkwLjUgMzcuNQ0KdDM3LjUgOTAuNXpNMzg0IDc2OHEwIDUzIC0zNy41IDkwLjV0LTkwLjUgMzcuNXQtOTAuNSAtMzcuNXQtMzcuNSAtOTAuNXQzNy41IC05MC41dDkwLjUgLTM3LjV0OTAuNSAzNy41dDM3LjUgOTAuNXpNMTE1MiAzODRxMCA1MyAtMzcuNSA5MC41dC05MC41IDM3LjV0LTkwLjUgLTM3LjV0LTM3LjUgLTkwLjV0MzcuNSAtOTAuNXQ5MC41IC0zNy41dDkwLjUgMzcuNXQzNy41IDkwLjV6TTc2OCA3NjhxMCA1MyAtMzcuNSA5MC41dC05MC41IDM3LjUNCnQtOTAuNSAtMzcuNXQtMzcuNSAtOTAuNXQzNy41IC05MC41dDkwLjUgLTM3LjV0OTAuNSAzNy41dDM3LjUgOTAuNXpNMTUzNiAwdjM4NHEwIDUyIC0zOCA5MHQtOTAgMzh0LTkwIC0zOHQtMzggLTkwdi0zODRxMCAtNTIgMzggLTkwdDkwIC0zOHQ5MCAzOHQzOCA5MHpNMTE1MiA3NjhxMCA1MyAtMzcuNSA5MC41dC05MC41IDM3LjV0LTkwLjUgLTM3LjV0LTM3LjUgLTkwLjV0MzcuNSAtOTAuNXQ5MC41IC0zNy41dDkwLjUgMzcuNXQzNy41IDkwLjV6DQpNMTUzNiAxMDg4djI1NnEwIDI2IC0xOSA0NXQtNDUgMTloLTEyODBxLTI2IDAgLTQ1IC0xOXQtMTkgLTQ1di0yNTZxMCAtMjYgMTkgLTQ1dDQ1IC0xOWgxMjgwcTI2IDAgNDUgMTl0MTkgNDV6TTE1MzYgNzY4cTAgNTMgLTM3LjUgOTAuNXQtOTAuNSAzNy41dC05MC41IC0zNy41dC0zNy41IC05MC41dDM3LjUgLTkwLjV0OTAuNSAtMzcuNXQ5MC41IDM3LjV0MzcuNSA5MC41ek0xNjY0IDE0MDh2LTE1MzZxMCAtNTIgLTM4IC05MHQtOTAgLTM4DQpoLTE0MDhxLTUyIDAgLTkwIDM4dC0zOCA5MHYxNTM2cTAgNTIgMzggOTB0OTAgMzhoMTQwOHE1MiAwIDkwIC0zOHQzOCAtOTB6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDYzIiB1bmljb2RlPSImI3hmMWVkOyIgDQpkPSJNMTUxOSA4OTBxMTggLTg0IC00IC0yMDRxLTg3IC00NDQgLTU2NSAtNDQ0aC00NHEtMjUgMCAtNDQgLTE2LjV0LTI0IC00Mi41bC00IC0xOWwtNTUgLTM0NmwtMiAtMTVxLTUgLTI2IC0yNC41IC00Mi41dC00NC41IC0xNi41aC0yNTFxLTIxIDAgLTMzIDE1dC05IDM2cTkgNTYgMjYuNSAxNjh0MjYuNSAxNjh0MjcgMTY3LjV0MjcgMTY3LjVxNSAzNyA0MyAzN2gxMzFxMTMzIC0yIDIzNiAyMXExNzUgMzkgMjg3IDE0NHExMDIgOTUgMTU1IDI0Ng0KcTI0IDcwIDM1IDEzM3ExIDYgMi41IDcuNXQzLjUgMXQ2IC0zLjVxNzkgLTU5IDk4IC0xNjJ6TTEzNDcgMTE3MnEwIC0xMDcgLTQ2IC0yMzZxLTgwIC0yMzMgLTMwMiAtMzE1cS0xMTMgLTQwIC0yNTIgLTQycTAgLTEgLTkwIC0xbC05MCAxcS0xMDAgMCAtMTE4IC05NnEtMiAtOCAtODUgLTUzMHEtMSAtMTAgLTEyIC0xMGgtMjk1cS0yMiAwIC0zNi41IDE2LjV0LTExLjUgMzguNWwyMzIgMTQ3MXE1IDI5IDI3LjUgNDh0NTEuNSAxOWg1OTgNCnEzNCAwIDk3LjUgLTEzdDExMS41IC0zMnExMDcgLTQxIDE2My41IC0xMjN0NTYuNSAtMTk2eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQ2NCIgdW5pY29kZT0iJiN4ZjFlZTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik00NDEgODY0cTMzIDAgNTIgLTI2cTI2NiAtMzY0IDM2MiAtNzc0aC00NDZxLTEyNyA0NDEgLTM2NyA3NDlxLTEyIDE2IC0zIDMzLjV0MjkgMTcuNWgzNzN6TTEwMDAgNTA3cS00OSAtMTk5IC0xMjUgLTM5M3EtNzkgMzEwIC0yNTYgNTk0cTQwIDIyMSA0NCA0NDlxMjExIC0zNDAgMzM3IC02NTB6TTEwOTkgMTIxNnEyMzUgLTMyNCAzODQuNSAtNjk4LjV0MTg0LjUgLTc3My41aC00NTFxLTQxIDY2NSAtNTUzIDE0NzJoNDM1ek0xNzkyIDY0MA0KcTAgLTQyNCAtMTAxIC04MTJxLTY3IDU2MCAtMzU5IDEwODNxLTI1IDMwMSAtMTA2IDU4NHEtNCAxNiA1LjUgMjguNXQyNS41IDEyLjVoMzU5cTIxIDAgMzguNSAtMTN0MjIuNSAtMzNxMTE1IC00MDkgMTE1IC04NTB6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMUYwIiB1bmljb2RlPSImI3hmMWYwOyIgaG9yaXotYWR2LXg9IjIzMDQiIA0KZD0iTTE5NzUgNTQ2aC0xMzhxMTQgMzcgNjYgMTc5bDMgOXE0IDEwIDEwIDI2dDkgMjZsMTIgLTU1ek01MzEgNjExbC01OCAyOTVxLTExIDU0IC03NSA1NGgtMjY4bC0yIC0xM3EzMTEgLTc5IDQwMyAtMzM2ek03MTAgOTYwbC0xNjIgLTQzOGwtMTcgODlxLTI2IDcwIC04NSAxMjkuNXQtMTMxIDg4LjVsMTM1IC01MTBoMTc1bDI2MSA2NDFoLTE3NnpNODQ5IDMxOGgxNjZsMTA0IDY0MmgtMTY2ek0xNjE3IDk0NHEtNjkgMjcgLTE0OSAyNw0KcS0xMjMgMCAtMjAxIC01OXQtNzkgLTE1M3EtMSAtMTAyIDE0NSAtMTc0cTQ4IC0yMyA2NyAtNDF0MTkgLTM5cTAgLTMwIC0zMCAtNDZ0LTY5IC0xNnEtODYgMCAtMTU2IDMzbC0yMiAxMWwtMjMgLTE0NHE3NCAtMzQgMTg1IC0zNHExMzAgLTEgMjA4LjUgNTl0ODAuNSAxNjBxMCAxMDYgLTE0MCAxNzRxLTQ5IDI1IC03MSA0MnQtMjIgMzhxMCAyMiAyNC41IDM4LjV0NzAuNSAxNi41cTcwIDEgMTI0IC0yNGwxNSAtOHpNMjA0MiA5NjBoLTEyOA0KcS02NSAwIC04NyAtNTRsLTI0NiAtNTg4aDE3NGwzNSA5NmgyMTJxNSAtMjIgMjAgLTk2aDE1NHpNMjMwNCAxMjgwdi0xMjgwcTAgLTUyIC0zOCAtOTB0LTkwIC0zOGgtMjA0OHEtNTIgMCAtOTAgMzh0LTM4IDkwdjEyODBxMCA1MiAzOCA5MHQ5MCAzOGgyMDQ4cTUyIDAgOTAgLTM4dDM4IC05MHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80NjYiIHVuaWNvZGU9IiYjeGYxZjE7IiBob3Jpei1hZHYteD0iMjMwNCIgDQpkPSJNMTExOSAxMTk1cS0xMjggODUgLTI4MSA4NXEtMTAzIDAgLTE5Ny41IC00MC41dC0xNjIuNSAtMTA4LjV0LTEwOC41IC0xNjJ0LTQwLjUgLTE5N3EwIC0xMDQgNDAuNSAtMTk4dDEwOC41IC0xNjJ0MTYyIC0xMDguNXQxOTggLTQwLjVxMTUzIDAgMjgxIDg1cS0xMzEgMTA3IC0xNzggMjY1LjV0MC41IDMxNi41dDE3Ny41IDI2NXpNMTE1MiAxMTcxcS0xMjYgLTk5IC0xNzIgLTI0OS41dC0wLjUgLTMwMC41dDE3Mi41IC0yNDkNCnExMjcgOTkgMTcyLjUgMjQ5dC0wLjUgMzAwLjV0LTE3MiAyNDkuNXpNMTE4NSAxMTk1cTEzMCAtMTA3IDE3Ny41IC0yNjUuNXQwLjUgLTMxN3QtMTc4IC0yNjQuNXExMjggLTg1IDI4MSAtODVxMTA0IDAgMTk4IDQwLjV0MTYyIDEwOC41dDEwOC41IDE2MnQ0MC41IDE5OHEwIDEwMyAtNDAuNSAxOTd0LTEwOC41IDE2MnQtMTYyLjUgMTA4LjV0LTE5Ny41IDQwLjVxLTE1MyAwIC0yODEgLTg1ek0xOTI2IDQ3M2g3djNoLTE3di0zaDd2LTE3aDN2MTd6DQpNMTk1NSA0NTZoNHYyMGgtNWwtNiAtMTNsLTYgMTNoLTV2LTIwaDN2MTVsNiAtMTNoNGw1IDEzdi0xNXpNMTk0NyAxNnYtMmgtMmgtM3YzaDNoMnYtMXpNMTk0NyA3aDNsLTQgNWgybDEgMXExIDEgMSAzdC0xIDNsLTEgMWgtM2gtNnYtMTNoM3Y1aDF6TTY4NSA3NXEwIDE5IDExIDMxdDMwIDEycTE4IDAgMjkgLTEyLjV0MTEgLTMwLjVxMCAtMTkgLTExIC0zMXQtMjkgLTEycS0xOSAwIC0zMCAxMnQtMTEgMzF6TTExNTggMTE5cTMwIDAgMzUgLTMyDQpoLTcwcTUgMzIgMzUgMzJ6TTE1MTQgNzVxMCAxOSAxMSAzMXQyOSAxMnQyOS41IC0xMi41dDExLjUgLTMwLjVxMCAtMTkgLTExIC0zMXQtMzAgLTEycS0xOCAwIC0yOSAxMnQtMTEgMzF6TTE3ODYgNzVxMCAxOCAxMS41IDMwLjV0MjkuNSAxMi41dDI5LjUgLTEyLjV0MTEuNSAtMzAuNXEwIC0xOSAtMTEuNSAtMzF0LTI5LjUgLTEydC0yOS41IDEyLjV0LTExLjUgMzAuNXpNMTk0NCAzcS0yIDAgLTQgMXEtMSAwIC0zIDJ0LTIgM3EtMSAyIC0xIDQNCnEwIDMgMSA0cTAgMiAyIDRsMSAxcTIgMCAyIDFxMiAxIDQgMXEzIDAgNCAtMWw0IC0ybDIgLTR2LTFxMSAtMiAxIC0zbC0xIC0xdi0zdC0xIC0xbC0xIC0ycS0yIC0yIC00IC0ycS0xIC0xIC00IC0xek01OTkgN2gzMHY4NXEwIDI0IC0xNC41IDM4LjV0LTM5LjUgMTUuNXEtMzIgMCAtNDcgLTI0cS0xNCAyNCAtNDUgMjRxLTI0IDAgLTM5IC0yMHYxNmgtMzB2LTEzNWgzMHY3NXEwIDM2IDMzIDM2cTMwIDAgMzAgLTM2di03NWgyOXY3NQ0KcTAgMzYgMzMgMzZxMzAgMCAzMCAtMzZ2LTc1ek03NjUgN2gyOXY2OHY2N2gtMjl2LTE2cS0xNyAyMCAtNDMgMjBxLTI5IDAgLTQ4IC0yMHQtMTkgLTUxdDE5IC01MXQ0OCAtMjBxMjggMCA0MyAyMHYtMTd6TTk0MyA0OHEwIDM0IC00NyA0MGwtMTQgMnEtMjMgNCAtMjMgMTRxMCAxNSAyNSAxNXEyMyAwIDQzIC0xMWwxMiAyNHEtMjIgMTQgLTU1IDE0cS0yNiAwIC00MSAtMTJ0LTE1IC0zMnEwIC0zMyA0NyAtMzlsMTMgLTJxMjQgLTQgMjQgLTE0DQpxMCAtMTcgLTMxIC0xN3EtMjUgMCAtNDUgMTRsLTEzIC0yM3EyNSAtMTcgNTggLTE3cTI5IDAgNDUuNSAxMnQxNi41IDMyek0xMDczIDE0bC04IDI1cS0xMyAtNyAtMjYgLTdxLTE5IDAgLTE5IDIydjYxaDQ4djI3aC00OHY0MWgtMzB2LTQxaC0yOHYtMjdoMjh2LTYxcTAgLTUwIDQ3IC01MHEyMSAwIDM2IDEwek0xMTU5IDE0NnEtMjkgMCAtNDggLTIwdC0xOSAtNTFxMCAtMzIgMTkuNSAtNTEuNXQ0OS41IC0xOS41cTMzIDAgNTUgMTlsLTE0IDIyDQpxLTE4IC0xNSAtMzkgLTE1cS0zNCAwIC00MSAzM2gxMDF2MTJxMCAzMiAtMTggNTEuNXQtNDYgMTkuNXpNMTMxOCAxNDZxLTIzIDAgLTM1IC0yMHYxNmgtMzB2LTEzNWgzMHY3NnEwIDM1IDI5IDM1cTEwIDAgMTggLTRsOSAyOHEtOSA0IC0yMSA0ek0xMzQ4IDc1cTAgLTMxIDE5LjUgLTUxdDUyLjUgLTIwcTI5IDAgNDggMTZsLTE0IDI0cS0xOCAtMTMgLTM1IC0xMnEtMTggMCAtMjkuNSAxMnQtMTEuNSAzMXQxMS41IDMxdDI5LjUgMTINCnExOSAwIDM1IC0xMmwxNCAyNHEtMjAgMTYgLTQ4IDE2cS0zMyAwIC01Mi41IC0yMHQtMTkuNSAtNTF6TTE1OTMgN2gzMHY2OHY2N2gtMzB2LTE2cS0xNSAyMCAtNDIgMjBxLTI5IDAgLTQ4LjUgLTIwdC0xOS41IC01MXQxOS41IC01MXQ0OC41IC0yMHEyOCAwIDQyIDIwdi0xN3pNMTcyNiAxNDZxLTIzIDAgLTM1IC0yMHYxNmgtMjl2LTEzNWgyOXY3NnEwIDM1IDI5IDM1cTEwIDAgMTggLTRsOSAyOHEtOCA0IC0yMSA0ek0xODY2IDdoMjl2Njh2MTIyDQpoLTI5di03MXEtMTUgMjAgLTQzIDIwdC00Ny41IC0yMC41dC0xOS41IC01MC41dDE5LjUgLTUwLjV0NDcuNSAtMjAuNXEyOSAwIDQzIDIwdi0xN3pNMTk0NCAyN2wtMiAtMWgtM3EtMiAtMSAtNCAtM3EtMyAtMSAtMyAtNHEtMSAtMiAtMSAtNnEwIC0zIDEgLTVxMCAtMiAzIC00cTIgLTIgNCAtM3Q1IC0xcTQgMCA2IDFxMCAxIDIgMmwyIDFxMSAxIDMgNHExIDIgMSA1cTAgNCAtMSA2cS0xIDEgLTMgNHEwIDEgLTIgMmwtMiAxcS0xIDAgLTMgMC41DQp0LTMgMC41ek0yMzA0IDEyODB2LTEyODBxMCAtNTIgLTM4IC05MHQtOTAgLTM4aC0yMDQ4cS01MiAwIC05MCAzOHQtMzggOTB2MTI4MHEwIDUyIDM4IDkwdDkwIDM4aDIwNDhxNTIgMCA5MCAtMzh0MzggLTkweiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQ2NyIgdW5pY29kZT0iJiN4ZjFmMjsiIGhvcml6LWFkdi14PSIyMzA0IiANCmQ9Ik0zMTMgNzU5cTAgLTUxIC0zNiAtODRxLTI5IC0yNiAtODkgLTI2aC0xN3YyMjBoMTdxNjEgMCA4OSAtMjdxMzYgLTMxIDM2IC04M3pNMjA4OSA4MjRxMCAtNTIgLTY0IC01MmgtMTl2MTAxaDIwcTYzIDAgNjMgLTQ5ek0zODAgNzU5cTAgNzQgLTUwIDEyMC41dC0xMjkgNDYuNWgtOTV2LTMzM2g5NXE3NCAwIDExOSAzOHE2MCA1MSA2MCAxMjh6TTQxMCA1OTNoNjV2MzMzaC02NXYtMzMzek03MzAgNjk0cTAgNDAgLTIwLjUgNjJ0LTc1LjUgNDINCnEtMjkgMTAgLTM5LjUgMTl0LTEwLjUgMjNxMCAxNiAxMy41IDI2LjV0MzQuNSAxMC41cTI5IDAgNTMgLTI3bDM0IDQ0cS00MSAzNyAtOTggMzdxLTQ0IDAgLTc0IC0yNy41dC0zMCAtNjcuNXEwIC0zNSAxOCAtNTUuNXQ2NCAtMzYuNXEzNyAtMTMgNDUgLTE5cTE5IC0xMiAxOSAtMzRxMCAtMjAgLTE0IC0zMy41dC0zNiAtMTMuNXEtNDggMCAtNzEgNDRsLTQyIC00MHE0NCAtNjQgMTE1IC02NHE1MSAwIDgzIDMwLjV0MzIgNzkuNXpNMTAwOCA2MDQNCnY3N3EtMzcgLTM3IC03OCAtMzdxLTQ5IDAgLTgwLjUgMzIuNXQtMzEuNSA4Mi41cTAgNDggMzEuNSA4MS41dDc3LjUgMzMuNXE0MyAwIDgxIC0zOHY3N3EtNDAgMjAgLTgwIDIwcS03NCAwIC0xMjUuNSAtNTAuNXQtNTEuNSAtMTIzLjV0NTEgLTEyMy41dDEyNSAtNTAuNXE0MiAwIDgxIDE5ek0yMjQwIDB2NTI3cS02NSAtNDAgLTE0NC41IC04NHQtMjM3LjUgLTExN3QtMzI5LjUgLTEzNy41dC00MTcuNSAtMTM0LjV0LTUwNCAtMTE4aDE1NjkNCnEyNiAwIDQ1IDE5dDE5IDQ1ek0xMzg5IDc1N3EwIDc1IC01MyAxMjh0LTEyOCA1M3QtMTI4IC01M3QtNTMgLTEyOHQ1MyAtMTI4dDEyOCAtNTN0MTI4IDUzdDUzIDEyOHpNMTU0MSA1ODRsMTQ0IDM0MmgtNzFsLTkwIC0yMjRsLTg5IDIyNGgtNzFsMTQyIC0zNDJoMzV6TTE3MTQgNTkzaDE4NHY1NmgtMTE5djkwaDExNXY1NmgtMTE1djc0aDExOXY1N2gtMTg0di0zMzN6TTIxMDUgNTkzaDgwbC0xMDUgMTQwcTc2IDE2IDc2IDk0cTAgNDcgLTMxIDczDQp0LTg3IDI2aC05N3YtMzMzaDY1djEzM2g5ek0yMzA0IDEyNzR2LTEyNjhxMCAtNTYgLTM4LjUgLTk1dC05My41IC0zOWgtMjA0MHEtNTUgMCAtOTMuNSAzOXQtMzguNSA5NXYxMjY4cTAgNTYgMzguNSA5NXQ5My41IDM5aDIwNDBxNTUgMCA5My41IC0zOXQzOC41IC05NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImYxZjMiIHVuaWNvZGU9IiYjeGYxZjM7IiBob3Jpei1hZHYteD0iMjMwNCIgDQpkPSJNMTE5IDg1NGg4OWwtNDUgMTA4ek03NDAgMzI4bDc0IDc5bC03MCA3OWgtMTYzdi00OWgxNDJ2LTU1aC0xNDJ2LTU0aDE1OXpNODk4IDQwNmw5OSAtMTEwdjIxN3pNMTE4NiA0NTNxMCAzMyAtNDAgMzNoLTg0di02OWg4M3E0MSAwIDQxIDM2ek0xNDc1IDQ1N3EwIDI5IC00MiAyOWgtODJ2LTYxaDgxcTQzIDAgNDMgMzJ6TTExOTcgOTIzcTAgMjkgLTQyIDI5aC04MnYtNjBoODFxNDMgMCA0MyAzMXpNMTY1NiA4NTRoODlsLTQ0IDEwOHoNCk02OTkgMTAwOXYtMjcxaC02NnYyMTJsLTk0IC0yMTJoLTU3bC05NCAyMTJ2LTIxMmgtMTMybC0yNSA2MGgtMTM1bC0yNSAtNjBoLTcwbDExNiAyNzFoOTZsMTEwIC0yNTd2MjU3aDEwNmw4NSAtMTg0bDc3IDE4NGgxMDh6TTEyNTUgNDUzcTAgLTIwIC01LjUgLTM1dC0xNCAtMjV0LTIyLjUgLTE2LjV0LTI2IC0xMHQtMzEuNSAtNC41dC0zMS41IC0xdC0zMi41IDAuNXQtMjkuNSAwLjV2LTkxaC0xMjZsLTgwIDkwbC04MyAtOTBoLTI1NnYyNzFoMjYwDQpsODAgLTg5bDgyIDg5aDIwN3ExMDkgMCAxMDkgLTg5ek05NjQgNzk0di01NmgtMjE3djI3MWgyMTd2LTU3aC0xNTJ2LTQ5aDE0OHYtNTVoLTE0OHYtNTRoMTUyek0yMzA0IDIzNXYtMjI5cTAgLTU1IC0zOC41IC05NC41dC05My41IC0zOS41aC0yMDQwcS01NSAwIC05My41IDM5LjV0LTM4LjUgOTQuNXY2NzhoMTExbDI1IDYxaDU1bDI1IC02MWgyMTh2NDZsMTkgLTQ2aDExM2wyMCA0N3YtNDdoNTQxdjk5bDEwIDFxMTAgMCAxMCAtMTR2LTg2aDI3OQ0KdjIzcTIzIC0xMiA1NSAtMTh0NTIuNSAtNi41dDYzIDAuNXQ1MS41IDFsMjUgNjFoNTZsMjUgLTYxaDIyN3Y1OGwzNCAtNThoMTgydjM3OGgtMTgwdi00NGwtMjUgNDRoLTE4NXYtNDRsLTIzIDQ0aC0yNDlxLTY5IDAgLTEwOSAtMjJ2MjJoLTE3MnYtMjJxLTI0IDIyIC03MyAyMmgtNjI4bC00MyAtOTdsLTQzIDk3aC0xOTh2LTQ0bC0yMiA0NGgtMTY5bC03OCAtMTc5djM5MXEwIDU1IDM4LjUgOTQuNXQ5My41IDM5LjVoMjA0MA0KcTU1IDAgOTMuNSAtMzkuNXQzOC41IC05NC41di02NzhoLTEyMHEtNTEgMCAtODEgLTIydjIyaC0xNzdxLTU1IDAgLTc4IC0yMnYyMmgtMzE2di0yMnEtMzEgMjIgLTg3IDIyaC0yMDl2LTIycS0yMyAyMiAtOTEgMjJoLTIzNGwtNTQgLTU4bC01MCA1OGgtMzQ5di0zNzhoMzQzbDU1IDU5bDUyIC01OWgyMTF2ODloMjFxNTkgMCA5MCAxM3YtMTAyaDE3NHY5OWg4cTggMCAxMCAtMnQyIC0xMHYtODdoNTI5cTU3IDAgODggMjR2LTI0aDE2OA0KcTYwIDAgOTUgMTd6TTE1NDYgNDY5cTAgLTIzIC0xMiAtNDN0LTM0IC0yOXEyNSAtOSAzNCAtMjZ0OSAtNDZ2LTU0aC02NXY0NXEwIDMzIC0xMiA0My41dC00NiAxMC41aC02OXYtOTloLTY1djI3MWgxNTRxNDggMCA3NyAtMTV0MjkgLTU4ek0xMjY5IDkzNnEwIC0yNCAtMTIuNSAtNDR0LTMzLjUgLTI5cTI2IC05IDM0LjUgLTI1LjV0OC41IC00Ni41di01M2gtNjVxMCA5IDAuNSAyNi41dDAgMjV0LTMgMTguNXQtOC41IDE2dC0xNy41IDguNQ0KdC0yOS41IDMuNWgtNzB2LTk4aC02NHYyNzFsMTUzIC0xcTQ5IDAgNzggLTE0LjV0MjkgLTU3LjV6TTE3OTggMzI3di01NmgtMjE2djI3MWgyMTZ2LTU2aC0xNTF2LTQ5aDE0OHYtNTVoLTE0OHYtNTR6TTEzNzIgMTAwOXYtMjcxaC02NnYyNzFoNjZ6TTIwNjUgMzU3cTAgLTg2IC0xMDIgLTg2aC0xMjZ2NThoMTI2cTM0IDAgMzQgMjVxMCAxNiAtMTcgMjF0LTQxLjUgNXQtNDkuNSAzLjV0LTQyIDIyLjV0LTE3IDU1cTAgMzkgMjYgNjB0NjYgMjENCmgxMzB2LTU3aC0xMTlxLTM2IDAgLTM2IC0yNXEwIC0xNiAxNy41IC0yMC41dDQyIC00dDQ5IC0yLjV0NDIgLTIxLjV0MTcuNSAtNTQuNXpNMjMwNCA0MDd2LTEwMXEtMjQgLTM1IC04OCAtMzVoLTEyNXY1OGgxMjVxMzMgMCAzMyAyNXEwIDEzIC0xMi41IDE5dC0zMSA1LjV0LTQwIDJ0LTQwIDh0LTMxIDI0dC0xMi41IDQ4LjVxMCAzOSAyNi41IDYwdDY2LjUgMjFoMTI5di01N2gtMTE4cS0zNiAwIC0zNiAtMjVxMCAtMjAgMjkgLTIydDY4LjUgLTUNCnQ1Ni41IC0yNnpNMjEzOSAxMDA4di0yNzBoLTkybC0xMjIgMjAzdi0yMDNoLTEzMmwtMjYgNjBoLTEzNGwtMjUgLTYwaC03NXEtMTI5IDAgLTEyOSAxMzNxMCAxMzggMTMzIDEzOGg2M3YtNTlxLTcgMCAtMjggMXQtMjguNSAwLjV0LTIzIC0ydC0yMS41IC02LjV0LTE0LjUgLTEzLjV0LTExLjUgLTIzdC0zIC0zMy41cTAgLTM4IDEzLjUgLTU4dDQ5LjUgLTIwaDI5bDkyIDIxM2g5N2wxMDkgLTI1NnYyNTZoOTlsMTE0IC0xODh2MTg4aDY2eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQ2OSIgdW5pY29kZT0iJiN4ZjFmNDsiIGhvcml6LWFkdi14PSIyMzA0IiANCmQ9Ik03NDUgNjMwcTAgLTM3IC0yNS41IC02MS41dC02Mi41IC0yNC41cS0yOSAwIC00Ni41IDE2dC0xNy41IDQ0cTAgMzcgMjUgNjIuNXQ2MiAyNS41cTI4IDAgNDYuNSAtMTYuNXQxOC41IC00NS41ek0xNTMwIDc3OXEwIC00MiAtMjIgLTU3dC02NiAtMTVsLTMyIC0xbDE3IDEwN3EyIDExIDEzIDExaDE4cTIyIDAgMzUgLTJ0MjUgLTEyLjV0MTIgLTMwLjV6TTE4ODEgNjMwcTAgLTM2IC0yNS41IC02MXQtNjEuNSAtMjVxLTI5IDAgLTQ3IDE2DQp0LTE4IDQ0cTAgMzcgMjUgNjIuNXQ2MiAyNS41cTI4IDAgNDYuNSAtMTYuNXQxOC41IC00NS41ek01MTMgODAxcTAgNTkgLTM4LjUgODUuNXQtMTAwLjUgMjYuNWgtMTYwcS0xOSAwIC0yMSAtMTlsLTY1IC00MDhxLTEgLTYgMyAtMTF0MTAgLTVoNzZxMjAgMCAyMiAxOWwxOCAxMTBxMSA4IDcgMTN0MTUgNi41dDE3IDEuNXQxOSAtMXQxNCAtMXE4NiAwIDEzNSA0OC41dDQ5IDEzNC41ek04MjIgNDg5bDQxIDI2MXExIDYgLTMgMTF0LTEwIDVoLTc2DQpxLTE0IDAgLTE3IC0zM3EtMjcgNDAgLTk1IDQwcS03MiAwIC0xMjIuNSAtNTR0LTUwLjUgLTEyN3EwIC01OSAzNC41IC05NHQ5Mi41IC0zNXEyOCAwIDU4IDEydDQ4IDMycS00IC0xMiAtNCAtMjFxMCAtMTYgMTMgLTE2aDY5cTE5IDAgMjIgMTl6TTEyNjkgNzUycTAgNSAtNCA5LjV0LTkgNC41aC03N3EtMTEgMCAtMTggLTEwbC0xMDYgLTE1NmwtNDQgMTUwcS01IDE2IC0yMiAxNmgtNzVxLTUgMCAtOSAtNC41dC00IC05LjVxMCAtMiAxOS41IC01OQ0KdDQyIC0xMjN0MjMuNSAtNzBxLTgyIC0xMTIgLTgyIC0xMjBxMCAtMTMgMTMgLTEzaDc3cTExIDAgMTggMTBsMjU1IDM2OHEyIDIgMiA3ek0xNjQ5IDgwMXEwIDU5IC0zOC41IDg1LjV0LTEwMC41IDI2LjVoLTE1OXEtMjAgMCAtMjIgLTE5bC02NSAtNDA4cS0xIC02IDMgLTExdDEwIC01aDgycTEyIDAgMTYgMTNsMTggMTE2cTEgOCA3IDEzdDE1IDYuNXQxNyAxLjV0MTkgLTF0MTQgLTFxODYgMCAxMzUgNDguNXQ0OSAxMzQuNXpNMTk1OCA0ODkNCmw0MSAyNjFxMSA2IC0zIDExdC0xMCA1aC03NnEtMTQgMCAtMTcgLTMzcS0yNiA0MCAtOTUgNDBxLTcyIDAgLTEyMi41IC01NHQtNTAuNSAtMTI3cTAgLTU5IDM0LjUgLTk0dDkyLjUgLTM1cTI5IDAgNTkgMTJ0NDcgMzJxMCAtMSAtMiAtOXQtMiAtMTJxMCAtMTYgMTMgLTE2aDY5cTE5IDAgMjIgMTl6TTIxNzYgODk4djFxMCAxNCAtMTMgMTRoLTc0cS0xMSAwIC0xMyAtMTFsLTY1IC00MTZsLTEgLTJxMCAtNSA0IC05LjV0MTAgLTQuNWg2Ng0KcTE5IDAgMjEgMTl6TTM5MiA3NjRxLTUgLTM1IC0yNiAtNDZ0LTYwIC0xMWwtMzMgLTFsMTcgMTA3cTIgMTEgMTMgMTFoMTlxNDAgMCA1OCAtMTEuNXQxMiAtNDguNXpNMjMwNCAxMjgwdi0xMjgwcTAgLTUyIC0zOCAtOTB0LTkwIC0zOGgtMjA0OHEtNTIgMCAtOTAgMzh0LTM4IDkwdjEyODBxMCA1MiAzOCA5MHQ5MCAzOGgyMDQ4cTUyIDAgOTAgLTM4dDM4IC05MHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80NzAiIHVuaWNvZGU9IiYjeGYxZjU7IiBob3Jpei1hZHYteD0iMjMwNCIgDQpkPSJNMTU5NyA2MzNxMCAtNjkgLTIxIC0xMDZxLTE5IC0zNSAtNTIgLTM1cS0yMyAwIC00MSA5djIyNHEyOSAzMCA1NyAzMHE1NyAwIDU3IC0xMjJ6TTIwMzUgNjY5aC0xMTBxNiA5OCA1NiA5OHE1MSAwIDU0IC05OHpNNDc2IDUzNHEwIDU5IC0zMyA5MS41dC0xMDEgNTcuNXEtMzYgMTMgLTUyIDI0dC0xNiAyNXEwIDI2IDM4IDI2cTU4IDAgMTI0IC0zM2wxOCAxMTJxLTY3IDMyIC0xNDkgMzJxLTc3IDAgLTEyMyAtMzhxLTQ4IC0zOSAtNDggLTEwOQ0KcTAgLTU4IDMyLjUgLTkwLjV0OTkuNSAtNTYuNXEzOSAtMTQgNTQuNSAtMjUuNXQxNS41IC0yNy41cTAgLTMxIC00OCAtMzFxLTI5IDAgLTcwIDEyLjV0LTcyIDMwLjVsLTE4IC0xMTNxNzIgLTQxIDE2OCAtNDFxODEgMCAxMjkgMzdxNTEgNDEgNTEgMTE3ek03NzEgNzQ5bDE5IDExMWgtOTZ2MTM1bC0xMjkgLTIxbC0xOCAtMTE0bC00NiAtOGwtMTcgLTEwM2g2MnYtMjE5cTAgLTg0IDQ0IC0xMjBxMzggLTMwIDExMSAtMzBxMzIgMCA3OSAxMXYxMTgNCnEtMzIgLTcgLTQ0IC03cS00MiAwIC00MiA1MHYxOTdoNzd6TTEwODcgNzI0djEzOXEtMTUgMyAtMjggM3EtMzIgMCAtNTUuNSAtMTZ0LTMzLjUgLTQ2bC0xMCA1NmgtMTMxdi00NzFoMTUwdjMwNnEyNiAzMSA4MiAzMXExNiAwIDI2IC0yek0xMTI0IDM4OWgxNTB2NDcxaC0xNTB2LTQ3MXpNMTc0NiA2MzhxMCAxMjIgLTQ1IDE3OXEtNDAgNTIgLTExMSA1MnEtNjQgMCAtMTE3IC01NmwtOCA0N2gtMTMydi02NDVsMTUwIDI1djE1MQ0KcTM2IC0xMSA2OCAtMTFxODMgMCAxMzQgNTZxNjEgNjUgNjEgMjAyek0xMjc4IDk4NnEwIDMzIC0yMyA1NnQtNTYgMjN0LTU2IC0yM3QtMjMgLTU2dDIzIC01Ni41dDU2IC0yMy41dDU2IDIzLjV0MjMgNTYuNXpNMjE3NiA2MjlxMCAxMTMgLTQ4IDE3NnEtNTAgNjQgLTE0NCA2NHEtOTYgMCAtMTUxLjUgLTY2dC01NS41IC0xODBxMCAtMTI4IDYzIC0xODhxNTUgLTU1IDE2MSAtNTVxMTAxIDAgMTYwIDQwbC0xNiAxMDNxLTU3IC0zMSAtMTI4IC0zMQ0KcS00MyAwIC02MyAxOXEtMjMgMTkgLTI4IDY2aDI0OHEyIDE0IDIgNTJ6TTIzMDQgMTI4MHYtMTI4MHEwIC01MiAtMzggLTkwdC05MCAtMzhoLTIwNDhxLTUyIDAgLTkwIDM4dC0zOCA5MHYxMjgwcTAgNTIgMzggOTB0OTAgMzhoMjA0OHE1MiAwIDkwIC0zOHQzOCAtOTB6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDcxIiB1bmljb2RlPSImI3hmMWY2OyIgaG9yaXotYWR2LXg9IjIwNDgiIA0KZD0iTTE1NTggNjg0cTYxIC0zNTYgMjk4IC01NTZxMCAtNTIgLTM4IC05MHQtOTAgLTM4aC00NDhxMCAtMTA2IC03NSAtMTgxdC0xODEgLTc1dC0xODAuNSA3NC41dC03NS41IDE4MC41ek0xMDI0IC0xNzZxMTYgMCAxNiAxNnQtMTYgMTZxLTU5IDAgLTEwMS41IDQyLjV0LTQyLjUgMTAxLjVxMCAxNiAtMTYgMTZ0LTE2IC0xNnEwIC03MyA1MS41IC0xMjQuNXQxMjQuNSAtNTEuNXpNMjAyNiAxNDI0cTggLTEwIDcuNSAtMjMuNXQtMTAuNSAtMjIuNQ0KbC0xODcyIC0xNjIycS0xMCAtOCAtMjMuNSAtN3QtMjEuNSAxMWwtODQgOTZxLTggMTAgLTcuNSAyMy41dDEwLjUgMjEuNWwxODYgMTYxcS0xOSAzMiAtMTkgNjZxNTAgNDIgOTEgODh0ODUgMTE5LjV0NzQuNSAxNTguNXQ1MCAyMDZ0MTkuNSAyNjBxMCAxNTIgMTE3IDI4Mi41dDMwNyAxNTguNXEtOCAxOSAtOCAzOXEwIDQwIDI4IDY4dDY4IDI4dDY4IC0yOHQyOCAtNjhxMCAtMjAgLTggLTM5cTEyNCAtMTggMjE5IC04Mi41dDE0OCAtMTU3LjUNCmw0MTggMzYzcTEwIDggMjMuNSA3dDIxLjUgLTExeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQ3MiIgdW5pY29kZT0iJiN4ZjFmNzsiIGhvcml6LWFkdi14PSIyMDQ4IiANCmQ9Ik0xMDQwIC0xNjBxMCAxNiAtMTYgMTZxLTU5IDAgLTEwMS41IDQyLjV0LTQyLjUgMTAxLjVxMCAxNiAtMTYgMTZ0LTE2IC0xNnEwIC03MyA1MS41IC0xMjQuNXQxMjQuNSAtNTEuNXExNiAwIDE2IDE2ek01MDMgMzE1bDg3NyA3NjBxLTQyIDg4IC0xMzIuNSAxNDYuNXQtMjIzLjUgNTguNXEtOTMgMCAtMTY5LjUgLTMxLjV0LTEyMS41IC04MC41dC02OSAtMTAzdC0yNCAtMTA1cTAgLTM4NCAtMTM3IC02NDV6TTE4NTYgMTI4DQpxMCAtNTIgLTM4IC05MHQtOTAgLTM4aC00NDhxMCAtMTA2IC03NSAtMTgxdC0xODEgLTc1dC0xODAuNSA3NC41dC03NS41IDE4MC41bDE0OSAxMjloNzU3cS0xNjYgMTg3IC0yMjcgNDU5bDExMSA5N3E2MSAtMzU2IDI5OCAtNTU2ek0xOTQyIDE1MjBsODQgLTk2cTggLTEwIDcuNSAtMjMuNXQtMTAuNSAtMjIuNWwtMTg3MiAtMTYyMnEtMTAgLTggLTIzLjUgLTd0LTIxLjUgMTFsLTg0IDk2cS04IDEwIC03LjUgMjMuNXQxMC41IDIxLjVsMTg2IDE2MQ0KcS0xOSAzMiAtMTkgNjZxNTAgNDIgOTEgODh0ODUgMTE5LjV0NzQuNSAxNTguNXQ1MCAyMDZ0MTkuNSAyNjBxMCAxNTIgMTE3IDI4Mi41dDMwNyAxNTguNXEtOCAxOSAtOCAzOXEwIDQwIDI4IDY4dDY4IDI4dDY4IC0yOHQyOCAtNjhxMCAtMjAgLTggLTM5cTEyNCAtMTggMjE5IC04Mi41dDE0OCAtMTU3LjVsNDE4IDM2M3ExMCA4IDIzLjUgN3QyMS41IC0xMXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80NzMiIHVuaWNvZGU9IiYjeGYxZjg7IiBob3Jpei1hZHYteD0iMTQwOCIgDQpkPSJNNTEyIDE2MHY3MDRxMCAxNCAtOSAyM3QtMjMgOWgtNjRxLTE0IDAgLTIzIC05dC05IC0yM3YtNzA0cTAgLTE0IDkgLTIzdDIzIC05aDY0cTE0IDAgMjMgOXQ5IDIzek03NjggMTYwdjcwNHEwIDE0IC05IDIzdC0yMyA5aC02NHEtMTQgMCAtMjMgLTl0LTkgLTIzdi03MDRxMCAtMTQgOSAtMjN0MjMgLTloNjRxMTQgMCAyMyA5dDkgMjN6TTEwMjQgMTYwdjcwNHEwIDE0IC05IDIzdC0yMyA5aC02NHEtMTQgMCAtMjMgLTl0LTkgLTIzdi03MDQNCnEwIC0xNCA5IC0yM3QyMyAtOWg2NHExNCAwIDIzIDl0OSAyM3pNNDgwIDExNTJoNDQ4bC00OCAxMTdxLTcgOSAtMTcgMTFoLTMxN3EtMTAgLTIgLTE3IC0xMXpNMTQwOCAxMTIwdi02NHEwIC0xNCAtOSAtMjN0LTIzIC05aC05NnYtOTQ4cTAgLTgzIC00NyAtMTQzLjV0LTExMyAtNjAuNWgtODMycS02NiAwIC0xMTMgNTguNXQtNDcgMTQxLjV2OTUyaC05NnEtMTQgMCAtMjMgOXQtOSAyM3Y2NHEwIDE0IDkgMjN0MjMgOWgzMDlsNzAgMTY3DQpxMTUgMzcgNTQgNjN0NzkgMjZoMzIwcTQwIDAgNzkgLTI2dDU0IC02M2w3MCAtMTY3aDMwOXExNCAwIDIzIC05dDkgLTIzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQ3NCIgdW5pY29kZT0iJiN4ZjFmOTsiIA0KZD0iTTExNTAgNDYydi0xMDlxMCAtNTAgLTM2LjUgLTg5dC05NCAtNjAuNXQtMTE4IC0zMi41dC0xMTcuNSAtMTFxLTIwNSAwIC0zNDIuNSAxMzl0LTEzNy41IDM0NnEwIDIwMyAxMzYgMzM5dDMzOSAxMzZxMzQgMCA3NS41IC00LjV0OTMgLTE4dDkyLjUgLTM0dDY5IC01Ni41dDI4IC04MXYtMTA5cTAgLTE2IC0xNiAtMTZoLTExOHEtMTYgMCAtMTYgMTZ2NzBxMCA0MyAtNjUuNSA2Ny41dC0xMzcuNSAyNC41cS0xNDAgMCAtMjI4LjUgLTkxLjUNCnQtODguNSAtMjM3LjVxMCAtMTUxIDkxLjUgLTI0OS41dDIzMy41IC05OC41cTY4IDAgMTM4IDI0dDcwIDY2djcwcTAgNyA0LjUgMTEuNXQxMC41IDQuNWgxMTlxNiAwIDExIC00LjV0NSAtMTEuNXpNNzY4IDEyODBxLTEzMCAwIC0yNDguNSAtNTF0LTIwNCAtMTM2LjV0LTEzNi41IC0yMDR0LTUxIC0yNDguNXQ1MSAtMjQ4LjV0MTM2LjUgLTIwNHQyMDQgLTEzNi41dDI0OC41IC01MXQyNDguNSA1MXQyMDQgMTM2LjV0MTM2LjUgMjA0dDUxIDI0OC41DQp0LTUxIDI0OC41dC0xMzYuNSAyMDR0LTIwNCAxMzYuNXQtMjQ4LjUgNTF6TTE1MzYgNjQwcTAgLTIwOSAtMTAzIC0zODUuNXQtMjc5LjUgLTI3OS41dC0zODUuNSAtMTAzdC0zODUuNSAxMDN0LTI3OS41IDI3OS41dC0xMDMgMzg1LjV0MTAzIDM4NS41dDI3OS41IDI3OS41dDM4NS41IDEwM3QzODUuNSAtMTAzdDI3OS41IC0yNzkuNXQxMDMgLTM4NS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQ3NSIgdW5pY29kZT0iJiN4ZjFmYTsiIA0KZD0iTTk3MiA3NjFxMCAxMDggLTUzLjUgMTY5dC0xNDcuNSA2MXEtNjMgMCAtMTI0IC0zMC41dC0xMTAgLTg0LjV0LTc5LjUgLTEzN3QtMzAuNSAtMTgwcTAgLTExMiA1My41IC0xNzN0MTUwLjUgLTYxcTk2IDAgMTc2IDY2LjV0MTIyLjUgMTY2dDQyLjUgMjAzLjV6TTE1MzYgNjQwcTAgLTExMSAtMzcgLTE5N3QtOTguNSAtMTM1dC0xMzEuNSAtNzQuNXQtMTQ1IC0yNy41cS02IDAgLTE1LjUgLTAuNXQtMTYuNSAtMC41cS05NSAwIC0xNDIgNTMNCnEtMjggMzMgLTMzIDgzcS01MiAtNjYgLTEzMS41IC0xMTB0LTE3My41IC00NHEtMTYxIDAgLTI0OS41IDk1LjV0LTg4LjUgMjY5LjVxMCAxNTcgNjYgMjkwdDE3OSAyMTAuNXQyNDYgNzcuNXE4NyAwIDE1NSAtMzUuNXQxMDYgLTk5LjVsMiAxOWwxMSA1NnExIDYgNS41IDEydDkuNSA2aDExOHE1IDAgMTMgLTExcTUgLTUgMyAtMTZsLTEyMCAtNjE0cS01IC0yNCAtNSAtNDhxMCAtMzkgMTIuNSAtNTJ0NDQuNSAtMTNxMjggMSA1NyA1LjV0NzMgMjQNCnQ3NyA1MHQ1NyA4OS41dDI0IDEzN3EwIDI5MiAtMTc0IDQ2NnQtNDY2IDE3NHEtMTMwIDAgLTI0OC41IC01MXQtMjA0IC0xMzYuNXQtMTM2LjUgLTIwNHQtNTEgLTI0OC41dDUxIC0yNDguNXQxMzYuNSAtMjA0dDIwNCAtMTM2LjV0MjQ4LjUgLTUxcTIyOCAwIDQwNSAxNDRxMTEgOSAyNCA4dDIxIC0xMmw0MSAtNDlxOCAtMTIgNyAtMjRxLTIgLTEzIC0xMiAtMjJxLTEwMiAtODMgLTIyNy41IC0xMjh0LTI1OC41IC00NXEtMTU2IDAgLTI5OCA2MQ0KdC0yNDUgMTY0dC0xNjQgMjQ1dC02MSAyOTh0NjEgMjk4dDE2NCAyNDV0MjQ1IDE2NHQyOTggNjFxMzQ0IDAgNTU2IC0yMTJ0MjEyIC01NTZ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDc2IiB1bmljb2RlPSImI3hmMWZiOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE2OTggMTQ0MnE5NCAtOTQgOTQgLTIyNi41dC05NCAtMjI1LjVsLTIyNSAtMjIzbDEwNCAtMTA0cTEwIC0xMCAxMCAtMjN0LTEwIC0yM2wtMjEwIC0yMTBxLTEwIC0xMCAtMjMgLTEwdC0yMyAxMGwtMTA1IDEwNWwtNjAzIC02MDNxLTM3IC0zNyAtOTAgLTM3aC0yMDNsLTI1NiAtMTI4bC02NCA2NGwxMjggMjU2djIwM3EwIDUzIDM3IDkwbDYwMyA2MDNsLTEwNSAxMDVxLTEwIDEwIC0xMCAyM3QxMCAyM2wyMTAgMjEwcTEwIDEwIDIzIDEwDQp0MjMgLTEwbDEwNCAtMTA0bDIyMyAyMjVxOTMgOTQgMjI1LjUgOTR0MjI2LjUgLTk0ek01MTIgNjRsNTc2IDU3NmwtMTkyIDE5MmwtNTc2IC01NzZ2LTE5MmgxOTJ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJmMWZjIiB1bmljb2RlPSImI3hmMWZjOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE2MTUgMTUzNnE3MCAwIDEyMi41IC00Ni41dDUyLjUgLTExNi41cTAgLTYzIC00NSAtMTUxcS0zMzIgLTYyOSAtNDY1IC03NTJxLTk3IC05MSAtMjE4IC05MXEtMTI2IDAgLTIxNi41IDkyLjV0LTkwLjUgMjE5LjVxMCAxMjggOTIgMjEybDYzOCA1NzlxNTkgNTQgMTMwIDU0ek03MDYgNTAycTM5IC03NiAxMDYuNSAtMTMwdDE1MC41IC03NmwxIC03MXE0IC0yMTMgLTEyOS41IC0zNDd0LTM0OC41IC0xMzRxLTEyMyAwIC0yMTggNDYuNQ0KdC0xNTIuNSAxMjcuNXQtODYuNSAxODN0LTI5IDIyMHE3IC01IDQxIC0zMHQ2MiAtNDQuNXQ1OSAtMzYuNXQ0NiAtMTdxNDEgMCA1NSAzN3EyNSA2NiA1Ny41IDExMi41dDY5LjUgNzZ0ODggNDcuNXQxMDMgMjUuNXQxMjUgMTAuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80NzgiIHVuaWNvZGU9IiYjeGYxZmQ7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTc5MiAxMjh2LTM4NGgtMTc5MnYzODRxNDUgMCA4NSAxNHQ1OSAyNy41dDQ3IDM3LjVxMzAgMjcgNTEuNSAzOHQ1Ni41IDExcTI0IDAgNDQgLTd0MzEgLTE1dDMzIC0yN3EyOSAtMjUgNDcgLTM4dDU4IC0yN3Q4NiAtMTRxNDUgMCA4NSAxNC41dDU4IDI3dDQ4IDM3LjVxMjEgMTkgMzIuNSAyN3QzMSAxNXQ0My41IDdxMzUgMCA1Ni41IC0xMXQ1MS41IC0zOHEyOCAtMjQgNDcgLTM3LjV0NTkgLTI3LjV0ODUgLTE0dDg1IDE0dDU5IDI3LjUNCnQ0NyAzNy41cTMwIDI3IDUxLjUgMzh0NTYuNSAxMXEzNCAwIDU1LjUgLTExdDUxLjUgLTM4cTI4IC0yNCA0NyAtMzcuNXQ1OSAtMjcuNXQ4NSAtMTR6TTE3OTIgNDQ4di0xOTJxLTI0IDAgLTQ0IDd0LTMxIDE1dC0zMyAyN3EtMjkgMjUgLTQ3IDM4dC01OCAyN3QtODUgMTRxLTQ2IDAgLTg2IC0xNHQtNTggLTI3dC00NyAtMzhxLTIyIC0xOSAtMzMgLTI3dC0zMSAtMTV0LTQ0IC03cS0zNSAwIC01Ni41IDExdC01MS41IDM4cS0yOSAyNSAtNDcgMzgNCnQtNTggMjd0LTg2IDE0cS00NSAwIC04NSAtMTQuNXQtNTggLTI3dC00OCAtMzcuNXEtMjEgLTE5IC0zMi41IC0yN3QtMzEgLTE1dC00My41IC03cS0zNSAwIC01Ni41IDExdC01MS41IDM4cS0yOCAyNCAtNDcgMzcuNXQtNTkgMjcuNXQtODUgMTRxLTQ2IDAgLTg2IC0xNHQtNTggLTI3dC00NyAtMzhxLTMwIC0yNyAtNTEuNSAtMzh0LTU2LjUgLTExdjE5MnEwIDgwIDU2IDEzNnQxMzYgNTZoNjR2NDQ4aDI1NnYtNDQ4aDI1NnY0NDhoMjU2di00NDgNCmgyNTZ2NDQ4aDI1NnYtNDQ4aDY0cTgwIDAgMTM2IC01NnQ1NiAtMTM2ek01MTIgMTMxMnEwIC03NyAtMzYgLTExOC41dC05MiAtNDEuNXEtNTMgMCAtOTAuNSAzNy41dC0zNy41IDkwLjVxMCAyOSA5LjUgNTF0MjMuNSAzNHQzMSAyOHQzMSAzMS41dDIzLjUgNDQuNXQ5LjUgNjdxMzggMCA4MyAtNzR0NDUgLTE1MHpNMTAyNCAxMzEycTAgLTc3IC0zNiAtMTE4LjV0LTkyIC00MS41cS01MyAwIC05MC41IDM3LjV0LTM3LjUgOTAuNQ0KcTAgMjkgOS41IDUxdDIzLjUgMzR0MzEgMjh0MzEgMzEuNXQyMy41IDQ0LjV0OS41IDY3cTM4IDAgODMgLTc0dDQ1IC0xNTB6TTE1MzYgMTMxMnEwIC03NyAtMzYgLTExOC41dC05MiAtNDEuNXEtNTMgMCAtOTAuNSAzNy41dC0zNy41IDkwLjVxMCAyOSA5LjUgNTF0MjMuNSAzNHQzMSAyOHQzMSAzMS41dDIzLjUgNDQuNXQ5LjUgNjdxMzggMCA4MyAtNzR0NDUgLTE1MHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80NzkiIHVuaWNvZGU9IiYjeGYxZmU7IiBob3Jpei1hZHYteD0iMjA0OCIgDQpkPSJNMjA0OCAwdi0xMjhoLTIwNDh2MTUzNmgxMjh2LTE0MDhoMTkyMHpNMTY2NCAxMDI0bDI1NiAtODk2aC0xNjY0djU3Nmw0NDggNTc2bDU3NiAtNTc2eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQ4MCIgdW5pY29kZT0iJiN4ZjIwMDsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik03NjggNjQ2bDU0NiAtNTQ2cS0xMDYgLTEwOCAtMjQ3LjUgLTE2OHQtMjk4LjUgLTYwcS0yMDkgMCAtMzg1LjUgMTAzdC0yNzkuNSAyNzkuNXQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN2LTc2MnpNOTU1IDY0MGg3NzNxMCAtMTU3IC02MCAtMjk4LjV0LTE2OCAtMjQ3LjV6TTE2NjQgNzY4aC03Njh2NzY4cTIwOSAwIDM4NS41IC0xMDN0Mjc5LjUgLTI3OS41dDEwMyAtMzg1LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDgxIiB1bmljb2RlPSImI3hmMjAxOyIgaG9yaXotYWR2LXg9IjIwNDgiIA0KZD0iTTIwNDggMHYtMTI4aC0yMDQ4djE1MzZoMTI4di0xNDA4aDE5MjB6TTE5MjAgMTI0OHYtNDM1cTAgLTIxIC0xOS41IC0yOS41dC0zNS41IDcuNWwtMTIxIDEyMWwtNjMzIC02MzNxLTEwIC0xMCAtMjMgLTEwdC0yMyAxMGwtMjMzIDIzM2wtNDE2IC00MTZsLTE5MiAxOTJsNTg1IDU4NXExMCAxMCAyMyAxMHQyMyAtMTBsMjMzIC0yMzNsNDY0IDQ2NGwtMTIxIDEyMXEtMTYgMTYgLTcuNSAzNS41dDI5LjUgMTkuNWg0MzVxMTQgMCAyMyAtOQ0KdDkgLTIzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQ4MiIgdW5pY29kZT0iJiN4ZjIwMjsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xMjkyIDgzMnEwIC02IDEwIC00MXExMCAtMjkgMjUgLTQ5LjV0NDEgLTM0dDQ0IC0yMHQ1NSAtMTYuNXEzMjUgLTkxIDMyNSAtMzMycTAgLTE0NiAtMTA1LjUgLTI0Mi41dC0yNTQuNSAtOTYuNXEtNTkgMCAtMTExLjUgMTguNXQtOTEuNSA0NS41dC03NyA3NC41dC02MyA4Ny41dC01My41IDEwMy41dC00My41IDEwM3QtMzkuNSAxMDYuNXQtMzUuNSA5NXEtMzIgODEgLTYxLjUgMTMzLjV0LTczLjUgOTYuNXQtMTA0IDY0dC0xNDIgMjANCnEtOTYgMCAtMTgzIC01NS41dC0xMzggLTE0NC41dC01MSAtMTg1cTAgLTE2MCAxMDYuNSAtMjc5LjV0MjYzLjUgLTExOS41cTE3NyAwIDI1OCA5NXE1NiA2MyA4MyAxMTZsODQgLTE1MnEtMTUgLTM0IC00NCAtNzBsMSAtMXEtMTMxIC0xNTIgLTM4OCAtMTUycS0xNDcgMCAtMjY5LjUgNzl0LTE5MC41IDIwNy41dC02OCAyNzQuNXEwIDEwNSA0My41IDIwNnQxMTYgMTc2LjV0MTcyIDEyMS41dDIwNC41IDQ2cTg3IDAgMTU5IC0xOXQxMjMuNSAtNTANCnQ5NSAtODB0NzIuNSAtOTl0NTguNSAtMTE3dDUwLjUgLTEyNC41dDUwIC0xMzAuNXQ1NSAtMTI3cTk2IC0yMDAgMjMzIC0yMDBxODEgMCAxMzguNSA0OC41dDU3LjUgMTI4LjVxMCA0MiAtMTkgNzJ0LTUwLjUgNDZ0LTcyLjUgMzEuNXQtODQuNSAyN3QtODcuNSAzNHQtODEgNTJ0LTY1IDgydC0zOSAxMjIuNXEtMyAxNiAtMyAzM3EwIDExMCA4Ny41IDE5MnQxOTguNSA3OHE3OCAtMyAxMjAuNSAtMTQuNXQ5MC41IC01My41aC0xDQpxMTIgLTExIDIzIC0yNC41dDI2IC0zNnQxOSAtMjcuNWwtMTI5IC05OXEtMjYgNDkgLTU0IDcwdjFxLTIzIDIxIC05NyAyMXEtNDkgMCAtODQgLTMzdC0zNSAtODN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDgzIiB1bmljb2RlPSImI3hmMjAzOyIgDQpkPSJNMTQzMiA0ODRxMCAxNzMgLTIzNCAyMzlxLTM1IDEwIC01MyAxNi41dC0zOCAyNXQtMjkgNDYuNXEwIDIgLTIgOC41dC0zIDEydC0xIDcuNXEwIDM2IDI0LjUgNTkuNXQ2MC41IDIzLjVxNTQgMCA3MSAtMTVoLTFxMjAgLTE1IDM5IC01MWw5MyA3MXEtMzkgNTQgLTQ5IDY0cS0zMyAyOSAtNjcuNSAzOXQtODUuNSAxMHEtODAgMCAtMTQyIC01Ny41dC02MiAtMTM3LjVxMCAtNyAyIC0yM3ExNiAtOTYgNjQuNSAtMTQwdDE0OC41IC03Mw0KcTI5IC04IDQ5IC0xNS41dDQ1IC0yMS41dDM4LjUgLTM0LjV0MTMuNSAtNDYuNXYtNXExIC01OCAtNDAuNSAtOTN0LTEwMC41IC0zNXEtOTcgMCAtMTY3IDE0NHEtMjMgNDcgLTUxLjUgMTIxLjV0LTQ4IDEyNS41dC01NCAxMTAuNXQtNzQgOTUuNXQtMTAzLjUgNjAuNXQtMTQ3IDI0LjVxLTEwMSAwIC0xOTIgLTU2dC0xNDQgLTE0OHQtNTAgLTE5MnYtMXE0IC0xMDggNTAuNSAtMTk5dDEzMy41IC0xNDcuNXQxOTYgLTU2LjVxMTg2IDAgMjc5IDExMA0KcTIwIDI3IDMxIDUxbC02MCAxMDlxLTQyIC04MCAtOTkgLTExNnQtMTQ2IC0zNnEtMTE1IDAgLTE5MSA4N3QtNzYgMjA0cTAgMTA1IDgyIDE4OXQxODYgODRxMTEyIDAgMTcwIC01My41dDEwNCAtMTcyLjVxOCAtMjEgMjUuNSAtNjguNXQyOC41IC03Ni41dDMxLjUgLTc0LjV0MzguNSAtNzR0NDUuNSAtNjIuNXQ1NS41IC01My41dDY2IC0zM3Q4MCAtMTMuNXExMDcgMCAxODMgNjkuNXQ3NiAxNzQuNXpNMTUzNiAxMTIwdi05NjANCnEwIC0xMTkgLTg0LjUgLTIwMy41dC0yMDMuNSAtODQuNWgtOTYwcS0xMTkgMCAtMjAzLjUgODQuNXQtODQuNSAyMDMuNXY5NjBxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDk2MHExMTkgMCAyMDMuNSAtODQuNXQ4NC41IC0yMDMuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80ODQiIHVuaWNvZGU9IiYjeGYyMDQ7IiBob3Jpei1hZHYteD0iMjA0OCIgDQpkPSJNMTE1MiA2NDBxMCAxMDQgLTQwLjUgMTk4LjV0LTEwOS41IDE2My41dC0xNjMuNSAxMDkuNXQtMTk4LjUgNDAuNXQtMTk4LjUgLTQwLjV0LTE2My41IC0xMDkuNXQtMTA5LjUgLTE2My41dC00MC41IC0xOTguNXQ0MC41IC0xOTguNXQxMDkuNSAtMTYzLjV0MTYzLjUgLTEwOS41dDE5OC41IC00MC41dDE5OC41IDQwLjV0MTYzLjUgMTA5LjV0MTA5LjUgMTYzLjV0NDAuNSAxOTguNXpNMTkyMCA2NDBxMCAxMDQgLTQwLjUgMTk4LjUNCnQtMTA5LjUgMTYzLjV0LTE2My41IDEwOS41dC0xOTguNSA0MC41aC0zODZxMTE5IC05MCAxODguNSAtMjI0dDY5LjUgLTI4OHQtNjkuNSAtMjg4dC0xODguNSAtMjI0aDM4NnExMDQgMCAxOTguNSA0MC41dDE2My41IDEwOS41dDEwOS41IDE2My41dDQwLjUgMTk4LjV6TTIwNDggNjQwcTAgLTEzMCAtNTEgLTI0OC41dC0xMzYuNSAtMjA0dC0yMDQgLTEzNi41dC0yNDguNSAtNTFoLTc2OHEtMTMwIDAgLTI0OC41IDUxdC0yMDQgMTM2LjUNCnQtMTM2LjUgMjA0dC01MSAyNDguNXQ1MSAyNDguNXQxMzYuNSAyMDR0MjA0IDEzNi41dDI0OC41IDUxaDc2OHExMzAgMCAyNDguNSAtNTF0MjA0IC0xMzYuNXQxMzYuNSAtMjA0dDUxIC0yNDguNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80ODUiIHVuaWNvZGU9IiYjeGYyMDU7IiBob3Jpei1hZHYteD0iMjA0OCIgDQpkPSJNMCA2NDBxMCAxMzAgNTEgMjQ4LjV0MTM2LjUgMjA0dDIwNCAxMzYuNXQyNDguNSA1MWg3NjhxMTMwIDAgMjQ4LjUgLTUxdDIwNCAtMTM2LjV0MTM2LjUgLTIwNHQ1MSAtMjQ4LjV0LTUxIC0yNDguNXQtMTM2LjUgLTIwNHQtMjA0IC0xMzYuNXQtMjQ4LjUgLTUxaC03NjhxLTEzMCAwIC0yNDguNSA1MXQtMjA0IDEzNi41dC0xMzYuNSAyMDR0LTUxIDI0OC41ek0xNDA4IDEyOHExMDQgMCAxOTguNSA0MC41dDE2My41IDEwOS41DQp0MTA5LjUgMTYzLjV0NDAuNSAxOTguNXQtNDAuNSAxOTguNXQtMTA5LjUgMTYzLjV0LTE2My41IDEwOS41dC0xOTguNSA0MC41dC0xOTguNSAtNDAuNXQtMTYzLjUgLTEwOS41dC0xMDkuNSAtMTYzLjV0LTQwLjUgLTE5OC41dDQwLjUgLTE5OC41dDEwOS41IC0xNjMuNXQxNjMuNSAtMTA5LjV0MTk4LjUgLTQwLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDg2IiB1bmljb2RlPSImI3hmMjA2OyIgaG9yaXotYWR2LXg9IjIzMDQiIA0KZD0iTTc2MiAzODRoLTMxNHEtNDAgMCAtNTcuNSAzNXQ2LjUgNjdsMTg4IDI1MXEtNjUgMzEgLTEzNyAzMXEtMTMyIDAgLTIyNiAtOTR0LTk0IC0yMjZ0OTQgLTIyNnQyMjYgLTk0cTExNSAwIDIwMyA3Mi41dDExMSAxODMuNXpNNTc2IDUxMmgxODZxLTE4IDg1IC03NSAxNDh6TTEwNTYgNTEybDI4OCAzODRoLTQ4MGwtOTkgLTEzMnExMDUgLTEwMyAxMjYgLTI1MmgxNjV6TTIxNzYgNDQ4cTAgMTMyIC05NCAyMjZ0LTIyNiA5NA0KcS02MCAwIC0xMjEgLTI0bDE3NCAtMjYwcTE1IC0yMyAxMCAtNDl0LTI3IC00MHEtMTUgLTExIC0zNiAtMTFxLTM1IDAgLTUzIDI5bC0xNzQgMjYwcS05MyAtOTUgLTkzIC0yMjVxMCAtMTMyIDk0IC0yMjZ0MjI2IC05NHQyMjYgOTR0OTQgMjI2ek0yMzA0IDQ0OHEwIC0xODUgLTEzMS41IC0zMTYuNXQtMzE2LjUgLTEzMS41dC0zMTYuNSAxMzEuNXQtMTMxLjUgMzE2LjVxMCA5NyAzOS41IDE4My41dDEwOS41IDE0OS41bC02NSA5OGwtMzUzIC00NjkNCnEtMTggLTI2IC01MSAtMjZoLTE5N3EtMjMgLTE2NCAtMTQ5IC0yNzR0LTI5NCAtMTEwcS0xODUgMCAtMzE2LjUgMTMxLjV0LTEzMS41IDMxNi41dDEzMS41IDMxNi41dDMxNi41IDEzMS41cTExNCAwIDIxNSAtNTVsMTM3IDE4M2gtMjI0cS0yNiAwIC00NSAxOXQtMTkgNDV0MTkgNDV0NDUgMTloMzg0di0xMjhoNDM1bC04NSAxMjhoLTIyMnEtMjYgMCAtNDUgMTl0LTE5IDQ1dDE5IDQ1dDQ1IDE5aDI1NnEzMyAwIDUzIC0yOGwyNjcgLTQwMA0KcTkxIDQ0IDE5MiA0NHExODUgMCAzMTYuNSAtMTMxLjV0MTMxLjUgLTMxNi41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQ4NyIgdW5pY29kZT0iJiN4ZjIwNzsiIA0KZD0iTTM4NCAzMjBxMCA1MyAtMzcuNSA5MC41dC05MC41IDM3LjV0LTkwLjUgLTM3LjV0LTM3LjUgLTkwLjV0MzcuNSAtOTAuNXQ5MC41IC0zNy41dDkwLjUgMzcuNXQzNy41IDkwLjV6TTE0MDggMzIwcTAgNTMgLTM3LjUgOTAuNXQtOTAuNSAzNy41dC05MC41IC0zNy41dC0zNy41IC05MC41dDM3LjUgLTkwLjV0OTAuNSAtMzcuNXQ5MC41IDM3LjV0MzcuNSA5MC41ek0xMzYyIDcxNmwtNzIgMzg0cS01IDIzIC0yMi41IDM3LjV0LTQwLjUgMTQuNQ0KaC05MThxLTIzIDAgLTQwLjUgLTE0LjV0LTIyLjUgLTM3LjVsLTcyIC0zODRxLTUgLTMwIDE0IC01M3Q0OSAtMjNoMTA2MnEzMCAwIDQ5IDIzdDE0IDUzek0xMTM2IDEzMjhxMCAyMCAtMTQgMzR0LTM0IDE0aC02NDBxLTIwIDAgLTM0IC0xNHQtMTQgLTM0dDE0IC0zNHQzNCAtMTRoNjQwcTIwIDAgMzQgMTR0MTQgMzR6TTE1MzYgNjAzdi02MDNoLTEyOHYtMTI4cTAgLTUzIC0zNy41IC05MC41dC05MC41IC0zNy41dC05MC41IDM3LjUNCnQtMzcuNSA5MC41djEyOGgtNzY4di0xMjhxMCAtNTMgLTM3LjUgLTkwLjV0LTkwLjUgLTM3LjV0LTkwLjUgMzcuNXQtMzcuNSA5MC41djEyOGgtMTI4djYwM3EwIDExMiAyNSAyMjNsMTAzIDQ1NHE5IDc4IDk3LjUgMTM3dDIzMCA4OXQzMTIuNSAzMHQzMTIuNSAtMzB0MjMwIC04OXQ5Ny41IC0xMzdsMTA1IC00NTRxMjMgLTEwMiAyMyAtMjIzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQ4OCIgdW5pY29kZT0iJiN4ZjIwODsiIGhvcml6LWFkdi14PSIyMDQ4IiANCmQ9Ik0xNDYzIDcwNHEwIC0zNSAtMjUgLTYwLjV0LTYxIC0yNS41aC03MDJxLTM2IDAgLTYxIDI1LjV0LTI1IDYwLjV0MjUgNjAuNXQ2MSAyNS41aDcwMnEzNiAwIDYxIC0yNS41dDI1IC02MC41ek0xNjc3IDcwNHEwIDg2IC0yMyAxNzBoLTk4MnEtMzYgMCAtNjEgMjV0LTI1IDYwcTAgMzYgMjUgNjF0NjEgMjVoOTA4cS04OCAxNDMgLTIzNSAyMjd0LTMyMCA4NHEtMTc3IDAgLTMyNy41IC04Ny41dC0yMzggLTIzNy41dC04Ny41IC0zMjcNCnEwIC04NiAyMyAtMTcwaDk4MnEzNiAwIDYxIC0yNXQyNSAtNjBxMCAtMzYgLTI1IC02MXQtNjEgLTI1aC05MDhxODggLTE0MyAyMzUuNSAtMjI3dDMyMC41IC04NHExMzIgMCAyNTMgNTEuNXQyMDggMTM5dDEzOSAyMDh0NTIgMjUzLjV6TTIwNDggOTU5cTAgLTM1IC0yNSAtNjB0LTYxIC0yNWgtMTMxcTE3IC04NSAxNyAtMTcwcTAgLTE2NyAtNjUuNSAtMzE5LjV0LTE3NS41IC0yNjN0LTI2Mi41IC0xNzZ0LTMxOS41IC02NS41DQpxLTI0NiAwIC00NDguNSAxMzN0LTMwMS41IDM1MGgtMTg5cS0zNiAwIC02MSAyNXQtMjUgNjFxMCAzNSAyNSA2MHQ2MSAyNWgxMzJxLTE3IDg1IC0xNyAxNzBxMCAxNjcgNjUuNSAzMTkuNXQxNzUuNSAyNjN0MjYyLjUgMTc2dDMyMC41IDY1LjVxMjQ1IDAgNDQ3LjUgLTEzM3QzMDEuNSAtMzUwaDE4OHEzNiAwIDYxIC0yNXQyNSAtNjF6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDg5IiB1bmljb2RlPSImI3hmMjA5OyIgaG9yaXotYWR2LXg9IjEyODAiIA0KZD0iTTk1MyAxMTU4bC0xMTQgLTMyOGwxMTcgLTIxcTE2NSA0NTEgMTY1IDUxOHEwIDU2IC0zOCA1NnEtNTcgMCAtMTMwIC0yMjV6TTY1NCA0NzFsMzMgLTg4cTM3IDQyIDcxIDY3bC0zMyA1LjV0LTM4LjUgN3QtMzIuNSA4LjV6TTM2MiAxMzY3cTAgLTk4IDE1OSAtNTIxcTE3IDEwIDQ5IDEwcTE1IDAgNzUgLTVsLTEyMSAzNTFxLTc1IDIyMCAtMTIzIDIyMHEtMTkgMCAtMjkgLTE3LjV0LTEwIC0zNy41ek0yODMgNjA4cTAgLTM2IDUxLjUgLTExOQ0KdDExNy41IC0xNTN0MTAwIC03MHExNCAwIDI1LjUgMTN0MTEuNSAyN3EwIDI0IC0zMiAxMDJxLTEzIDMyIC0zMiA3MnQtNDcuNSA4OXQtNjEuNSA4MXQtNjIgMzJxLTIwIDAgLTQ1LjUgLTI3dC0yNS41IC00N3pNMTI1IDI3M3EwIC00MSAyNSAtMTA0cTU5IC0xNDUgMTgzLjUgLTIyN3QyODEuNSAtODJxMjI3IDAgMzgyIDE3MHExNTIgMTY5IDE1MiA0MjdxMCA0MyAtMSA2N3QtMTEuNSA2MnQtMzAuNSA1NnEtNTYgNDkgLTIxMS41IDc1LjUNCnQtMjcwLjUgMjYuNXEtMzcgMCAtNDkgLTExcS0xMiAtNSAtMTIgLTM1cTAgLTM0IDIxLjUgLTYwdDU1LjUgLTQwdDc3LjUgLTIzLjV0ODcuNSAtMTEuNXQ4NSAtNHQ3MCAwaDIzcTI0IDAgNDAgLTE5cTE1IC0xOSAxOSAtNTVxLTI4IC0yOCAtOTYgLTU0cS02MSAtMjIgLTkzIC00NnEtNjQgLTQ2IC0xMDguNSAtMTE0dC00NC41IC0xMzdxMCAtMzEgMTguNSAtODguNXQxOC41IC04Ny41bC0zIC0xMnEtNCAtMTIgLTQgLTE0DQpxLTEzNyAxMCAtMTQ2IDIxNnEtOCAtMiAtNDEgLTJxMiAtNyAyIC0yMXEwIC01MyAtNDAuNSAtODkuNXQtOTQuNSAtMzYuNXEtODIgMCAtMTY2LjUgNzh0LTg0LjUgMTU5cTAgMzQgMzMgNjdxNTIgLTY0IDYwIC03NnE3NyAtMTA0IDEzMyAtMTA0cTEyIDAgMjYuNSA4LjV0MTQuNSAyMC41cTAgMzQgLTg3LjUgMTQ1dC0xMTYuNSAxMTFxLTQzIDAgLTcwIC00NC41dC0yNyAtOTAuNXpNMTEgMjY0cTAgMTAxIDQyLjUgMTYzdDEzNi41IDg4DQpxLTI4IDc0IC0yOCAxMDRxMCA2MiA2MSAxMjN0MTIyIDYxcTI5IDAgNzAgLTE1cS0xNjMgNDYyIC0xNjMgNTY3cTAgODAgNDEgMTMwLjV0MTE5IDUwLjVxMTMxIDAgMzI1IC01ODFxNiAtMTcgOCAtMjNxNiAxNiAyOSA3OS41dDQzLjUgMTE4LjV0NTQgMTI3LjV0NjQuNSAxMjN0NzAuNSA4Ni41dDc2LjUgMzZxNzEgMCAxMTIgLTQ5dDQxIC0xMjJxMCAtMTA4IC0xNTkgLTU1MHE2MSAtMTUgMTAwLjUgLTQ2dDU4LjUgLTc4dDI2IC05My41DQp0NyAtMTEwLjVxMCAtMTUwIC00NyAtMjgwdC0xMzIgLTIyNXQtMjExIC0xNTB0LTI3OCAtNTVxLTExMSAwIC0yMjMgNDJxLTE0OSA1NyAtMjU4IDE5MS41dC0xMDkgMjg2LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDkwIiB1bmljb2RlPSImI3hmMjBhOyIgaG9yaXotYWR2LXg9IjIwNDgiIA0KZD0iTTc4NSA1MjhoMjA3cS0xNCAtMTU4IC05OC41IC0yNDguNXQtMjE0LjUgLTkwLjVxLTE2MiAwIC0yNTQuNSAxMTZ0LTkyLjUgMzE2cTAgMTk0IDkzIDMxMS41dDIzMyAxMTcuNXExNDggMCAyMzIgLTg3dDk3IC0yNDdoLTIwM3EtNSA2NCAtMzUuNSA5OXQtODEuNSAzNXEtNTcgMCAtODguNSAtNjAuNXQtMzEuNSAtMTc3LjVxMCAtNDggNSAtODR0MTggLTY5LjV0NDAgLTUxLjV0NjYgLTE4cTk1IDAgMTA5IDEzOXpNMTQ5NyA1MjhoMjA2DQpxLTE0IC0xNTggLTk4IC0yNDguNXQtMjE0IC05MC41cS0xNjIgMCAtMjU0LjUgMTE2dC05Mi41IDMxNnEwIDE5NCA5MyAzMTEuNXQyMzMgMTE3LjVxMTQ4IDAgMjMyIC04N3Q5NyAtMjQ3aC0yMDRxLTQgNjQgLTM1IDk5dC04MSAzNXEtNTcgMCAtODguNSAtNjAuNXQtMzEuNSAtMTc3LjVxMCAtNDggNSAtODR0MTggLTY5LjV0MzkuNSAtNTEuNXQ2NS41IC0xOHE0OSAwIDc2LjUgMzh0MzMuNSAxMDF6TTE4NTYgNjQ3cTAgMjA3IC0xNS41IDMwNw0KdC02MC41IDE2MXEtNiA4IC0xMy41IDE0dC0yMS41IDE1dC0xNiAxMXEtODYgNjMgLTY5NyA2M3EtNjI1IDAgLTcxMCAtNjNxLTUgLTQgLTE3LjUgLTExLjV0LTIxIC0xNHQtMTQuNSAtMTQuNXEtNDUgLTYwIC02MCAtMTU5LjV0LTE1IC0zMDguNXEwIC0yMDggMTUgLTMwNy41dDYwIC0xNjAuNXE2IC04IDE1IC0xNXQyMC41IC0xNHQxNy41IC0xMnE0NCAtMzMgMjM5LjUgLTQ5dDQ3MC41IC0xNnE2MTAgMCA2OTcgNjVxNSA0IDE3IDExdDIwLjUgMTQNCnQxMy41IDE2cTQ2IDYwIDYxIDE1OXQxNSAzMDl6TTIwNDggMTQwOHYtMTUzNmgtMjA0OHYxNTM2aDIwNDh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNDkxIiB1bmljb2RlPSImI3hmMjBiOyIgDQpkPSJNOTkyIDkxMnYtNDk2cTAgLTE0IC05IC0yM3QtMjMgLTloLTE2MHEtMTQgMCAtMjMgOXQtOSAyM3Y0OTZxMCAxMTIgLTgwIDE5MnQtMTkyIDgwaC0yNzJ2LTExNTJxMCAtMTQgLTkgLTIzdC0yMyAtOWgtMTYwcS0xNCAwIC0yMyA5dC05IDIzdjEzNDRxMCAxNCA5IDIzdDIzIDloNDY0cTEzNSAwIDI0OSAtNjYuNXQxODAuNSAtMTgwLjV0NjYuNSAtMjQ5ek0xMzc2IDEzNzZ2LTg4MHEwIC0xMzUgLTY2LjUgLTI0OXQtMTgwLjUgLTE4MC41DQp0LTI0OSAtNjYuNWgtNDY0cS0xNCAwIC0yMyA5dC05IDIzdjk2MHEwIDE0IDkgMjN0MjMgOWgxNjBxMTQgMCAyMyAtOXQ5IC0yM3YtNzY4aDI3MnExMTIgMCAxOTIgODB0ODAgMTkydjg4MHEwIDE0IDkgMjN0MjMgOWgxNjBxMTQgMCAyMyAtOXQ5IC0yM3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80OTIiIHVuaWNvZGU9IiYjeGYyMGM7IiANCmQ9Ik0xMzExIDY5NHYtMTE0cTAgLTI0IC0xMy41IC0zOHQtMzcuNSAtMTRoLTIwMnEtMjQgMCAtMzggMTR0LTE0IDM4djExNHEwIDI0IDE0IDM4dDM4IDE0aDIwMnEyNCAwIDM3LjUgLTE0dDEzLjUgLTM4ek04MjEgNDY0djI1MHEwIDUzIC0zMi41IDg1LjV0LTg1LjUgMzIuNWgtMTMzcS02OCAwIC05NiAtNTJxLTI4IDUyIC05NiA1MmgtMTMwcS01MyAwIC04NS41IC0zMi41dC0zMi41IC04NS41di0yNTBxMCAtMjIgMjEgLTIyaDU1DQpxMjIgMCAyMiAyMnYyMzBxMCAyNCAxMy41IDM4dDM4LjUgMTRoOTRxMjQgMCAzOCAtMTR0MTQgLTM4di0yMzBxMCAtMjIgMjEgLTIyaDU0cTIyIDAgMjIgMjJ2MjMwcTAgMjQgMTQgMzh0MzggMTRoOTdxMjQgMCAzNy41IC0xNHQxMy41IC0zOHYtMjMwcTAgLTIyIDIyIC0yMmg1NXEyMSAwIDIxIDIyek0xNDEwIDU2MHYxNTRxMCA1MyAtMzMgODUuNXQtODYgMzIuNWgtMjY0cS01MyAwIC04NiAtMzIuNXQtMzMgLTg1LjV2LTQxMA0KcTAgLTIxIDIyIC0yMWg1NXEyMSAwIDIxIDIxdjE4MHEzMSAtNDIgOTQgLTQyaDE5MXE1MyAwIDg2IDMyLjV0MzMgODUuNXpNMTUzNiAxMTc2di0xMDcycTAgLTk2IC02OCAtMTY0dC0xNjQgLTY4aC0xMDcycS05NiAwIC0xNjQgNjh0LTY4IDE2NHYxMDcycTAgOTYgNjggMTY0dDE2NCA2OGgxMDcycTk2IDAgMTY0IC02OHQ2OCAtMTY0eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQ5MyIgdW5pY29kZT0iJiN4ZjIwZDsiIA0KZD0iTTkxNSA0NTBoLTI5NGwxNDcgNTUxek0xMDAxIDEyOGgzMTFsLTMyNCAxMDI0aC00NDBsLTMyNCAtMTAyNGgzMTFsMzgzIDMxNHpNMTUzNiAxMTIwdi05NjBxMCAtMTE4IC04NSAtMjAzdC0yMDMgLTg1aC05NjBxLTExOCAwIC0yMDMgODV0LTg1IDIwM3Y5NjBxMCAxMTggODUgMjAzdDIwMyA4NWg5NjBxMTE4IDAgMjAzIC04NXQ4NSAtMjAzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQ5NCIgdW5pY29kZT0iJiN4ZjIwZTsiIGhvcml6LWFkdi14PSIyMDQ4IiANCmQ9Ik0yMDQ4IDY0MXEwIC0yMSAtMTMgLTM2LjV0LTMzIC0xOS41bC0yMDUgLTM1NnEzIC05IDMgLTE4cTAgLTIwIC0xMi41IC0zNS41dC0zMi41IC0xOS41bC0xOTMgLTMzN3EzIC04IDMgLTE2cTAgLTIzIC0xNi41IC00MHQtNDAuNSAtMTdxLTI1IDAgLTQxIDE4aC00MDBxLTE3IC0yMCAtNDMgLTIwdC00MyAyMGgtMzk5cS0xNyAtMjAgLTQzIC0yMHEtMjMgMCAtNDAgMTYuNXQtMTcgNDAuNXEwIDggNCAyMGwtMTkzIDMzNQ0KcS0yMCA0IC0zMi41IDE5LjV0LTEyLjUgMzUuNXEwIDkgMyAxOGwtMjA2IDM1NnEtMjAgNSAtMzIuNSAyMC41dC0xMi41IDM1LjVxMCAyMSAxMy41IDM2LjV0MzMuNSAxOS41bDE5OSAzNDRxMCAxIC0wLjUgM3QtMC41IDNxMCAzNiAzNCA1MWwyMDkgMzYzcS00IDEwIC00IDE4cTAgMjQgMTcgNDAuNXQ0MCAxNi41cTI2IDAgNDQgLTIxaDM5NnExNiAyMSA0MyAyMXQ0MyAtMjFoMzk4cTE4IDIxIDQ0IDIxcTIzIDAgNDAgLTE2LjV0MTcgLTQwLjUNCnEwIC02IC00IC0xOGwyMDcgLTM1OHEyMyAtMSAzOSAtMTcuNXQxNiAtMzguNXEwIC0xMyAtNyAtMjdsMTg3IC0zMjRxMTkgLTQgMzEuNSAtMTkuNXQxMi41IC0zNS41ek0xMDYzIC0xNThoMzg5bC0zNDIgMzU0aC0xNDNsLTM0MiAtMzU0aDM2MHExOCAxNiAzOSAxNnQzOSAtMTZ6TTExMiA2NTRxMSAtNCAxIC0xM3EwIC0xMCAtMiAtMTVsMjA4IC0zNjBsMTUgLTZsMTg4IDE5OXYzNDdsLTE4NyAxOTRxLTEzIC04IC0yOSAtMTB6TTk4NiAxNDM4DQpoLTM4OGwxOTAgLTIwMGw1NTQgMjAwaC0yODBxLTE2IC0xNiAtMzggLTE2dC0zOCAxNnpNMTY4OSAyMjZxMSA2IDUgMTFsLTY0IDY4bC0xNyAtNzloNzZ6TTE1ODMgMjI2bDIyIDEwNWwtMjUyIDI2NmwtMjk2IC0zMDdsNjMgLTY0aDQ2M3pNMTQ5NSAtMTQybDE2IDI4bDY1IDMxMGgtNDI3bDMzMyAtMzQzcTggNCAxMyA1ek01NzggLTE1OGg1bDM0MiAzNTRoLTM3M3YtMzM1bDQgLTZxMTQgLTUgMjIgLTEzek01NTIgMjI2aDQwMmw2NCA2Ng0KbC0zMDkgMzIxbC0xNTcgLTE2NnYtMjIxek0zNTkgMjI2aDE2M3YxODlsLTE2OCAtMTc3cTQgLTggNSAtMTJ6TTM1OCAxMDUxcTAgLTEgMC41IC0ydDAuNSAtMnEwIC0xNiAtOCAtMjlsMTcxIC0xNzd2MjY5ek01NTIgMTEyMXYtMzExbDE1MyAtMTU3bDI5NyAzMTRsLTIyMyAyMzZ6TTU1NiAxNDI1bC00IC04di0yNjRsMjA1IDc0bC0xOTEgMjAxcS02IC0yIC0xMCAtM3pNMTQ0NyAxNDM4aC0xNmwtNjIxIC0yMjRsMjEzIC0yMjV6TTEwMjMgOTQ2DQpsLTI5NyAtMzE1bDMxMSAtMzE5bDI5NiAzMDd6TTY4OCA2MzRsLTEzNiAxNDF2LTI4NHpNMTAzOCAyNzBsLTQyIC00NGg4NXpNMTM3NCA2MThsMjM4IC0yNTFsMTMyIDYyNGwtMyA1bC0xIDF6TTE3MTggMTAxOHEtOCAxMyAtOCAyOXYybC0yMTYgMzc2cS01IDEgLTEzIDVsLTQzNyAtNDYzbDMxMCAtMzI3ek01MjIgMTE0MnYyMjNsLTE2MyAtMjgyek01MjIgMTk2aC0xNjNsMTYzIC0yODN2Mjgzek0xNjA3IDE5NmwtNDggLTIyN2wxMzAgMjI3aC04Mg0Kek0xNzI5IDI2NmwyMDcgMzYxcS0yIDEwIC0yIDE0cTAgMSAzIDE2bC0xNzEgMjk2bC0xMjkgLTYxMmw3NyAtODJxNSAzIDE1IDd6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJmMjEwIiB1bmljb2RlPSImI3hmMjEwOyIgDQpkPSJNMCA4NTZxMCAxMzEgOTEuNSAyMjYuNXQyMjIuNSA5NS41aDc0MmwzNTIgMzU4di0xNDcwcTAgLTEzMiAtOTEuNSAtMjI3dC0yMjIuNSAtOTVoLTc4MHEtMTMxIDAgLTIyMi41IDk1dC05MS41IDIyN3Y3OTB6TTEyMzIgMTAybC0xNzYgMTgwdjQyNXEwIDQ2IC0zMiA3OXQtNzggMzNoLTQ4NHEtNDYgMCAtNzggLTMzdC0zMiAtNzl2LTQ5MnEwIC00NiAzMi41IC03OS41dDc3LjUgLTMzLjVoNzcweiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQ5NiIgdW5pY29kZT0iJiN4ZjIxMTsiIA0KZD0iTTkzNCAxMzg2cS0zMTcgLTEyMSAtNTU2IC0zNjIuNXQtMzU4IC01NjAuNXEtMjAgODkgLTIwIDE3NnEwIDIwOCAxMDIuNSAzODQuNXQyNzguNSAyNzl0Mzg0IDEwMi41cTgyIDAgMTY5IC0xOXpNMTIwMyAxMjY3cTkzIC02NSAxNjQgLTE1NXEtMzg5IC0xMTMgLTY3NC41IC00MDAuNXQtMzk2LjUgLTY3Ni41cS05MyA3MiAtMTU1IDE2MnExMTIgMzg2IDM5NSA2NzF0NjY3IDM5OXpNNDcwIC02N3ExMTUgMzU2IDM3OS41IDYyMnQ2MTkuNSAzODQNCnE0MCAtOTIgNTQgLTE5NXEtMjkyIC0xMjAgLTUxNiAtMzQ1dC0zNDMgLTUxOHEtMTAzIDE0IC0xOTQgNTJ6TTE1MzYgLTEyNXEtMTkzIDUwIC0zNjcgMTE1cS0xMzUgLTg0IC0yOTAgLTEwN3ExMDkgMjA1IDI3NCAzNzAuNXQzNjkgMjc1LjVxLTIxIC0xNTIgLTEwMSAtMjg0cTY1IC0xNzUgMTE1IC0zNzB6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJmMjEyIiB1bmljb2RlPSImI3hmMjEyOyIgaG9yaXotYWR2LXg9IjIwNDgiIA0KZD0iTTE4OTMgMTE0NGwxNTUgLTEyNzJxLTEzMSAwIC0yNTcgNTdxLTIwMCA5MSAtMzkzIDkxcS0yMjYgMCAtMzc0IC0xNDhxLTE0OCAxNDggLTM3NCAxNDhxLTE5MyAwIC0zOTMgLTkxcS0xMjggLTU3IC0yNTIgLTU3aC01bDE1NSAxMjcycTIyNCAxMjcgNDgyIDEyN3EyMzMgMCAzODcgLTEwNnExNTQgMTA2IDM4NyAxMDZxMjU4IDAgNDgyIC0xMjd6TTEzOTggMTU3cTEyOSAwIDIzMiAtMjguNXQyNjAgLTkzLjVsLTEyNCAxMDIxDQpxLTE3MSA3OCAtMzY4IDc4cS0yMjQgMCAtMzc0IC0xNDFxLTE1MCAxNDEgLTM3NCAxNDFxLTE5NyAwIC0zNjggLTc4bC0xMjQgLTEwMjFxMTA1IDQzIDE2NS41IDY1dDE0OC41IDM5LjV0MTc4IDE3LjVxMjAyIDAgMzc0IC0xMDhxMTcyIDEwOCAzNzQgMTA4ek0xNDM4IDE5MWwtNTUgOTA3cS0yMTEgLTQgLTM1OSAtMTU1cS0xNTIgMTU1IC0zNzQgMTU1cS0xNzYgMCAtMzM2IC02NmwtMTE0IC05NDFxMTI0IDUxIDIyOC41IDc2dDIyMS41IDI1DQpxMjA5IDAgMzc0IC0xMDJxMTcyIDEwNyAzNzQgMTAyeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzQ5OCIgdW5pY29kZT0iJiN4ZjIxMzsiIGhvcml6LWFkdi14PSIyMDQ4IiANCmQ9Ik0xNTAwIDE2NXY3MzNxMCAyMSAtMTUgMzZ0LTM1IDE1aC05M3EtMjAgMCAtMzUgLTE1dC0xNSAtMzZ2LTczM3EwIC0yMCAxNSAtMzV0MzUgLTE1aDkzcTIwIDAgMzUgMTV0MTUgMzV6TTEyMTYgMTY1djUzMXEwIDIwIC0xNSAzNXQtMzUgMTVoLTEwMXEtMjAgMCAtMzUgLTE1dC0xNSAtMzV2LTUzMXEwIC0yMCAxNSAtMzV0MzUgLTE1aDEwMXEyMCAwIDM1IDE1dDE1IDM1ek05MjQgMTY1djQyOXEwIDIwIC0xNSAzNXQtMzUgMTVoLTEwMQ0KcS0yMCAwIC0zNSAtMTV0LTE1IC0zNXYtNDI5cTAgLTIwIDE1IC0zNXQzNSAtMTVoMTAxcTIwIDAgMzUgMTV0MTUgMzV6TTYzMiAxNjV2MzYycTAgMjAgLTE1IDM1dC0zNSAxNWgtMTAxcS0yMCAwIC0zNSAtMTV0LTE1IC0zNXYtMzYycTAgLTIwIDE1IC0zNXQzNSAtMTVoMTAxcTIwIDAgMzUgMTV0MTUgMzV6TTIwNDggMzExcTAgLTE2NiAtMTE4IC0yODR0LTI4NCAtMTE4aC0xMjQ0cS0xNjYgMCAtMjg0IDExOHQtMTE4IDI4NA0KcTAgMTE2IDYzIDIxNC41dDE2OCAxNDguNXEtMTAgMzQgLTEwIDczcTAgMTEzIDgwLjUgMTkzLjV0MTkzLjUgODAuNXExMDIgMCAxODAgLTY3cTQ1IDE4MyAxOTQgMzAwdDMzOCAxMTdxMTQ5IDAgMjc1IC03My41dDE5OS41IC0xOTkuNXQ3My41IC0yNzVxMCAtNjYgLTE0IC0xMjJxMTM1IC0zMyAyMjEgLTE0Mi41dDg2IC0yNDcuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il80OTkiIHVuaWNvZGU9IiYjeGYyMTQ7IiANCmQ9Ik0wIDE1MzZoMTUzNnYtMTM5MmwtNzc2IC0zMzhsLTc2MCAzMzh2MTM5MnpNMTQzNiAyMDl2OTI2aC0xMzM2di05MjZsNjYxIC0yOTR6TTE0MzYgMTIzNXYyMDFoLTEzMzZ2LTIwMWgxMzM2ek0xODEgOTM3di0xMTVoLTM3djExNWgzN3pNMTgxIDc4OXYtMTE1aC0zN3YxMTVoMzd6TTE4MSA2NDF2LTExNWgtMzd2MTE1aDM3ek0xODEgNDkzdi0xMTVoLTM3djExNWgzN3pNMTgxIDM0NXYtMTE1aC0zN3YxMTVoMzd6TTIwNyAyMDJsMTUgMzQNCmwxMDUgLTQ3bC0xNSAtMzN6TTM0MyAxNDJsMTUgMzRsMTA1IC00NmwtMTUgLTM0ek00NzggODJsMTUgMzRsMTA1IC00NmwtMTUgLTM0ek02MTQgMjNsMTUgMzNsMTA0IC00NmwtMTUgLTM0ek03OTcgMTBsMTA1IDQ2bDE1IC0zM2wtMTA1IC00N3pNOTMyIDcwbDEwNSA0NmwxNSAtMzRsLTEwNSAtNDZ6TTEwNjggMTMwbDEwNSA0NmwxNSAtMzRsLTEwNSAtNDZ6TTEyMDMgMTg5bDEwNSA0N2wxNSAtMzRsLTEwNSAtNDZ6TTI1OSAxMzg5di0zNmgtMTE0DQp2MzZoMTE0ek00MjEgMTM4OXYtMzZoLTExNXYzNmgxMTV6TTU4MyAxMzg5di0zNmgtMTE1djM2aDExNXpNNzQ0IDEzODl2LTM2aC0xMTR2MzZoMTE0ek05MDYgMTM4OXYtMzZoLTExNHYzNmgxMTR6TTEwNjggMTM4OXYtMzZoLTExNXYzNmgxMTV6TTEyMzAgMTM4OXYtMzZoLTExNXYzNmgxMTV6TTEzOTEgMTM4OXYtMzZoLTExNHYzNmgxMTR6TTE4MSAxMDQ5di03OWgtMzd2MTE1aDExNXYtMzZoLTc4ek00MjEgMTA4NXYtMzZoLTExNXYzNmgxMTV6DQpNNTgzIDEwODV2LTM2aC0xMTV2MzZoMTE1ek03NDQgMTA4NXYtMzZoLTExNHYzNmgxMTR6TTkwNiAxMDg1di0zNmgtMTE0djM2aDExNHpNMTA2OCAxMDg1di0zNmgtMTE1djM2aDExNXpNMTIzMCAxMDg1di0zNmgtMTE1djM2aDExNXpNMTM1NSA5NzB2NzloLTc4djM2aDExNXYtMTE1aC0zN3pNMTM1NSA4MjJ2MTE1aDM3di0xMTVoLTM3ek0xMzU1IDY3NHYxMTVoMzd2LTExNWgtMzd6TTEzNTUgNTI2djExNWgzN3YtMTE1aC0zN3pNMTM1NSAzNzgNCnYxMTVoMzd2LTExNWgtMzd6TTEzNTUgMjMwdjExNWgzN3YtMTE1aC0zN3pNNzYwIDI2NXEtMTI5IDAgLTIyMSA5MS41dC05MiAyMjEuNXEwIDEyOSA5MiAyMjF0MjIxIDkycTEzMCAwIDIyMS41IC05MnQ5MS41IC0yMjFxMCAtMTMwIC05MS41IC0yMjEuNXQtMjIxLjUgLTkxLjV6TTU5NSA2NDZxMCAtMzYgMTkuNSAtNTYuNXQ0OS41IC0yNXQ2NCAtN3Q2NCAtMnQ0OS41IC05dDE5LjUgLTMwLjVxMCAtNDkgLTExMiAtNDlxLTk3IDAgLTEyMyA1MQ0KaC0zbC0zMSAtNjNxNjcgLTQyIDE2MiAtNDJxMjkgMCA1Ni41IDV0NTUuNSAxNnQ0NS41IDMzdDE3LjUgNTNxMCA0NiAtMjcuNSA2OS41dC02Ny41IDI3dC03OS41IDN0LTY3IDV0LTI3LjUgMjUuNXEwIDIxIDIwLjUgMzN0NDAuNSAxNXQ0MSAzcTM0IDAgNzAuNSAtMTF0NTEuNSAtMzRoM2wzMCA1OHEtMyAxIC0yMSA4LjV0LTIyLjUgOXQtMTkuNSA3dC0yMiA3dC0yMCA0LjV0LTI0IDR0LTIzIDFxLTI5IDAgLTU2LjUgLTV0LTU0IC0xNi41DQp0LTQzIC0zNHQtMTYuNSAtNTMuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81MDAiIHVuaWNvZGU9IiYjeGYyMTU7IiBob3Jpei1hZHYteD0iMjA0OCIgDQpkPSJNODYzIDUwNHEwIDExMiAtNzkuNSAxOTEuNXQtMTkxLjUgNzkuNXQtMTkxIC03OS41dC03OSAtMTkxLjV0NzkgLTE5MXQxOTEgLTc5dDE5MS41IDc5dDc5LjUgMTkxek0xNzI2IDUwNXEwIDExMiAtNzkgMTkxdC0xOTEgNzl0LTE5MS41IC03OXQtNzkuNSAtMTkxcTAgLTExMyA3OS41IC0xOTJ0MTkxLjUgLTc5dDE5MSA3OS41dDc5IDE5MS41ek0yMDQ4IDEzMTR2LTEzNDhxMCAtNDQgLTMxLjUgLTc1LjV0LTc2LjUgLTMxLjVoLTE4MzINCnEtNDUgMCAtNzYuNSAzMS41dC0zMS41IDc1LjV2MTM0OHEwIDQ0IDMxLjUgNzUuNXQ3Ni41IDMxLjVoNDMxcTQ0IDAgNzYgLTMxLjV0MzIgLTc1LjV2LTE2MWg3NTR2MTYxcTAgNDQgMzIgNzUuNXQ3NiAzMS41aDQzMXE0NSAwIDc2LjUgLTMxLjV0MzEuNSAtNzUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81MDEiIHVuaWNvZGU9IiYjeGYyMTY7IiBob3Jpei1hZHYteD0iMjA0OCIgDQpkPSJNMTQzMCA5NTN6TTE2OTAgNzQ5cTE0OCAwIDI1MyAtOTguNXQxMDUgLTI0NC41cTAgLTE1NyAtMTA5IC0yNjEuNXQtMjY3IC0xMDQuNXEtODUgMCAtMTYyIDI3LjV0LTEzOCA3My41dC0xMTggMTA2dC0xMDkgMTI2dC0xMDMuNSAxMzIuNXQtMTA4LjUgMTI2LjV0LTExNyAxMDZ0LTEzNiA3My41dC0xNTkgMjcuNXEtMTU0IDAgLTI1MS41IC05MS41dC05Ny41IC0yNDQuNXEwIC0xNTcgMTA0IC0yNTB0MjYzIC05M3ExMDAgMCAyMDggMzcuNQ0KdDE5MyA5OC41cTUgNCAyMSAxOC41dDMwIDI0dDIyIDkuNXExNCAwIDI0LjUgLTEwLjV0MTAuNSAtMjQuNXEwIC0yNCAtNjAgLTc3cS0xMDEgLTg4IC0yMzQuNSAtMTQydC0yNjAuNSAtNTRxLTEzMyAwIC0yNDUuNSA1OHQtMTgwIDE2NXQtNjcuNSAyNDFxMCAyMDUgMTQxLjUgMzQxdDM0Ny41IDEzNnExMjAgMCAyMjYuNSAtNDMuNXQxODUuNSAtMTEzdDE1MS41IC0xNTN0MTM5IC0xNjcuNXQxMzMuNSAtMTUzLjV0MTQ5LjUgLTExMw0KdDE3Mi41IC00My41cTEwMiAwIDE2OC41IDYxLjV0NjYuNSAxNjIuNXEwIDk1IC02NC41IDE1OXQtMTU5LjUgNjRxLTMwIDAgLTgxLjUgLTE4LjV0LTY4LjUgLTE4LjVxLTIwIDAgLTM1LjUgMTV0LTE1LjUgMzVxMCAxOCA4LjUgNTd0OC41IDU5cTAgMTU5IC0xMDcuNSAyNjN0LTI2Ni41IDEwNHEtNTggMCAtMTExLjUgLTE4LjV0LTg0IC00MC41dC01NS41IC00MC41dC0zMyAtMTguNXEtMTUgMCAtMjUuNSAxMC41dC0xMC41IDI1LjUNCnEwIDE5IDI1IDQ2cTU5IDY3IDE0NyAxMDMuNXQxODIgMzYuNXExOTEgMCAzMTggLTEyNS41dDEyNyAtMzE1LjVxMCAtMzcgLTQgLTY2cTU3IDE1IDExNSAxNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81MDIiIHVuaWNvZGU9IiYjeGYyMTc7IiBob3Jpei1hZHYteD0iMTY2NCIgDQpkPSJNMTIxNiA4MzJxMCAyNiAtMTkgNDV0LTQ1IDE5aC0xMjh2MTI4cTAgMjYgLTE5IDQ1dC00NSAxOXQtNDUgLTE5dC0xOSAtNDV2LTEyOGgtMTI4cS0yNiAwIC00NSAtMTl0LTE5IC00NXQxOSAtNDV0NDUgLTE5aDEyOHYtMTI4cTAgLTI2IDE5IC00NXQ0NSAtMTl0NDUgMTl0MTkgNDV2MTI4aDEyOHEyNiAwIDQ1IDE5dDE5IDQ1ek02NDAgMHEwIC01MyAtMzcuNSAtOTAuNXQtOTAuNSAtMzcuNXQtOTAuNSAzNy41dC0zNy41IDkwLjUNCnQzNy41IDkwLjV0OTAuNSAzNy41dDkwLjUgLTM3LjV0MzcuNSAtOTAuNXpNMTUzNiAwcTAgLTUzIC0zNy41IC05MC41dC05MC41IC0zNy41dC05MC41IDM3LjV0LTM3LjUgOTAuNXQzNy41IDkwLjV0OTAuNSAzNy41dDkwLjUgLTM3LjV0MzcuNSAtOTAuNXpNMTY2NCAxMDg4di01MTJxMCAtMjQgLTE2IC00Mi41dC00MSAtMjEuNWwtMTA0NCAtMTIycTEgLTcgNC41IC0yMS41dDYgLTI2LjV0Mi41IC0yMnEwIC0xNiAtMjQgLTY0aDkyMA0KcTI2IDAgNDUgLTE5dDE5IC00NXQtMTkgLTQ1dC00NSAtMTloLTEwMjRxLTI2IDAgLTQ1IDE5dC0xOSA0NXEwIDE0IDExIDM5LjV0MjkuNSA1OS41dDIwLjUgMzhsLTE3NyA4MjNoLTIwNHEtMjYgMCAtNDUgMTl0LTE5IDQ1dDE5IDQ1dDQ1IDE5aDI1NnExNiAwIDI4LjUgLTYuNXQyMCAtMTUuNXQxMyAtMjQuNXQ3LjUgLTI2LjV0NS41IC0yOS41dDQuNSAtMjUuNWgxMjAxcTI2IDAgNDUgLTE5dDE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81MDMiIHVuaWNvZGU9IiYjeGYyMTg7IiBob3Jpei1hZHYteD0iMTY2NCIgDQpkPSJNMTI4MCA4MzJxMCAyNiAtMTkgNDV0LTQ1IDE5dC00NSAtMTlsLTE0NyAtMTQ2djI5M3EwIDI2IC0xOSA0NXQtNDUgMTl0LTQ1IC0xOXQtMTkgLTQ1di0yOTNsLTE0NyAxNDZxLTE5IDE5IC00NSAxOXQtNDUgLTE5dC0xOSAtNDV0MTkgLTQ1bDI1NiAtMjU2cTE5IC0xOSA0NSAtMTl0NDUgMTlsMjU2IDI1NnExOSAxOSAxOSA0NXpNNjQwIDBxMCAtNTMgLTM3LjUgLTkwLjV0LTkwLjUgLTM3LjV0LTkwLjUgMzcuNXQtMzcuNSA5MC41DQp0MzcuNSA5MC41dDkwLjUgMzcuNXQ5MC41IC0zNy41dDM3LjUgLTkwLjV6TTE1MzYgMHEwIC01MyAtMzcuNSAtOTAuNXQtOTAuNSAtMzcuNXQtOTAuNSAzNy41dC0zNy41IDkwLjV0MzcuNSA5MC41dDkwLjUgMzcuNXQ5MC41IC0zNy41dDM3LjUgLTkwLjV6TTE2NjQgMTA4OHYtNTEycTAgLTI0IC0xNiAtNDIuNXQtNDEgLTIxLjVsLTEwNDQgLTEyMnExIC03IDQuNSAtMjEuNXQ2IC0yNi41dDIuNSAtMjJxMCAtMTYgLTI0IC02NGg5MjANCnEyNiAwIDQ1IC0xOXQxOSAtNDV0LTE5IC00NXQtNDUgLTE5aC0xMDI0cS0yNiAwIC00NSAxOXQtMTkgNDVxMCAxNCAxMSAzOS41dDI5LjUgNTkuNXQyMC41IDM4bC0xNzcgODIzaC0yMDRxLTI2IDAgLTQ1IDE5dC0xOSA0NXQxOSA0NXQ0NSAxOWgyNTZxMTYgMCAyOC41IC02LjV0MjAgLTE1LjV0MTMgLTI0LjV0Ny41IC0yNi41dDUuNSAtMjkuNXQ0LjUgLTI1LjVoMTIwMXEyNiAwIDQ1IC0xOXQxOSAtNDV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTA0IiB1bmljb2RlPSImI3hmMjE5OyIgaG9yaXotYWR2LXg9IjIwNDgiIA0KZD0iTTIxMiA3NjhsNjIzIC02NjVsLTMwMCA2NjVoLTMyM3pNMTAyNCAtNGwzNDkgNzcyaC02OTh6TTUzOCA4OTZsMjA0IDM4NGgtMjYybC0yODggLTM4NGgzNDZ6TTEyMTMgMTAzbDYyMyA2NjVoLTMyM3pNNjgzIDg5Nmg2ODJsLTIwNCAzODRoLTI3NHpNMTUxMCA4OTZoMzQ2bC0yODggMzg0aC0yNjJ6TTE2NTEgMTM4MmwzODQgLTUxMnExNCAtMTggMTMgLTQxLjV0LTE3IC00MC41bC05NjAgLTEwMjRxLTE4IC0yMCAtNDcgLTIwdC00NyAyMA0KbC05NjAgMTAyNHEtMTYgMTcgLTE3IDQwLjV0MTMgNDEuNWwzODQgNTEycTE4IDI2IDUxIDI2aDExNTJxMzMgMCA1MSAtMjZ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTA1IiB1bmljb2RlPSImI3hmMjFhOyIgaG9yaXotYWR2LXg9IjIwNDgiIA0KZD0iTTE4MTEgLTE5cTE5IDE5IDQ1IDE5dDQ1IC0xOWwxMjggLTEyOGwtOTAgLTkwbC04MyA4M2wtODMgLTgzcS0xOCAtMTkgLTQ1IC0xOXQtNDUgMTlsLTgzIDgzbC04MyAtODNxLTE5IC0xOSAtNDUgLTE5dC00NSAxOWwtODMgODNsLTgzIC04M3EtMTkgLTE5IC00NSAtMTl0LTQ1IDE5bC04MyA4M2wtODMgLTgzcS0xOSAtMTkgLTQ1IC0xOXQtNDUgMTlsLTgzIDgzbC04MyAtODNxLTE5IC0xOSAtNDUgLTE5dC00NSAxOWwtODMgODNsLTgzIC04Mw0KcS0xOSAtMTkgLTQ1IC0xOXQtNDUgMTlsLTgzIDgzbC04MyAtODNxLTE5IC0xOSAtNDUgLTE5dC00NSAxOWwtMTI4IDEyOGw5MCA5MGw4MyAtODNsODMgODNxMTkgMTkgNDUgMTl0NDUgLTE5bDgzIC04M2w4MyA4M3ExOSAxOSA0NSAxOXQ0NSAtMTlsODMgLTgzbDgzIDgzcTE5IDE5IDQ1IDE5dDQ1IC0xOWw4MyAtODNsODMgODNxMTkgMTkgNDUgMTl0NDUgLTE5bDgzIC04M2w4MyA4M3ExOSAxOSA0NSAxOXQ0NSAtMTlsODMgLTgzbDgzIDgzDQpxMTkgMTkgNDUgMTl0NDUgLTE5bDgzIC04M3pNMjM3IDE5cS0xOSAtMTkgLTQ1IC0xOXQtNDUgMTlsLTEyOCAxMjhsOTAgOTBsODMgLTgybDgzIDgycTE5IDE5IDQ1IDE5dDQ1IC0xOWw4MyAtODJsNjQgNjR2MjkzbC0yMTAgMzE0cS0xNyAyNiAtNyA1Ni41dDQwIDQwLjVsMTc3IDU4djI5OWgxMjh2MTI4aDI1NnYxMjhoMjU2di0xMjhoMjU2di0xMjhoMTI4di0yOTlsMTc3IC01OHEzMCAtMTAgNDAgLTQwLjV0LTcgLTU2LjVsLTIxMCAtMzE0DQp2LTI5M2wxOSAxOHExOSAxOSA0NSAxOXQ0NSAtMTlsODMgLTgybDgzIDgycTE5IDE5IDQ1IDE5dDQ1IC0xOWwxMjggLTEyOGwtOTAgLTkwbC04MyA4M2wtODMgLTgzcS0xOCAtMTkgLTQ1IC0xOXQtNDUgMTlsLTgzIDgzbC04MyAtODNxLTE5IC0xOSAtNDUgLTE5dC00NSAxOWwtODMgODNsLTgzIC04M3EtMTkgLTE5IC00NSAtMTl0LTQ1IDE5bC04MyA4M2wtODMgLTgzcS0xOSAtMTkgLTQ1IC0xOXQtNDUgMTlsLTgzIDgzbC04MyAtODMNCnEtMTkgLTE5IC00NSAtMTl0LTQ1IDE5bC04MyA4M2wtODMgLTgzcS0xOSAtMTkgLTQ1IC0xOXQtNDUgMTlsLTgzIDgzek02NDAgMTE1MnYtMTI4bDM4NCAxMjhsMzg0IC0xMjh2MTI4aC0xMjh2MTI4aC01MTJ2LTEyOGgtMTI4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzUwNiIgdW5pY29kZT0iJiN4ZjIxYjsiIA0KZD0iTTU3NiAwbDk2IDQ0OGwtOTYgMTI4bC0xMjggNjR6TTgzMiAwbDEyOCA2NDBsLTEyOCAtNjRsLTk2IC0xMjh6TTk5MiAxMDEwcS0yIDQgLTQgNnEtMTAgOCAtOTYgOHEtNzAgMCAtMTY3IC0xOXEtNyAtMiAtMjEgLTJ0LTIxIDJxLTk3IDE5IC0xNjcgMTlxLTg2IDAgLTk2IC04cS0yIC0yIC00IC02cTIgLTE4IDQgLTI3cTIgLTMgNy41IC02LjV0Ny41IC0xMC41cTIgLTQgNy41IC0yMC41dDcgLTIwLjV0Ny41IC0xN3Q4LjUgLTE3dDkgLTE0DQp0MTIgLTEzLjV0MTQgLTkuNXQxNy41IC04dDIwLjUgLTR0MjQuNSAtMnEzNiAwIDU5IDEyLjV0MzIuNSAzMHQxNC41IDM0LjV0MTEuNSAyOS41dDE3LjUgMTIuNWgxMnExMSAwIDE3LjUgLTEyLjV0MTEuNSAtMjkuNXQxNC41IC0zNC41dDMyLjUgLTMwdDU5IC0xMi41cTEzIDAgMjQuNSAydDIwLjUgNHQxNy41IDh0MTQgOS41dDEyIDEzLjV0OSAxNHQ4LjUgMTd0Ny41IDE3dDcgMjAuNXQ3LjUgMjAuNXEyIDcgNy41IDEwLjV0Ny41IDYuNQ0KcTIgOSA0IDI3ek0xNDA4IDEzMXEwIC0xMjEgLTczIC0xOTB0LTE5NCAtNjloLTg3NHEtMTIxIDAgLTE5NCA2OXQtNzMgMTkwcTAgNjEgNC41IDExOHQxOSAxMjUuNXQzNy41IDEyMy41dDYzLjUgMTAzLjV0OTMuNSA3NC41bC05MCAyMjBoMjE0cS0yMiA2NCAtMjIgMTI4cTAgMTIgMiAzMnEtMTk0IDQwIC0xOTQgOTZxMCA1NyAyMTAgOTlxMTcgNjIgNTEuNSAxMzR0NzAuNSAxMTRxMzIgMzcgNzYgMzdxMzAgMCA4NCAtMzF0ODQgLTMxdDg0IDMxDQp0ODQgMzFxNDQgMCA3NiAtMzdxMzYgLTQyIDcwLjUgLTExNHQ1MS41IC0xMzRxMjEwIC00MiAyMTAgLTk5cTAgLTU2IC0xOTQgLTk2cTcgLTgxIC0yMCAtMTYwaDIxNGwtODIgLTIyNXE2MyAtMzMgMTA3LjUgLTk2LjV0NjUuNSAtMTQzLjV0MjkgLTE1MS41dDggLTE0OC41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzUwNyIgdW5pY29kZT0iJiN4ZjIxYzsiIGhvcml6LWFkdi14PSIyMzA0IiANCmQ9Ik0yMzAxIDUwMHExMiAtMTAzIC0yMiAtMTk4LjV0LTk5IC0xNjMuNXQtMTU4LjUgLTEwNnQtMTk2LjUgLTMxcS0xNjEgMTEgLTI3OS41IDEyNXQtMTM0LjUgMjc0cS0xMiAxMTEgMjcuNSAyMTAuNXQxMTguNSAxNzAuNWwtNzEgMTA3cS05NiAtODAgLTE1MSAtMTk0dC01NSAtMjQ0cTAgLTI3IC0xOC41IC00Ni41dC00NS41IC0xOS41aC0yNTZoLTY5cS0yMyAtMTY0IC0xNDkgLTI3NHQtMjk0IC0xMTBxLTE4NSAwIC0zMTYuNSAxMzEuNQ0KdC0xMzEuNSAzMTYuNXQxMzEuNSAzMTYuNXQzMTYuNSAxMzEuNXE3NiAwIDE1MiAtMjdsMjQgNDVxLTEyMyAxMTAgLTMwNCAxMTBoLTY0cS0yNiAwIC00NSAxOXQtMTkgNDV0MTkgNDV0NDUgMTloMTI4cTc4IDAgMTQ1IC0xMy41dDExNi41IC0zOC41dDcxLjUgLTM5LjV0NTEgLTM2LjVoNTEyaDExNWwtODUgMTI4aC0yMjJxLTMwIDAgLTQ5IDIyLjV0LTE0IDUyLjVxNCAyMyAyMyAzOHQ0MyAxNWgyNTNxMzMgMCA1MyAtMjhsNzAgLTEwNQ0KbDExNCAxMTRxMTkgMTkgNDYgMTloMTAxcTI2IDAgNDUgLTE5dDE5IC00NXYtMTI4cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtMTc5bDExNSAtMTcycTEzMSA2MyAyNzUgMzZxMTQzIC0yNiAyNDQgLTEzNC41dDExOCAtMjUzLjV6TTQ0OCAxMjhxMTE1IDAgMjAzIDcyLjV0MTExIDE4My41aC0zMTRxLTM1IDAgLTU1IDMxcS0xOCAzMiAtMSA2M2wxNDcgMjc3cS00NyAxMyAtOTEgMTNxLTEzMiAwIC0yMjYgLTk0dC05NCAtMjI2dDk0IC0yMjYNCnQyMjYgLTk0ek0xODU2IDEyOHExMzIgMCAyMjYgOTR0OTQgMjI2dC05NCAyMjZ0LTIyNiA5NHEtNjAgMCAtMTIxIC0yNGwxNzQgLTI2MHExNSAtMjMgMTAgLTQ5dC0yNyAtNDBxLTE1IC0xMSAtMzYgLTExcS0zNSAwIC01MyAyOWwtMTc0IDI2MHEtOTMgLTk1IC05MyAtMjI1cTAgLTEzMiA5NCAtMjI2dDIyNiAtOTR6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTA4IiB1bmljb2RlPSImI3hmMjFkOyIgDQpkPSJNMTQwOCAwcTAgLTYzIC02MS41IC0xMTMuNXQtMTY0IC04MXQtMjI1IC00NnQtMjUzLjUgLTE1LjV0LTI1My41IDE1LjV0LTIyNSA0NnQtMTY0IDgxdC02MS41IDExMy41cTAgNDkgMzMgODguNXQ5MSA2Ni41dDExOCA0NC41dDEzMSAyOS41cTI2IDUgNDggLTEwLjV0MjYgLTQxLjVxNSAtMjYgLTEwLjUgLTQ4dC00MS41IC0yNnEtNTggLTEwIC0xMDYgLTIzLjV0LTc2LjUgLTI1LjV0LTQ4LjUgLTIzLjV0LTI3LjUgLTE5LjV0LTguNSAtMTINCnEzIC0xMSAyNyAtMjYuNXQ3MyAtMzN0MTE0IC0zMi41dDE2MC41IC0yNXQyMDEuNSAtMTB0MjAxLjUgMTB0MTYwLjUgMjV0MTE0IDMzdDczIDMzLjV0MjcgMjcuNXEtMSA0IC04LjUgMTF0LTI3LjUgMTl0LTQ4LjUgMjMuNXQtNzYuNSAyNXQtMTA2IDIzLjVxLTI2IDQgLTQxLjUgMjZ0LTEwLjUgNDhxNCAyNiAyNiA0MS41dDQ4IDEwLjVxNzEgLTEyIDEzMSAtMjkuNXQxMTggLTQ0LjV0OTEgLTY2LjV0MzMgLTg4LjV6TTEwMjQgODk2di0zODQNCnEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTY0di0zODRxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC0yNTZxLTI2IDAgLTQ1IDE5dC0xOSA0NXYzODRoLTY0cS0yNiAwIC00NSAxOXQtMTkgNDV2Mzg0cTAgNTMgMzcuNSA5MC41dDkwLjUgMzcuNWgzODRxNTMgMCA5MC41IC0zNy41dDM3LjUgLTkwLjV6TTkyOCAxMjgwcTAgLTkzIC02NS41IC0xNTguNXQtMTU4LjUgLTY1LjV0LTE1OC41IDY1LjV0LTY1LjUgMTU4LjV0NjUuNSAxNTguNXQxNTguNSA2NS41DQp0MTU4LjUgLTY1LjV0NjUuNSAtMTU4LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTA5IiB1bmljb2RlPSImI3hmMjFlOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTEyODAgNTEyaDMwNXEtNSAtNiAtMTAgLTEwLjV0LTkgLTcuNWwtMyAtNGwtNjIzIC02MDBxLTE4IC0xOCAtNDQgLTE4dC00NCAxOGwtNjI0IDYwMnEtNSAyIC0yMSAyMGgzNjlxMjIgMCAzOS41IDEzLjV0MjIuNSAzNC41bDcwIDI4MWwxOTAgLTY2N3E2IC0yMCAyMyAtMzN0MzkgLTEzcTIxIDAgMzggMTN0MjMgMzNsMTQ2IDQ4NWw1NiAtMTEycTE4IC0zNSA1NyAtMzV6TTE3OTIgOTQwcTAgLTE0NSAtMTAzIC0zMDBoLTM2OWwtMTExIDIyMQ0KcS04IDE3IC0yNS41IDI3dC0zNi41IDhxLTQ1IC01IC01NiAtNDZsLTEyOSAtNDMwbC0xOTYgNjg2cS02IDIwIC0yMy41IDMzdC0zOS41IDEzdC0zOSAtMTMuNXQtMjIgLTM0LjVsLTExNiAtNDY0aC00MjNxLTEwMyAxNTUgLTEwMyAzMDBxMCAyMjAgMTI3IDM0NHQzNTEgMTI0cTYyIDAgMTI2LjUgLTIxLjV0MTIwIC01OHQ5NS41IC02OC41dDc2IC02OHEzNiAzNiA3NiA2OHQ5NS41IDY4LjV0MTIwIDU4dDEyNi41IDIxLjVxMjI0IDAgMzUxIC0xMjQNCnQxMjcgLTM0NHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InZlbnVzIiB1bmljb2RlPSImI3hmMjIxOyIgaG9yaXotYWR2LXg9IjEyODAiIA0KZD0iTTExNTIgOTYwcTAgLTIyMSAtMTQ3LjUgLTM4NC41dC0zNjQuNSAtMTg3LjV2LTI2MGgyMjRxMTQgMCAyMyAtOXQ5IC0yM3YtNjRxMCAtMTQgLTkgLTIzdC0yMyAtOWgtMjI0di0yMjRxMCAtMTQgLTkgLTIzdC0yMyAtOWgtNjRxLTE0IDAgLTIzIDl0LTkgMjN2MjI0aC0yMjRxLTE0IDAgLTIzIDl0LTkgMjN2NjRxMCAxNCA5IDIzdDIzIDloMjI0djI2MHEtMTUwIDE2IC0yNzEuNSAxMDN0LTE4NiAyMjR0LTUyLjUgMjkyDQpxMTEgMTM0IDgwLjUgMjQ5dDE4MiAxODh0MjQ1LjUgODhxMTcwIDE5IDMxOSAtNTR0MjM2IC0yMTJ0ODcgLTMwNnpNMTI4IDk2MHEwIC0xODUgMTMxLjUgLTMxNi41dDMxNi41IC0xMzEuNXQzMTYuNSAxMzEuNXQxMzEuNSAzMTYuNXQtMTMxLjUgMzE2LjV0LTMxNi41IDEzMS41dC0zMTYuNSAtMTMxLjV0LTEzMS41IC0zMTYuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81MTEiIHVuaWNvZGU9IiYjeGYyMjI7IiANCmQ9Ik0xNDcyIDE0MDhxMjYgMCA0NSAtMTl0MTkgLTQ1di00MTZxMCAtMTQgLTkgLTIzdC0yMyAtOWgtNjRxLTE0IDAgLTIzIDl0LTkgMjN2MjYybC0zODIgLTM4M3ExMjYgLTE1NiAxMjYgLTM1OXEwIC0xMTcgLTQ1LjUgLTIyMy41dC0xMjMgLTE4NHQtMTg0IC0xMjN0LTIyMy41IC00NS41dC0yMjMuNSA0NS41dC0xODQgMTIzdC0xMjMgMTg0dC00NS41IDIyMy41dDQ1LjUgMjIzLjV0MTIzIDE4NHQxODQgMTIzdDIyMy41IDQ1LjUNCnEyMDMgMCAzNTkgLTEyNmwzODIgMzgyaC0yNjFxLTE0IDAgLTIzIDl0LTkgMjN2NjRxMCAxNCA5IDIzdDIzIDloNDE2ek01NzYgMHExODUgMCAzMTYuNSAxMzEuNXQxMzEuNSAzMTYuNXQtMTMxLjUgMzE2LjV0LTMxNi41IDEzMS41dC0zMTYuNSAtMTMxLjV0LTEzMS41IC0zMTYuNXQxMzEuNSAtMzE2LjV0MzE2LjUgLTEzMS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzUxMiIgdW5pY29kZT0iJiN4ZjIyMzsiIGhvcml6LWFkdi14PSIxMjgwIiANCmQ9Ik04MzAgMTIyMHExNDUgLTcyIDIzMy41IC0yMTAuNXQ4OC41IC0zMDUuNXEwIC0yMjEgLTE0Ny41IC0zODQuNXQtMzY0LjUgLTE4Ny41di0xMzJoOTZxMTQgMCAyMyAtOXQ5IC0yM3YtNjRxMCAtMTQgLTkgLTIzdC0yMyAtOWgtOTZ2LTk2cTAgLTE0IC05IC0yM3QtMjMgLTloLTY0cS0xNCAwIC0yMyA5dC05IDIzdjk2aC05NnEtMTQgMCAtMjMgOXQtOSAyM3Y2NHEwIDE0IDkgMjN0MjMgOWg5NnYxMzJxLTIxNyAyNCAtMzY0LjUgMTg3LjUNCnQtMTQ3LjUgMzg0LjVxMCAxNjcgODguNSAzMDUuNXQyMzMuNSAyMTAuNXEtMTY1IDk2IC0yMjggMjczcS02IDE2IDMuNSAyOS41dDI2LjUgMTMuNWg2OXEyMSAwIDI5IC0yMHE0NCAtMTA2IDE0MCAtMTcxdDIxNCAtNjV0MjE0IDY1dDE0MCAxNzFxOCAyMCAzNyAyMGg2MXExNyAwIDI2LjUgLTEzLjV0My41IC0yOS41cS02MyAtMTc3IC0yMjggLTI3M3pNNTc2IDI1NnExODUgMCAzMTYuNSAxMzEuNXQxMzEuNSAzMTYuNXQtMTMxLjUgMzE2LjUNCnQtMzE2LjUgMTMxLjV0LTMxNi41IC0xMzEuNXQtMTMxLjUgLTMxNi41dDEzMS41IC0zMTYuNXQzMTYuNSAtMTMxLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTEzIiB1bmljb2RlPSImI3hmMjI0OyIgDQpkPSJNMTAyNCAxNTA0cTAgMTQgOSAyM3QyMyA5aDI4OHEyNiAwIDQ1IC0xOXQxOSAtNDV2LTI4OHEwIC0xNCAtOSAtMjN0LTIzIC05aC02NHEtMTQgMCAtMjMgOXQtOSAyM3YxMzRsLTI1NCAtMjU1cTEyNiAtMTU4IDEyNiAtMzU5cTAgLTIyMSAtMTQ3LjUgLTM4NC41dC0zNjQuNSAtMTg3LjV2LTEzMmg5NnExNCAwIDIzIC05dDkgLTIzdi02NHEwIC0xNCAtOSAtMjN0LTIzIC05aC05NnYtOTZxMCAtMTQgLTkgLTIzdC0yMyAtOWgtNjQNCnEtMTQgMCAtMjMgOXQtOSAyM3Y5NmgtOTZxLTE0IDAgLTIzIDl0LTkgMjN2NjRxMCAxNCA5IDIzdDIzIDloOTZ2MTMycS0xNDkgMTYgLTI3MC41IDEwM3QtMTg2LjUgMjIzLjV0LTUzIDI5MS41cTE2IDIwNCAxNjAgMzUzLjV0MzQ3IDE3Mi41cTExOCAxNCAyMjggLTE5dDE5OCAtMTAzbDI1NSAyNTRoLTEzNHEtMTQgMCAtMjMgOXQtOSAyM3Y2NHpNNTc2IDI1NnExODUgMCAzMTYuNSAxMzEuNXQxMzEuNSAzMTYuNXQtMTMxLjUgMzE2LjUNCnQtMzE2LjUgMTMxLjV0LTMxNi41IC0xMzEuNXQtMTMxLjUgLTMxNi41dDEzMS41IC0zMTYuNXQzMTYuNSAtMTMxLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTE0IiB1bmljb2RlPSImI3hmMjI1OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTEyODAgMTUwNHEwIDE0IDkgMjN0MjMgOWgyODhxMjYgMCA0NSAtMTl0MTkgLTQ1di0yODhxMCAtMTQgLTkgLTIzdC0yMyAtOWgtNjRxLTE0IDAgLTIzIDl0LTkgMjN2MTM0bC0yNTQgLTI1NXExMjYgLTE1OCAxMjYgLTM1OXEwIC0yMjEgLTE0Ny41IC0zODQuNXQtMzY0LjUgLTE4Ny41di0xMzJoOTZxMTQgMCAyMyAtOXQ5IC0yM3YtNjRxMCAtMTQgLTkgLTIzdC0yMyAtOWgtOTZ2LTk2cTAgLTE0IC05IC0yM3QtMjMgLTloLTY0DQpxLTE0IDAgLTIzIDl0LTkgMjN2OTZoLTk2cS0xNCAwIC0yMyA5dC05IDIzdjY0cTAgMTQgOSAyM3QyMyA5aDk2djEzMnEtMjE3IDI0IC0zNjQuNSAxODcuNXQtMTQ3LjUgMzg0LjVxMCAyMDEgMTI2IDM1OWwtNTIgNTNsLTEwMSAtMTExcS05IC0xMCAtMjIgLTEwLjV0LTIzIDcuNWwtNDggNDRxLTEwIDggLTEwLjUgMjEuNXQ4LjUgMjMuNWwxMDUgMTE1bC0xMTEgMTEydi0xMzRxMCAtMTQgLTkgLTIzdC0yMyAtOWgtNjRxLTE0IDAgLTIzIDkNCnQtOSAyM3YyODhxMCAyNiAxOSA0NXQ0NSAxOWgyODhxMTQgMCAyMyAtOXQ5IC0yM3YtNjRxMCAtMTQgLTkgLTIzdC0yMyAtOWgtMTMzbDEwNiAtMTA3bDg2IDk0cTkgMTAgMjIgMTAuNXQyMyAtNy41bDQ4IC00NHExMCAtOCAxMC41IC0yMS41dC04LjUgLTIzLjVsLTkwIC05OWw1NyAtNTZxMTU4IDEyNiAzNTkgMTI2dDM1OSAtMTI2bDI1NSAyNTRoLTEzNHEtMTQgMCAtMjMgOXQtOSAyM3Y2NHpNODMyIDI1NnExODUgMCAzMTYuNSAxMzEuNQ0KdDEzMS41IDMxNi41dC0xMzEuNSAzMTYuNXQtMzE2LjUgMTMxLjV0LTMxNi41IC0xMzEuNXQtMTMxLjUgLTMxNi41dDEzMS41IC0zMTYuNXQzMTYuNSAtMTMxLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTE1IiB1bmljb2RlPSImI3hmMjI2OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE3OTAgMTAwN3ExMiAtMTU1IC01Mi41IC0yOTJ0LTE4NiAtMjI0dC0yNzEuNSAtMTAzdi0yNjBoMjI0cTE0IDAgMjMgLTl0OSAtMjN2LTY0cTAgLTE0IC05IC0yM3QtMjMgLTloLTIyNHYtMjI0cTAgLTE0IC05IC0yM3QtMjMgLTloLTY0cS0xNCAwIC0yMyA5dC05IDIzdjIyNGgtNTEydi0yMjRxMCAtMTQgLTkgLTIzdC0yMyAtOWgtNjRxLTE0IDAgLTIzIDl0LTkgMjN2MjI0aC0yMjRxLTE0IDAgLTIzIDl0LTkgMjN2NjRxMCAxNCA5IDIzDQp0MjMgOWgyMjR2MjYwcS0xNTAgMTYgLTI3MS41IDEwM3QtMTg2IDIyNHQtNTIuNSAyOTJxMTcgMjA2IDE2NC41IDM1Ni41dDM1Mi41IDE2OS41cTIwNiAyMSAzNzcgLTk0cTE3MSAxMTUgMzc3IDk0cTIwNSAtMTkgMzUyLjUgLTE2OS41dDE2NC41IC0zNTYuNXpNODk2IDY0N3ExMjggMTMxIDEyOCAzMTN0LTEyOCAzMTNxLTEyOCAtMTMxIC0xMjggLTMxM3QxMjggLTMxM3pNNTc2IDUxMnExMTUgMCAyMTggNTdxLTE1NCAxNjUgLTE1NCAzOTENCnEwIDIyNCAxNTQgMzkxcS0xMDMgNTcgLTIxOCA1N3EtMTg1IDAgLTMxNi41IC0xMzEuNXQtMTMxLjUgLTMxNi41dDEzMS41IC0zMTYuNXQzMTYuNSAtMTMxLjV6TTExNTIgMTI4djI2MHEtMTM3IDE1IC0yNTYgOTRxLTExOSAtNzkgLTI1NiAtOTR2LTI2MGg1MTJ6TTEyMTYgNTEycTE4NSAwIDMxNi41IDEzMS41dDEzMS41IDMxNi41dC0xMzEuNSAzMTYuNXQtMzE2LjUgMTMxLjVxLTExNSAwIC0yMTggLTU3cTE1NCAtMTY3IDE1NCAtMzkxDQpxMCAtMjI2IC0xNTQgLTM5MXExMDMgLTU3IDIxOCAtNTd6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTE2IiB1bmljb2RlPSImI3hmMjI3OyIgaG9yaXotYWR2LXg9IjE5MjAiIA0KZD0iTTE1MzYgMTEyMHEwIDE0IDkgMjN0MjMgOWgyODhxMjYgMCA0NSAtMTl0MTkgLTQ1di0yODhxMCAtMTQgLTkgLTIzdC0yMyAtOWgtNjRxLTE0IDAgLTIzIDl0LTkgMjN2MTM0bC0yNTQgLTI1NXE3NiAtOTUgMTA3LjUgLTIxNHQ5LjUgLTI0N3EtMzEgLTE4MiAtMTY2IC0zMTJ0LTMxOCAtMTU2cS0yMTAgLTI5IC0zODQuNSA4MHQtMjQxLjUgMzAwcS0xMTcgNiAtMjIxIDU3LjV0LTE3Ny41IDEzM3QtMTEzLjUgMTkyLjV0LTMyIDIzMA0KcTkgMTM1IDc4IDI1MnQxODIgMTkxLjV0MjQ4IDg5LjVxMTE4IDE0IDIyNy41IC0xOXQxOTguNSAtMTAzbDI1NSAyNTRoLTEzNHEtMTQgMCAtMjMgOXQtOSAyM3Y2NHEwIDE0IDkgMjN0MjMgOWgyODhxMjYgMCA0NSAtMTl0MTkgLTQ1di0yODhxMCAtMTQgLTkgLTIzdC0yMyAtOWgtNjRxLTE0IDAgLTIzIDl0LTkgMjN2MTM0bC0yNTQgLTI1NXE1OSAtNzQgOTMgLTE2OXExODIgLTkgMzI4IC0xMjRsMjU1IDI1NGgtMTM0cS0xNCAwIC0yMyA5DQp0LTkgMjN2NjR6TTEwMjQgNzA0cTAgMjAgLTQgNThxLTE2MiAtMjUgLTI3MSAtMTUwdC0xMDkgLTI5MnEwIC0yMCA0IC01OHExNjIgMjUgMjcxIDE1MHQxMDkgMjkyek0xMjggNzA0cTAgLTE2OCAxMTEgLTI5NHQyNzYgLTE0OXEtMyAyOSAtMyA1OXEwIDIxMCAxMzUgMzY5LjV0MzM4IDE5Ni41cS01MyAxMjAgLTE2My41IDE5M3QtMjQ1LjUgNzNxLTE4NSAwIC0zMTYuNSAtMTMxLjV0LTEzMS41IC0zMTYuNXpNMTA4OCAtMTI4DQpxMTg1IDAgMzE2LjUgMTMxLjV0MTMxLjUgMzE2LjVxMCAxNjggLTExMSAyOTR0LTI3NiAxNDlxMyAtMjggMyAtNTlxMCAtMjEwIC0xMzUgLTM2OS41dC0zMzggLTE5Ni41cTUzIC0xMjAgMTYzLjUgLTE5M3QyNDUuNSAtNzN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTE3IiB1bmljb2RlPSImI3hmMjI4OyIgaG9yaXotYWR2LXg9IjIwNDgiIA0KZD0iTTE2NjQgMTUwNHEwIDE0IDkgMjN0MjMgOWgyODhxMjYgMCA0NSAtMTl0MTkgLTQ1di0yODhxMCAtMTQgLTkgLTIzdC0yMyAtOWgtNjRxLTE0IDAgLTIzIDl0LTkgMjN2MTM0bC0yNTQgLTI1NXE3NiAtOTUgMTA3LjUgLTIxNHQ5LjUgLTI0N3EtMzIgLTE4MCAtMTY0LjUgLTMxMHQtMzEzLjUgLTE1N3EtMjIzIC0zNCAtNDA5IDkwcS0xMTcgLTc4IC0yNTYgLTkzdi0xMzJoOTZxMTQgMCAyMyAtOXQ5IC0yM3YtNjRxMCAtMTQgLTkgLTIzDQp0LTIzIC05aC05NnYtOTZxMCAtMTQgLTkgLTIzdC0yMyAtOWgtNjRxLTE0IDAgLTIzIDl0LTkgMjN2OTZoLTk2cS0xNCAwIC0yMyA5dC05IDIzdjY0cTAgMTQgOSAyM3QyMyA5aDk2djEzMnEtMTU1IDE3IC0yNzkuNSAxMDkuNXQtMTg3IDIzNy41dC0zOS41IDMwN3EyNSAxODcgMTU5LjUgMzIyLjV0MzIwLjUgMTY0LjVxMjI0IDM0IDQxMCAtOTBxMTQ2IDk3IDMyMCA5N3EyMDEgMCAzNTkgLTEyNmwyNTUgMjU0aC0xMzRxLTE0IDAgLTIzIDkNCnQtOSAyM3Y2NHpNODk2IDM5MXExMjggMTMxIDEyOCAzMTN0LTEyOCAzMTNxLTEyOCAtMTMxIC0xMjggLTMxM3QxMjggLTMxM3pNMTI4IDcwNHEwIC0xODUgMTMxLjUgLTMxNi41dDMxNi41IC0xMzEuNXExMTcgMCAyMTggNTdxLTE1NCAxNjcgLTE1NCAzOTF0MTU0IDM5MXEtMTAxIDU3IC0yMTggNTdxLTE4NSAwIC0zMTYuNSAtMTMxLjV0LTEzMS41IC0zMTYuNXpNMTIxNiAyNTZxMTg1IDAgMzE2LjUgMTMxLjV0MTMxLjUgMzE2LjUNCnQtMTMxLjUgMzE2LjV0LTMxNi41IDEzMS41cS0xMTcgMCAtMjE4IC01N3ExNTQgLTE2NyAxNTQgLTM5MXQtMTU0IC0zOTFxMTAxIC01NyAyMTggLTU3eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzUxOCIgdW5pY29kZT0iJiN4ZjIyOTsiIA0KZD0iTTE0NzIgMTQwOHEyNiAwIDQ1IC0xOXQxOSAtNDV2LTQxNnEwIC0xNCAtOSAtMjN0LTIzIC05aC02NHEtMTQgMCAtMjMgOXQtOSAyM3YyNjJsLTIxMyAtMjE0bDE0MCAtMTQwcTkgLTEwIDkgLTIzdC05IC0yMmwtNDYgLTQ2cS05IC05IC0yMiAtOXQtMjMgOWwtMTQwIDE0MWwtNzggLTc5cTEyNiAtMTU2IDEyNiAtMzU5cTAgLTExNyAtNDUuNSAtMjIzLjV0LTEyMyAtMTg0dC0xODQgLTEyM3QtMjIzLjUgLTQ1LjV0LTIyMy41IDQ1LjUNCnQtMTg0IDEyM3QtMTIzIDE4NHQtNDUuNSAyMjMuNXQ0NS41IDIyMy41dDEyMyAxODR0MTg0IDEyM3QyMjMuNSA0NS41cTIwMyAwIDM1OSAtMTI2bDc4IDc4bC0xNzIgMTcycS05IDEwIC05IDIzdDkgMjJsNDYgNDZxOSA5IDIyIDl0MjMgLTlsMTcyIC0xNzJsMjEzIDIxM2gtMjYxcS0xNCAwIC0yMyA5dC05IDIzdjY0cTAgMTQgOSAyM3QyMyA5aDQxNnpNNTc2IDBxMTg1IDAgMzE2LjUgMTMxLjV0MTMxLjUgMzE2LjV0LTEzMS41IDMxNi41DQp0LTMxNi41IDEzMS41dC0zMTYuNSAtMTMxLjV0LTEzMS41IC0zMTYuNXQxMzEuNSAtMzE2LjV0MzE2LjUgLTEzMS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzUxOSIgdW5pY29kZT0iJiN4ZjIyYTsiIGhvcml6LWFkdi14PSIxMjgwIiANCmQ9Ik02NDAgODkycTIxNyAtMjQgMzY0LjUgLTE4Ny41dDE0Ny41IC0zODQuNXEwIC0xNjcgLTg3IC0zMDZ0LTIzNiAtMjEydC0zMTkgLTU0cS0xMzMgMTUgLTI0NS41IDg4dC0xODIgMTg4dC04MC41IDI0OXEtMTIgMTU1IDUyLjUgMjkydDE4NiAyMjR0MjcxLjUgMTAzdjEzMmgtMTYwcS0xNCAwIC0yMyA5dC05IDIzdjY0cTAgMTQgOSAyM3QyMyA5aDE2MHYxNjVsLTkyIC05MnEtMTAgLTkgLTIzIC05dC0yMiA5bC00NiA0NnEtOSA5IC05IDIyDQp0OSAyM2wyMDIgMjAxcTE5IDE5IDQ1IDE5dDQ1IC0xOWwyMDIgLTIwMXE5IC0xMCA5IC0yM3QtOSAtMjJsLTQ2IC00NnEtOSAtOSAtMjIgLTl0LTIzIDlsLTkyIDkydi0xNjVoMTYwcTE0IDAgMjMgLTl0OSAtMjN2LTY0cTAgLTE0IC05IC0yM3QtMjMgLTloLTE2MHYtMTMyek01NzYgLTEyOHExODUgMCAzMTYuNSAxMzEuNXQxMzEuNSAzMTYuNXQtMTMxLjUgMzE2LjV0LTMxNi41IDEzMS41dC0zMTYuNSAtMTMxLjV0LTEzMS41IC0zMTYuNQ0KdDEzMS41IC0zMTYuNXQzMTYuNSAtMTMxLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTIwIiB1bmljb2RlPSImI3hmMjJiOyIgaG9yaXotYWR2LXg9IjIwNDgiIA0KZD0iTTE5MDEgNjIxcTE5IC0xOSAxOSAtNDV0LTE5IC00NWwtMjk0IC0yOTRxLTkgLTEwIC0yMi41IC0xMHQtMjIuNSAxMGwtNDUgNDVxLTEwIDkgLTEwIDIyLjV0MTAgMjIuNWwxODUgMTg1aC0yOTR2LTIyNHEwIC0xNCAtOSAtMjN0LTIzIC05aC02NHEtMTQgMCAtMjMgOXQtOSAyM3YyMjRoLTEzMnEtMjQgLTIxNyAtMTg3LjUgLTM2NC41dC0zODQuNSAtMTQ3LjVxLTE2NyAwIC0zMDYgODd0LTIxMiAyMzZ0LTU0IDMxOXExNSAxMzMgODggMjQ1LjUNCnQxODggMTgydDI0OSA4MC41cTE1NSAxMiAyOTIgLTUyLjV0MjI0IC0xODZ0MTAzIC0yNzEuNWgxMzJ2MjI0cTAgMTQgOSAyM3QyMyA5aDY0cTE0IDAgMjMgLTl0OSAtMjN2LTIyNGgyOTRsLTE4NSAxODVxLTEwIDkgLTEwIDIyLjV0MTAgMjIuNWw0NSA0NXE5IDEwIDIyLjUgMTB0MjIuNSAtMTB6TTU3NiAxMjhxMTg1IDAgMzE2LjUgMTMxLjV0MTMxLjUgMzE2LjV0LTEzMS41IDMxNi41dC0zMTYuNSAxMzEuNXQtMzE2LjUgLTEzMS41DQp0LTEzMS41IC0zMTYuNXQxMzEuNSAtMzE2LjV0MzE2LjUgLTEzMS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzUyMSIgdW5pY29kZT0iJiN4ZjIyYzsiIGhvcml6LWFkdi14PSIxMjgwIiANCmQ9Ik0xMTUyIDk2MHEwIC0yMjEgLTE0Ny41IC0zODQuNXQtMzY0LjUgLTE4Ny41di02MTJxMCAtMTQgLTkgLTIzdC0yMyAtOWgtNjRxLTE0IDAgLTIzIDl0LTkgMjN2NjEycS0yMTcgMjQgLTM2NC41IDE4Ny41dC0xNDcuNSAzODQuNXEwIDExNyA0NS41IDIyMy41dDEyMyAxODR0MTg0IDEyM3QyMjMuNSA0NS41dDIyMy41IC00NS41dDE4NCAtMTIzdDEyMyAtMTg0dDQ1LjUgLTIyMy41ek01NzYgNTEycTE4NSAwIDMxNi41IDEzMS41DQp0MTMxLjUgMzE2LjV0LTEzMS41IDMxNi41dC0zMTYuNSAxMzEuNXQtMzE2LjUgLTEzMS41dC0xMzEuNSAtMzE2LjV0MTMxLjUgLTMxNi41dDMxNi41IC0xMzEuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81MjIiIHVuaWNvZGU9IiYjeGYyMmQ7IiBob3Jpei1hZHYteD0iMTI4MCIgDQpkPSJNMTAyNCA1NzZxMCAxODUgLTEzMS41IDMxNi41dC0zMTYuNSAxMzEuNXQtMzE2LjUgLTEzMS41dC0xMzEuNSAtMzE2LjV0MTMxLjUgLTMxNi41dDMxNi41IC0xMzEuNXQzMTYuNSAxMzEuNXQxMzEuNSAzMTYuNXpNMTE1MiA1NzZxMCAtMTE3IC00NS41IC0yMjMuNXQtMTIzIC0xODR0LTE4NCAtMTIzdC0yMjMuNSAtNDUuNXQtMjIzLjUgNDUuNXQtMTg0IDEyM3QtMTIzIDE4NHQtNDUuNSAyMjMuNXQ0NS41IDIyMy41dDEyMyAxODR0MTg0IDEyMw0KdDIyMy41IDQ1LjV0MjIzLjUgLTQ1LjV0MTg0IC0xMjN0MTIzIC0xODR0NDUuNSAtMjIzLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTIzIiB1bmljb2RlPSImI3hmMjJlOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81MjQiIHVuaWNvZGU9IiYjeGYyMmY7IiBob3Jpei1hZHYteD0iMTc5MiIgDQogLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzUyNSIgdW5pY29kZT0iJiN4ZjIzMDsiIA0KZD0iTTE0NTEgMTQwOHEzNSAwIDYwIC0yNXQyNSAtNjB2LTEzNjZxMCAtMzUgLTI1IC02MHQtNjAgLTI1aC0zOTF2NTk1aDE5OWwzMCAyMzJoLTIyOXYxNDhxMCA1NiAyMy41IDg0dDkxLjUgMjhsMTIyIDF2MjA3cS02MyA5IC0xNzggOXEtMTM2IDAgLTIxNy41IC04MHQtODEuNSAtMjI2di0xNzFoLTIwMHYtMjMyaDIwMHYtNTk1aC03MzVxLTM1IDAgLTYwIDI1dC0yNSA2MHYxMzY2cTAgMzUgMjUgNjB0NjAgMjVoMTM2NnoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81MjYiIHVuaWNvZGU9IiYjeGYyMzE7IiBob3Jpei1hZHYteD0iMTI4MCIgDQpkPSJNMCA5MzlxMCAxMDggMzcuNSAyMDMuNXQxMDMuNSAxNjYuNXQxNTIgMTIzdDE4NSA3OHQyMDIgMjZxMTU4IDAgMjk0IC02Ni41dDIyMSAtMTkzLjV0ODUgLTI4N3EwIC05NiAtMTkgLTE4OHQtNjAgLTE3N3QtMTAwIC0xNDkuNXQtMTQ1IC0xMDN0LTE4OSAtMzguNXEtNjggMCAtMTM1IDMydC05NiA4OHEtMTAgLTM5IC0yOCAtMTEyLjV0LTIzLjUgLTk1dC0yMC41IC03MXQtMjYgLTcxdC0zMiAtNjIuNXQtNDYgLTc3LjV0LTYyIC04Ni41DQpsLTE0IC01bC05IDEwcS0xNSAxNTcgLTE1IDE4OHEwIDkyIDIxLjUgMjA2LjV0NjYuNSAyODcuNXQ1MiAyMDNxLTMyIDY1IC0zMiAxNjlxMCA4MyA1MiAxNTZ0MTMyIDczcTYxIDAgOTUgLTQwLjV0MzQgLTEwMi41cTAgLTY2IC00NCAtMTkxdC00NCAtMTg3cTAgLTYzIDQ1IC0xMDQuNXQxMDkgLTQxLjVxNTUgMCAxMDIgMjV0NzguNSA2OHQ1NiA5NXQzOCAxMTAuNXQyMCAxMTF0Ni41IDk5LjVxMCAxNzMgLTEwOS41IDI2OS41dC0yODUuNSA5Ni41DQpxLTIwMCAwIC0zMzQgLTEyOS41dC0xMzQgLTMyOC41cTAgLTQ0IDEyLjUgLTg1dDI3IC02NXQyNyAtNDUuNXQxMi41IC0zMC41cTAgLTI4IC0xNSAtNzN0LTM3IC00NXEtMiAwIC0xNyAzcS01MSAxNSAtOTAuNSA1NnQtNjEgOTQuNXQtMzIuNSAxMDh0LTExIDEwNi41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzUyNyIgdW5pY29kZT0iJiN4ZjIzMjsiIA0KZD0iTTk4NSA1NjJxMTMgMCA5Ny41IC00NHQ4OS41IC01M3EyIC01IDIgLTE1cTAgLTMzIC0xNyAtNzZxLTE2IC0zOSAtNzEgLTY1LjV0LTEwMiAtMjYuNXEtNTcgMCAtMTkwIDYycS05OCA0NSAtMTcwIDExOHQtMTQ4IDE4NXEtNzIgMTA3IC03MSAxOTR2OHEzIDkxIDc0IDE1OHEyNCAyMiA1MiAyMnE2IDAgMTggLTEuNXQxOSAtMS41cTE5IDAgMjYuNSAtNi41dDE1LjUgLTI3LjVxOCAtMjAgMzMgLTg4dDI1IC03NXEwIC0yMSAtMzQuNSAtNTcuNQ0KdC0zNC41IC00Ni41cTAgLTcgNSAtMTVxMzQgLTczIDEwMiAtMTM3cTU2IC01MyAxNTEgLTEwMXExMiAtNyAyMiAtN3ExNSAwIDU0IDQ4LjV0NTIgNDguNXpNNzgyIDMycTEyNyAwIDI0My41IDUwdDIwMC41IDEzNHQxMzQgMjAwLjV0NTAgMjQzLjV0LTUwIDI0My41dC0xMzQgMjAwLjV0LTIwMC41IDEzNHQtMjQzLjUgNTB0LTI0My41IC01MHQtMjAwLjUgLTEzNHQtMTM0IC0yMDAuNXQtNTAgLTI0My41cTAgLTIwMyAxMjAgLTM2OGwtNzkgLTIzMw0KbDI0MiA3N3ExNTggLTEwNCAzNDUgLTEwNHpNNzgyIDE0MTRxMTUzIDAgMjkyLjUgLTYwdDI0MC41IC0xNjF0MTYxIC0yNDAuNXQ2MCAtMjkyLjV0LTYwIC0yOTIuNXQtMTYxIC0yNDAuNXQtMjQwLjUgLTE2MXQtMjkyLjUgLTYwcS0xOTUgMCAtMzY1IDk0bC00MTcgLTEzNGwxMzYgNDA1cS0xMDggMTc4IC0xMDggMzg5cTAgMTUzIDYwIDI5Mi41dDE2MSAyNDAuNXQyNDAuNSAxNjF0MjkyLjUgNjB6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTI4IiB1bmljb2RlPSImI3hmMjMzOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTEyOCAxMjhoMTAyNHYxMjhoLTEwMjR2LTEyOHpNMTI4IDY0MGgxMDI0djEyOGgtMTAyNHYtMTI4ek0xNjk2IDE5MnEwIDQwIC0yOCA2OHQtNjggMjh0LTY4IC0yOHQtMjggLTY4dDI4IC02OHQ2OCAtMjh0NjggMjh0MjggNjh6TTEyOCAxMTUyaDEwMjR2MTI4aC0xMDI0di0xMjh6TTE2OTYgNzA0cTAgNDAgLTI4IDY4dC02OCAyOHQtNjggLTI4dC0yOCAtNjh0MjggLTY4dDY4IC0yOHQ2OCAyOHQyOCA2OHpNMTY5NiAxMjE2DQpxMCA0MCAtMjggNjh0LTY4IDI4dC02OCAtMjh0LTI4IC02OHQyOCAtNjh0NjggLTI4dDY4IDI4dDI4IDY4ek0xNzkyIDM4NHYtMzg0aC0xNzkydjM4NGgxNzkyek0xNzkyIDg5NnYtMzg0aC0xNzkydjM4NGgxNzkyek0xNzkyIDE0MDh2LTM4NGgtMTc5MnYzODRoMTc5MnoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81MjkiIHVuaWNvZGU9IiYjeGYyMzQ7IiBob3Jpei1hZHYteD0iMjA0OCIgDQpkPSJNNzA0IDY0MHEtMTU5IDAgLTI3MS41IDExMi41dC0xMTIuNSAyNzEuNXQxMTIuNSAyNzEuNXQyNzEuNSAxMTIuNXQyNzEuNSAtMTEyLjV0MTEyLjUgLTI3MS41dC0xMTIuNSAtMjcxLjV0LTI3MS41IC0xMTIuNXpNMTY2NCA1MTJoMzUycTEzIDAgMjIuNSAtOS41dDkuNSAtMjIuNXYtMTkycTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtMzUydi0zNTJxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41aC0xOTJxLTEzIDAgLTIyLjUgOS41DQp0LTkuNSAyMi41djM1MmgtMzUycS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2MTkycTAgMTMgOS41IDIyLjV0MjIuNSA5LjVoMzUydjM1MnEwIDEzIDkuNSAyMi41dDIyLjUgOS41aDE5MnExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV2LTM1MnpNOTI4IDI4OHEwIC01MiAzOCAtOTB0OTAgLTM4aDI1NnYtMjM4cS02OCAtNTAgLTE3MSAtNTBoLTg3NHEtMTIxIDAgLTE5NCA2OXQtNzMgMTkwcTAgNTMgMy41IDEwMy41dDE0IDEwOXQyNi41IDEwOC41DQp0NDMgOTcuNXQ2MiA4MXQ4NS41IDUzLjV0MTExLjUgMjBxMTkgMCAzOSAtMTdxNzkgLTYxIDE1NC41IC05MS41dDE2NC41IC0zMC41dDE2NC41IDMwLjV0MTU0LjUgOTEuNXEyMCAxNyAzOSAxN3ExMzIgMCAyMTcgLTk2aC0yMjNxLTUyIDAgLTkwIC0zOHQtMzggLTkwdi0xOTJ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTMwIiB1bmljb2RlPSImI3hmMjM1OyIgaG9yaXotYWR2LXg9IjIwNDgiIA0KZD0iTTcwNCA2NDBxLTE1OSAwIC0yNzEuNSAxMTIuNXQtMTEyLjUgMjcxLjV0MTEyLjUgMjcxLjV0MjcxLjUgMTEyLjV0MjcxLjUgLTExMi41dDExMi41IC0yNzEuNXQtMTEyLjUgLTI3MS41dC0yNzEuNSAtMTEyLjV6TTE3ODEgMzIwbDI0OSAtMjQ5cTkgLTkgOSAtMjNxMCAtMTMgLTkgLTIybC0xMzYgLTEzNnEtOSAtOSAtMjIgLTlxLTE0IDAgLTIzIDlsLTI0OSAyNDlsLTI0OSAtMjQ5cS05IC05IC0yMyAtOXEtMTMgMCAtMjIgOWwtMTM2IDEzNg0KcS05IDkgLTkgMjJxMCAxNCA5IDIzbDI0OSAyNDlsLTI0OSAyNDlxLTkgOSAtOSAyM3EwIDEzIDkgMjJsMTM2IDEzNnE5IDkgMjIgOXExNCAwIDIzIC05bDI0OSAtMjQ5bDI0OSAyNDlxOSA5IDIzIDlxMTMgMCAyMiAtOWwxMzYgLTEzNnE5IC05IDkgLTIycTAgLTE0IC05IC0yM3pNMTI4MyAzMjBsLTE4MSAtMTgxcS0zNyAtMzcgLTM3IC05MXEwIC01MyAzNyAtOTBsODMgLTgzcS0yMSAtMyAtNDQgLTNoLTg3NHEtMTIxIDAgLTE5NCA2OQ0KdC03MyAxOTBxMCA1MyAzLjUgMTAzLjV0MTQgMTA5dDI2LjUgMTA4LjV0NDMgOTcuNXQ2MiA4MXQ4NS41IDUzLjV0MTExLjUgMjBxMTkgMCAzOSAtMTdxMTU0IC0xMjIgMzE5IC0xMjJ0MzE5IDEyMnEyMCAxNyAzOSAxN3EyOCAwIDU3IC02cS0yOCAtMjcgLTQxIC01MHQtMTMgLTU2cTAgLTU0IDM3IC05MXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81MzEiIHVuaWNvZGU9IiYjeGYyMzY7IiBob3Jpei1hZHYteD0iMjA0OCIgDQpkPSJNMjU2IDUxMmgxNzI4cTI2IDAgNDUgLTE5dDE5IC00NXYtNDQ4aC0yNTZ2MjU2aC0xNTM2di0yNTZoLTI1NnYxMjE2cTAgMjYgMTkgNDV0NDUgMTloMTI4cTI2IDAgNDUgLTE5dDE5IC00NXYtNzA0ek04MzIgODMycTAgMTA2IC03NSAxODF0LTE4MSA3NXQtMTgxIC03NXQtNzUgLTE4MXQ3NSAtMTgxdDE4MSAtNzV0MTgxIDc1dDc1IDE4MXpNMjA0OCA1NzZ2NjRxMCAxNTkgLTExMi41IDI3MS41dC0yNzEuNSAxMTIuNWgtNzA0DQpxLTI2IDAgLTQ1IC0xOXQtMTkgLTQ1di0zODRoMTE1MnoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81MzIiIHVuaWNvZGU9IiYjeGYyMzc7IiANCmQ9Ik0xNTM2IDE1MzZsLTE5MiAtNDQ4aDE5MnYtMTkyaC0yNzRsLTU1IC0xMjhoMzI5di0xOTJoLTQxMWwtMzU3IC04MzJsLTM1NyA4MzJoLTQxMXYxOTJoMzI5bC01NSAxMjhoLTI3NHYxOTJoMTkybC0xOTIgNDQ4aDI1NmwzMjMgLTc2OGgzNzhsMzIzIDc2OGgyNTZ6TTc2OCAzMjBsMTA4IDI1NmgtMjE2eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzUzMyIgdW5pY29kZT0iJiN4ZjIzODsiIA0KZD0iTTEwODggMTUzNnExODUgMCAzMTYuNSAtOTMuNXQxMzEuNSAtMjI2LjV2LTg5NnEwIC0xMzAgLTEyNS41IC0yMjJ0LTMwNS41IC05N2wyMTMgLTIwMnExNiAtMTUgOCAtMzV0LTMwIC0yMGgtMTA1NnEtMjIgMCAtMzAgMjB0OCAzNWwyMTMgMjAycS0xODAgNSAtMzA1LjUgOTd0LTEyNS41IDIyMnY4OTZxMCAxMzMgMTMxLjUgMjI2LjV0MzE2LjUgOTMuNWg2NDB6TTc2OCAxOTJxODAgMCAxMzYgNTZ0NTYgMTM2dC01NiAxMzZ0LTEzNiA1Ng0KdC0xMzYgLTU2dC01NiAtMTM2dDU2IC0xMzZ0MTM2IC01NnpNMTM0NCA3Njh2NTEyaC0xMTUydi01MTJoMTE1MnoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81MzQiIHVuaWNvZGU9IiYjeGYyMzk7IiANCmQ9Ik0xMDg4IDE1MzZxMTg1IDAgMzE2LjUgLTkzLjV0MTMxLjUgLTIyNi41di04OTZxMCAtMTMwIC0xMjUuNSAtMjIydC0zMDUuNSAtOTdsMjEzIC0yMDJxMTYgLTE1IDggLTM1dC0zMCAtMjBoLTEwNTZxLTIyIDAgLTMwIDIwdDggMzVsMjEzIDIwMnEtMTgwIDUgLTMwNS41IDk3dC0xMjUuNSAyMjJ2ODk2cTAgMTMzIDEzMS41IDIyNi41dDMxNi41IDkzLjVoNjQwek0yODggMjI0cTY2IDAgMTEzIDQ3dDQ3IDExM3QtNDcgMTEzdC0xMTMgNDcNCnQtMTEzIC00N3QtNDcgLTExM3Q0NyAtMTEzdDExMyAtNDd6TTcwNCA3Njh2NTEyaC01NDR2LTUxMmg1NDR6TTEyNDggMjI0cTY2IDAgMTEzIDQ3dDQ3IDExM3QtNDcgMTEzdC0xMTMgNDd0LTExMyAtNDd0LTQ3IC0xMTN0NDcgLTExM3QxMTMgLTQ3ek0xNDA4IDc2OHY1MTJoLTU3NnYtNTEyaDU3NnoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81MzUiIHVuaWNvZGU9IiYjeGYyM2E7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNNTk3IDExMTV2LTExNzNxMCAtMjUgLTEyLjUgLTQyLjV0LTM2LjUgLTE3LjVxLTE3IDAgLTMzIDhsLTQ2NSAyMzNxLTIxIDEwIC0zNS41IDMzLjV0LTE0LjUgNDYuNXYxMTQwcTAgMjAgMTAgMzR0MjkgMTRxMTQgMCA0NCAtMTVsNTExIC0yNTZxMyAtMyAzIC01ek02NjEgMTAxNGw1MzQgLTg2NmwtNTM0IDI2NnY2MDB6TTE3OTIgOTk2di0xMDU0cTAgLTI1IC0xNCAtNDAuNXQtMzggLTE1LjV0LTQ3IDEzbC00NDEgMjIwek0xNzg5IDExMTYNCnEwIC0zIC0yNTYuNSAtNDE5LjV0LTMwMC41IC00ODcuNWwtMzkwIDYzNGwzMjQgNTI3cTE3IDI4IDUyIDI4cTE0IDAgMjYgLTZsNTQxIC0yNzBxNCAtMiA0IC02eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzUzNiIgdW5pY29kZT0iJiN4ZjIzYjsiIA0KZD0iTTgwOSA1MzJsMjY2IDQ5OWgtMTEybC0xNTcgLTMxMnEtMjQgLTQ4IC00NCAtOTJsLTQyIDkybC0xNTUgMzEyaC0xMjBsMjYzIC00OTN2LTMyNGgxMDF2MzE4ek0xNTM2IDE0MDh2LTE1MzZoLTE1MzZ2MTUzNmgxNTM2eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzUzNyIgdW5pY29kZT0iJiN4ZjIzYzsiIGhvcml6LWFkdi14PSIyMjk2IiANCmQ9Ik00NzggLTEzOXEtOCAtMTYgLTI3IC0zNC41dC0zNyAtMjUuNXEtMjUgLTkgLTUxLjUgMy41dC0yOC41IDMxLjVxLTEgMjIgNDAgNTV0NjggMzhxMjMgNCAzNCAtMjEuNXQyIC00Ni41ek0xODE5IC0xMzlxNyAtMTYgMjYgLTM0LjV0MzggLTI1LjVxMjUgLTkgNTEuNSAzLjV0MjcuNSAzMS41cTIgMjIgLTM5LjUgNTV0LTY4LjUgMzhxLTIyIDQgLTMzIC0yMS41dC0yIC00Ni41ek0xODY3IC0zMHExMyAtMjcgNTYuNSAtNTkuNXQ3Ny41IC00MS41DQpxNDUgLTEzIDgyIDQuNXQzNyA1MC41cTAgNDYgLTY3LjUgMTAwLjV0LTExNS41IDU5LjVxLTQwIDUgLTYzLjUgLTM3LjV0LTYuNSAtNzYuNXpNNDI4IC0zMHEtMTMgLTI3IC01NiAtNTkuNXQtNzcgLTQxLjVxLTQ1IC0xMyAtODIgNC41dC0zNyA1MC41cTAgNDYgNjcuNSAxMDAuNXQxMTUuNSA1OS41cTQwIDUgNjMgLTM3LjV0NiAtNzYuNXpNMTE1OCAxMDk0aDFxLTQxIDAgLTc2IC0xNXEyNyAtOCA0NCAtMzAuNXQxNyAtNDkuNQ0KcTAgLTM1IC0yNyAtNjB0LTY1IC0yNXEtNTIgMCAtODAgNDNxLTUgLTIzIC01IC00MnEwIC03NCA1NiAtMTI2LjV0MTM1IC01Mi41cTgwIDAgMTM2IDUyLjV0NTYgMTI2LjV0LTU2IDEyNi41dC0xMzYgNTIuNXpNMTQ2MiAxMzEycS05OSAxMDkgLTIyMC41IDEzMS41dC0yNDUuNSAtNDQuNXEyNyA2MCA4Mi41IDk2LjV0MTE4IDM5LjV0MTIxLjUgLTE3dDk5LjUgLTc0LjV0NDQuNSAtMTMxLjV6TTIyMTIgNzNxOCAtMTEgLTExIC00Mg0KcTcgLTIzIDcgLTQwcTEgLTU2IC00NC41IC0xMTIuNXQtMTA5LjUgLTkxLjV0LTExOCAtMzdxLTQ4IC0yIC05MiAyMS41dC02NiA2NS41cS02ODcgLTI1IC0xMjU5IDBxLTIzIC00MSAtNjYuNSAtNjV0LTkyLjUgLTIycS04NiAzIC0xNzkuNSA4MC41dC05Mi41IDE2MC41cTIgMjIgNyA0MHEtMTkgMzEgLTExIDQycTYgMTAgMzEgMXExNCAyMiA0MSA1MXEtNyAyOSAyIDM4cTExIDEwIDM5IC00cTI5IDIwIDU5IDM0cTAgMjkgMTMgMzcNCnEyMyAxMiA1MSAtMTZxMzUgNSA2MSAtMnExOCAtNCAzOCAtMTl2NzNxLTExIDAgLTE4IDJxLTUzIDEwIC05NyA0NC41dC01NSA4Ny41cS05IDM4IDAgODFxMTUgNjIgOTMgOTVxMiAxNyAxOSAzNS41dDM2IDIzLjV0MzMgLTcuNXQxOSAtMzAuNWgxM3E0NiAtNSA2MCAtMjNxMyAtMyA1IC03cTEwIDEgMzAuNSAzLjV0MzAuNSAzLjVxLTE1IDExIC0zMCAxN3EtMjMgNDAgLTkxIDQzcTAgNiAxIDEwcS02MiAyIC0xMTguNSAxOC41dC04NC41IDQ3LjUNCnEtMzIgMzYgLTQyLjUgOTJ0LTIuNSAxMTJxMTYgMTI2IDkwIDE3OXEyMyAxNiA1MiA0LjV0MzIgLTQwLjVxMCAtMSAxLjUgLTE0dDIuNSAtMjF0MyAtMjB0NS41IC0xOXQ4LjUgLTEwcTI3IC0xNCA3NiAtMTJxNDggNDYgOTggNzRxLTQwIDQgLTE2MiAtMTRsNDcgNDZxNjEgNTggMTYzIDExMXExNDUgNzMgMjgyIDg2cS0yMCA4IC00MSAxNS41dC00NyAxNHQtNDIuNSAxMC41dC00Ny41IDExdC00MyAxMHE1OTUgMTI2IDkwNCAtMTM5DQpxOTggLTg0IDE1OCAtMjIycTg1IC0xMCAxMjEgOWgxcTUgMyA4LjUgMTB0NS41IDE5dDMgMTkuNXQzIDIxLjVsMSAxNHEzIDI4IDMyIDQwdDUyIC01cTczIC01MiA5MSAtMTc4cTcgLTU3IC0zLjUgLTExM3QtNDIuNSAtOTFxLTI4IC0zMiAtODMuNSAtNDguNXQtMTE1LjUgLTE4LjV2LTEwcS03MSAtMiAtOTUgLTQzcS0xNCAtNSAtMzEgLTE3cTExIC0xIDMyIC0zLjV0MzAgLTMuNXExIDUgNSA4cTE2IDE4IDYwIDIzaDEzcTUgMTggMTkgMzB0MzMgOA0KdDM2IC0yM3QxOSAtMzZxNzkgLTMyIDkzIC05NXE5IC00MCAxIC04MXEtMTIgLTUzIC01NiAtODh0LTk3IC00NHEtMTAgLTIgLTE3IC0ycTAgLTQ5IC0xIC03M3EyMCAxNSAzOCAxOXEyNiA3IDYxIDJxMjggMjggNTEgMTZxMTQgLTkgMTQgLTM3cTMzIC0xNiA1OSAtMzRxMjcgMTMgMzggNHExMCAtMTAgMiAtMzhxMjggLTMwIDQxIC01MXEyMyA4IDMxIC0xek0xOTM3IDEwMjVxMCAtMjkgLTkgLTU0cTgyIC0zMiAxMTIgLTEzMg0KcTQgMzcgLTkuNSA5OC41dC00MS41IDkwLjVxLTIwIDE5IC0zNiAxN3QtMTYgLTIwek0xODU5IDkyNXEzNSAtNDIgNDcuNSAtMTA4LjV0LTAuNSAtMTI0LjVxNjcgMTMgOTcgNDVxMTMgMTQgMTggMjhxLTMgNjQgLTMxIDExNC41dC03OSA2Ni41cS0xNSAtMTUgLTUyIC0yMXpNMTgyMiA5MjFxLTMwIDAgLTQ0IDFxNDIgLTExNSA1MyAtMjM5cTIxIDAgNDMgM3ExNiA2OCAxIDEzNXQtNTMgMTAwek0yNTggODM5cTMwIDEwMCAxMTIgMTMyDQpxLTkgMjUgLTkgNTRxMCAxOCAtMTYuNSAyMHQtMzUuNSAtMTdxLTI4IC0yOSAtNDEuNSAtOTAuNXQtOS41IC05OC41ek0yOTQgNzM3cTI5IC0zMSA5NyAtNDVxLTEzIDU4IC0wLjUgMTI0LjV0NDcuNSAxMDguNXYwcS0zNyA2IC01MiAyMXEtNTEgLTE2IC03OC41IC02NnQtMzEuNSAtMTE1cTkgLTE3IDE4IC0yOHpNNDcxIDY4M3ExNCAxMjQgNzMgMjM1cS0xOSAtNCAtNTUgLTE4bC00NSAtMTl2MXEtNDYgLTg5IC0yMCAtMTk2cTI1IC0zIDQ3IC0zeg0KTTE0MzQgNjQ0cTggLTM4IDE2LjUgLTEwOC41dDExLjUgLTg5LjVxMyAtMTggOS41IC0yMS41dDIzLjUgNC41cTQwIDIwIDYyIDg1LjV0MjMgMTI1LjVxLTI0IDIgLTE0NiA0ek0xMTUyIDEyODVxLTExNiAwIC0xOTkgLTgyLjV0LTgzIC0xOTguNXEwIC0xMTcgODMgLTE5OS41dDE5OSAtODIuNXQxOTkgODIuNXQ4MyAxOTkuNXEwIDExNiAtODMgMTk4LjV0LTE5OSA4Mi41ek0xMzgwIDY0NnEtMTA1IDIgLTIxMSAwdjFxLTEgLTI3IDIuNSAtODYNCnQxMy41IC02NnEyOSAtMTQgOTMuNSAtMTQuNXQ5NS41IDEwLjVxOSAzIDExIDM5dC0wLjUgNjkuNXQtNC41IDQ2LjV6TTExMTIgNDQ3cTggNCA5LjUgNDh0LTAuNSA4OHQtNCA2M3YxcS0yMTIgLTMgLTIxNCAtM3EtNCAtMjAgLTcgLTYydDAgLTgzdDE0IC00NnEzNCAtMTUgMTAxIC0xNnQxMDEgMTB6TTcxOCA2MzZxLTE2IC01OSA0LjUgLTExOC41dDc3LjUgLTg0LjVxMTUgLTggMjQgLTV0MTIgMjFxMyAxNiA4IDkwdDEwIDEwMw0KcS02OSAtMiAtMTM2IC02ek01OTEgNTEwcTMgLTIzIC0zNCAtMzZxMTMyIC0xNDEgMjcxLjUgLTI0MHQzMDUuNSAtMTU0cTE3MiA0OSAzMTAuNSAxNDZ0MjkzLjUgMjUwcS0zMyAxMyAtMzAgMzRxMCAyIDAuNSAzLjV0MS41IDN0MSAyLjV2MXYtMXEtMTcgMiAtNTAgNS41dC00OCA0LjVxLTI2IC05MCAtODIgLTEzMnEtNTEgLTM4IC04MiAxcS01IDYgLTkgMTRxLTcgMTMgLTE3IDYycS0yIC01IC01IC05dC03LjUgLTd0LTggLTUuNXQtOS41IC00DQpsLTEwIC0yLjV0LTEyIC0ybC0xMiAtMS41dC0xMy41IC0xdC0xMy41IC0wLjVxLTEwNiAtOSAtMTYzIDExcS00IC0xNyAtMTAgLTI2LjV0LTIxIC0xNXQtMjMgLTd0LTM2IC0zLjVxLTYgLTEgLTkgLTFxLTE3OSAtMTcgLTIwMyA0MHEtMiAtNjMgLTU2IC01NHEtNDcgOCAtOTEgNTRxLTEyIDEzIC0yMCAyNnEtMTcgMjkgLTI2IDY1cS01OCAtNiAtODcgLTEwcTEgLTIgNCAtMTB6TTUwNyAtMTE4cTMgMTQgMyAzMHEtMTcgNzEgLTUxIDEzMA0KdC03MyA3MHEtNDEgMTIgLTEwMS41IC0xNC41dC0xMDQuNSAtODB0LTM5IC0xMDcuNXEzNSAtNTMgMTAwIC05M3QxMTkgLTQycTUxIC0yIDk0IDI4dDUzIDc5ek01MTAgNTNxMjMgLTYzIDI3IC0xMTlxMTk1IDExMyAzOTIgMTc0cS05OCA1MiAtMTgwLjUgMTIwdC0xNzkuNSAxNjVxLTYgLTQgLTI5IC0xM3EwIC0xIC0xIC00dC0xIC01cTMxIC0xOCAyMiAtMzdxLTEyIC0yMyAtNTYgLTM0cS0xMCAtMTMgLTI5IC0yNGgtMXEtMiAtODMgMSAtMTUwDQpxMTkgLTM0IDM1IC03M3pNNTc5IC0xMTNxNTMyIC0yMSAxMTQ1IDBxLTI1NCAxNDcgLTQyOCAxOTZxLTc2IC0zNSAtMTU2IC01N3EtOCAtMyAtMTYgMHEtNjUgMjEgLTEyOSA0OXEtMjA4IC02MCAtNDE2IC0xODhoLTF2LTFxMSAwIDEgMXpNMTc2MyAtNjdxNCA1NCAyOCAxMjBxMTQgMzggMzMgNzFsLTEgLTFxMyA3NyAzIDE1M3EtMTUgOCAtMzAgMjVxLTQyIDkgLTU2IDMzcS05IDIwIDIyIDM4cS0yIDQgLTIgOXEtMTYgNCAtMjggMTINCnEtMjA0IC0xOTAgLTM4MyAtMjg0cTE5OCAtNTkgNDE0IC0xNzZ6TTIxNTUgLTkwcTUgNTQgLTM5IDEwNy41dC0xMDQgODB0LTEwMiAxNC41cS0zOCAtMTEgLTcyLjUgLTcwLjV0LTUxLjUgLTEyOS41cTAgLTE2IDMgLTMwcTEwIC00OSA1MyAtNzl0OTQgLTI4cTU0IDIgMTE5IDQydDEwMCA5M3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81MzgiIHVuaWNvZGU9IiYjeGYyM2Q7IiBob3Jpei1hZHYteD0iMjMwNCIgDQpkPSJNMTUyNCAtMjVxMCAtNjggLTQ4IC0xMTZ0LTExNiAtNDh0LTExNi41IDQ4dC00OC41IDExNnQ0OC41IDExNi41dDExNi41IDQ4LjV0MTE2IC00OC41dDQ4IC0xMTYuNXpNNzc1IC0yNXEwIC02OCAtNDguNSAtMTE2dC0xMTYuNSAtNDh0LTExNiA0OHQtNDggMTE2dDQ4IDExNi41dDExNiA0OC41dDExNi41IC00OC41dDQ4LjUgLTExNi41ek0wIDE0NjlxNTcgLTYwIDExMC41IC0xMDQuNXQxMjEgLTgydDEzNiAtNjN0MTY2IC00NS41DQp0MjAwIC0zMS41dDI1MCAtMTguNXQzMDQgLTkuNXQzNzIuNSAtMi41cTEzOSAwIDI0NC41IC01dDE4MSAtMTYuNXQxMjQgLTI3LjV0NzEgLTM5LjV0MjQgLTUxLjV0LTE5LjUgLTY0dC01Ni41IC03Ni41dC04OS41IC05MXQtMTE2IC0xMDQuNXQtMTM5IC0xMTlxLTE4NSAtMTU3IC0yODYgLTI0N3EyOSA1MSA3Ni41IDEwOXQ5NCAxMDUuNXQ5NC41IDk4LjV0ODMgOTEuNXQ1NCA4MC41dDEzIDcwdC00NS41IDU1LjV0LTExNi41IDQxdC0yMDQgMjMuNQ0KdC0zMDQgNXEtMTY4IC0yIC0zMTQgNnQtMjU2IDIzdC0yMDQuNSA0MXQtMTU5LjUgNTEuNXQtMTIyLjUgNjIuNXQtOTEuNSA2Ni41dC02OCA3MS41dC01MC41IDY5LjV0LTQwIDY4dC0zNi41IDU5LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTM5IiB1bmljb2RlPSImI3hmMjNlOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTg5NiAxNDcycS0xNjkgMCAtMzIzIC02NnQtMjY1LjUgLTE3Ny41dC0xNzcuNSAtMjY1LjV0LTY2IC0zMjN0NjYgLTMyM3QxNzcuNSAtMjY1LjV0MjY1LjUgLTE3Ny41dDMyMyAtNjZ0MzIzIDY2dDI2NS41IDE3Ny41dDE3Ny41IDI2NS41dDY2IDMyM3QtNjYgMzIzdC0xNzcuNSAyNjUuNXQtMjY1LjUgMTc3LjV0LTMyMyA2NnpNODk2IDE1MzZxMTgyIDAgMzQ4IC03MXQyODYgLTE5MXQxOTEgLTI4NnQ3MSAtMzQ4dC03MSAtMzQ4DQp0LTE5MSAtMjg2dC0yODYgLTE5MXQtMzQ4IC03MXQtMzQ4IDcxdC0yODYgMTkxdC0xOTEgMjg2dC03MSAzNDh0NzEgMzQ4dDE5MSAyODZ0Mjg2IDE5MXQzNDggNzF6TTQ5NiA3MDRxMTYgMCAxNiAtMTZ2LTQ4MHEwIC0xNiAtMTYgLTE2aC0zMnEtMTYgMCAtMTYgMTZ2NDgwcTAgMTYgMTYgMTZoMzJ6TTg5NiA2NDBxNTMgMCA5MC41IC0zNy41dDM3LjUgLTkwLjVxMCAtMzUgLTE3LjUgLTY0dC00Ni41IC00NnYtMTE0cTAgLTE0IC05IC0yMw0KdC0yMyAtOWgtNjRxLTE0IDAgLTIzIDl0LTkgMjN2MTE0cS0yOSAxNyAtNDYuNSA0NnQtMTcuNSA2NHEwIDUzIDM3LjUgOTAuNXQ5MC41IDM3LjV6TTg5NiAxNDA4cTIwOSAwIDM4NS41IC0xMDN0Mjc5LjUgLTI3OS41dDEwMyAtMzg1LjV0LTEwMyAtMzg1LjV0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzdC0yNzkuNSAyNzkuNXQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN6TTU0NCA5Mjh2LTk2DQpxMCAtMTQgOSAtMjN0MjMgLTloNjRxMTQgMCAyMyA5dDkgMjN2OTZxMCA5MyA2NS41IDE1OC41dDE1OC41IDY1LjV0MTU4LjUgLTY1LjV0NjUuNSAtMTU4LjV2LTk2cTAgLTE0IDkgLTIzdDIzIC05aDY0cTE0IDAgMjMgOXQ5IDIzdjk2cTAgMTQ2IC0xMDMgMjQ5dC0yNDkgMTAzdC0yNDkgLTEwM3QtMTAzIC0yNDl6TTE0MDggMTkydjUxMnEwIDI2IC0xOSA0NXQtNDUgMTloLTg5NnEtMjYgMCAtNDUgLTE5dC0xOSAtNDV2LTUxMg0KcTAgLTI2IDE5IC00NXQ0NSAtMTloODk2cTI2IDAgNDUgMTl0MTkgNDV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTQwIiB1bmljb2RlPSImI3hmMjQwOyIgaG9yaXotYWR2LXg9IjIzMDQiIA0KZD0iTTE5MjAgMTAyNHYtNzY4aC0xNjY0djc2OGgxNjY0ek0yMDQ4IDQ0OGgxMjh2Mzg0aC0xMjh2Mjg4cTAgMTQgLTkgMjN0LTIzIDloLTE4NTZxLTE0IDAgLTIzIC05dC05IC0yM3YtOTYwcTAgLTE0IDkgLTIzdDIzIC05aDE4NTZxMTQgMCAyMyA5dDkgMjN2Mjg4ek0yMzA0IDgzMnYtMzg0cTAgLTUzIC0zNy41IC05MC41dC05MC41IC0zNy41di0xNjBxMCAtNjYgLTQ3IC0xMTN0LTExMyAtNDdoLTE4NTZxLTY2IDAgLTExMyA0N3QtNDcgMTEzDQp2OTYwcTAgNjYgNDcgMTEzdDExMyA0N2gxODU2cTY2IDAgMTEzIC00N3Q0NyAtMTEzdi0xNjBxNTMgMCA5MC41IC0zNy41dDM3LjUgLTkwLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTQxIiB1bmljb2RlPSImI3hmMjQxOyIgaG9yaXotYWR2LXg9IjIzMDQiIA0KZD0iTTI1NiAyNTZ2NzY4aDEyODB2LTc2OGgtMTI4MHpNMjE3NiA5NjBxNTMgMCA5MC41IC0zNy41dDM3LjUgLTkwLjV2LTM4NHEwIC01MyAtMzcuNSAtOTAuNXQtOTAuNSAtMzcuNXYtMTYwcTAgLTY2IC00NyAtMTEzdC0xMTMgLTQ3aC0xODU2cS02NiAwIC0xMTMgNDd0LTQ3IDExM3Y5NjBxMCA2NiA0NyAxMTN0MTEzIDQ3aDE4NTZxNjYgMCAxMTMgLTQ3dDQ3IC0xMTN2LTE2MHpNMjE3NiA0NDh2Mzg0aC0xMjh2Mjg4cTAgMTQgLTkgMjN0LTIzIDkNCmgtMTg1NnEtMTQgMCAtMjMgLTl0LTkgLTIzdi05NjBxMCAtMTQgOSAtMjN0MjMgLTloMTg1NnExNCAwIDIzIDl0OSAyM3YyODhoMTI4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzU0MiIgdW5pY29kZT0iJiN4ZjI0MjsiIGhvcml6LWFkdi14PSIyMzA0IiANCmQ9Ik0yNTYgMjU2djc2OGg4OTZ2LTc2OGgtODk2ek0yMTc2IDk2MHE1MyAwIDkwLjUgLTM3LjV0MzcuNSAtOTAuNXYtMzg0cTAgLTUzIC0zNy41IC05MC41dC05MC41IC0zNy41di0xNjBxMCAtNjYgLTQ3IC0xMTN0LTExMyAtNDdoLTE4NTZxLTY2IDAgLTExMyA0N3QtNDcgMTEzdjk2MHEwIDY2IDQ3IDExM3QxMTMgNDdoMTg1NnE2NiAwIDExMyAtNDd0NDcgLTExM3YtMTYwek0yMTc2IDQ0OHYzODRoLTEyOHYyODhxMCAxNCAtOSAyM3QtMjMgOQ0KaC0xODU2cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTk2MHEwIC0xNCA5IC0yM3QyMyAtOWgxODU2cTE0IDAgMjMgOXQ5IDIzdjI4OGgxMjh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTQzIiB1bmljb2RlPSImI3hmMjQzOyIgaG9yaXotYWR2LXg9IjIzMDQiIA0KZD0iTTI1NiAyNTZ2NzY4aDUxMnYtNzY4aC01MTJ6TTIxNzYgOTYwcTUzIDAgOTAuNSAtMzcuNXQzNy41IC05MC41di0zODRxMCAtNTMgLTM3LjUgLTkwLjV0LTkwLjUgLTM3LjV2LTE2MHEwIC02NiAtNDcgLTExM3QtMTEzIC00N2gtMTg1NnEtNjYgMCAtMTEzIDQ3dC00NyAxMTN2OTYwcTAgNjYgNDcgMTEzdDExMyA0N2gxODU2cTY2IDAgMTEzIC00N3Q0NyAtMTEzdi0xNjB6TTIxNzYgNDQ4djM4NGgtMTI4djI4OHEwIDE0IC05IDIzdC0yMyA5DQpoLTE4NTZxLTE0IDAgLTIzIC05dC05IC0yM3YtOTYwcTAgLTE0IDkgLTIzdDIzIC05aDE4NTZxMTQgMCAyMyA5dDkgMjN2Mjg4aDEyOHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81NDQiIHVuaWNvZGU9IiYjeGYyNDQ7IiBob3Jpei1hZHYteD0iMjMwNCIgDQpkPSJNMjE3NiA5NjBxNTMgMCA5MC41IC0zNy41dDM3LjUgLTkwLjV2LTM4NHEwIC01MyAtMzcuNSAtOTAuNXQtOTAuNSAtMzcuNXYtMTYwcTAgLTY2IC00NyAtMTEzdC0xMTMgLTQ3aC0xODU2cS02NiAwIC0xMTMgNDd0LTQ3IDExM3Y5NjBxMCA2NiA0NyAxMTN0MTEzIDQ3aDE4NTZxNjYgMCAxMTMgLTQ3dDQ3IC0xMTN2LTE2MHpNMjE3NiA0NDh2Mzg0aC0xMjh2Mjg4cTAgMTQgLTkgMjN0LTIzIDloLTE4NTZxLTE0IDAgLTIzIC05dC05IC0yMw0Kdi05NjBxMCAtMTQgOSAtMjN0MjMgLTloMTg1NnExNCAwIDIzIDl0OSAyM3YyODhoMTI4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzU0NSIgdW5pY29kZT0iJiN4ZjI0NTsiIGhvcml6LWFkdi14PSIxMjgwIiANCmQ9Ik0xMTMzIDQ5M3EzMSAtMzAgMTQgLTY5cS0xNyAtNDAgLTU5IC00MGgtMzgybDIwMSAtNDc2cTEwIC0yNSAwIC00OXQtMzQgLTM1bC0xNzcgLTc1cS0yNSAtMTAgLTQ5IDB0LTM1IDM0bC0xOTEgNDUybC0zMTIgLTMxMnEtMTkgLTE5IC00NSAtMTlxLTEyIDAgLTI0IDVxLTQwIDE3IC00MCA1OXYxNTA0cTAgNDIgNDAgNTlxMTIgNSAyNCA1cTI3IDAgNDUgLTE5eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzU0NiIgdW5pY29kZT0iJiN4ZjI0NjsiIGhvcml6LWFkdi14PSIxMDI0IiANCmQ9Ik04MzIgMTQwOHEtMzIwIDAgLTMyMCAtMjI0di00MTZoMTI4di0xMjhoLTEyOHYtNTQ0cTAgLTIyNCAzMjAgLTIyNGg2NHYtMTI4aC02NHEtMjcyIDAgLTM4NCAxNDZxLTExMiAtMTQ2IC0zODQgLTE0NmgtNjR2MTI4aDY0cTMyMCAwIDMyMCAyMjR2NTQ0aC0xMjh2MTI4aDEyOHY0MTZxMCAyMjQgLTMyMCAyMjRoLTY0djEyOGg2NHEyNzIgMCAzODQgLTE0NnExMTIgMTQ2IDM4NCAxNDZoNjR2LTEyOGgtNjR6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTQ3IiB1bmljb2RlPSImI3hmMjQ3OyIgaG9yaXotYWR2LXg9IjIwNDgiIA0KZD0iTTIwNDggMTE1MmgtMTI4di0xMDI0aDEyOHYtMzg0aC0zODR2MTI4aC0xMjgwdi0xMjhoLTM4NHYzODRoMTI4djEwMjRoLTEyOHYzODRoMzg0di0xMjhoMTI4MHYxMjhoMzg0di0zODR6TTE3OTIgMTQwOHYtMTI4aDEyOHYxMjhoLTEyOHpNMTI4IDE0MDh2LTEyOGgxMjh2MTI4aC0xMjh6TTI1NiAtMTI4djEyOGgtMTI4di0xMjhoMTI4ek0xNjY0IDB2MTI4aDEyOHYxMDI0aC0xMjh2MTI4aC0xMjgwdi0xMjhoLTEyOHYtMTAyNGgxMjh2LTEyOA0KaDEyODB6TTE5MjAgLTEyOHYxMjhoLTEyOHYtMTI4aDEyOHpNMTI4MCA4OTZoMzg0di03NjhoLTg5NnYyNTZoLTM4NHY3NjhoODk2di0yNTZ6TTUxMiA1MTJoNjQwdjUxMmgtNjQwdi01MTJ6TTE1MzYgMjU2djUxMmgtMjU2di0zODRoLTM4NHYtMTI4aDY0MHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81NDgiIHVuaWNvZGU9IiYjeGYyNDg7IiBob3Jpei1hZHYteD0iMjMwNCIgDQpkPSJNMjMwNCA3NjhoLTEyOHYtNjQwaDEyOHYtMzg0aC0zODR2MTI4aC04OTZ2LTEyOGgtMzg0djM4NGgxMjh2MTI4aC0zODR2LTEyOGgtMzg0djM4NGgxMjh2NjQwaC0xMjh2Mzg0aDM4NHYtMTI4aDg5NnYxMjhoMzg0di0zODRoLTEyOHYtMTI4aDM4NHYxMjhoMzg0di0zODR6TTIwNDggMTAyNHYtMTI4aDEyOHYxMjhoLTEyOHpNMTQwOCAxNDA4di0xMjhoMTI4djEyOGgtMTI4ek0xMjggMTQwOHYtMTI4aDEyOHYxMjhoLTEyOHpNMjU2IDI1Ng0KdjEyOGgtMTI4di0xMjhoMTI4ek0xNTM2IDM4NGgtMTI4di0xMjhoMTI4djEyOHpNMzg0IDM4NGg4OTZ2MTI4aDEyOHY2NDBoLTEyOHYxMjhoLTg5NnYtMTI4aC0xMjh2LTY0MGgxMjh2LTEyOHpNODk2IC0xMjh2MTI4aC0xMjh2LTEyOGgxMjh6TTIxNzYgLTEyOHYxMjhoLTEyOHYtMTI4aDEyOHpNMjA0OCAxMjh2NjQwaC0xMjh2MTI4aC0zODR2LTM4NGgxMjh2LTM4NGgtMzg0djEyOGgtMzg0di0xMjhoMTI4di0xMjhoODk2djEyOGgxMjh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTQ5IiB1bmljb2RlPSImI3hmMjQ5OyIgDQpkPSJNMTAyNCAyODh2LTQxNmgtOTI4cS00MCAwIC02OCAyOHQtMjggNjh2MTM0NHEwIDQwIDI4IDY4dDY4IDI4aDEzNDRxNDAgMCA2OCAtMjh0MjggLTY4di05MjhoLTQxNnEtNDAgMCAtNjggLTI4dC0yOCAtNjh6TTExNTIgMjU2aDM4MXEtMTUgLTgyIC02NSAtMTMybC0xODQgLTE4NHEtNTAgLTUwIC0xMzIgLTY1djM4MXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81NTAiIHVuaWNvZGU9IiYjeGYyNGE7IiANCmQ9Ik0xNDAwIDI1NmgtMjQ4di0yNDhxMjkgMTAgNDEgMjJsMTg1IDE4NXExMiAxMiAyMiA0MXpNMTEyMCAzODRoMjg4djg5NmgtMTI4MHYtMTI4MGg4OTZ2Mjg4cTAgNDAgMjggNjh0NjggMjh6TTE1MzYgMTMxMnYtMTAyNHEwIC00MCAtMjAgLTg4dC00OCAtNzZsLTE4NCAtMTg0cS0yOCAtMjggLTc2IC00OHQtODggLTIwaC0xMDI0cS00MCAwIC02OCAyOHQtMjggNjh2MTM0NHEwIDQwIDI4IDY4dDY4IDI4aDEzNDRxNDAgMCA2OCAtMjh0MjggLTY4DQp6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTUxIiB1bmljb2RlPSImI3hmMjRiOyIgaG9yaXotYWR2LXg9IjIzMDQiIA0KZD0iTTE5NTEgNTM4cTAgLTI2IC0xNS41IC00NC41dC0zOC41IC0yMy41cS04IC0yIC0xOCAtMmgtMTUzdjE0MGgxNTNxMTAgMCAxOCAtMnEyMyAtNSAzOC41IC0yMy41dDE1LjUgLTQ0LjV6TTE5MzMgNzUxcTAgLTI1IC0xNSAtNDJ0LTM4IC0yMXEtMyAtMSAtMTUgLTFoLTEzOXYxMjloMTM5cTMgMCA4LjUgLTAuNXQ2LjUgLTAuNXEyMyAtNCAzOCAtMjEuNXQxNSAtNDIuNXpNNzI4IDU4N3YzMDhoLTIyOHYtMzA4cTAgLTU4IC0zOCAtOTQuNQ0KdC0xMDUgLTM2LjVxLTEwOCAwIC0yMjkgNTl2LTExMnE1MyAtMTUgMTIxIC0yM3QxMDkgLTlsNDIgLTFxMzI4IDAgMzI4IDIxN3pNMTQ0MiA0MDN2MTEzcS05OSAtNTIgLTIwMCAtNTlxLTEwOCAtOCAtMTY5IDQxdC02MSAxNDJ0NjEgMTQydDE2OSA0MXExMDEgLTcgMjAwIC01OHYxMTJxLTQ4IDEyIC0xMDAgMTkuNXQtODAgOS41bC0yOCAycS0xMjcgNiAtMjE4LjUgLTE0dC0xNDAuNSAtNjB0LTcxIC04OHQtMjIgLTEwNnQyMiAtMTA2dDcxIC04OA0KdDE0MC41IC02MHQyMTguNSAtMTRxMTAxIDQgMjA4IDMxek0yMTc2IDUxOHEwIDU0IC00MyA4OC41dC0xMDkgMzkuNXYzcTU3IDggODkgNDEuNXQzMiA3OS41cTAgNTUgLTQxIDg4dC0xMDcgMzZxLTMgMCAtMTIgMC41dC0xNCAwLjVoLTQ1NXYtNTEwaDQ5MXE3NCAwIDEyMS41IDM2LjV0NDcuNSA5Ni41ek0yMzA0IDEyODB2LTEyODBxMCAtNTIgLTM4IC05MHQtOTAgLTM4aC0yMDQ4cS01MiAwIC05MCAzOHQtMzggOTB2MTI4MHEwIDUyIDM4IDkwDQp0OTAgMzhoMjA0OHE1MiAwIDkwIC0zOHQzOCAtOTB6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTUyIiB1bmljb2RlPSImI3hmMjRjOyIgaG9yaXotYWR2LXg9IjIzMDQiIA0KZD0iTTg1OCAyOTV2NjkzcS0xMDYgLTQxIC0xNzIgLTEzNS41dC02NiAtMjExLjV0NjYgLTIxMS41dDE3MiAtMTM0LjV6TTEzNjIgNjQxcTAgMTE3IC02NiAyMTEuNXQtMTcyIDEzNS41di02OTRxMTA2IDQxIDE3MiAxMzUuNXQ2NiAyMTEuNXpNMTU3NyA2NDFxMCAtMTU5IC03OC41IC0yOTR0LTIxMy41IC0yMTMuNXQtMjk0IC03OC41cS0xMTkgMCAtMjI3LjUgNDYuNXQtMTg3IDEyNXQtMTI1IDE4N3QtNDYuNSAyMjcuNXEwIDE1OSA3OC41IDI5NA0KdDIxMy41IDIxMy41dDI5NCA3OC41dDI5NCAtNzguNXQyMTMuNSAtMjEzLjV0NzguNSAtMjk0ek0xOTYwIDYzNHEwIDEzOSAtNTUuNSAyNjEuNXQtMTQ3LjUgMjA1LjV0LTIxMy41IDEzMXQtMjUyLjUgNDhoLTMwMXEtMTc2IDAgLTMyMy41IC04MXQtMjM1IC0yMzB0LTg3LjUgLTMzNXEwIC0xNzEgODcgLTMxNy41dDIzNiAtMjMxLjV0MzIzIC04NWgzMDFxMTI5IDAgMjUxLjUgNTAuNXQyMTQuNSAxMzV0MTQ3LjUgMjAyLjV0NTUuNSAyNDZ6DQpNMjMwNCAxMjgwdi0xMjgwcTAgLTUyIC0zOCAtOTB0LTkwIC0zOGgtMjA0OHEtNTIgMCAtOTAgMzh0LTM4IDkwdjEyODBxMCA1MiAzOCA5MHQ5MCAzOGgyMDQ4cTUyIDAgOTAgLTM4dDM4IC05MHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81NTMiIHVuaWNvZGU9IiYjeGYyNGQ7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTY2NCAtOTZ2MTA4OHEwIDEzIC05LjUgMjIuNXQtMjIuNSA5LjVoLTEwODhxLTEzIDAgLTIyLjUgLTkuNXQtOS41IC0yMi41di0xMDg4cTAgLTEzIDkuNSAtMjIuNXQyMi41IC05LjVoMTA4OHExMyAwIDIyLjUgOS41dDkuNSAyMi41ek0xNzkyIDk5MnYtMTA4OHEwIC02NiAtNDcgLTExM3QtMTEzIC00N2gtMTA4OHEtNjYgMCAtMTEzIDQ3dC00NyAxMTN2MTA4OHEwIDY2IDQ3IDExM3QxMTMgNDdoMTA4OHE2NiAwIDExMyAtNDd0NDcgLTExMw0Kek0xNDA4IDEzNzZ2LTE2MGgtMTI4djE2MHEwIDEzIC05LjUgMjIuNXQtMjIuNSA5LjVoLTEwODhxLTEzIDAgLTIyLjUgLTkuNXQtOS41IC0yMi41di0xMDg4cTAgLTEzIDkuNSAtMjIuNXQyMi41IC05LjVoMTYwdi0xMjhoLTE2MHEtNjYgMCAtMTEzIDQ3dC00NyAxMTN2MTA4OHEwIDY2IDQ3IDExM3QxMTMgNDdoMTA4OHE2NiAwIDExMyAtNDd0NDcgLTExM3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81NTQiIHVuaWNvZGU9IiYjeGYyNGU7IiBob3Jpei1hZHYteD0iMjMwNCIgDQpkPSJNMTcyOCAxMDg4bC0zODQgLTcwNGg3Njh6TTQ0OCAxMDg4bC0zODQgLTcwNGg3Njh6TTEyNjkgMTI4MHEtMTQgLTQwIC00NS41IC03MS41dC03MS41IC00NS41di0xMjkxaDYwOHExNCAwIDIzIC05dDkgLTIzdi02NHEwIC0xNCAtOSAtMjN0LTIzIC05aC0xMzQ0cS0xNCAwIC0yMyA5dC05IDIzdjY0cTAgMTQgOSAyM3QyMyA5aDYwOHYxMjkxcS00MCAxNCAtNzEuNSA0NS41dC00NS41IDcxLjVoLTQ5MXEtMTQgMCAtMjMgOXQtOSAyM3Y2NA0KcTAgMTQgOSAyM3QyMyA5aDQ5MXEyMSA1NyA3MCA5Mi41dDExMSAzNS41dDExMSAtMzUuNXQ3MCAtOTIuNWg0OTFxMTQgMCAyMyAtOXQ5IC0yM3YtNjRxMCAtMTQgLTkgLTIzdC0yMyAtOWgtNDkxek0xMDg4IDEyNjRxMzMgMCA1Ni41IDIzLjV0MjMuNSA1Ni41dC0yMy41IDU2LjV0LTU2LjUgMjMuNXQtNTYuNSAtMjMuNXQtMjMuNSAtNTYuNXQyMy41IC01Ni41dDU2LjUgLTIzLjV6TTIxNzYgMzg0cTAgLTczIC00Ni41IC0xMzF0LTExNy41IC05MQ0KdC0xNDQuNSAtNDkuNXQtMTM5LjUgLTE2LjV0LTEzOS41IDE2LjV0LTE0NC41IDQ5LjV0LTExNy41IDkxdC00Ni41IDEzMXEwIDExIDM1IDgxdDkyIDE3NC41dDEwNyAxOTUuNXQxMDIgMTg0dDU2IDEwMHExOCAzMyA1NiAzM3Q1NiAtMzNxNCAtNyA1NiAtMTAwdDEwMiAtMTg0dDEwNyAtMTk1LjV0OTIgLTE3NC41dDM1IC04MXpNODk2IDM4NHEwIC03MyAtNDYuNSAtMTMxdC0xMTcuNSAtOTF0LTE0NC41IC00OS41dC0xMzkuNSAtMTYuNQ0KdC0xMzkuNSAxNi41dC0xNDQuNSA0OS41dC0xMTcuNSA5MXQtNDYuNSAxMzFxMCAxMSAzNSA4MXQ5MiAxNzQuNXQxMDcgMTk1LjV0MTAyIDE4NHQ1NiAxMDBxMTggMzMgNTYgMzN0NTYgLTMzcTQgLTcgNTYgLTEwMHQxMDIgLTE4NHQxMDcgLTE5NS41dDkyIC0xNzQuNXQzNSAtODF6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTU1IiB1bmljb2RlPSImI3hmMjUwOyIgDQpkPSJNMTQwOCAxNDA4cTAgLTI2MSAtMTA2LjUgLTQ2MS41dC0yNjYuNSAtMzA2LjVxMTYwIC0xMDYgMjY2LjUgLTMwNi41dDEwNi41IC00NjEuNWg5NnExNCAwIDIzIC05dDkgLTIzdi02NHEwIC0xNCAtOSAtMjN0LTIzIC05aC0xNDcycS0xNCAwIC0yMyA5dC05IDIzdjY0cTAgMTQgOSAyM3QyMyA5aDk2cTAgMjYxIDEwNi41IDQ2MS41dDI2Ni41IDMwNi41cS0xNjAgMTA2IC0yNjYuNSAzMDYuNXQtMTA2LjUgNDYxLjVoLTk2cS0xNCAwIC0yMyA5DQp0LTkgMjN2NjRxMCAxNCA5IDIzdDIzIDloMTQ3MnExNCAwIDIzIC05dDkgLTIzdi02NHEwIC0xNCAtOSAtMjN0LTIzIC05aC05NnpNODc0IDcwMHE3NyAyOSAxNDkgOTIuNXQxMjkuNSAxNTIuNXQ5Mi41IDIxMHQzNSAyNTNoLTEwMjRxMCAtMTMyIDM1IC0yNTN0OTIuNSAtMjEwdDEyOS41IC0xNTIuNXQxNDkgLTkyLjVxMTkgLTcgMzAuNSAtMjMuNXQxMS41IC0zNi41dC0xMS41IC0zNi41dC0zMC41IC0yMy41cS03NyAtMjkgLTE0OSAtOTIuNQ0KdC0xMjkuNSAtMTUyLjV0LTkyLjUgLTIxMHQtMzUgLTI1M2gxMDI0cTAgMTMyIC0zNSAyNTN0LTkyLjUgMjEwdC0xMjkuNSAxNTIuNXQtMTQ5IDkyLjVxLTE5IDcgLTMwLjUgMjMuNXQtMTEuNSAzNi41dDExLjUgMzYuNXQzMC41IDIzLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTU2IiB1bmljb2RlPSImI3hmMjUxOyIgDQpkPSJNMTQwOCAxNDA4cTAgLTI2MSAtMTA2LjUgLTQ2MS41dC0yNjYuNSAtMzA2LjVxMTYwIC0xMDYgMjY2LjUgLTMwNi41dDEwNi41IC00NjEuNWg5NnExNCAwIDIzIC05dDkgLTIzdi02NHEwIC0xNCAtOSAtMjN0LTIzIC05aC0xNDcycS0xNCAwIC0yMyA5dC05IDIzdjY0cTAgMTQgOSAyM3QyMyA5aDk2cTAgMjYxIDEwNi41IDQ2MS41dDI2Ni41IDMwNi41cS0xNjAgMTA2IC0yNjYuNSAzMDYuNXQtMTA2LjUgNDYxLjVoLTk2cS0xNCAwIC0yMyA5DQp0LTkgMjN2NjRxMCAxNCA5IDIzdDIzIDloMTQ3MnExNCAwIDIzIC05dDkgLTIzdi02NHEwIC0xNCAtOSAtMjN0LTIzIC05aC05NnpNMTI4MCAxNDA4aC0xMDI0cTAgLTY2IDkgLTEyOGgxMDA2cTkgNjEgOSAxMjh6TTEyODAgLTEyOHEwIDEzMCAtMzQgMjQ5LjV0LTkwLjUgMjA4dC0xMjYuNSAxNTJ0LTE0NiA5NC41aC0yMzBxLTc2IC0zMSAtMTQ2IC05NC41dC0xMjYuNSAtMTUydC05MC41IC0yMDh0LTM0IC0yNDkuNWgxMDI0eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzU1NyIgdW5pY29kZT0iJiN4ZjI1MjsiIA0KZD0iTTE0MDggMTQwOHEwIC0yNjEgLTEwNi41IC00NjEuNXQtMjY2LjUgLTMwNi41cTE2MCAtMTA2IDI2Ni41IC0zMDYuNXQxMDYuNSAtNDYxLjVoOTZxMTQgMCAyMyAtOXQ5IC0yM3YtNjRxMCAtMTQgLTkgLTIzdC0yMyAtOWgtMTQ3MnEtMTQgMCAtMjMgOXQtOSAyM3Y2NHEwIDE0IDkgMjN0MjMgOWg5NnEwIDI2MSAxMDYuNSA0NjEuNXQyNjYuNSAzMDYuNXEtMTYwIDEwNiAtMjY2LjUgMzA2LjV0LTEwNi41IDQ2MS41aC05NnEtMTQgMCAtMjMgOQ0KdC05IDIzdjY0cTAgMTQgOSAyM3QyMyA5aDE0NzJxMTQgMCAyMyAtOXQ5IC0yM3YtNjRxMCAtMTQgLTkgLTIzdC0yMyAtOWgtOTZ6TTEyODAgMTQwOGgtMTAyNHEwIC0yMDYgODUgLTM4NGg4NTRxODUgMTc4IDg1IDM4NHpNMTIyMyAxOTJxLTU0IDE0MSAtMTQ1LjUgMjQxLjV0LTE5NC41IDE0Mi41aC0yMzBxLTEwMyAtNDIgLTE5NC41IC0xNDIuNXQtMTQ1LjUgLTI0MS41aDkxMHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81NTgiIHVuaWNvZGU9IiYjeGYyNTM7IiANCmQ9Ik0xNDA4IDE0MDhxMCAtMjYxIC0xMDYuNSAtNDYxLjV0LTI2Ni41IC0zMDYuNXExNjAgLTEwNiAyNjYuNSAtMzA2LjV0MTA2LjUgLTQ2MS41aDk2cTE0IDAgMjMgLTl0OSAtMjN2LTY0cTAgLTE0IC05IC0yM3QtMjMgLTloLTE0NzJxLTE0IDAgLTIzIDl0LTkgMjN2NjRxMCAxNCA5IDIzdDIzIDloOTZxMCAyNjEgMTA2LjUgNDYxLjV0MjY2LjUgMzA2LjVxLTE2MCAxMDYgLTI2Ni41IDMwNi41dC0xMDYuNSA0NjEuNWgtOTZxLTE0IDAgLTIzIDkNCnQtOSAyM3Y2NHEwIDE0IDkgMjN0MjMgOWgxNDcycTE0IDAgMjMgLTl0OSAtMjN2LTY0cTAgLTE0IC05IC0yM3QtMjMgLTloLTk2ek04NzQgNzAwcTc3IDI5IDE0OSA5Mi41dDEyOS41IDE1Mi41dDkyLjUgMjEwdDM1IDI1M2gtMTAyNHEwIC0xMzIgMzUgLTI1M3Q5Mi41IC0yMTB0MTI5LjUgLTE1Mi41dDE0OSAtOTIuNXExOSAtNyAzMC41IC0yMy41dDExLjUgLTM2LjV0LTExLjUgLTM2LjV0LTMwLjUgLTIzLjVxLTEzNyAtNTEgLTI0NCAtMTk2DQpoNzAwcS0xMDcgMTQ1IC0yNDQgMTk2cS0xOSA3IC0zMC41IDIzLjV0LTExLjUgMzYuNXQxMS41IDM2LjV0MzAuNSAyMy41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzU1OSIgdW5pY29kZT0iJiN4ZjI1NDsiIA0KZD0iTTE1MDQgLTY0cTE0IDAgMjMgLTl0OSAtMjN2LTEyOHEwIC0xNCAtOSAtMjN0LTIzIC05aC0xNDcycS0xNCAwIC0yMyA5dC05IDIzdjEyOHEwIDE0IDkgMjN0MjMgOWgxNDcyek0xMzAgMHEzIDU1IDE2IDEwN3QzMCA5NXQ0NiA4N3Q1My41IDc2dDY0LjUgNjkuNXQ2NiA2MHQ3MC41IDU1dDY2LjUgNDcuNXQ2NSA0M3EtNDMgMjggLTY1IDQzdC02Ni41IDQ3LjV0LTcwLjUgNTV0LTY2IDYwdC02NC41IDY5LjV0LTUzLjUgNzZ0LTQ2IDg3DQp0LTMwIDk1dC0xNiAxMDdoMTI3NnEtMyAtNTUgLTE2IC0xMDd0LTMwIC05NXQtNDYgLTg3dC01My41IC03NnQtNjQuNSAtNjkuNXQtNjYgLTYwdC03MC41IC01NXQtNjYuNSAtNDcuNXQtNjUgLTQzcTQzIC0yOCA2NSAtNDN0NjYuNSAtNDcuNXQ3MC41IC01NXQ2NiAtNjB0NjQuNSAtNjkuNXQ1My41IC03NnQ0NiAtODd0MzAgLTk1dDE2IC0xMDdoLTEyNzZ6TTE1MDQgMTUzNnExNCAwIDIzIC05dDkgLTIzdi0xMjhxMCAtMTQgLTkgLTIzdC0yMyAtOQ0KaC0xNDcycS0xNCAwIC0yMyA5dC05IDIzdjEyOHEwIDE0IDkgMjN0MjMgOWgxNDcyeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzU2MCIgdW5pY29kZT0iJiN4ZjI1NTsiIA0KZD0iTTc2OCAxMTUycS01MyAwIC05MC41IC0zNy41dC0zNy41IC05MC41di0xMjhoLTMydjkzcTAgNDggLTMyIDgxLjV0LTgwIDMzLjVxLTQ2IDAgLTc5IC0zM3QtMzMgLTc5di00MjlsLTMyIDMwdjE3MnEwIDQ4IC0zMiA4MS41dC04MCAzMy41cS00NiAwIC03OSAtMzN0LTMzIC03OXYtMjI0cTAgLTQ3IDM1IC04MmwzMTAgLTI5NnEzOSAtMzkgMzkgLTEwMnEwIC0yNiAxOSAtNDV0NDUgLTE5aDY0MHEyNiAwIDQ1IDE5dDE5IDQ1djI1DQpxMCA0MSAxMCA3N2wxMDggNDM2cTEwIDM2IDEwIDc3djI0NnEwIDQ4IC0zMiA4MS41dC04MCAzMy41cS00NiAwIC03OSAtMzN0LTMzIC03OXYtMzJoLTMydjEyNXEwIDQwIC0yNSA3Mi41dC02NCA0MC41cS0xNCAyIC0yMyAycS00NiAwIC03OSAtMzN0LTMzIC03OXYtMTI4aC0zMnYxMjJxMCA1MSAtMzIuNSA4OS41dC04Mi41IDQzLjVxLTUgMSAtMTMgMXpNNzY4IDEyODBxODQgMCAxNDkgLTUwcTU3IDM0IDEyMyAzNHE1OSAwIDExMSAtMjcNCnQ4NiAtNzZxMjcgNyA1OSA3cTEwMCAwIDE3MCAtNzEuNXQ3MCAtMTcxLjV2LTI0NnEwIC01MSAtMTMgLTEwOGwtMTA5IC00MzZxLTYgLTI0IC02IC03MXEwIC04MCAtNTYgLTEzNnQtMTM2IC01NmgtNjQwcS04NCAwIC0xMzggNTguNXQtNTQgMTQyLjVsLTMwOCAyOTZxLTc2IDczIC03NiAxNzV2MjI0cTAgOTkgNzAuNSAxNjkuNXQxNjkuNSA3MC41cTExIDAgMTYgLTFxNiA5NSA3NS41IDE2MHQxNjQuNSA2NXE1MiAwIDk4IC0yMQ0KcTcyIDY5IDE3NCA2OXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81NjEiIHVuaWNvZGU9IiYjeGYyNTY7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNODgwIDE0MDhxLTQ2IDAgLTc5IC0zM3QtMzMgLTc5di02NTZoLTMydjUyOHEwIDQ2IC0zMyA3OXQtNzkgMzN0LTc5IC0zM3QtMzMgLTc5di01Mjh2LTI1NmwtMTU0IDIwNXEtMzggNTEgLTEwMiA1MXEtNTMgMCAtOTAuNSAtMzcuNXQtMzcuNSAtOTAuNXEwIC00MyAyNiAtNzdsMzg0IC01MTJxMzggLTUxIDEwMiAtNTFoNjg4cTM0IDAgNjEgMjJ0MzQgNTZsNzYgNDA1cTUgMzIgNSA1OXY0OThxMCA0NiAtMzMgNzl0LTc5IDMzdC03OSAtMzMNCnQtMzMgLTc5di0yNzJoLTMydjUyOHEwIDQ2IC0zMyA3OXQtNzkgMzN0LTc5IC0zM3QtMzMgLTc5di01MjhoLTMydjY1NnEwIDQ2IC0zMyA3OXQtNzkgMzN6TTg4MCAxNTM2cTY4IDAgMTI1LjUgLTM1LjV0ODguNSAtOTYuNXExOSA0IDQyIDRxOTkgMCAxNjkuNSAtNzAuNXQ3MC41IC0xNjkuNXYtMTdxMTA1IDYgMTgwLjUgLTY0dDc1LjUgLTE3NXYtNDk4cTAgLTQwIC04IC04M2wtNzYgLTQwNHEtMTQgLTc5IC03Ni41IC0xMzF0LTE0My41IC01Mg0KaC02ODhxLTYwIDAgLTExNC41IDI3LjV0LTkwLjUgNzQuNWwtMzg0IDUxMnEtNTEgNjggLTUxIDE1NHEwIDEwNiA3NSAxODF0MTgxIDc1cTc4IDAgMTI4IC0zNHY0MzRxMCA5OSA3MC41IDE2OS41dDE2OS41IDcwLjVxMjMgMCA0MiAtNHEzMSA2MSA4OC41IDk2LjV0MTI1LjUgMzUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81NjIiIHVuaWNvZGU9IiYjeGYyNTc7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTA3MyAtMTI4aC0xNzdxLTE2MyAwIC0yMjYgMTQxcS0yMyA0OSAtMjMgMTAydjVxLTYyIDMwIC05OC41IDg4LjV0LTM2LjUgMTI3LjVxMCAzOCA1IDQ4aC0yNjFxLTEwNiAwIC0xODEgNzV0LTc1IDE4MXQ3NSAxODF0MTgxIDc1aDExM2wtNDQgMTdxLTc0IDI4IC0xMTkuNSA5My41dC00NS41IDE0NS41cTAgMTA2IDc1IDE4MXQxODEgNzVxNDYgMCA5MSAtMTdsNjI4IC0yMzloNDAxcTEwNiAwIDE4MSAtNzV0NzUgLTE4MXYtNjY4DQpxMCAtODggLTU0IC0xNTcuNXQtMTQwIC05MC41bC0zMzkgLTg1cS05MiAtMjMgLTE4NiAtMjN6TTEwMjQgNTgzbC0xNTUgLTcxbC0xNjMgLTc0cS0zMCAtMTQgLTQ4IC00MS41dC0xOCAtNjAuNXEwIC00NiAzMyAtNzl0NzkgLTMzcTI2IDAgNDYgMTBsMzM4IDE1NHEtNDkgMTAgLTgwLjUgNTB0LTMxLjUgOTB2NTV6TTEzNDQgMjcycTAgNDYgLTMzIDc5dC03OSAzM3EtMjYgMCAtNDYgLTEwbC0yOTAgLTEzMnEtMjggLTEzIC0zNyAtMTcNCnQtMzAuNSAtMTd0LTI5LjUgLTIzLjV0LTE2IC0yOXQtOCAtNDAuNXEwIC01MCAzMS41IC04MnQ4MS41IC0zMnEyMCAwIDM4IDlsMzUyIDE2MHEzMCAxNCA0OCA0MS41dDE4IDYwLjV6TTExMTIgMTAyNGwtNjUwIDI0OHEtMjQgOCAtNDYgOHEtNTMgMCAtOTAuNSAtMzcuNXQtMzcuNSAtOTAuNXEwIC00MCAyMi41IC03M3Q1OS41IC00N2w1MjYgLTIwMHYtNjRoLTY0MHEtNTMgMCAtOTAuNSAtMzcuNXQtMzcuNSAtOTAuNXQzNy41IC05MC41DQp0OTAuNSAtMzcuNWg1MzVsMjMzIDEwNnYxOThxMCA2MyA0NiAxMDZsMTExIDEwMmgtNjl6TTEwNzMgMHE4MiAwIDE1NSAxOWwzMzkgODVxNDMgMTEgNzAgNDUuNXQyNyA3OC41djY2OHEwIDUzIC0zNy41IDkwLjV0LTkwLjUgMzcuNWgtMzA4bC0xMzYgLTEyNnEtMzYgLTMzIC0zNiAtODJ2LTI5NnEwIC00NiAzMyAtNzd0NzkgLTMxdDc5IDM1dDMzIDgxdjIwOGgzMnYtMjA4cTAgLTcwIC01NyAtMTE0cTUyIC04IDg2LjUgLTQ4LjV0MzQuNSAtOTMuNQ0KcTAgLTQyIC0yMyAtNzh0LTYxIC01M2wtMzEwIC0xNDFoOTF6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTYzIiB1bmljb2RlPSImI3hmMjU4OyIgaG9yaXotYWR2LXg9IjIwNDgiIA0KZD0iTTExNTEgMTUzNnE2MSAwIDExNiAtMjh0OTEgLTc3bDU3MiAtNzgxcTExOCAtMTU5IDExOCAtMzU5di0zNTVxMCAtODAgLTU2IC0xMzZ0LTEzNiAtNTZoLTM4NHEtODAgMCAtMTM2IDU2dC01NiAxMzZ2MTc3bC0yODYgMTQzaC01NDZxLTgwIDAgLTEzNiA1NnQtNTYgMTM2djMycTAgMTE5IDg0LjUgMjAzLjV0MjAzLjUgODQuNWg0MjBsNDIgMTI4aC02ODZxLTEwMCAwIC0xNzMuNSA2Ny41dC04MS41IDE2Ni41cS02NSA3OSAtNjUgMTgydjMyDQpxMCA4MCA1NiAxMzZ0MTM2IDU2aDk1OXpNMTkyMCAtNjR2MzU1cTAgMTU3IC05MyAyODRsLTU3MyA3ODFxLTM5IDUyIC0xMDMgNTJoLTk1OXEtMjYgMCAtNDUgLTE5dC0xOSAtNDVxMCAtMzIgMS41IC00OS41dDkuNSAtNDAuNXQyNSAtNDNxMTAgMzEgMzUuNSA1MHQ1Ni41IDE5aDgzMnYtMzJoLTgzMnEtMjYgMCAtNDUgLTE5dC0xOSAtNDVxMCAtNDQgMyAtNThxOCAtNDQgNDQgLTczdDgxIC0yOWg2NDBoOTFxNDAgMCA2OCAtMjh0MjggLTY4DQpxMCAtMTUgLTUgLTMwbC02NCAtMTkycS0xMCAtMjkgLTM1IC00Ny41dC01NiAtMTguNWgtNDQzcS02NiAwIC0xMTMgLTQ3dC00NyAtMTEzdi0zMnEwIC0yNiAxOSAtNDV0NDUgLTE5aDU2MXExNiAwIDI5IC03bDMxNyAtMTU4cTI0IC0xMyAzOC41IC0zNnQxNC41IC01MHYtMTk3cTAgLTI2IDE5IC00NXQ0NSAtMTloMzg0cTI2IDAgNDUgMTl0MTkgNDV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTY0IiB1bmljb2RlPSImI3hmMjU5OyIgaG9yaXotYWR2LXg9IjIwNDgiIA0KZD0iTTQ1OSAtMjU2cS03NyAwIC0xMzcuNSA0Ny41dC03OS41IDEyMi41bC0xMDEgNDAxcS0xMyA1NyAtMTMgMTA4cTAgNDUgLTUgNjdsLTExNiA0NzdxLTcgMjcgLTcgNTdxMCA5MyA2MiAxNjF0MTU1IDc4cTE3IDg1IDgyLjUgMTM5dDE1Mi41IDU0cTgzIDAgMTQ4IC01MS41dDg1IC0xMzIuNWw4MyAtMzQ4bDEwMyA0MjhxMjAgODEgODUgMTMyLjV0MTQ4IDUxLjVxODkgMCAxNTUuNSAtNTcuNXQ4MC41IC0xNDQuNXE5MiAtMTAgMTUyIC03OQ0KdDYwIC0xNjJxMCAtMjQgLTcgLTU5bC0xMjMgLTUxMnExMCA3IDM3LjUgMjguNXQzOC41IDI5LjV0MzUgMjN0NDEgMjAuNXQ0MS41IDExdDQ5LjUgNS41cTEwNSAwIDE4MCAtNzR0NzUgLTE3OXEwIC02MiAtMjguNSAtMTE4dC03OC41IC05NGwtNTA3IC0zODBxLTY4IC01MSAtMTUzIC01MWgtNjk0ek0xMTA0IDE0MDhxLTM4IDAgLTY4LjUgLTI0dC0zOS41IC02MmwtMTY0IC02ODJoLTEyN2wtMTQ1IDYwMnEtOSAzOCAtMzkuNSA2MnQtNjguNSAyNA0KcS00OCAwIC04MCAtMzN0LTMyIC04MHEwIC0xNSAzIC0yOGwxMzIgLTU0N2gtMjZsLTk5IDQwOHEtOSAzNyAtNDAgNjIuNXQtNjkgMjUuNXEtNDcgMCAtODAgLTMzdC0zMyAtNzlxMCAtMTQgMyAtMjZsMTE2IC00NzhxNyAtMjggOSAtODZ0MTAgLTg4bDEwMCAtNDAxcTggLTMyIDM0IC01Mi41dDU5IC0yMC41aDY5NHE0MiAwIDc2IDI2bDUwNyAzNzlxNTYgNDMgNTYgMTEwcTAgNTIgLTM3LjUgODguNXQtODkuNSAzNi41cS00MyAwIC03NyAtMjYNCmwtMzA3IC0yMzB2MjI3cTAgNCAzMiAxMzh0NjggMjgydDM5IDE2MXE0IDE4IDQgMjlxMCA0NyAtMzIgODF0LTc5IDM0cS0zOSAwIC02OS41IC0yNHQtMzkuNSAtNjJsLTExNiAtNDgyaC0yNmwxNTAgNjI0cTMgMTQgMyAyOHEwIDQ4IC0zMS41IDgydC03OS41IDM0eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzU2NSIgdW5pY29kZT0iJiN4ZjI1YTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik02NDAgMTQwOHEtNTMgMCAtOTAuNSAtMzcuNXQtMzcuNSAtOTAuNXYtNTEydi0zODRsLTE1MSAyMDJxLTQxIDU0IC0xMDcgNTRxLTUyIDAgLTg5IC0zOHQtMzcgLTkwcTAgLTQzIDI2IC03N2wzODQgLTUxMnEzOCAtNTEgMTAyIC01MWg3MThxMjIgMCAzOS41IDEzLjV0MjIuNSAzNC41bDkyIDM2OHEyNCA5NiAyNCAxOTR2MjE3cTAgNDEgLTI4IDcxdC02OCAzMHQtNjggLTI4dC0yOCAtNjhoLTMydjYxcTAgNDggLTMyIDgxLjV0LTgwIDMzLjUNCnEtNDYgMCAtNzkgLTMzdC0zMyAtNzl2LTY0aC0zMnY5MHEwIDU1IC0zNyA5NC41dC05MSAzOS41cS01MyAwIC05MC41IC0zNy41dC0zNy41IC05MC41di05NmgtMzJ2NTcwcTAgNTUgLTM3IDk0LjV0LTkxIDM5LjV6TTY0MCAxNTM2cTEwNyAwIDE4MS41IC03Ny41dDc0LjUgLTE4NC41di0yMjBxMjIgMiAzMiAycTk5IDAgMTczIC02OXE0NyAyMSA5OSAyMXExMTMgMCAxODQgLTg3cTI3IDcgNTYgN3E5NCAwIDE1OSAtNjcuNXQ2NSAtMTYxLjUNCnYtMjE3cTAgLTExNiAtMjggLTIyNWwtOTIgLTM2OHEtMTYgLTY0IC02OCAtMTA0LjV0LTExOCAtNDAuNWgtNzE4cS02MCAwIC0xMTQuNSAyNy41dC05MC41IDc0LjVsLTM4NCA1MTJxLTUxIDY4IC01MSAxNTRxMCAxMDUgNzQuNSAxODAuNXQxNzkuNSA3NS41cTcxIDAgMTMwIC0zNXY1NDdxMCAxMDYgNzUgMTgxdDE4MSA3NXpNNzY4IDEyOHYzODRoLTMydi0zODRoMzJ6TTEwMjQgMTI4djM4NGgtMzJ2LTM4NGgzMnpNMTI4MCAxMjh2Mzg0aC0zMg0Kdi0zODRoMzJ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTY2IiB1bmljb2RlPSImI3hmMjViOyIgDQpkPSJNMTI4OCA4ODlxNjAgMCAxMDcgLTIzcTE0MSAtNjMgMTQxIC0yMjZ2LTE3N3EwIC05NCAtMjMgLTE4NmwtODUgLTMzOXEtMjEgLTg2IC05MC41IC0xNDB0LTE1Ny41IC01NGgtNjY4cS0xMDYgMCAtMTgxIDc1dC03NSAxODF2NDAxbC0yMzkgNjI4cS0xNyA0NSAtMTcgOTFxMCAxMDYgNzUgMTgxdDE4MSA3NXE4MCAwIDE0NS41IC00NS41dDkzLjUgLTExOS41bDE3IC00NHYxMTNxMCAxMDYgNzUgMTgxdDE4MSA3NXQxODEgLTc1dDc1IC0xODENCnYtMjYxcTI3IDUgNDggNXE2OSAwIDEyNy41IC0zNi41dDg4LjUgLTk4LjV6TTEwNzIgODk2cS0zMyAwIC02MC41IC0xOHQtNDEuNSAtNDhsLTc0IC0xNjNsLTcxIC0xNTVoNTVxNTAgMCA5MCAtMzEuNXQ1MCAtODAuNWwxNTQgMzM4cTEwIDIwIDEwIDQ2cTAgNDYgLTMzIDc5dC03OSAzM3pNMTI5MyA3NjFxLTIyIDAgLTQwLjUgLTh0LTI5IC0xNnQtMjMuNSAtMjkuNXQtMTcgLTMwLjV0LTE3IC0zN2wtMTMyIC0yOTBxLTEwIC0yMCAtMTAgLTQ2DQpxMCAtNDYgMzMgLTc5dDc5IC0zM3EzMyAwIDYwLjUgMTh0NDEuNSA0OGwxNjAgMzUycTkgMTggOSAzOHEwIDUwIC0zMiA4MS41dC04MiAzMS41ek0xMjggMTEyMHEwIC0yMiA4IC00NmwyNDggLTY1MHYtNjlsMTAyIDExMXE0MyA0NiAxMDYgNDZoMTk4bDEwNiAyMzN2NTM1cTAgNTMgLTM3LjUgOTAuNXQtOTAuNSAzNy41dC05MC41IC0zNy41dC0zNy41IC05MC41di02NDBoLTY0bC0yMDAgNTI2cS0xNCAzNyAtNDcgNTkuNXQtNzMgMjIuNQ0KcS01MyAwIC05MC41IC0zNy41dC0zNy41IC05MC41ek0xMTgwIC0xMjhxNDQgMCA3OC41IDI3dDQ1LjUgNzBsODUgMzM5cTE5IDczIDE5IDE1NXY5MWwtMTQxIC0zMTBxLTE3IC0zOCAtNTMgLTYxdC03OCAtMjNxLTUzIDAgLTkzLjUgMzQuNXQtNDguNSA4Ni41cS00NCAtNTcgLTExNCAtNTdoLTIwOHYzMmgyMDhxNDYgMCA4MSAzM3QzNSA3OXQtMzEgNzl0LTc3IDMzaC0yOTZxLTQ5IDAgLTgyIC0zNmwtMTI2IC0xMzZ2LTMwOA0KcTAgLTUzIDM3LjUgLTkwLjV0OTAuNSAtMzcuNWg2Njh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTY3IiB1bmljb2RlPSImI3hmMjVjOyIgaG9yaXotYWR2LXg9IjE5NzMiIA0KZD0iTTg1NyA5OTJ2LTExN3EwIC0xMyAtOS41IC0yMnQtMjIuNSAtOWgtMjk4di04MTJxMCAtMTMgLTkgLTIyLjV0LTIyIC05LjVoLTEzNXEtMTMgMCAtMjIuNSA5dC05LjUgMjN2ODEyaC0yOTdxLTEzIDAgLTIyLjUgOXQtOS41IDIydjExN3EwIDE0IDkgMjN0MjMgOWg3OTNxMTMgMCAyMi41IC05LjV0OS41IC0yMi41ek0xODk1IDk5NWw3NyAtOTYxcTEgLTEzIC04IC0yNHEtMTAgLTEwIC0yMyAtMTBoLTEzNHEtMTIgMCAtMjEgOC41DQp0LTEwIDIwLjVsLTQ2IDU4OGwtMTg5IC00MjVxLTggLTE5IC0yOSAtMTloLTEyMHEtMjAgMCAtMjkgMTlsLTE4OCA0MjdsLTQ1IC01OTBxLTEgLTEyIC0xMCAtMjAuNXQtMjEgLTguNWgtMTM1cS0xMyAwIC0yMyAxMHEtOSAxMCAtOSAyNGw3OCA5NjFxMSAxMiAxMCAyMC41dDIxIDguNWgxNDJxMjAgMCAyOSAtMTlsMjIwIC01MjBxMTAgLTI0IDIwIC01MXEzIDcgOS41IDI0LjV0MTAuNSAyNi41bDIyMSA1MjBxOSAxOSAyOSAxOWgxNDENCnExMyAwIDIyIC04LjV0MTAgLTIwLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTY4IiB1bmljb2RlPSImI3hmMjVkOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTEwNDIgODMzcTAgODggLTYwIDEyMXEtMzMgMTggLTExNyAxOGgtMTIzdi0yODFoMTYycTY2IDAgMTAyIDM3dDM2IDEwNXpNMTA5NCA1NDhsMjA1IC0zNzNxOCAtMTcgLTEgLTMxcS04IC0xNiAtMjcgLTE2aC0xNTJxLTIwIDAgLTI4IDE3bC0xOTQgMzY1aC0xNTV2LTM1MHEwIC0xNCAtOSAtMjN0LTIzIC05aC0xMzRxLTE0IDAgLTIzIDl0LTkgMjN2OTYwcTAgMTQgOSAyM3QyMyA5aDI5NHExMjggMCAxOTAgLTI0cTg1IC0zMSAxMzQgLTEwOQ0KdDQ5IC0xODBxMCAtOTIgLTQyLjUgLTE2NS41dC0xMTUuNSAtMTA5LjVxNiAtMTAgOSAtMTZ6TTg5NiAxMzc2cS0xNTAgMCAtMjg2IC01OC41dC0yMzQuNSAtMTU3dC0xNTcgLTIzNC41dC01OC41IC0yODZ0NTguNSAtMjg2dDE1NyAtMjM0LjV0MjM0LjUgLTE1N3QyODYgLTU4LjV0Mjg2IDU4LjV0MjM0LjUgMTU3dDE1NyAyMzQuNXQ1OC41IDI4NnQtNTguNSAyODZ0LTE1NyAyMzQuNXQtMjM0LjUgMTU3dC0yODYgNTguNXpNMTc5MiA2NDANCnEwIC0xODIgLTcxIC0zNDh0LTE5MSAtMjg2dC0yODYgLTE5MXQtMzQ4IC03MXQtMzQ4IDcxdC0yODYgMTkxdC0xOTEgMjg2dC03MSAzNDh0NzEgMzQ4dDE5MSAyODZ0Mjg2IDE5MXQzNDggNzF0MzQ4IC03MXQyODYgLTE5MXQxOTEgLTI4NnQ3MSAtMzQ4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzU2OSIgdW5pY29kZT0iJiN4ZjI1ZTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik02MDUgMzAzcTE1MyAwIDI1NyAxMDRxMTQgMTggMyAzNmwtNDUgODJxLTYgMTMgLTI0IDE3cS0xNiAyIC0yNyAtMTFsLTQgLTNxLTQgLTQgLTExLjUgLTEwdC0xNy41IC0xMy41dC0yMy41IC0xNC41dC0yOC41IC0xM3QtMzMuNSAtOS41dC0zNy41IC0zLjVxLTc2IDAgLTEyNSA1MHQtNDkgMTI3cTAgNzYgNDggMTI1LjV0MTIyIDQ5LjVxMzcgMCA3MS41IC0xNHQ1MC41IC0yOGwxNiAtMTRxMTEgLTExIDI2IC0xMHExNiAyIDI0IDE0bDUzIDc4DQpxMTMgMjAgLTIgMzlxLTMgNCAtMTEgMTJ0LTMwIDIzLjV0LTQ4LjUgMjh0LTY3LjUgMjIuNXQtODYgMTBxLTE0OCAwIC0yNDYgLTk2LjV0LTk4IC0yNDAuNXEwIC0xNDYgOTcgLTI0MS41dDI0NyAtOTUuNXpNMTIzNSAzMDNxMTUzIDAgMjU3IDEwNHExNCAxOCA0IDM2bC00NSA4MnEtOCAxNCAtMjUgMTdxLTE2IDIgLTI3IC0xMWwtNCAtM3EtNCAtNCAtMTEuNSAtMTB0LTE3LjUgLTEzLjV0LTIzLjUgLTE0LjV0LTI4LjUgLTEzdC0zMy41IC05LjUNCnQtMzcuNSAtMy41cS03NiAwIC0xMjUgNTB0LTQ5IDEyN3EwIDc2IDQ4IDEyNS41dDEyMiA0OS41cTM3IDAgNzEuNSAtMTR0NTAuNSAtMjhsMTYgLTE0cTExIC0xMSAyNiAtMTBxMTYgMiAyNCAxNGw1MyA3OHExMyAyMCAtMiAzOXEtMyA0IC0xMSAxMnQtMzAgMjMuNXQtNDguNSAyOHQtNjcuNSAyMi41dC04NiAxMHEtMTQ3IDAgLTI0NS41IC05Ni41dC05OC41IC0yNDAuNXEwIC0xNDYgOTcgLTI0MS41dDI0NyAtOTUuNXpNODk2IDEzNzYNCnEtMTUwIDAgLTI4NiAtNTguNXQtMjM0LjUgLTE1N3QtMTU3IC0yMzQuNXQtNTguNSAtMjg2dDU4LjUgLTI4NnQxNTcgLTIzNC41dDIzNC41IC0xNTd0Mjg2IC01OC41dDI4NiA1OC41dDIzNC41IDE1N3QxNTcgMjM0LjV0NTguNSAyODZ0LTU4LjUgMjg2dC0xNTcgMjM0LjV0LTIzNC41IDE1N3QtMjg2IDU4LjV6TTg5NiAxNTM2cTE4MiAwIDM0OCAtNzF0Mjg2IC0xOTF0MTkxIC0yODZ0NzEgLTM0OHQtNzEgLTM0OHQtMTkxIC0yODZ0LTI4NiAtMTkxDQp0LTM0OCAtNzF0LTM0OCA3MXQtMjg2IDE5MXQtMTkxIDI4NnQtNzEgMzQ4dDcxIDM0OHQxOTEgMjg2dDI4NiAxOTF0MzQ4IDcxeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZjI2MCIgdW5pY29kZT0iJiN4ZjI2MDsiIGhvcml6LWFkdi14PSIyMDQ4IiANCmQ9Ik03MzYgNzM2bDM4NCAtMzg0bC0zODQgLTM4NGwtNjcyIDY3Mmw2NzIgNjcybDE2OCAtMTY4bC05NiAtOTZsLTcyIDcybC00ODAgLTQ4MGw0ODAgLTQ4MGwxOTMgMTkzbC0yODkgMjg3ek0xMzEyIDEzMTJsNjcyIC02NzJsLTY3MiAtNjcybC0xNjggMTY4bDk2IDk2bDcyIC03Mmw0ODAgNDgwbC00ODAgNDgwbC0xOTMgLTE5M2wyODkgLTI4N2wtOTYgLTk2bC0zODQgMzg0eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iZjI2MSIgdW5pY29kZT0iJiN4ZjI2MTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik03MTcgMTgybDI3MSAyNzFsLTI3OSAyNzlsLTg4IC04OGwxOTIgLTE5MWwtOTYgLTk2bC0yNzkgMjc5bDI3OSAyNzlsNDAgLTQwbDg3IDg3bC0xMjcgMTI4bC00NTQgLTQ1NHpNMTA3NSAxOTBsNDU0IDQ1NGwtNDU0IDQ1NGwtMjcxIC0yNzFsMjc5IC0yNzlsODggODhsLTE5MiAxOTFsOTYgOTZsMjc5IC0yNzlsLTI3OSAtMjc5bC00MCA0MGwtODcgLTg4ek0xNzkyIDY0MHEwIC0xODIgLTcxIC0zNDh0LTE5MSAtMjg2dC0yODYgLTE5MQ0KdC0zNDggLTcxdC0zNDggNzF0LTI4NiAxOTF0LTE5MSAyODZ0LTcxIDM0OHQ3MSAzNDh0MTkxIDI4NnQyODYgMTkxdDM0OCA3MXQzNDggLTcxdDI4NiAtMTkxdDE5MSAtMjg2dDcxIC0zNDh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTcyIiB1bmljb2RlPSImI3hmMjYyOyIgaG9yaXotYWR2LXg9IjIzMDQiIA0KZD0iTTY1MSA1MzlxMCAtMzkgLTI3LjUgLTY2LjV0LTY1LjUgLTI3LjVxLTM5IDAgLTY2LjUgMjcuNXQtMjcuNSA2Ni41cTAgMzggMjcuNSA2NS41dDY2LjUgMjcuNXEzOCAwIDY1LjUgLTI3LjV0MjcuNSAtNjUuNXpNMTgwNSA1NDBxMCAtMzkgLTI3LjUgLTY2LjV0LTY2LjUgLTI3LjV0LTY2LjUgMjcuNXQtMjcuNSA2Ni41dDI3LjUgNjZ0NjYuNSAyN3Q2Ni41IC0yN3QyNy41IC02NnpNNzY1IDUzOXEwIDc5IC01Ni41IDEzNnQtMTM2LjUgNTcNCnQtMTM2LjUgLTU2LjV0LTU2LjUgLTEzNi41dDU2LjUgLTEzNi41dDEzNi41IC01Ni41dDEzNi41IDU2LjV0NTYuNSAxMzYuNXpNMTkxOCA1NDBxMCA4MCAtNTYuNSAxMzYuNXQtMTM2LjUgNTYuNXEtNzkgMCAtMTM2IC01Ni41dC01NyAtMTM2LjV0NTYuNSAtMTM2LjV0MTM2LjUgLTU2LjV0MTM2LjUgNTYuNXQ1Ni41IDEzNi41ek04NTAgNTM5cTAgLTExNiAtODEuNSAtMTk3LjV0LTE5Ni41IC04MS41cS0xMTYgMCAtMTk3LjUgODJ0LTgxLjUgMTk3DQp0ODIgMTk2LjV0MTk3IDgxLjV0MTk2LjUgLTgxLjV0ODEuNSAtMTk2LjV6TTIwMDQgNTQwcTAgLTExNSAtODEuNSAtMTk2LjV0LTE5Ny41IC04MS41cS0xMTUgMCAtMTk2LjUgODEuNXQtODEuNSAxOTYuNXQ4MS41IDE5Ni41dDE5Ni41IDgxLjVxMTE2IDAgMTk3LjUgLTgxLjV0ODEuNSAtMTk2LjV6TTEwNDAgNTM3cTAgMTkxIC0xMzUuNSAzMjYuNXQtMzI2LjUgMTM1LjVxLTEyNSAwIC0yMzEgLTYydC0xNjggLTE2OC41dC02MiAtMjMxLjUNCnQ2MiAtMjMxLjV0MTY4IC0xNjguNXQyMzEgLTYycTE5MSAwIDMyNi41IDEzNS41dDEzNS41IDMyNi41ek0xNzA4IDExMTBxLTI1NCAxMTEgLTU1NiAxMTFxLTMxOSAwIC01NzMgLTExMHExMTcgMCAyMjMgLTQ1LjV0MTgyLjUgLTEyMi41dDEyMiAtMTgzdDQ1LjUgLTIyM3EwIDExNSA0My41IDIxOS41dDExOCAxODAuNXQxNzcuNSAxMjN0MjE3IDUwek0yMTg3IDUzN3EwIDE5MSAtMTM1IDMyNi41dC0zMjYgMTM1LjV0LTMyNi41IC0xMzUuNQ0KdC0xMzUuNSAtMzI2LjV0MTM1LjUgLTMyNi41dDMyNi41IC0xMzUuNXQzMjYgMTM1LjV0MTM1IDMyNi41ek0xOTIxIDExMDNoMzgzcS00NCAtNTEgLTc1IC0xMTQuNXQtNDAgLTExNC41cTExMCAtMTUxIDExMCAtMzM3cTAgLTE1NiAtNzcgLTI4OHQtMjA5IC0yMDguNXQtMjg3IC03Ni41cS0xMzMgMCAtMjQ5IDU2dC0xOTYgMTU1cS00NyAtNTYgLTEyOSAtMTc5cS0xMSAyMiAtNTMuNSA4Mi41dC03NC41IDk3LjUNCnEtODAgLTk5IC0xOTYuNSAtMTU1LjV0LTI0OS41IC01Ni41cS0xNTUgMCAtMjg3IDc2LjV0LTIwOSAyMDguNXQtNzcgMjg4cTAgMTg2IDExMCAzMzdxLTkgNTEgLTQwIDExNC41dC03NSAxMTQuNWgzNjVxMTQ5IDEwMCAzNTUgMTU2LjV0NDMyIDU2LjVxMjI0IDAgNDIxIC01NnQzNDggLTE1N3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9ImYyNjMiIHVuaWNvZGU9IiYjeGYyNjM7IiBob3Jpei1hZHYteD0iMTI4MCIgDQpkPSJNNjQwIDYyOXEtMTg4IDAgLTMyMSAxMzN0LTEzMyAzMjBxMCAxODggMTMzIDMyMXQzMjEgMTMzdDMyMSAtMTMzdDEzMyAtMzIxcTAgLTE4NyAtMTMzIC0zMjB0LTMyMSAtMTMzek02NDAgMTMwNnEtOTIgMCAtMTU3LjUgLTY1LjV0LTY1LjUgLTE1OC41cTAgLTkyIDY1LjUgLTE1Ny41dDE1Ny41IC02NS41dDE1Ny41IDY1LjV0NjUuNSAxNTcuNXEwIDkzIC02NS41IDE1OC41dC0xNTcuNSA2NS41ek0xMTYzIDU3NHExMyAtMjcgMTUgLTQ5LjUNCnQtNC41IC00MC41dC0yNi41IC0zOC41dC00Mi41IC0zN3QtNjEuNSAtNDEuNXEtMTE1IC03MyAtMzE1IC05NGw3MyAtNzJsMjY3IC0yNjdxMzAgLTMxIDMwIC03NHQtMzAgLTczbC0xMiAtMTNxLTMxIC0zMCAtNzQgLTMwdC03NCAzMHEtNjcgNjggLTI2NyAyNjhsLTI2NyAtMjY4cS0zMSAtMzAgLTc0IC0zMHQtNzMgMzBsLTEyIDEzcS0zMSAzMCAtMzEgNzN0MzEgNzRsMjY3IDI2N2w3MiA3MnEtMjAzIDIxIC0zMTcgOTQNCnEtMzkgMjUgLTYxLjUgNDEuNXQtNDIuNSAzN3QtMjYuNSAzOC41dC00LjUgNDAuNXQxNSA0OS41cTEwIDIwIDI4IDM1dDQyIDIydDU2IC0ydDY1IC0zNXE1IC00IDE1IC0xMXQ0MyAtMjQuNXQ2OSAtMzAuNXQ5MiAtMjR0MTEzIC0xMXE5MSAwIDE3NCAyNS41dDEyMCA1MC41bDM4IDI1cTMzIDI2IDY1IDM1dDU2IDJ0NDIgLTIydDI4IC0zNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81NzQiIHVuaWNvZGU9IiYjeGYyNjQ7IiANCmQ9Ik05MjcgOTU2cTAgLTY2IC00Ni41IC0xMTIuNXQtMTEyLjUgLTQ2LjV0LTExMi41IDQ2LjV0LTQ2LjUgMTEyLjV0NDYuNSAxMTIuNXQxMTIuNSA0Ni41dDExMi41IC00Ni41dDQ2LjUgLTExMi41ek0xMTQxIDU5M3EtMTAgMjAgLTI4IDMydC00Ny41IDkuNXQtNjAuNSAtMjcuNXEtMTAgLTggLTI5IC0yMHQtODEgLTMydC0xMjcgLTIwdC0xMjQgMTh0LTg2IDM2bC0yNyAxOHEtMzEgMjUgLTYwLjUgMjcuNXQtNDcuNSAtOS41dC0yOCAtMzINCnEtMjIgLTQ1IC0yIC03NC41dDg3IC03My41cTgzIC01MyAyMjYgLTY3bC01MSAtNTJxLTE0MiAtMTQyIC0xOTEgLTE5MHEtMjIgLTIyIC0yMiAtNTIuNXQyMiAtNTIuNWw5IC05cTIyIC0yMiA1Mi41IC0yMnQ1Mi41IDIybDE5MSAxOTFxMTE0IC0xMTUgMTkxIC0xOTFxMjIgLTIyIDUyLjUgLTIydDUyLjUgMjJsOSA5cTIyIDIyIDIyIDUyLjV0LTIyIDUyLjVsLTE5MSAxOTBsLTUyIDUycTE0MSAxNCAyMjUgNjdxNjcgNDQgODcgNzMuNXQtMiA3NC41DQp6TTEwOTIgOTU2cTAgMTM0IC05NSAyMjl0LTIyOSA5NXQtMjI5IC05NXQtOTUgLTIyOXQ5NSAtMjI5dDIyOSAtOTV0MjI5IDk1dDk1IDIyOXpNMTUzNiAxMTIwdi05NjBxMCAtMTE5IC04NC41IC0yMDMuNXQtMjAzLjUgLTg0LjVoLTk2MHEtMTE5IDAgLTIwMy41IDg0LjV0LTg0LjUgMjAzLjV2OTYwcTAgMTE5IDg0LjUgMjAzLjV0MjAzLjUgODQuNWg5NjBxMTE5IDAgMjAzLjUgLTg0LjV0ODQuNSAtMjAzLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTc1IiB1bmljb2RlPSImI3hmMjY1OyIgaG9yaXotYWR2LXg9IjE3MjAiIA0KZD0iTTE1NjUgMTQwOHE2NSAwIDExMCAtNDUuNXQ0NSAtMTEwLjV2LTUxOXEwIC0xNzYgLTY4IC0zMzZ0LTE4Mi41IC0yNzV0LTI3NCAtMTgyLjV0LTMzNC41IC02Ny41cS0xNzYgMCAtMzM1LjUgNjcuNXQtMjc0LjUgMTgyLjV0LTE4MyAyNzV0LTY4IDMzNnY1MTlxMCA2NCA0NiAxMTB0MTEwIDQ2aDE0MDl6TTg2MSAzNDRxNDcgMCA4MiAzM2w0MDQgMzg4cTM3IDM1IDM3IDg1cTAgNDkgLTM0LjUgODMuNXQtODMuNSAzNC41cS00NyAwIC04MiAtMzMNCmwtMzIzIC0zMTBsLTMyMyAzMTBxLTM1IDMzIC04MSAzM3EtNDkgMCAtODMuNSAtMzQuNXQtMzQuNSAtODMuNXEwIC01MSAzNiAtODVsNDA1IC0zODhxMzMgLTMzIDgxIC0zM3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81NzYiIHVuaWNvZGU9IiYjeGYyNjY7IiBob3Jpei1hZHYteD0iMjMwNCIgDQpkPSJNMTQ5NCAtMTAzbC0yOTUgNjk1cS0yNSAtNDkgLTE1OC41IC0zMDUuNXQtMTk4LjUgLTM4OS41cS0xIC0xIC0yNy41IC0wLjV0LTI2LjUgMS41cS04MiAxOTMgLTI1NS41IDU4N3QtMjU5LjUgNTk2cS0yMSA1MCAtNjYuNSAxMDcuNXQtMTAzLjUgMTAwLjV0LTEwMiA0M3EwIDUgLTAuNSAyNHQtMC41IDI3aDU4M3YtNTBxLTM5IC0yIC03OS41IC0xNnQtNjYuNSAtNDN0LTEwIC02NHEyNiAtNTkgMjE2LjUgLTQ5OXQyMzUuNSAtNTQwDQpxMzEgNjEgMTQwIDI2Ni41dDEzMSAyNDcuNXEtMTkgMzkgLTEyNiAyODF0LTEzNiAyOTVxLTM4IDY5IC0yMDEgNzF2NTBsNTEzIC0xdi00N3EtNjAgLTIgLTkzLjUgLTI1dC0xMi41IC02OXEzMyAtNzAgODcgLTE4OS41dDg2IC0xODcuNXExMTAgMjE0IDE3MyAzNjNxMjQgNTUgLTEwIDc5LjV0LTEyOSAyNi41cTEgNyAxIDI1djI0cTY0IDAgMTcwLjUgMC41dDE4MCAxdDkyLjUgMC41di00OXEtNjIgLTIgLTExOSAtMzN0LTkwIC04MQ0KbC0yMTMgLTQ0MnExMyAtMzMgMTI3LjUgLTI5MHQxMjEuNSAtMjc0bDQ0MSAxMDE3cS0xNCAzOCAtNDkuNSA2Mi41dC02NSAzMS41dC01NS41IDh2NTBsNDYwIC00bDEgLTJsLTEgLTQ0cS0xMzkgLTQgLTIwMSAtMTQ1cS01MjYgLTEyMTYgLTU1OSAtMTI5MWgtNDl6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTc3IiB1bmljb2RlPSImI3hmMjY3OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTk0OSA2NDNxMCAtMjYgLTE2LjUgLTQ1dC00MS41IC0xOXEtMjYgMCAtNDUgMTYuNXQtMTkgNDEuNXEwIDI2IDE3IDQ1dDQyIDE5dDQ0IC0xNi41dDE5IC00MS41ek05NjQgNTg1bDM1MCA1ODFxLTkgLTggLTY3LjUgLTYyLjV0LTEyNS41IC0xMTYuNXQtMTM2LjUgLTEyN3QtMTE3IC0xMTAuNXQtNTAuNSAtNTEuNWwtMzQ5IC01ODBxNyA3IDY3IDYydDEyNiAxMTYuNXQxMzYgMTI3dDExNyAxMTF0NTAgNTAuNXpNMTYxMSA2NDANCnEwIC0yMDEgLTEwNCAtMzcxcS0zIDIgLTE3IDExdC0yNi41IDE2LjV0LTE2LjUgNy41cS0xMyAwIC0xMyAtMTNxMCAtMTAgNTkgLTQ0cS03NCAtMTEyIC0xODQuNSAtMTkwLjV0LTI0MS41IC0xMTAuNWwtMTYgNjdxLTEgMTAgLTE1IDEwcS01IDAgLTggLTUuNXQtMiAtOS41bDE2IC02OHEtNzIgLTE1IC0xNDYgLTE1cS0xOTkgMCAtMzcyIDEwNXExIDIgMTMgMjAuNXQyMS41IDMzLjV0OS41IDE5cTAgMTMgLTEzIDEzcS02IDAgLTE3IC0xNC41DQp0LTIyLjUgLTM0LjV0LTEzLjUgLTIzcS0xMTMgNzUgLTE5MiAxODcuNXQtMTEwIDI0NC41bDY5IDE1cTEwIDMgMTAgMTVxMCA1IC01LjUgOHQtMTAuNSAybC02OCAtMTVxLTE0IDcyIC0xNCAxMzlxMCAyMDYgMTA5IDM3OXEyIC0xIDE4LjUgLTEydDMwIC0xOXQxNy41IC04cTEzIDAgMTMgMTJxMCA2IC0xMi41IDE1LjV0LTMyLjUgMjEuNWwtMjAgMTJxNzcgMTEyIDE4OSAxODl0MjQ0IDEwN2wxNSAtNjdxMiAtMTAgMTUgLTEwcTUgMCA4IDUuNQ0KdDIgMTAuNWwtMTUgNjZxNzEgMTMgMTM0IDEzcTIwNCAwIDM3OSAtMTA5cS0zOSAtNTYgLTM5IC02NXEwIC0xMyAxMiAtMTNxMTEgMCA0OCA2NHExMTEgLTc1IDE4Ny41IC0xODZ0MTA3LjUgLTI0MWwtNTYgLTEycS0xMCAtMiAtMTAgLTE2cTAgLTUgNS41IC04dDkuNSAtMmw1NyAxM3ExNCAtNzIgMTQgLTE0MHpNMTY5NiA2NDBxMCAxNjMgLTYzLjUgMzExdC0xNzAuNSAyNTV0LTI1NSAxNzAuNXQtMzExIDYzLjV0LTMxMSAtNjMuNQ0KdC0yNTUgLTE3MC41dC0xNzAuNSAtMjU1dC02My41IC0zMTF0NjMuNSAtMzExdDE3MC41IC0yNTV0MjU1IC0xNzAuNXQzMTEgLTYzLjV0MzExIDYzLjV0MjU1IDE3MC41dDE3MC41IDI1NXQ2My41IDMxMXpNMTc5MiA2NDBxMCAtMTgyIC03MSAtMzQ4dC0xOTEgLTI4NnQtMjg2IC0xOTF0LTM0OCAtNzF0LTM0OCA3MXQtMjg2IDE5MXQtMTkxIDI4NnQtNzEgMzQ4dDcxIDM0OHQxOTEgMjg2dDI4NiAxOTF0MzQ4IDcxdDM0OCAtNzF0Mjg2IC0xOTENCnQxOTEgLTI4NnQ3MSAtMzQ4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzU3OCIgdW5pY29kZT0iJiN4ZjI2ODsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik04OTMgMTUzNnEyNDAgMiA0NTEgLTEyMHEyMzIgLTEzNCAzNTIgLTM3MmwtNzQyIDM5cS0xNjAgOSAtMjk0IC03NC41dC0xODUgLTIyOS41bC0yNzYgNDI0cTEyOCAxNTkgMzExIDI0NS41dDM4MyA4Ny41ek0xNDYgMTEzMWwzMzcgLTY2M3E3MiAtMTQzIDIxMSAtMjE3dDI5MyAtNDVsLTIzMCAtNDUxcS0yMTIgMzMgLTM4NSAxNTcuNXQtMjcyLjUgMzE2dC05OS41IDQxMS41cTAgMjY3IDE0NiA0OTF6TTE3MzIgOTYyDQpxNTggLTE1MCA1OS41IC0zMTAuNXQtNDguNSAtMzA2dC0xNTMgLTI3MnQtMjQ2IC0yMDkuNXEtMjMwIC0xMzMgLTQ5OCAtMTE5bDQwNSA2MjNxODggMTMxIDgyLjUgMjkwLjV0LTEwNi41IDI3Ny41ek04OTYgOTQycTEyNSAwIDIxMy41IC04OC41dDg4LjUgLTIxMy41dC04OC41IC0yMTMuNXQtMjEzLjUgLTg4LjV0LTIxMy41IDg4LjV0LTg4LjUgMjEzLjV0ODguNSAyMTMuNXQyMTMuNSA4OC41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzU3OSIgdW5pY29kZT0iJiN4ZjI2OTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik05MDMgLTI1NnEtMjgzIDAgLTUwNC41IDE1MC41dC0zMjkuNSAzOTguNXEtNTggMTMxIC02NyAzMDF0MjYgMzMyLjV0MTExIDMxMnQxNzkgMjQyLjVsLTExIC0yODFxMTEgMTQgNjggMTUuNXQ3MCAtMTUuNXE0MiA4MSAxNjAuNSAxMzh0MjM0LjUgNTlxLTU0IC00NSAtMTE5LjUgLTE0OC41dC01OC41IC0xNjMuNXEyNSAtOCA2Mi41IC0xMy41dDYzIC03LjV0NjggLTR0NTAuNSAtM3ExNSAtNSA5LjUgLTQ1LjV0LTMwLjUgLTc1LjUNCnEtNSAtNyAtMTYuNSAtMTguNXQtNTYuNSAtMzUuNXQtMTAxIC0zNGwxNSAtMTg5bC0xMzkgNjdxLTE4IC00MyAtNy41IC04MS41dDM2IC02Ni41dDY1LjUgLTQxLjV0ODEgLTYuNXE1MSA5IDk4IDM0LjV0ODMuNSA0NXQ3My41IDE3LjVxNjEgLTQgODkuNSAtMzN0MTkuNSAtNjVxLTEgLTIgLTIuNSAtNS41dC04LjUgLTEyLjV0LTE4IC0xNS41dC0zMS41IC0xMC41dC00Ni41IC0xcS02MCAtOTUgLTE0NC41IC0xMzUuNXQtMjA5LjUgLTI5LjUNCnE3NCAtNjEgMTYyLjUgLTgyLjV0MTY4LjUgLTZ0MTU0LjUgNTJ0MTI4IDg3LjV0ODAuNSAxMDRxNDMgOTEgMzkgMTkyLjV0LTM3LjUgMTg4LjV0LTc4LjUgMTI1cTg3IC0zOCAxMzcgLTc5LjV0NzcgLTExMi41cTE1IDE3MCAtNTcuNSAzNDN0LTIwOS41IDI4NHEyNjUgLTc3IDQxMiAtMjc5LjV0MTUxIC01MTcuNXEyIC0xMjcgLTQwLjUgLTI1NXQtMTIzLjUgLTIzOHQtMTg5IC0xOTZ0LTI0Ny41IC0xMzUuNXQtMjg4LjUgLTQ5LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTgwIiB1bmljb2RlPSImI3hmMjZhOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE0OTMgMTMwOHEtMTY1IDExMCAtMzU5IDExMHEtMTU1IDAgLTI5MyAtNzN0LTI0MCAtMjAwcS03NSAtOTMgLTExOS41IC0yMTh0LTQ4LjUgLTI2NnYtNDJxNCAtMTQxIDQ4LjUgLTI2NnQxMTkuNSAtMjE4cTEwMiAtMTI3IDI0MCAtMjAwdDI5MyAtNzNxMTk0IDAgMzU5IDExMHEtMTIxIC0xMDggLTI3NC41IC0xNjh0LTMyMi41IC02MHEtMjkgMCAtNDMgMXEtMTc1IDggLTMzMyA4MnQtMjcyIDE5M3QtMTgxIDI4MXQtNjcgMzM5DQpxMCAxODIgNzEgMzQ4dDE5MSAyODZ0Mjg2IDE5MXQzNDggNzFoM3ExNjggLTEgMzIwLjUgLTYwLjV0MjczLjUgLTE2Ny41ek0xNzkyIDY0MHEwIC0xOTIgLTc3IC0zNjIuNXQtMjEzIC0yOTYuNXEtMTA0IC02MyAtMjIyIC02M3EtMTM3IDAgLTI1NSA4NHExNTQgNTYgMjUzLjUgMjMzdDk5LjUgNDA1cTAgMjI3IC05OSA0MDR0LTI1MyAyMzRxMTE5IDgzIDI1NCA4M3ExMTkgMCAyMjYgLTY1cTEzNSAtMTI1IDIxMC41IC0yOTV0NzUuNSAtMzYxeg0KIiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTgxIiB1bmljb2RlPSImI3hmMjZiOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE3OTIgNTk5cTAgLTU2IC03IC0xMDRoLTExNTFxMCAtMTQ2IDEwOS41IC0yNDQuNXQyNTcuNSAtOTguNXE5OSAwIDE4NS41IDQ2LjV0MTM2LjUgMTMwLjVoNDIzcS01NiAtMTU5IC0xNzAuNSAtMjgxdC0yNjcuNSAtMTg4LjV0LTMyMSAtNjYuNXEtMTg3IDAgLTM1NiA4M3EtMjI4IC0xMTYgLTM5NCAtMTE2cS0yMzcgMCAtMjM3IDI2M3EwIDExNSA0NSAyNzVxMTcgNjAgMTA5IDIyOXExOTkgMzYwIDQ3NSA2MDYNCnEtMTg0IC03OSAtNDI3IC0zNTRxNjMgMjc0IDI4My41IDQ0OS41dDUwMS41IDE3NS41cTMwIDAgNDUgLTFxMjU1IDExNyA0MzMgMTE3cTY0IDAgMTE2IC0xM3Q5NC41IC00MC41dDY2LjUgLTc2LjV0MjQgLTExNXEwIC0xMTYgLTc1IC0yODZxMTAxIC0xODIgMTAxIC0zOTB6TTE3MjIgMTIzOXEwIDgzIC01MyAxMzJ0LTEzNyA0OXEtMTA4IDAgLTI1NCAtNzBxMTIxIC00NyAyMjIuNSAtMTMxLjV0MTcwLjUgLTE5NS41cTUxIDEzNSA1MSAyMTZ6DQpNMTI4IDJxMCAtODYgNDguNSAtMTMyLjV0MTM0LjUgLTQ2LjVxMTE1IDAgMjY2IDgzcS0xMjIgNzIgLTIxMy41IDE4M3QtMTM3LjUgMjQ1cS05OCAtMjA1IC05OCAtMzMyek02MzIgNzE1aDcyOHEtNSAxNDIgLTExMyAyMzd0LTI1MSA5NXEtMTQ0IDAgLTI1MS41IC05NXQtMTEyLjUgLTIzN3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81ODIiIHVuaWNvZGU9IiYjeGYyNmM7IiBob3Jpei1hZHYteD0iMjA0OCIgDQpkPSJNMTc5MiAyODh2OTYwcTAgMTMgLTkuNSAyMi41dC0yMi41IDkuNWgtMTYwMHEtMTMgMCAtMjIuNSAtOS41dC05LjUgLTIyLjV2LTk2MHEwIC0xMyA5LjUgLTIyLjV0MjIuNSAtOS41aDE2MDBxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXpNMTkyMCAxMjQ4di05NjBxMCAtNjYgLTQ3IC0xMTN0LTExMyAtNDdoLTczNnYtMTI4aDM1MnExNCAwIDIzIC05dDkgLTIzdi02NHEwIC0xNCAtOSAtMjN0LTIzIC05aC04MzJxLTE0IDAgLTIzIDl0LTkgMjMNCnY2NHEwIDE0IDkgMjN0MjMgOWgzNTJ2MTI4aC03MzZxLTY2IDAgLTExMyA0N3QtNDcgMTEzdjk2MHEwIDY2IDQ3IDExM3QxMTMgNDdoMTYwMHE2NiAwIDExMyAtNDd0NDcgLTExM3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81ODMiIHVuaWNvZGU9IiYjeGYyNmQ7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTM4IDE0MDhoMTk3cS03MCAtNjQgLTEyNiAtMTQ5cS0zNiAtNTYgLTU5IC0xMTV0LTMwIC0xMjUuNXQtOC41IC0xMjB0MTAuNSAtMTMydDIxIC0xMjZ0MjggLTEzNi41cTQgLTE5IDYgLTI4cTUxIC0yMzggODEgLTMyOXE1NyAtMTcxIDE1MiAtMjc1aC0yNzJxLTQ4IDAgLTgyIDM0dC0zNCA4MnYxMzA0cTAgNDggMzQgODJ0ODIgMzR6TTEzNDYgMTQwOGgzMDhxNDggMCA4MiAtMzR0MzQgLTgydi0xMzA0cTAgLTQ4IC0zNCAtODJ0LTgyIC0zNA0KaC0xNzhxMjEyIDIxMCAxOTYgNTY1bC00NjkgLTEwMXEtMiAtNDUgLTEyIC04MnQtMzEgLTcydC01OS41IC01OS41dC05My41IC0zNi41cS0xMjMgLTI2IC0xOTkgNDBxLTMyIDI3IC01MyA2MXQtNTEuNSAxMjl0LTY0LjUgMjU4cS0zNSAxNjMgLTQ1LjUgMjYzdC01LjUgMTM5dDIzIDc3cTIwIDQxIDYyLjUgNzN0MTAyLjUgNDVxNDUgMTIgODMuNSA2LjV0NjcgLTE3dDU0IC0zNXQ0MyAtNDh0MzQuNSAtNTYuNWw0NjggMTAwDQpxLTY4IDE3NSAtMTgwIDI4N3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81ODQiIHVuaWNvZGU9IiYjeGYyNmU7IiANCmQ9Ik0xNDAxIC0xMWwtNiAtNnEtMTEzIC0xMTMgLTI1OSAtMTc1cS0xNTQgLTY0IC0zMTcgLTY0cS0xNjUgMCAtMzE3IDY0cS0xNDggNjMgLTI1OSAxNzVxLTExMyAxMTIgLTE3NSAyNThxLTQyIDEwMyAtNTQgMTg5cS00IDI4IDQ4IDM2cTUxIDggNTYgLTIwcTEgLTEgMSAtNHExOCAtOTAgNDYgLTE1OXE1MCAtMTI0IDE1MiAtMjI2cTk4IC05OCAyMjYgLTE1MnExMzIgLTU2IDI3NiAtNTZxMTQzIDAgMjc2IDU2cTEyOCA1NSAyMjUgMTUybDYgNg0KcTEwIDEwIDI1IDZxMTIgLTMgMzMgLTIycTM2IC0zNyAxNyAtNTh6TTkyOSA2MDRsLTY2IC02Nmw2MyAtNjNxMjEgLTIxIC03IC00OXEtMTcgLTE3IC0zMiAtMTdxLTEwIDAgLTE5IDEwbC02MiA2MWwtNjYgLTY2cS01IC01IC0xNSAtNXEtMTUgMCAtMzEgMTZsLTIgMnEtMTggMTUgLTE4IDI5cTAgNyA4IDE3bDY2IDY1bC02NiA2NnEtMTYgMTYgMTQgNDVxMTggMTggMzEgMThxNiAwIDEzIC01bDY1IC02Nmw2NSA2NXExOCAxNyA0OCAtMTMNCnEyNyAtMjcgMTEgLTQ0ek0xNDAwIDU0N3EwIC0xMTggLTQ2IC0yMjhxLTQ1IC0xMDUgLTEyNiAtMTg2cS04MCAtODAgLTE4NyAtMTI2dC0yMjggLTQ2dC0yMjggNDZ0LTE4NyAxMjZxLTgyIDgyIC0xMjUgMTg2cS0xNSAzMyAtMTUgNDBoLTFxLTkgMjcgNDMgNDRxNTAgMTYgNjAgLTEycTM3IC05OSA5NyAtMTY3aDF2MzM5djJxMyAxMzYgMTAyIDIzMnExMDUgMTAzIDI1MyAxMDNxMTQ3IDAgMjUxIC0xMDN0MTA0IC0yNDkNCnEwIC0xNDcgLTEwNC41IC0yNTF0LTI1MC41IC0xMDRxLTU4IDAgLTExMiAxNnEtMjggMTEgLTEzIDYxcTE2IDUxIDQ0IDQzbDE0IC0zcTE0IC0zIDMzIC02dDMwIC0zcTEwNCAwIDE3NiA3MS41dDcyIDE3NC41cTAgMTAxIC03MiAxNzFxLTcxIDcxIC0xNzUgNzFxLTEwNyAwIC0xNzggLTgwcS02NCAtNzIgLTY0IC0xNjB2LTQxM3ExMTAgLTY3IDI0MiAtNjdxOTYgMCAxODUgMzYuNXQxNTYgMTAzLjV0MTAzLjUgMTU1dDM2LjUgMTgzDQpxMCAxOTggLTE0MSAzMzlxLTE0MCAxNDAgLTMzOSAxNDBxLTIwMCAwIC0zNDAgLTE0MHEtNTMgLTUzIC03NyAtODdsLTIgLTJxLTggLTExIC0xMyAtMTUuNXQtMjEuNSAtOS41dC0zOC41IDNxLTIxIDUgLTM2LjUgMTYuNXQtMTUuNSAyNi41djY4MHEwIDE1IDEwLjUgMjYuNXQyNy41IDExLjVoODc3cTMwIDAgMzAgLTU1dC0zMCAtNTVoLTgxMXYtNDgzaDFxNDAgNDIgMTAyIDg0dDEwOCA2MXExMDkgNDYgMjMxIDQ2cTEyMSAwIDIyOCAtNDYNCnQxODcgLTEyNnE4MSAtODEgMTI2IC0xODZxNDYgLTExMiA0NiAtMjI5ek0xMzY5IDExMjhxOSAtOCA5IC0xOHQtNS41IC0xOHQtMTYuNSAtMjFxLTI2IC0yNiAtMzkgLTI2cS05IDAgLTE2IDdxLTEwNiA5MSAtMjA3IDEzM3EtMTI4IDU2IC0yNzYgNTZxLTEzMyAwIC0yNjIgLTQ5cS0yNyAtMTAgLTQ1IDM3cS05IDI1IC04IDM4cTMgMTYgMTYgMjBxMTMwIDU3IDI5OSA1N3ExNjQgMCAzMTYgLTY0cTEzNyAtNTggMjM1IC0xNTJ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTg1IiB1bmljb2RlPSImI3hmMjcwOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE1NTEgNjBxMTUgNiAyNiAzdDExIC0xNy41dC0xNSAtMzMuNXEtMTMgLTE2IC00NCAtNDMuNXQtOTUuNSAtNjh0LTE0MSAtNzR0LTE4OCAtNTh0LTIyOS41IC0yNC41cS0xMTkgMCAtMjM4IDMxdC0yMDkgNzYuNXQtMTcyLjUgMTA0dC0xMzIuNSAxMDV0LTg0IDg3LjVxLTggOSAtMTAgMTYuNXQxIDEydDggN3QxMS41IDJ0MTEuNSAtNC41cTE5MiAtMTE3IDMwMCAtMTY2cTM4OSAtMTc2IDc5OSAtOTBxMTkwIDQwIDM5MSAxMzV6DQpNMTc1OCAxNzVxMTEgLTE2IDIuNSAtNjkuNXQtMjguNSAtMTAyLjVxLTM0IC04MyAtODUgLTEyNHEtMTcgLTE0IC0yNiAtOXQwIDI0cTIxIDQ1IDQ0LjUgMTIxLjV0Ni41IDk4LjVxLTUgNyAtMTUuNSAxMS41dC0yNyA2dC0yOS41IDIuNXQtMzUgMHQtMzEuNSAtMnQtMzEgLTN0LTIyLjUgLTJxLTYgLTEgLTEzIC0xLjV0LTExIC0xdC04LjUgLTF0LTcgLTAuNWgtNS41aC00LjV0LTMgMC41dC0yIDEuNWwtMS41IDNxLTYgMTYgNDcgNDB0MTAzIDMwDQpxNDYgNyAxMDggMXQ3NiAtMjR6TTEzNjQgNjE4cTAgLTMxIDEzLjUgLTY0dDMyIC01OHQzNy41IC00NnQzMyAtMzJsMTMgLTExbC0yMjcgLTIyNHEtNDAgMzcgLTc5IDc1LjV0LTU4IDU4LjVsLTE5IDIwcS0xMSAxMSAtMjUgMzNxLTM4IC01OSAtOTcuNSAtMTAyLjV0LTEyNy41IC02My41dC0xNDAgLTIzdC0xMzcuNSAyMXQtMTE3LjUgNjUuNXQtODMgMTEzdC0zMSAxNjIuNXEwIDg0IDI4IDE1NHQ3MiAxMTYuNXQxMDYuNSA4M3QxMjIuNSA1Nw0KdDEzMCAzNC41dDExOS41IDE4LjV0OTkuNSA2LjV2MTI3cTAgNjUgLTIxIDk3cS0zNCA1MyAtMTIxIDUzcS02IDAgLTE2LjUgLTF0LTQwLjUgLTEydC01NiAtMjkuNXQtNTYgLTU5LjV0LTQ4IC05NmwtMjk0IDI3cTAgNjAgMjIgMTE5dDY3IDExM3QxMDggOTV0MTUxLjUgNjUuNXQxOTAuNSAyNC41cTEwMCAwIDE4MSAtMjV0MTI5LjUgLTYxLjV0ODEgLTgzdDQ1IC04NnQxMi41IC03My41di01ODl6TTY5MiA1OTdxMCAtODYgNzAgLTEzMw0KcTY2IC00NCAxMzkgLTIycTg0IDI1IDExNCAxMjNxMTQgNDUgMTQgMTAxdjE2MnEtNTkgLTIgLTExMSAtMTJ0LTEwNi41IC0zMy41dC04NyAtNzF0LTMyLjUgLTExNC41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzU4NiIgdW5pY29kZT0iJiN4ZjI3MTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNTM2IDEyODBxNTIgMCA5MCAtMzh0MzggLTkwdi0xMjgwcTAgLTUyIC0zOCAtOTB0LTkwIC0zOGgtMTQwOHEtNTIgMCAtOTAgMzh0LTM4IDkwdjEyODBxMCA1MiAzOCA5MHQ5MCAzOGgxMjh2OTZxMCA2NiA0NyAxMTN0MTEzIDQ3aDY0cTY2IDAgMTEzIC00N3Q0NyAtMTEzdi05NmgzODR2OTZxMCA2NiA0NyAxMTN0MTEzIDQ3aDY0cTY2IDAgMTEzIC00N3Q0NyAtMTEzdi05NmgxMjh6TTExNTIgMTM3NnYtMjg4cTAgLTE0IDkgLTIzdDIzIC05DQpoNjRxMTQgMCAyMyA5dDkgMjN2Mjg4cTAgMTQgLTkgMjN0LTIzIDloLTY0cS0xNCAwIC0yMyAtOXQtOSAtMjN6TTM4NCAxMzc2di0yODhxMCAtMTQgOSAtMjN0MjMgLTloNjRxMTQgMCAyMyA5dDkgMjN2Mjg4cTAgMTQgLTkgMjN0LTIzIDloLTY0cS0xNCAwIC0yMyAtOXQtOSAtMjN6TTE1MzYgLTEyOHYxMDI0aC0xNDA4di0xMDI0aDE0MDh6TTg5NiA0NDhoMjI0cTE0IDAgMjMgLTl0OSAtMjN2LTY0cTAgLTE0IC05IC0yM3QtMjMgLTloLTIyNA0Kdi0yMjRxMCAtMTQgLTkgLTIzdC0yMyAtOWgtNjRxLTE0IDAgLTIzIDl0LTkgMjN2MjI0aC0yMjRxLTE0IDAgLTIzIDl0LTkgMjN2NjRxMCAxNCA5IDIzdDIzIDloMjI0djIyNHEwIDE0IDkgMjN0MjMgOWg2NHExNCAwIDIzIC05dDkgLTIzdi0yMjR6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTg3IiB1bmljb2RlPSImI3hmMjcyOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTExNTIgNDE2di02NHEwIC0xNCAtOSAtMjN0LTIzIC05aC01NzZxLTE0IDAgLTIzIDl0LTkgMjN2NjRxMCAxNCA5IDIzdDIzIDloNTc2cTE0IDAgMjMgLTl0OSAtMjN6TTEyOCAtMTI4aDE0MDh2MTAyNGgtMTQwOHYtMTAyNHpNNTEyIDEwODh2Mjg4cTAgMTQgLTkgMjN0LTIzIDloLTY0cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTI4OHEwIC0xNCA5IC0yM3QyMyAtOWg2NHExNCAwIDIzIDl0OSAyM3pNMTI4MCAxMDg4djI4OHEwIDE0IC05IDIzDQp0LTIzIDloLTY0cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTI4OHEwIC0xNCA5IC0yM3QyMyAtOWg2NHExNCAwIDIzIDl0OSAyM3pNMTY2NCAxMTUydi0xMjgwcTAgLTUyIC0zOCAtOTB0LTkwIC0zOGgtMTQwOHEtNTIgMCAtOTAgMzh0LTM4IDkwdjEyODBxMCA1MiAzOCA5MHQ5MCAzOGgxMjh2OTZxMCA2NiA0NyAxMTN0MTEzIDQ3aDY0cTY2IDAgMTEzIC00N3Q0NyAtMTEzdi05NmgzODR2OTZxMCA2NiA0NyAxMTN0MTEzIDQ3aDY0cTY2IDAgMTEzIC00Nw0KdDQ3IC0xMTN2LTk2aDEyOHE1MiAwIDkwIC0zOHQzOCAtOTB6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTg4IiB1bmljb2RlPSImI3hmMjczOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTExMTEgMTUxbC00NiAtNDZxLTkgLTkgLTIyIC05dC0yMyA5bC0xODggMTg5bC0xODggLTE4OXEtMTAgLTkgLTIzIC05dC0yMiA5bC00NiA0NnEtOSA5IC05IDIydDkgMjNsMTg5IDE4OGwtMTg5IDE4OHEtOSAxMCAtOSAyM3Q5IDIybDQ2IDQ2cTkgOSAyMiA5dDIzIC05bDE4OCAtMTg4bDE4OCAxODhxMTAgOSAyMyA5dDIyIC05bDQ2IC00NnE5IC05IDkgLTIydC05IC0yM2wtMTg4IC0xODhsMTg4IC0xODhxOSAtMTAgOSAtMjN0LTkgLTIyeg0KTTEyOCAtMTI4aDE0MDh2MTAyNGgtMTQwOHYtMTAyNHpNNTEyIDEwODh2Mjg4cTAgMTQgLTkgMjN0LTIzIDloLTY0cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTI4OHEwIC0xNCA5IC0yM3QyMyAtOWg2NHExNCAwIDIzIDl0OSAyM3pNMTI4MCAxMDg4djI4OHEwIDE0IC05IDIzdC0yMyA5aC02NHEtMTQgMCAtMjMgLTl0LTkgLTIzdi0yODhxMCAtMTQgOSAtMjN0MjMgLTloNjRxMTQgMCAyMyA5dDkgMjN6TTE2NjQgMTE1MnYtMTI4MA0KcTAgLTUyIC0zOCAtOTB0LTkwIC0zOGgtMTQwOHEtNTIgMCAtOTAgMzh0LTM4IDkwdjEyODBxMCA1MiAzOCA5MHQ5MCAzOGgxMjh2OTZxMCA2NiA0NyAxMTN0MTEzIDQ3aDY0cTY2IDAgMTEzIC00N3Q0NyAtMTEzdi05NmgzODR2OTZxMCA2NiA0NyAxMTN0MTEzIDQ3aDY0cTY2IDAgMTEzIC00N3Q0NyAtMTEzdi05NmgxMjhxNTIgMCA5MCAtMzh0MzggLTkweiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzU4OSIgdW5pY29kZT0iJiN4ZjI3NDsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xMzAzIDU3MmwtNTEyIC01MTJxLTEwIC05IC0yMyAtOXQtMjMgOWwtMjg4IDI4OHEtOSAxMCAtOSAyM3Q5IDIybDQ2IDQ2cTkgOSAyMiA5dDIzIC05bDIyMCAtMjIwbDQ0NCA0NDRxMTAgOSAyMyA5dDIyIC05bDQ2IC00NnE5IC05IDkgLTIydC05IC0yM3pNMTI4IC0xMjhoMTQwOHYxMDI0aC0xNDA4di0xMDI0ek01MTIgMTA4OHYyODhxMCAxNCAtOSAyM3QtMjMgOWgtNjRxLTE0IDAgLTIzIC05dC05IC0yM3YtMjg4cTAgLTE0IDkgLTIzDQp0MjMgLTloNjRxMTQgMCAyMyA5dDkgMjN6TTEyODAgMTA4OHYyODhxMCAxNCAtOSAyM3QtMjMgOWgtNjRxLTE0IDAgLTIzIC05dC05IC0yM3YtMjg4cTAgLTE0IDkgLTIzdDIzIC05aDY0cTE0IDAgMjMgOXQ5IDIzek0xNjY0IDExNTJ2LTEyODBxMCAtNTIgLTM4IC05MHQtOTAgLTM4aC0xNDA4cS01MiAwIC05MCAzOHQtMzggOTB2MTI4MHEwIDUyIDM4IDkwdDkwIDM4aDEyOHY5NnEwIDY2IDQ3IDExM3QxMTMgNDdoNjRxNjYgMCAxMTMgLTQ3DQp0NDcgLTExM3YtOTZoMzg0djk2cTAgNjYgNDcgMTEzdDExMyA0N2g2NHE2NiAwIDExMyAtNDd0NDcgLTExM3YtOTZoMTI4cTUyIDAgOTAgLTM4dDM4IC05MHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81OTAiIHVuaWNvZGU9IiYjeGYyNzU7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNNDQ4IDE1MzZxMjYgMCA0NSAtMTl0MTkgLTQ1di04OTFsNTM2IDQyOXExNyAxNCA0MCAxNHEyNiAwIDQ1IC0xOXQxOSAtNDV2LTM3OWw1MzYgNDI5cTE3IDE0IDQwIDE0cTI2IDAgNDUgLTE5dDE5IC00NXYtMTE1MnEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTE2NjRxLTI2IDAgLTQ1IDE5dC0xOSA0NXYxNjY0cTAgMjYgMTkgNDV0NDUgMTloMzg0eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzU5MSIgdW5pY29kZT0iJiN4ZjI3NjsiIGhvcml6LWFkdi14PSIxMDI0IiANCmQ9Ik01MTIgNDQ4cTY2IDAgMTI4IDE1di02NTVxMCAtMjYgLTE5IC00NXQtNDUgLTE5aC0xMjhxLTI2IDAgLTQ1IDE5dC0xOSA0NXY2NTVxNjIgLTE1IDEyOCAtMTV6TTUxMiAxNTM2cTIxMiAwIDM2MiAtMTUwdDE1MCAtMzYydC0xNTAgLTM2MnQtMzYyIC0xNTB0LTM2MiAxNTB0LTE1MCAzNjJ0MTUwIDM2MnQzNjIgMTUwek01MTIgMTMxMnExNCAwIDIzIDl0OSAyM3QtOSAyM3QtMjMgOXEtMTQ2IDAgLTI0OSAtMTAzdC0xMDMgLTI0OQ0KcTAgLTE0IDkgLTIzdDIzIC05dDIzIDl0OSAyM3EwIDExOSA4NC41IDIwMy41dDIwMy41IDg0LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTkyIiB1bmljb2RlPSImI3hmMjc3OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE3NDUgMTIzOXExMCAtMTAgMTAgLTIzdC0xMCAtMjNsLTE0MSAtMTQxcS0yOCAtMjggLTY4IC0yOGgtMTM0NHEtMjYgMCAtNDUgMTl0LTE5IDQ1djI1NnEwIDI2IDE5IDQ1dDQ1IDE5aDU3NnY2NHEwIDI2IDE5IDQ1dDQ1IDE5aDEyOHEyNiAwIDQ1IC0xOXQxOSAtNDV2LTY0aDUxMnE0MCAwIDY4IC0yOHpNNzY4IDMyMGgyNTZ2LTUxMnEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTEyOHEtMjYgMCAtNDUgMTl0LTE5IDQ1djUxMnpNMTYwMCA3NjgNCnEyNiAwIDQ1IC0xOXQxOSAtNDV2LTI1NnEwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTEzNDRxLTQwIDAgLTY4IDI4bC0xNDEgMTQxcS0xMCAxMCAtMTAgMjN0MTAgMjNsMTQxIDE0MXEyOCAyOCA2OCAyOGg1MTJ2MTkyaDI1NnYtMTkyaDU3NnoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81OTMiIHVuaWNvZGU9IiYjeGYyNzg7IiBob3Jpei1hZHYteD0iMjA0OCIgDQpkPSJNMjAyMCAxNTI1cTI4IC0yMCAyOCAtNTN2LTE0MDhxMCAtMjAgLTExIC0zNnQtMjkgLTIzbC02NDAgLTI1NnEtMjQgLTExIC00OCAwbC02MTYgMjQ2bC02MTYgLTI0NnEtMTAgLTUgLTI0IC01cS0xOSAwIC0zNiAxMXEtMjggMjAgLTI4IDUzdjE0MDhxMCAyMCAxMSAzNnQyOSAyM2w2NDAgMjU2cTI0IDExIDQ4IDBsNjE2IC0yNDZsNjE2IDI0NnEzMiAxMyA2MCAtNnpNNzM2IDEzOTB2LTEyNzBsNTc2IC0yMzB2MTI3MHpNMTI4IDExNzMNCnYtMTI3MGw1NDQgMjE3djEyNzB6TTE5MjAgMTA3djEyNzBsLTU0NCAtMjE3di0xMjcweiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzU5NCIgdW5pY29kZT0iJiN4ZjI3OTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik01MTIgMTUzNnExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV2LTE0NzJxMCAtMjAgLTE3IC0yOGwtNDgwIC0yNTZxLTcgLTQgLTE1IC00cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2MTQ3MnEwIDIwIDE3IDI4bDQ4MCAyNTZxNyA0IDE1IDR6TTE3NjAgMTUzNnExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV2LTE0NzJxMCAtMjAgLTE3IC0yOGwtNDgwIC0yNTZxLTcgLTQgLTE1IC00cS0xMyAwIC0yMi41IDkuNXQtOS41IDIyLjV2MTQ3Mg0KcTAgMjAgMTcgMjhsNDgwIDI1NnE3IDQgMTUgNHpNNjQwIDE1MzZxOCAwIDE0IC0zbDUxMiAtMjU2cTE4IC0xMCAxOCAtMjl2LTE0NzJxMCAtMTMgLTkuNSAtMjIuNXQtMjIuNSAtOS41cS04IDAgLTE0IDNsLTUxMiAyNTZxLTE4IDEwIC0xOCAyOXYxNDcycTAgMTMgOS41IDIyLjV0MjIuNSA5LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTk1IiB1bmljb2RlPSImI3hmMjdhOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTY0MCA2NDBxMCA1MyAtMzcuNSA5MC41dC05MC41IDM3LjV0LTkwLjUgLTM3LjV0LTM3LjUgLTkwLjV0MzcuNSAtOTAuNXQ5MC41IC0zNy41dDkwLjUgMzcuNXQzNy41IDkwLjV6TTEwMjQgNjQwcTAgNTMgLTM3LjUgOTAuNXQtOTAuNSAzNy41dC05MC41IC0zNy41dC0zNy41IC05MC41dDM3LjUgLTkwLjV0OTAuNSAtMzcuNXQ5MC41IDM3LjV0MzcuNSA5MC41ek0xNDA4IDY0MHEwIDUzIC0zNy41IDkwLjV0LTkwLjUgMzcuNQ0KdC05MC41IC0zNy41dC0zNy41IC05MC41dDM3LjUgLTkwLjV0OTAuNSAtMzcuNXQ5MC41IDM3LjV0MzcuNSA5MC41ek0xNzkyIDY0MHEwIC0xNzQgLTEyMCAtMzIxLjV0LTMyNiAtMjMzdC00NTAgLTg1LjVxLTExMCAwIC0yMTEgMThxLTE3MyAtMTczIC00MzUgLTIyOXEtNTIgLTEwIC04NiAtMTNxLTEyIC0xIC0yMiA2dC0xMyAxOHEtNCAxNSAyMCAzN3E1IDUgMjMuNSAyMS41dDI1LjUgMjMuNXQyMy41IDI1LjV0MjQgMzEuNXQyMC41IDM3DQp0MjAgNDh0MTQuNSA1Ny41dDEyLjUgNzIuNXEtMTQ2IDkwIC0yMjkuNSAyMTYuNXQtODMuNSAyNjkuNXEwIDE3NCAxMjAgMzIxLjV0MzI2IDIzM3Q0NTAgODUuNXQ0NTAgLTg1LjV0MzI2IC0yMzN0MTIwIC0zMjEuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il81OTYiIHVuaWNvZGU9IiYjeGYyN2I7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNNjQwIDY0MHEwIC01MyAtMzcuNSAtOTAuNXQtOTAuNSAtMzcuNXQtOTAuNSAzNy41dC0zNy41IDkwLjV0MzcuNSA5MC41dDkwLjUgMzcuNXQ5MC41IC0zNy41dDM3LjUgLTkwLjV6TTEwMjQgNjQwcTAgLTUzIC0zNy41IC05MC41dC05MC41IC0zNy41dC05MC41IDM3LjV0LTM3LjUgOTAuNXQzNy41IDkwLjV0OTAuNSAzNy41dDkwLjUgLTM3LjV0MzcuNSAtOTAuNXpNMTQwOCA2NDBxMCAtNTMgLTM3LjUgLTkwLjV0LTkwLjUgLTM3LjUNCnQtOTAuNSAzNy41dC0zNy41IDkwLjV0MzcuNSA5MC41dDkwLjUgMzcuNXQ5MC41IC0zNy41dDM3LjUgLTkwLjV6TTg5NiAxMTUycS0yMDQgMCAtMzgxLjUgLTY5LjV0LTI4MiAtMTg3LjV0LTEwNC41IC0yNTVxMCAtMTEyIDcxLjUgLTIxMy41dDIwMS41IC0xNzUuNWw4NyAtNTBsLTI3IC05NnEtMjQgLTkxIC03MCAtMTcycTE1MiA2MyAyNzUgMTcxbDQzIDM4bDU3IC02cTY5IC04IDEzMCAtOHEyMDQgMCAzODEuNSA2OS41dDI4MiAxODcuNQ0KdDEwNC41IDI1NXQtMTA0LjUgMjU1dC0yODIgMTg3LjV0LTM4MS41IDY5LjV6TTE3OTIgNjQwcTAgLTE3NCAtMTIwIC0zMjEuNXQtMzI2IC0yMzN0LTQ1MCAtODUuNXEtNzAgMCAtMTQ1IDhxLTE5OCAtMTc1IC00NjAgLTI0MnEtNDkgLTE0IC0xMTQgLTIyaC01cS0xNSAwIC0yNyAxMC41dC0xNiAyNy41djFxLTMgNCAtMC41IDEydDIgMTB0NC41IDkuNWw2IDl0NyA4LjV0OCA5cTcgOCAzMSAzNC41dDM0LjUgMzh0MzEgMzkuNXQzMi41IDUxDQp0MjcgNTl0MjYgNzZxLTE1NyA4OSAtMjQ3LjUgMjIwdC05MC41IDI4MXEwIDEzMCA3MSAyNDguNXQxOTEgMjA0LjV0Mjg2IDEzNi41dDM0OCA1MC41dDM0OCAtNTAuNXQyODYgLTEzNi41dDE5MSAtMjA0LjV0NzEgLTI0OC41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzU5NyIgdW5pY29kZT0iJiN4ZjI3YzsiIGhvcml6LWFkdi14PSIxMDI0IiANCmQ9Ik01MTIgMzQ1bDUxMiAyOTV2LTU5MWwtNTEyIC0yOTZ2NTkyek0wIDY0MHYtNTkxbDUxMiAyOTZ6TTUxMiAxNTI3di01OTFsLTUxMiAtMjk2djU5MXpNNTEyIDkzNmw1MTIgMjk1di01OTF6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNTk4IiB1bmljb2RlPSImI3hmMjdkOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE3MDkgMTAxOHEtMTAgLTIzNiAtMzMyIC02NTFxLTMzMyAtNDMxIC01NjIgLTQzMXEtMTQyIDAgLTI0MCAyNjNxLTQ0IDE2MCAtMTMyIDQ4MnEtNzIgMjYyIC0xNTcgMjYycS0xOCAwIC0xMjcgLTc2bC03NyA5OHEyNCAyMSAxMDggOTYuNXQxMzAgMTE1LjVxMTU2IDEzOCAyNDEgMTQ2cTk1IDkgMTUzIC01NS41dDgxIC0yMDMuNXE0NCAtMjg3IDY2IC0zNzNxNTUgLTI0OSAxMjAgLTI0OXE1MSAwIDE1NCAxNjFxMTAxIDE2MSAxMDkgMjQ2DQpxMTMgMTM5IC0xMDkgMTM5cS01NyAwIC0xMjEgLTI2cTEyMCAzOTMgNDU5IDM4MnEyNTEgLTggMjM2IC0zMjZ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJmMjdlIiB1bmljb2RlPSImI3hmMjdlOyIgDQpkPSJNMCAxNDA4aDE1MzZ2LTE1MzZoLTE1MzZ2MTUzNnpNMTA4NSAyOTNsLTIyMSA2MzFsMjIxIDI5N2gtNjM0bDIyMSAtMjk3bC0yMjEgLTYzMWwzMTcgLTMwNHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyODAiIHVuaWNvZGU9IiYjeGYyODA7IiANCmQ9Ik0wIDE0MDhoMTUzNnYtMTUzNmgtMTUzNnYxNTM2ek05MDggMTA4OGwtMTIgLTMzbDc1IC04M2wtMzEgLTExNGwyNSAtMjVsMTA3IDU3bDEwNyAtNTdsMjUgMjVsLTMxIDExNGw3NSA4M2wtMTIgMzNoLTk1bC01MyA5NmgtMzJsLTUzIC05NmgtOTV6TTY0MSA5MjVxMzIgMCA0NC41IC0xNnQxMS41IC02M2wxNzQgMjFxMCA1NSAtMTcuNSA5Mi41dC01MC41IDU2dC02OSAyNS41dC04NSA3cS0xMzMgMCAtMTk5IC01Ny41dC02NiAtMTgyLjV2LTcyDQpoLTk2di0xMjhoNzZxMjAgMCAyMCAtOHYtMzgycTAgLTE0IC01IC0yMHQtMTggLTdsLTczIC03di04OGg0NDh2ODZsLTE0OSAxNHEtNiAxIC04LjUgMS41dC0zLjUgMi41dC0wLjUgNHQxIDd0MC41IDEwdjM4N2gxOTFsMzggMTI4aC0yMzFxLTYgMCAtMiA2dDQgOXY4MHEwIDI3IDEuNSA0MC41dDcuNSAyOHQxOS41IDIwdDM2LjUgNS41ek0xMjQ4IDk2djg2bC01NCA5cS03IDEgLTkuNSAyLjV0LTIuNSAzdDEgNy41dDEgMTJ2NTIwaC0yNzUNCmwtMjMgLTEwMWw4MyAtMjJxMjMgLTcgMjMgLTI3di0zNzBxMCAtMTQgLTYgLTE4LjV0LTIwIC02LjVsLTcwIC05di04NmgzNTJ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMjgxIiB1bmljb2RlPSImI3hmMjgxOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE3OTIgNjkwcTAgLTU4IC0yOS41IC0xMDUuNXQtNzkuNSAtNzIuNXExMiAtNDYgMTIgLTk2cTAgLTE1NSAtMTA2LjUgLTI4N3QtMjkwLjUgLTIwOC41dC00MDAgLTc2LjV0LTM5OS41IDc2LjV0LTI5MCAyMDguNXQtMTA2LjUgMjg3cTAgNDcgMTEgOTRxLTUxIDI1IC04MiA3My41dC0zMSAxMDYuNXEwIDgyIDU4IDE0MC41dDE0MSA1OC41cTg1IDAgMTQ1IC02M3EyMTggMTUyIDUxNSAxNjJsMTE2IDUyMXEzIDEzIDE1IDIxdDI2IDUNCmwzNjkgLTgxcTE4IDM3IDU0IDU5LjV0NzkgMjIuNXE2MiAwIDEwNiAtNDMuNXQ0NCAtMTA1LjV0LTQ0IC0xMDZ0LTEwNiAtNDR0LTEwNS41IDQzLjV0LTQzLjUgMTA1LjVsLTMzNCA3NGwtMTA0IC00NzJxMzAwIC05IDUxOSAtMTYwcTU4IDYxIDE0MyA2MXE4MyAwIDE0MSAtNTguNXQ1OCAtMTQwLjV6TTQxOCA0OTFxMCAtNjIgNDMuNSAtMTA2dDEwNS41IC00NHQxMDYgNDR0NDQgMTA2dC00NCAxMDUuNXQtMTA2IDQzLjVxLTYxIDAgLTEwNSAtNDQNCnQtNDQgLTEwNXpNMTIyOCAxMzZxMTEgMTEgMTEgMjZ0LTExIDI2cS0xMCAxMCAtMjUgMTB0LTI2IC0xMHEtNDEgLTQyIC0xMjEgLTYydC0xNjAgLTIwdC0xNjAgMjB0LTEyMSA2MnEtMTEgMTAgLTI2IDEwdC0yNSAtMTBxLTExIC0xMCAtMTEgLTI1LjV0MTEgLTI2LjVxNDMgLTQzIDExOC41IC02OHQxMjIuNSAtMjkuNXQ5MSAtNC41dDkxIDQuNXQxMjIuNSAyOS41dDExOC41IDY4ek0xMjI1IDM0MXE2MiAwIDEwNS41IDQ0dDQzLjUgMTA2DQpxMCA2MSAtNDQgMTA1dC0xMDUgNDRxLTYyIDAgLTEwNiAtNDMuNXQtNDQgLTEwNS41dDQ0IC0xMDZ0MTA2IC00NHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il82MDIiIHVuaWNvZGU9IiYjeGYyODI7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNNjkgNzQxaDFxMTYgMTI2IDU4LjUgMjQxLjV0MTE1IDIxN3QxNjcuNSAxNzZ0MjIzLjUgMTE3LjV0Mjc2LjUgNDNxMjMxIDAgNDE0IC0xMDUuNXQyOTQgLTMwMy41cTEwNCAtMTg3IDEwNCAtNDQydi0xODhoLTExMjVxMSAtMTExIDUzLjUgLTE5Mi41dDEzNi41IC0xMjIuNXQxODkuNSAtNTd0MjEzIC0zdDIwOCA0Ni41dDE3My41IDg0LjV2LTM3N3EtOTIgLTU1IC0yMjkuNSAtOTJ0LTMxMi41IC0zOHQtMzE2IDUzDQpxLTE4OSA3MyAtMzExLjUgMjQ5dC0xMjQuNSAzNzJxLTMgMjQyIDExMSA0MTJ0MzI1IDI2OHEtNDggLTYwIC03OCAtMTI1LjV0LTQ2IC0xNTkuNWg2MzVxOCA3NyAtOCAxNDB0LTQ3IDEwMS41dC03MC41IDY2LjV0LTgwLjUgNDF0LTc1IDIwLjV0LTU2IDguNWwtMjIgMXEtMTM1IC01IC0yNTkuNSAtNDQuNXQtMjIzLjUgLTEwNC41dC0xNzYgLTE0MC41dC0xMzggLTE2My41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzYwMyIgdW5pY29kZT0iJiN4ZjI4MzsiIGhvcml6LWFkdi14PSIyMzA0IiANCmQ9Ik0wIDMydjYwOGgyMzA0di02MDhxMCAtNjYgLTQ3IC0xMTN0LTExMyAtNDdoLTE5ODRxLTY2IDAgLTExMyA0N3QtNDcgMTEzek02NDAgMjU2di0xMjhoMzg0djEyOGgtMzg0ek0yNTYgMjU2di0xMjhoMjU2djEyOGgtMjU2ek0yMTQ0IDE0MDhxNjYgMCAxMTMgLTQ3dDQ3IC0xMTN2LTIyNGgtMjMwNHYyMjRxMCA2NiA0NyAxMTN0MTEzIDQ3aDE5ODR6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNjA0IiB1bmljb2RlPSImI3hmMjg0OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE1ODQgMjQ2bC0yMTggMTExcS03NCAtMTIwIC0xOTYuNSAtMTg5dC0yNjMuNSAtNjlxLTE0NyAwIC0yNzEgNzJ0LTE5NiAxOTZ0LTcyIDI3MHEwIDExMCA0Mi41IDIwOS41dDExNSAxNzJ0MTcyIDExNXQyMDkuNSA0Mi41cTEzMSAwIDI0Ny41IC02MC41dDE5Mi41IC0xNjguNWwyMTUgMTI1cS0xMTAgMTY5IC0yODYuNSAyNjV0LTM3OC41IDk2cS0xNjEgMCAtMzA4IC02M3QtMjUzIC0xNjl0LTE2OSAtMjUzdC02MyAtMzA4dDYzIC0zMDgNCnQxNjkgLTI1M3QyNTMgLTE2OXQzMDggLTYzcTIxMyAwIDM5Ny41IDEwN3QyOTAuNSAyOTJ6TTEwMzAgNjQzbDY5MyAtMzUycS0xMTYgLTI1MyAtMzM0LjUgLTQwMHQtNDkyLjUgLTE0N3EtMTgyIDAgLTM0OCA3MXQtMjg2IDE5MXQtMTkxIDI4NnQtNzEgMzQ4dDcxIDM0OHQxOTEgMjg2dDI4NiAxOTF0MzQ4IDcxcTI2MCAwIDQ3MC41IC0xMzMuNXQzMzUuNSAtMzY2LjV6TTE1NDMgNjQwaC0zOXYtMTYwaC05NnYzNTJoMTM2cTMyIDAgNTQuNSAtMjANCnQyOC41IC00OHQxIC01NnQtMjcuNSAtNDh0LTU3LjUgLTIweiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjI4NSIgdW5pY29kZT0iJiN4ZjI4NTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNDI3IDgyN2wtNjE0IDM4Nmw5MiAxNTFoODU1ek00MDUgNTYybC0xODQgMTE2djg1OGwxMTgzIC03NDN6TTE0MjQgNjk3bDE0NyAtOTV2LTg1OGwtNTMyIDMzNXpNMTM4NyA3MThsLTUwMCAtODAyaC04NTVsMzU2IDU3MXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyODYiIHVuaWNvZGU9IiYjeGYyODY7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNNjQwIDUyOHYyMjRxMCAxNiAtMTYgMTZoLTk2cS0xNiAwIC0xNiAtMTZ2LTIyNHEwIC0xNiAxNiAtMTZoOTZxMTYgMCAxNiAxNnpNMTE1MiA1Mjh2MjI0cTAgMTYgLTE2IDE2aC05NnEtMTYgMCAtMTYgLTE2di0yMjRxMCAtMTYgMTYgLTE2aDk2cTE2IDAgMTYgMTZ6TTE2NjQgNDk2di03NTJoLTY0MHYzMjBxMCA4MCAtNTYgMTM2dC0xMzYgNTZ0LTEzNiAtNTZ0LTU2IC0xMzZ2LTMyMGgtNjQwdjc1MnEwIDE2IDE2IDE2aDk2DQpxMTYgMCAxNiAtMTZ2LTExMmgxMjh2NjI0cTAgMTYgMTYgMTZoOTZxMTYgMCAxNiAtMTZ2LTExMmgxMjh2MTEycTAgMTYgMTYgMTZoOTZxMTYgMCAxNiAtMTZ2LTExMmgxMjh2MTEycTAgNiAyLjUgOS41dDguNSA1dDkuNSAydDExLjUgMHQ5IC0wLjV2MzkxcS0zMiAxNSAtMzIgNTBxMCAyMyAxNi41IDM5dDM4LjUgMTZ0MzguNSAtMTZ0MTYuNSAtMzlxMCAtMzUgLTMyIC01MHYtMTdxNDUgMTAgODMgMTBxMjEgMCA1OS41IC03LjV0NTQuNSAtNy41DQpxMTcgMCA0NyA3LjV0MzcgNy41cTE2IDAgMTYgLTE2di0yMTBxMCAtMTUgLTM1IC0yMS41dC02MiAtNi41cS0xOCAwIC01NC41IDcuNXQtNTUuNSA3LjVxLTQwIDAgLTkwIC0xMnYtMTMzcTEgMCA5IDAuNXQxMS41IDB0OS41IC0ydDguNSAtNXQyLjUgLTkuNXYtMTEyaDEyOHYxMTJxMCAxNiAxNiAxNmg5NnExNiAwIDE2IC0xNnYtMTEyaDEyOHYxMTJxMCAxNiAxNiAxNmg5NnExNiAwIDE2IC0xNnYtNjI0aDEyOHYxMTJxMCAxNiAxNiAxNmg5Ng0KcTE2IDAgMTYgLTE2eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzYwNyIgdW5pY29kZT0iJiN4ZjI4NzsiIGhvcml6LWFkdi14PSIyMzA0IiANCmQ9Ik0yMjg4IDczMXExNiAtOCAxNiAtMjd0LTE2IC0yN2wtMzIwIC0xOTJxLTggLTUgLTE2IC01cS05IDAgLTE2IDRxLTE2IDEwIC0xNiAyOHYxMjhoLTg1OHEzNyAtNTggODMgLTE2NXExNiAtMzcgMjQuNSAtNTV0MjQgLTQ5dDI3IC00N3QyNyAtMzR0MzEuNSAtMjZ0MzMgLThoOTZ2OTZxMCAxNCA5IDIzdDIzIDloMzIwcTE0IDAgMjMgLTl0OSAtMjN2LTMyMHEwIC0xNCAtOSAtMjN0LTIzIC05aC0zMjBxLTE0IDAgLTIzIDl0LTkgMjN2OTZoLTk2DQpxLTMyIDAgLTYxIDEwdC01MSAyMy41dC00NSA0MC41dC0zNyA0NnQtMzMuNSA1N3QtMjguNSA1Ny41dC0yOCA2MC41cS0yMyA1MyAtMzcgODEuNXQtMzYgNjV0LTQ0LjUgNTMuNXQtNDYuNSAxN2gtMzYwcS0yMiAtODQgLTkxIC0xMzh0LTE1NyAtNTRxLTEwNiAwIC0xODEgNzV0LTc1IDE4MXQ3NSAxODF0MTgxIDc1cTg4IDAgMTU3IC01NHQ5MSAtMTM4aDEwNHEyNCAwIDQ2LjUgMTd0NDQuNSA1My41dDM2IDY1dDM3IDgxLjVxMTkgNDEgMjggNjAuNQ0KdDI4LjUgNTcuNXQzMy41IDU3dDM3IDQ2dDQ1IDQwLjV0NTEgMjMuNXQ2MSAxMGgxMDdxMjEgNTcgNzAgOTIuNXQxMTEgMzUuNXE4MCAwIDEzNiAtNTZ0NTYgLTEzNnQtNTYgLTEzNnQtMTM2IC01NnEtNjIgMCAtMTExIDM1LjV0LTcwIDkyLjVoLTEwN3EtMTcgMCAtMzMgLTh0LTMxLjUgLTI2dC0yNyAtMzR0LTI3IC00N3QtMjQgLTQ5dC0yNC41IC01NXEtNDYgLTEwNyAtODMgLTE2NWgxMTE0djEyOHEwIDE4IDE2IDI4dDMyIC0xeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzYwOCIgdW5pY29kZT0iJiN4ZjI4ODsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xMTUwIDc3NHEwIC01NiAtMzkuNSAtOTV0LTk1LjUgLTM5aC0yNTN2MjY5aDI1M3E1NiAwIDk1LjUgLTM5LjV0MzkuNSAtOTUuNXpNMTMyOSA3NzRxMCAxMzAgLTkxLjUgMjIydC0yMjIuNSA5MmgtNDMzdi04OTZoMTgwdjI2OWgyNTNxMTMwIDAgMjIyIDkxLjV0OTIgMjIxLjV6TTE3OTIgNjQwcTAgLTE4MiAtNzEgLTM0OHQtMTkxIC0yODZ0LTI4NiAtMTkxdC0zNDggLTcxdC0zNDggNzF0LTI4NiAxOTF0LTE5MSAyODZ0LTcxIDM0OA0KdDcxIDM0OHQxOTEgMjg2dDI4NiAxOTF0MzQ4IDcxdDM0OCAtNzF0Mjg2IC0xOTF0MTkxIC0yODZ0NzEgLTM0OHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il82MDkiIHVuaWNvZGU9IiYjeGYyODk7IiBob3Jpei1hZHYteD0iMjMwNCIgDQpkPSJNMTY0NSA0MzhxMCA1OSAtMzQgMTA2LjV0LTg3IDY4LjVxLTcgLTQ1IC0yMyAtOTJxLTcgLTI0IC0yNy41IC0zOHQtNDQuNSAtMTRxLTEyIDAgLTI0IDNxLTMxIDEwIC00NSAzOC41dC00IDU4LjVxMjMgNzEgMjMgMTQzcTAgMTIzIC02MSAyMjcuNXQtMTY2IDE2NS41dC0yMjggNjFxLTEzNCAwIC0yNDcgLTczdC0xNjcgLTE5NHExMDggLTI4IDE4OCAtMTA2cTIyIC0yMyAyMiAtNTV0LTIyIC01NHQtNTQgLTIydC01NSAyMg0KcS03NSA3NSAtMTgwIDc1cS0xMDYgMCAtMTgxIC03NC41dC03NSAtMTgwLjV0NzUgLTE4MC41dDE4MSAtNzQuNWgxMDQ2cTc5IDAgMTM0LjUgNTUuNXQ1NS41IDEzMy41ek0xNzk4IDQzOHEwIC0xNDIgLTEwMC41IC0yNDJ0LTI0Mi41IC0xMDBoLTEwNDZxLTE2OSAwIC0yODkgMTE5LjV0LTEyMCAyODguNXEwIDE1MyAxMDAgMjY3dDI0OSAxMzZxNjIgMTg0IDIyMSAyOTh0MzU0IDExNHEyMzUgMCA0MDguNSAtMTU4LjV0MTk2LjUgLTM4OS41DQpxMTE2IC0yNSAxOTIuNSAtMTE4LjV0NzYuNSAtMjE0LjV6TTIwNDggNDM4cTAgLTE3NSAtOTcgLTMxOXEtMjMgLTMzIC02NCAtMzNxLTI0IDAgLTQzIDEzcS0yNiAxNyAtMzIgNDguNXQxMiA1Ny41cTcxIDEwNCA3MSAyMzN0LTcxIDIzM3EtMTggMjYgLTEyIDU3dDMyIDQ5dDU3LjUgMTEuNXQ0OS41IC0zMi41cTk3IC0xNDIgOTcgLTMxOHpNMjMwNCA0MzhxMCAtMjQ0IC0xMzQgLTQ0M3EtMjMgLTM0IC02NCAtMzRxLTIzIDAgLTQyIDEzDQpxLTI2IDE4IC0zMi41IDQ5dDExLjUgNTdxMTA4IDE2NCAxMDggMzU4cTAgMTk1IC0xMDggMzU3cS0xOCAyNiAtMTEuNSA1Ny41dDMyLjUgNDguNXEyNiAxOCA1NyAxMnQ0OSAtMzNxMTM0IC0xOTggMTM0IC00NDJ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNjEwIiB1bmljb2RlPSImI3hmMjhhOyIgDQpkPSJNMTUwMCAtMTNxMCAtODkgLTYzIC0xNTIuNXQtMTUzIC02My41dC0xNTMuNSA2My41dC02My41IDE1Mi41cTAgOTAgNjMuNSAxNTMuNXQxNTMuNSA2My41dDE1MyAtNjMuNXQ2MyAtMTUzLjV6TTEyNjcgMjY4cS0xMTUgLTE1IC0xOTIuNSAtMTAyLjV0LTc3LjUgLTIwNS41cTAgLTc0IDMzIC0xMzhxLTE0NiAtNzggLTM3OSAtNzhxLTEwOSAwIC0yMDEgMjF0LTE1My41IDU0LjV0LTExMC41IDc2LjV0LTc2IDg1dC00NC41IDgzDQp0LTIzLjUgNjYuNXQtNiAzOS41cTAgMTkgNC41IDQyLjV0MTguNSA1NnQzNi41IDU4dDY0IDQzLjV0OTQuNSAxOHQ5NCAtMTcuNXQ2MyAtNDF0MzUuNSAtNTN0MTcuNSAtNDl0NCAtMzMuNXEwIC0zNCAtMjMgLTgxcTI4IC0yNyA4MiAtNDJ0OTMgLTE3bDQwIC0xcTExNSAwIDE5MCA1MXQ3NSAxMzNxMCAyNiAtOSA0OC41dC0zMS41IDQ0LjV0LTQ5LjUgNDF0LTc0IDQ0dC05My41IDQ3LjV0LTExOS41IDU2LjVxLTI4IDEzIC00MyAyMA0KcS0xMTYgNTUgLTE4NyAxMDB0LTEyMi41IDEwMnQtNzIgMTI1LjV0LTIwLjUgMTYyLjVxMCA3OCAyMC41IDE1MHQ2NiAxMzcuNXQxMTIuNSAxMTR0MTY2LjUgNzd0MjIxLjUgMjguNXExMjAgMCAyMjAgLTI2dDE2NC41IC02N3QxMDkuNSAtOTR0NjQgLTEwNS41dDE5IC0xMDMuNXEwIC00NiAtMTUgLTgyLjV0LTM2LjUgLTU4dC00OC41IC0zNnQtNDkgLTE5LjV0LTM5IC01aC04aC0zMnQtMzkgNXQtNDQgMTR0LTQxIDI4dC0zNyA0NnQtMjQgNzAuNQ0KdC0xMCA5Ny41cS0xNSAxNiAtNTkgMjUuNXQtODEgMTAuNWwtMzcgMXEtNjggMCAtMTE3LjUgLTMxdC03MC41IC03MHQtMjEgLTc2cTAgLTI0IDUgLTQzdDI0IC00NnQ1MyAtNTF0OTcgLTUzLjV0MTUwIC01OC41cTc2IC0yNSAxMzguNSAtNTMuNXQxMDkgLTU1LjV0ODMgLTU5dDYwLjUgLTU5LjV0NDEgLTYyLjV0MjYuNSAtNjJ0MTQuNSAtNjMuNXQ2IC02MnQxIC02Mi41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzYxMSIgdW5pY29kZT0iJiN4ZjI4YjsiIA0KZD0iTTcwNCAzNTJ2NTc2cTAgMTQgLTkgMjN0LTIzIDloLTI1NnEtMTQgMCAtMjMgLTl0LTkgLTIzdi01NzZxMCAtMTQgOSAtMjN0MjMgLTloMjU2cTE0IDAgMjMgOXQ5IDIzek0xMTUyIDM1MnY1NzZxMCAxNCAtOSAyM3QtMjMgOWgtMjU2cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTU3NnEwIC0xNCA5IC0yM3QyMyAtOWgyNTZxMTQgMCAyMyA5dDkgMjN6TTE1MzYgNjQwcTAgLTIwOSAtMTAzIC0zODUuNXQtMjc5LjUgLTI3OS41dC0zODUuNSAtMTAzDQp0LTM4NS41IDEwM3QtMjc5LjUgMjc5LjV0LTEwMyAzODUuNXQxMDMgMzg1LjV0Mjc5LjUgMjc5LjV0Mzg1LjUgMTAzdDM4NS41IC0xMDN0Mjc5LjUgLTI3OS41dDEwMyAtMzg1LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNjEyIiB1bmljb2RlPSImI3hmMjhjOyIgDQpkPSJNNzY4IDE0MDhxMjA5IDAgMzg1LjUgLTEwM3QyNzkuNSAtMjc5LjV0MTAzIC0zODUuNXQtMTAzIC0zODUuNXQtMjc5LjUgLTI3OS41dC0zODUuNSAtMTAzdC0zODUuNSAxMDN0LTI3OS41IDI3OS41dC0xMDMgMzg1LjV0MTAzIDM4NS41dDI3OS41IDI3OS41dDM4NS41IDEwM3pNNzY4IDk2cTE0OCAwIDI3MyA3M3QxOTggMTk4dDczIDI3M3QtNzMgMjczdC0xOTggMTk4dC0yNzMgNzN0LTI3MyAtNzN0LTE5OCAtMTk4dC03MyAtMjczDQp0NzMgLTI3M3QxOTggLTE5OHQyNzMgLTczek04NjQgMzIwcS0xNCAwIC0yMyA5dC05IDIzdjU3NnEwIDE0IDkgMjN0MjMgOWgxOTJxMTQgMCAyMyAtOXQ5IC0yM3YtNTc2cTAgLTE0IC05IC0yM3QtMjMgLTloLTE5MnpNNDgwIDMyMHEtMTQgMCAtMjMgOXQtOSAyM3Y1NzZxMCAxNCA5IDIzdDIzIDloMTkycTE0IDAgMjMgLTl0OSAtMjN2LTU3NnEwIC0xNCAtOSAtMjN0LTIzIC05aC0xOTJ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNjEzIiB1bmljb2RlPSImI3hmMjhkOyIgDQpkPSJNMTA4OCAzNTJ2NTc2cTAgMTQgLTkgMjN0LTIzIDloLTU3NnEtMTQgMCAtMjMgLTl0LTkgLTIzdi01NzZxMCAtMTQgOSAtMjN0MjMgLTloNTc2cTE0IDAgMjMgOXQ5IDIzek0xNTM2IDY0MHEwIC0yMDkgLTEwMyAtMzg1LjV0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzdC0yNzkuNSAyNzkuNXQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN0Mzg1LjUgLTEwM3QyNzkuNSAtMjc5LjUNCnQxMDMgLTM4NS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzYxNCIgdW5pY29kZT0iJiN4ZjI4ZTsiIA0KZD0iTTc2OCAxNDA4cTIwOSAwIDM4NS41IC0xMDN0Mjc5LjUgLTI3OS41dDEwMyAtMzg1LjV0LTEwMyAtMzg1LjV0LTI3OS41IC0yNzkuNXQtMzg1LjUgLTEwM3QtMzg1LjUgMTAzdC0yNzkuNSAyNzkuNXQtMTAzIDM4NS41dDEwMyAzODUuNXQyNzkuNSAyNzkuNXQzODUuNSAxMDN6TTc2OCA5NnExNDggMCAyNzMgNzN0MTk4IDE5OHQ3MyAyNzN0LTczIDI3M3QtMTk4IDE5OHQtMjczIDczdC0yNzMgLTczdC0xOTggLTE5OHQtNzMgLTI3Mw0KdDczIC0yNzN0MTk4IC0xOTh0MjczIC03M3pNNDgwIDMyMHEtMTQgMCAtMjMgOXQtOSAyM3Y1NzZxMCAxNCA5IDIzdDIzIDloNTc2cTE0IDAgMjMgLTl0OSAtMjN2LTU3NnEwIC0xNCAtOSAtMjN0LTIzIC05aC01NzZ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNjE1IiB1bmljb2RlPSImI3hmMjkwOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE3NTcgMTI4bDM1IC0zMTNxMyAtMjggLTE2IC01MHEtMTkgLTIxIC00OCAtMjFoLTE2NjRxLTI5IDAgLTQ4IDIxcS0xOSAyMiAtMTYgNTBsMzUgMzEzaDE3MjJ6TTE2NjQgOTY3bDg2IC03NzVoLTE3MDhsODYgNzc1cTMgMjQgMjEgNDAuNXQ0MyAxNi41aDI1NnYtMTI4cTAgLTUzIDM3LjUgLTkwLjV0OTAuNSAtMzcuNXQ5MC41IDM3LjV0MzcuNSA5MC41djEyOGgzODR2LTEyOHEwIC01MyAzNy41IC05MC41dDkwLjUgLTM3LjUNCnQ5MC41IDM3LjV0MzcuNSA5MC41djEyOGgyNTZxMjUgMCA0MyAtMTYuNXQyMSAtNDAuNXpNMTI4MCAxMTUydi0yNTZxMCAtMjYgLTE5IC00NXQtNDUgLTE5dC00NSAxOXQtMTkgNDV2MjU2cTAgMTA2IC03NSAxODF0LTE4MSA3NXQtMTgxIC03NXQtNzUgLTE4MXYtMjU2cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOXQtNDUgMTl0LTE5IDQ1djI1NnEwIDE1OSAxMTIuNSAyNzEuNXQyNzEuNSAxMTIuNXQyNzEuNSAtMTEyLjV0MTEyLjUgLTI3MS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzYxNiIgdW5pY29kZT0iJiN4ZjI5MTsiIGhvcml6LWFkdi14PSIyMDQ4IiANCmQ9Ik0xOTIwIDc2OHE1MyAwIDkwLjUgLTM3LjV0MzcuNSAtOTAuNXQtMzcuNSAtOTAuNXQtOTAuNSAtMzcuNWgtMTVsLTExNSAtNjYycS04IC00NiAtNDQgLTc2dC04MiAtMzBoLTEyODBxLTQ2IDAgLTgyIDMwdC00NCA3NmwtMTE1IDY2MmgtMTVxLTUzIDAgLTkwLjUgMzcuNXQtMzcuNSA5MC41dDM3LjUgOTAuNXQ5MC41IDM3LjVoMTc5MnpNNDg1IC0zMnEyNiAyIDQzLjUgMjIuNXQxNS41IDQ2LjVsLTMyIDQxNnEtMiAyNiAtMjIuNSA0My41DQp0LTQ2LjUgMTUuNXQtNDMuNSAtMjIuNXQtMTUuNSAtNDYuNWwzMiAtNDE2cTIgLTI1IDIwLjUgLTQydDQzLjUgLTE3aDV6TTg5NiAzMnY0MTZxMCAyNiAtMTkgNDV0LTQ1IDE5dC00NSAtMTl0LTE5IC00NXYtNDE2cTAgLTI2IDE5IC00NXQ0NSAtMTl0NDUgMTl0MTkgNDV6TTEyODAgMzJ2NDE2cTAgMjYgLTE5IDQ1dC00NSAxOXQtNDUgLTE5dC0xOSAtNDV2LTQxNnEwIC0yNiAxOSAtNDV0NDUgLTE5dDQ1IDE5dDE5IDQ1ek0xNjMyIDI3bDMyIDQxNg0KcTIgMjYgLTE1LjUgNDYuNXQtNDMuNSAyMi41dC00Ni41IC0xNS41dC0yMi41IC00My41bC0zMiAtNDE2cS0yIC0yNiAxNS41IC00Ni41dDQzLjUgLTIyLjVoNXEyNSAwIDQzLjUgMTd0MjAuNSA0MnpNNDc2IDEyNDRsLTkzIC00MTJoLTEzMmwxMDEgNDQxcTE5IDg4IDg5IDE0My41dDE2MCA1NS41aDE2N3EwIDI2IDE5IDQ1dDQ1IDE5aDM4NHEyNiAwIDQ1IC0xOXQxOSAtNDVoMTY3cTkwIDAgMTYwIC01NS41dDg5IC0xNDMuNWwxMDEgLTQ0MQ0KaC0xMzJsLTkzIDQxMnEtMTEgNDQgLTQ1LjUgNzJ0LTc5LjUgMjhoLTE2N3EwIC0yNiAtMTkgLTQ1dC00NSAtMTloLTM4NHEtMjYgMCAtNDUgMTl0LTE5IDQ1aC0xNjdxLTQ1IDAgLTc5LjUgLTI4dC00NS41IC03MnoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il82MTciIHVuaWNvZGU9IiYjeGYyOTI7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNOTkxIDUxMmw2NCAyNTZoLTI1NGwtNjQgLTI1NmgyNTR6TTE3NTkgMTAxNmwtNTYgLTIyNHEtNyAtMjQgLTMxIC0yNGgtMzI3bC02NCAtMjU2aDMxMXExNSAwIDI1IC0xMnExMCAtMTQgNiAtMjhsLTU2IC0yMjRxLTUgLTI0IC0zMSAtMjRoLTMyN2wtODEgLTMyOHEtNyAtMjQgLTMxIC0yNGgtMjI0cS0xNiAwIC0yNiAxMnEtOSAxMiAtNiAyOGw3OCAzMTJoLTI1NGwtODEgLTMyOHEtNyAtMjQgLTMxIC0yNGgtMjI1cS0xNSAwIC0yNSAxMg0KcS05IDEyIC02IDI4bDc4IDMxMmgtMzExcS0xNSAwIC0yNSAxMnEtOSAxMiAtNiAyOGw1NiAyMjRxNyAyNCAzMSAyNGgzMjdsNjQgMjU2aC0zMTFxLTE1IDAgLTI1IDEycS0xMCAxNCAtNiAyOGw1NiAyMjRxNSAyNCAzMSAyNGgzMjdsODEgMzI4cTcgMjQgMzIgMjRoMjI0cTE1IDAgMjUgLTEycTkgLTEyIDYgLTI4bC03OCAtMzEyaDI1NGw4MSAzMjhxNyAyNCAzMiAyNGgyMjRxMTUgMCAyNSAtMTJxOSAtMTIgNiAtMjhsLTc4IC0zMTJoMzExDQpxMTUgMCAyNSAtMTJxOSAtMTIgNiAtMjh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNjE4IiB1bmljb2RlPSImI3hmMjkzOyIgDQpkPSJNODQxIDQ4M2wxNDggLTE0OGwtMTQ5IC0xNDl6TTg0MCAxMDk0bDE0OSAtMTQ5bC0xNDggLTE0OHpNNzEwIC0xMzBsNDY0IDQ2NGwtMzA2IDMwNmwzMDYgMzA2bC00NjQgNDY0di02MTFsLTI1NSAyNTVsLTkzIC05M2wzMjAgLTMyMWwtMzIwIC0zMjFsOTMgLTkzbDI1NSAyNTV2LTYxMXpNMTQyOSA2NDBxMCAtMjA5IC0zMiAtMzY1LjV0LTg3LjUgLTI1N3QtMTQwLjUgLTE2Mi41dC0xODEuNSAtODYuNXQtMjE5LjUgLTI0LjUNCnQtMjE5LjUgMjQuNXQtMTgxLjUgODYuNXQtMTQwLjUgMTYyLjV0LTg3LjUgMjU3dC0zMiAzNjUuNXQzMiAzNjUuNXQ4Ny41IDI1N3QxNDAuNSAxNjIuNXQxODEuNSA4Ni41dDIxOS41IDI0LjV0MjE5LjUgLTI0LjV0MTgxLjUgLTg2LjV0MTQwLjUgLTE2Mi41dDg3LjUgLTI1N3QzMiAtMzY1LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNjE5IiB1bmljb2RlPSImI3hmMjk0OyIgaG9yaXotYWR2LXg9IjEwMjQiIA0KZD0iTTU5NiAxMTNsMTczIDE3MmwtMTczIDE3MnYtMzQ0ek01OTYgODIzbDE3MyAxNzJsLTE3MyAxNzJ2LTM0NHpNNjI4IDY0MGwzNTYgLTM1NmwtNTM5IC01NDB2NzExbC0yOTcgLTI5NmwtMTA4IDEwOGwzNzIgMzczbC0zNzIgMzczbDEwOCAxMDhsMjk3IC0yOTZ2NzExbDUzOSAtNTQweiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzYyMCIgdW5pY29kZT0iJiN4ZjI5NTsiIA0KZD0iTTEyODAgMjU2cTAgNTIgLTM4IDkwdC05MCAzOHQtOTAgLTM4dC0zOCAtOTB0MzggLTkwdDkwIC0zOHQ5MCAzOHQzOCA5MHpNNTEyIDEwMjRxMCA1MiAtMzggOTB0LTkwIDM4dC05MCAtMzh0LTM4IC05MHQzOCAtOTB0OTAgLTM4dDkwIDM4dDM4IDkwek0xNTM2IDI1NnEwIC0xNTkgLTExMi41IC0yNzEuNXQtMjcxLjUgLTExMi41dC0yNzEuNSAxMTIuNXQtMTEyLjUgMjcxLjV0MTEyLjUgMjcxLjV0MjcxLjUgMTEyLjV0MjcxLjUgLTExMi41DQp0MTEyLjUgLTI3MS41ek0xNDQwIDEzNDRxMCAtMjAgLTEzIC0zOGwtMTA1NiAtMTQwOHEtMTkgLTI2IC01MSAtMjZoLTE2MHEtMjYgMCAtNDUgMTl0LTE5IDQ1cTAgMjAgMTMgMzhsMTA1NiAxNDA4cTE5IDI2IDUxIDI2aDE2MHEyNiAwIDQ1IC0xOXQxOSAtNDV6TTc2OCAxMDI0cTAgLTE1OSAtMTEyLjUgLTI3MS41dC0yNzEuNSAtMTEyLjV0LTI3MS41IDExMi41dC0xMTIuNSAyNzEuNXQxMTIuNSAyNzEuNXQyNzEuNSAxMTIuNQ0KdDI3MS41IC0xMTIuNXQxMTIuNSAtMjcxLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNjIxIiB1bmljb2RlPSImI3hmMjk2OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTEwNCA4MzBsNzkyIC0xMDE1bC04NjggNjMwcS0xOCAxMyAtMjUgMzQuNXQwIDQyLjVsMTAxIDMwOHYwek01NjYgODMwaDY2MGwtMzMwIC0xMDE1djB6TTM2OCAxNDQybDE5OCAtNjEyaC00NjJsMTk4IDYxMnE4IDIzIDMzIDIzdDMzIC0yM3pNMTY4OCA4MzBsMTAxIC0zMDhxNyAtMjEgMCAtNDIuNXQtMjUgLTM0LjVsLTg2OCAtNjMwbDc5MiAxMDE1djB6TTE2ODggODMwaC00NjJsMTk4IDYxMnE4IDIzIDMzIDIzdDMzIC0yM3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il82MjIiIHVuaWNvZGU9IiYjeGYyOTc7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMzg0IDcwNGgxNjB2MjI0aC0xNjB2LTIyNHpNMTIyMSAzNzJ2OTJxLTEwNCAtMzYgLTI0MyAtMzhxLTEzNSAtMSAtMjU5LjUgNDYuNXQtMjIwLjUgMTIyLjVsMSAtOTZxODggLTgwIDIxMiAtMTI4LjV0MjcyIC00Ny41cTEyOSAwIDIzOCA0OXpNNjQwIDcwNGg2NDB2MjI0aC02NDB2LTIyNHpNMTc5MiA3MzZxMCAtMTg3IC05OSAtMzUycTg5IC0xMDIgODkgLTIyOXEwIC0xNTcgLTEyOS41IC0yNjh0LTMxMy41IC0xMTENCnEtMTIyIDAgLTIyNSA1Mi41dC0xNjEgMTQwLjVxLTE5IC0xIC01NyAtMXQtNTcgMXEtNTggLTg4IC0xNjEgLTE0MC41dC0yMjUgLTUyLjVxLTE4NCAwIC0zMTMuNSAxMTF0LTEyOS41IDI2OHEwIDEyNyA4OSAyMjlxLTk5IDE2NSAtOTkgMzUycTAgMjA5IDEyMCAzODUuNXQzMjYuNSAyNzkuNXQ0NDkuNSAxMDN0NDQ5LjUgLTEwM3QzMjYuNSAtMjc5LjV0MTIwIC0zODUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il82MjMiIHVuaWNvZGU9IiYjeGYyOTg7IiANCmQ9Ik01MTUgNjI1di0xMjhoLTI1MnYxMjhoMjUyek01MTUgODgwdi0xMjdoLTI1MnYxMjdoMjUyek0xMjczIDM2OXYtMTI4aC0zNDF2MTI4aDM0MXpNMTI3MyA2MjV2LTEyOGgtNjcydjEyOGg2NzJ6TTEyNzMgODgwdi0xMjdoLTY3MnYxMjdoNjcyek0xNDA4IDIwdjEyNDBxMCA4IC02IDE0dC0xNCA2aC0zMmwtMzc4IC0yNTZsLTIxMCAxNzFsLTIxMCAtMTcxbC0zNzggMjU2aC0zMnEtOCAwIC0xNCAtNnQtNiAtMTR2LTEyNDBxMCAtOCA2IC0xNA0KdDE0IC02aDEyNDBxOCAwIDE0IDZ0NiAxNHpNNTUzIDExMzBsMTg1IDE1MGgtNDA2ek05ODMgMTEzMGwyMjEgMTUwaC00MDZ6TTE1MzYgMTI2MHYtMTI0MHEwIC02MiAtNDMgLTEwNXQtMTA1IC00M2gtMTI0MHEtNjIgMCAtMTA1IDQzdC00MyAxMDV2MTI0MHEwIDYyIDQzIDEwNXQxMDUgNDNoMTI0MHE2MiAwIDEwNSAtNDN0NDMgLTEwNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Il82MjQiIHVuaWNvZGU9IiYjeGYyOTk7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNODk2IDcyMHEtMTA0IDE5NiAtMTYwIDI3OHEtMTM5IDIwMiAtMzQ3IDMxOHEtMzQgMTkgLTcwIDM2cS04OSA0MCAtOTQgMzJ0MzQgLTM4bDM5IC0zMXE2MiAtNDMgMTEyLjUgLTkzLjV0OTQuNSAtMTE2LjV0NzAuNSAtMTEzdDcwLjUgLTEzMXE5IC0xNyAxMyAtMjVxNDQgLTg0IDg0IC0xNTN0OTggLTE1NHQxMTUuNSAtMTUwdDEzMSAtMTIzLjV0MTQ4LjUgLTkwLjVxMTUzIC02NiAxNTQgLTYwcTEgMyAtNDkgMzdxLTUzIDM2IC04MSA1Nw0KcS03NyA1OCAtMTc5IDIxMXQtMTg1IDMxMHpNNTQ5IDE3N3EtNzYgNjAgLTEzMi41IDEyNXQtOTggMTQzLjV0LTcxIDE1NC41dC01OC41IDE4NnQtNTIgMjA5dC02MC41IDI1MnQtNzYuNSAyODlxMjczIDAgNDk3LjUgLTM2dDM3OSAtOTJ0MjcxIC0xNDQuNXQxODUuNSAtMTcyLjV0MTEwIC0xOTguNXQ1NiAtMTk5LjV0MTIuNSAtMTk4LjV0LTkuNSAtMTczdC0yMCAtMTQzLjV0LTEzIC0xMDdsMzIzIC0zMjdoLTEwNGwtMjgxIDI4NQ0KcS0yMiAtMiAtOTEuNSAtMTR0LTEyMS41IC0xOXQtMTM4IC02dC0xNjAuNSAxN3QtMTY3LjUgNTl0LTE3OSAxMTF6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNjI1IiB1bmljb2RlPSImI3hmMjlhOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTEzNzQgODc5cS02IDI2IC0yOC41IDM5LjV0LTQ4LjUgNy41cS0yNjEgLTYyIC00MDEgLTYydC00MDEgNjJxLTI2IDYgLTQ4LjUgLTcuNXQtMjguNSAtMzkuNXQ3LjUgLTQ4LjV0MzkuNSAtMjguNXExOTQgLTQ2IDMwMyAtNThxLTIgLTE1OCAtMTUuNSAtMjY5dC0yNi41IC0xNTUuNXQtNDEgLTExNS41bC05IC0yMXEtMTAgLTI1IDEgLTQ5dDM2IC0zNHE5IC00IDIzIC00cTQ0IDAgNjAgNDFsOCAyMHE1NCAxMzkgNzEgMjU5aDQyDQpxMTcgLTEyMCA3MSAtMjU5bDggLTIwcTE2IC00MSA2MCAtNDFxMTQgMCAyMyA0cTI1IDEwIDM2IDM0dDEgNDlsLTkgMjFxLTI4IDcxIC00MSAxMTUuNXQtMjYuNSAxNTUuNXQtMTUuNSAyNjlxMTA5IDEyIDMwMyA1OHEyNiA2IDM5LjUgMjguNXQ3LjUgNDguNXpNMTAyNCAxMDI0cTAgNTMgLTM3LjUgOTAuNXQtOTAuNSAzNy41dC05MC41IC0zNy41dC0zNy41IC05MC41dDM3LjUgLTkwLjV0OTAuNSAtMzcuNXQ5MC41IDM3LjV0MzcuNSA5MC41eg0KTTE2MDAgNjQwcTAgLTE0MyAtNTUuNSAtMjczLjV0LTE1MCAtMjI1dC0yMjUgLTE1MHQtMjczLjUgLTU1LjV0LTI3My41IDU1LjV0LTIyNSAxNTB0LTE1MCAyMjV0LTU1LjUgMjczLjV0NTUuNSAyNzMuNXQxNTAgMjI1dDIyNSAxNTB0MjczLjUgNTUuNXQyNzMuNSAtNTUuNXQyMjUgLTE1MHQxNTAgLTIyNXQ1NS41IC0yNzMuNXpNODk2IDE0MDhxLTE1NiAwIC0yOTggLTYxdC0yNDUgLTE2NHQtMTY0IC0yNDV0LTYxIC0yOTh0NjEgLTI5OA0KdDE2NCAtMjQ1dDI0NSAtMTY0dDI5OCAtNjF0Mjk4IDYxdDI0NSAxNjR0MTY0IDI0NXQ2MSAyOTh0LTYxIDI5OHQtMTY0IDI0NXQtMjQ1IDE2NHQtMjk4IDYxek0xNzkyIDY0MHEwIC0xODIgLTcxIC0zNDh0LTE5MSAtMjg2dC0yODYgLTE5MXQtMzQ4IC03MXQtMzQ4IDcxdC0yODYgMTkxdC0xOTEgMjg2dC03MSAzNDh0NzEgMzQ4dDE5MSAyODZ0Mjg2IDE5MXQzNDggNzF0MzQ4IC03MXQyODYgLTE5MXQxOTEgLTI4NnQ3MSAtMzQ4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iXzYyNiIgdW5pY29kZT0iJiN4ZjI5YjsiIA0KZD0iTTE0MzggNzIzcTM0IC0zNSAyOSAtODJsLTQ0IC01NTFxLTQgLTQyIC0zNC41IC03MHQtNzEuNSAtMjhxLTYgMCAtOSAxcS00NCAzIC03Mi41IDM2LjV0LTI1LjUgNzcuNWwzNSA0MjlsLTE0MyAtOHE1NSAtMTEzIDU1IC0yNDBxMCAtMjE2IC0xNDggLTM3MmwtMTM3IDEzN3E5MSAxMDEgOTEgMjM1cTAgMTQ1IC0xMDIuNSAyNDh0LTI0Ny41IDEwM3EtMTM0IDAgLTIzNiAtOTJsLTEzNyAxMzhxMTIwIDExNCAyODQgMTQxbDI2NCAzMDANCmwtMTQ5IDg3bC0xODEgLTE2MXEtMzMgLTMwIC03NyAtMjcuNXQtNzMgMzUuNXQtMjYuNSA3N3QzNC41IDczbDIzOSAyMTNxMjYgMjMgNjAgMjYuNXQ2NCAtMTQuNWw0ODggLTI4M3EzNiAtMjEgNDggLTY4cTE3IC02NyAtMjYgLTExN2wtMjA1IC0yMzJsMzcxIDIwcTQ5IDMgODMgLTMyek0xMjQwIDExODBxLTc0IDAgLTEyNiA1MnQtNTIgMTI2dDUyIDEyNnQxMjYgNTJ0MTI2LjUgLTUydDUyLjUgLTEyNnQtNTIuNSAtMTI2dC0xMjYuNSAtNTJ6DQpNNjEzIC02MnExMDYgMCAxOTYgNjFsMTM5IC0xMzlxLTE0NiAtMTE2IC0zMzUgLTExNnEtMTQ4IDAgLTI3My41IDczdC0xOTguNSAxOTh0LTczIDI3M3EwIDE4OCAxMTYgMzM2bDEzOSAtMTM5cS02MCAtODggLTYwIC0xOTdxMCAtMTQ1IDEwMi41IC0yNDcuNXQyNDcuNSAtMTAyLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNjI3IiB1bmljb2RlPSImI3hmMjljOyIgDQpkPSJNODgwIDMzNnYtMTYwcTAgLTE0IC05IC0yM3QtMjMgLTloLTE2MHEtMTQgMCAtMjMgOXQtOSAyM3YxNjBxMCAxNCA5IDIzdDIzIDloMTYwcTE0IDAgMjMgLTl0OSAtMjN6TTExMzYgODMycTAgLTUwIC0xNSAtOTB0LTQ1LjUgLTY5dC01MiAtNDR0LTU5LjUgLTM2cS0zMiAtMTggLTQ2LjUgLTI4dC0yNiAtMjR0LTExLjUgLTI5di0zMnEwIC0xNCAtOSAtMjN0LTIzIC05aC0xNjBxLTE0IDAgLTIzIDl0LTkgMjN2NjhxMCAzNSAxMC41IDY0LjUNCnQyNCA0Ny41dDM5IDM1LjV0NDEgMjUuNXQ0NC41IDIxcTUzIDI1IDc1IDQzdDIyIDQ5cTAgNDIgLTQzLjUgNzEuNXQtOTUuNSAyOS41cS01NiAwIC05NSAtMjdxLTI5IC0yMCAtODAgLTgzcS05IC0xMiAtMjUgLTEycS0xMSAwIC0xOSA2bC0xMDggODJxLTEwIDcgLTEyIDIwdDUgMjNxMTIyIDE5MiAzNDkgMTkycTEyOSAwIDIzOC41IC04OS41dDEwOS41IC0yMTQuNXpNNzY4IDEyODBxLTEzMCAwIC0yNDguNSAtNTF0LTIwNCAtMTM2LjUNCnQtMTM2LjUgLTIwNHQtNTEgLTI0OC41dDUxIC0yNDguNXQxMzYuNSAtMjA0dDIwNCAtMTM2LjV0MjQ4LjUgLTUxdDI0OC41IDUxdDIwNCAxMzYuNXQxMzYuNSAyMDR0NTEgMjQ4LjV0LTUxIDI0OC41dC0xMzYuNSAyMDR0LTIwNCAxMzYuNXQtMjQ4LjUgNTF6TTE1MzYgNjQwcTAgLTIwOSAtMTAzIC0zODUuNXQtMjc5LjUgLTI3OS41dC0zODUuNSAtMTAzdC0zODUuNSAxMDN0LTI3OS41IDI3OS41dC0xMDMgMzg1LjV0MTAzIDM4NS41DQp0Mjc5LjUgMjc5LjV0Mzg1LjUgMTAzdDM4NS41IC0xMDN0Mjc5LjUgLTI3OS41dDEwMyAtMzg1LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNjI4IiB1bmljb2RlPSImI3hmMjlkOyIgaG9yaXotYWR2LXg9IjE0MDgiIA0KZD0iTTM2NiAxMjI1cS02NCAwIC0xMTAgNDUuNXQtNDYgMTEwLjVxMCA2NCA0NiAxMDkuNXQxMTAgNDUuNXQxMDkuNSAtNDUuNXQ0NS41IC0xMDkuNXEwIC02NSAtNDUuNSAtMTEwLjV0LTEwOS41IC00NS41ek05MTcgNTgzcTAgLTUwIC0zMCAtNjcuNXQtNjMuNSAtNi41dC00Ny41IDM0bC0zNjcgNDM4cS03IDEyIC0xNCAxNS41dC0xMSAxLjVsLTMgLTNxLTcgLTggNCAtMjFsMTIyIC0xMzlsMSAtMzU0bC0xNjEgLTQ1Nw0KcS02NyAtMTkyIC05MiAtMjM0cS0xNSAtMjYgLTI4IC0zMnEtNTAgLTI2IC0xMDMgLTFxLTI5IDEzIC00MS41IDQzdC05LjUgNTdxMiAxNyAxOTcgNjE4bDUgNDE2bC04NSAtMTY0bDM1IC0yMjJxNCAtMjQgLTEgLTQydC0xNCAtMjcuNXQtMTkgLTE2dC0xNyAtNy41bC03IC0ycS0xOSAtMyAtMzQuNSAzdC0yNCAxNnQtMTQgMjJ0LTcuNSAxOS41dC0yIDkuNWwtNDYgMjk5bDIxMSAzODFxMjMgMzQgMTEzIDM0cTc1IDAgMTA3IC00MGw0MjQgLTUyMQ0KcTcgLTUgMTQgLTE3bDMgLTNsLTEgLTFxNyAtMTMgNyAtMjl6TTUxNCA0MzNxNDMgLTExMyA4OC41IC0yMjV0NjkuNSAtMTY4bDI0IC01NXEzNiAtOTMgNDIgLTEyNXExMSAtNzAgLTM2IC05N3EtMzUgLTIyIC02NiAtMTZ0LTUxIDIydC0yOSAzNWgtMXEtNiAxNiAtOCAyNWwtMTI0IDM1MXpNMTMzOCAtMTU5cTMxIC00OSAzMSAtNTdxMCAtNSAtMyAtN3EtOSAtNSAtMTQuNSAwLjV0LTE1LjUgMjZ0LTE2IDMwLjVxLTExNCAxNzIgLTQyMyA2NjENCnEzIC0xIDcgMXQ3IDRsMyAycTExIDkgMTEgMTd6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNjI5IiB1bmljb2RlPSImI3hmMjllOyIgaG9yaXotYWR2LXg9IjIzMDQiIA0KZD0iTTUwNCA1NDJoMTcxbC0xIDI2NXpNMTUzMCA2NDFxMCA4NyAtNTAuNSAxNDB0LTE0Ni41IDUzaC01NHYtMzg4aDUycTkxIDAgMTQ1IDU3dDU0IDEzOHpNOTU2IDEwMThsMSAtNzU2cTAgLTE0IC05LjUgLTI0dC0yMy41IC0xMGgtMjE2cS0xNCAwIC0yMy41IDEwdC05LjUgMjR2NjJoLTI5MWwtNTUgLTgxcS0xMCAtMTUgLTI4IC0xNWgtMjY3cS0yMSAwIC0zMC41IDE4dDMuNSAzNWw1NTYgNzU3cTkgMTQgMjcgMTRoMzMycTE0IDAgMjQgLTEwDQp0MTAgLTI0ek0xNzgzIDY0MXEwIC0xOTMgLTEyNS41IC0zMDN0LTMyNC41IC0xMTBoLTI3MHEtMTQgMCAtMjQgMTB0LTEwIDI0djc1NnEwIDE0IDEwIDI0dDI0IDEwaDI2OHEyMDAgMCAzMjYgLTEwOXQxMjYgLTMwMnpNMTkzOSA2NDBxMCAtMTEgLTAuNSAtMjl0LTggLTcxLjV0LTIxLjUgLTEwMnQtNDQuNSAtMTA4dC03My41IC0xMDIuNWgtNTFxMzggNDUgNjYuNSAxMDQuNXQ0MS41IDExMnQyMSA5OHQ5IDcyLjVsMSAyN3EwIDggLTAuNSAyMi41DQp0LTcuNSA2MHQtMjAgOTEuNXQtNDEgMTExLjV0LTY2IDEyNC41aDQzcTQxIC00NyA3MiAtMTA3dDQ1LjUgLTExMS41dDIzIC05NnQxMC41IC03MC41ek0yMTIzIDY0MHEwIC0xMSAtMC41IC0yOXQtOCAtNzEuNXQtMjEuNSAtMTAydC00NSAtMTA4dC03NCAtMTAyLjVoLTUxcTM4IDQ1IDY2LjUgMTA0LjV0NDEuNSAxMTJ0MjEgOTh0OSA3Mi41bDEgMjdxMCA4IC0wLjUgMjIuNXQtNy41IDYwdC0xOS41IDkxLjV0LTQwLjUgMTExLjV0LTY2IDEyNC41DQpoNDNxNDEgLTQ3IDcyIC0xMDd0NDUuNSAtMTExLjV0MjMgLTk2dDEwLjUgLTcwLjV6TTIzMDQgNjQwcTAgLTExIC0wLjUgLTI5dC04IC03MS41dC0yMS41IC0xMDJ0LTQ0LjUgLTEwOHQtNzMuNSAtMTAyLjVoLTUxcTM4IDQ1IDY2IDEwNC41dDQxIDExMnQyMSA5OHQ5IDcyLjVsMSAyN3EwIDggLTAuNSAyMi41dC03LjUgNjB0LTE5LjUgOTEuNXQtNDAuNSAxMTEuNXQtNjYgMTI0LjVoNDNxNDEgLTQ3IDcyIC0xMDd0NDUuNSAtMTExLjV0MjMgLTk2DQp0OS41IC03MC41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJBMCIgdW5pY29kZT0iJiN4ZjJhMDsiIGhvcml6LWFkdi14PSIxNDA4IiANCmQ9Ik02MTcgLTE1M3EwIDExIC0xMyA1OHQtMzEgMTA3dC0yMCA2OXEtMSA0IC01IDI2LjV0LTguNSAzNnQtMTMuNSAyMS41cS0xNSAxNCAtNTEgMTRxLTIzIDAgLTcwIC01LjV0LTcxIC01LjVxLTM0IDAgLTQ3IDExcS02IDUgLTExIDE1LjV0LTcuNSAyMHQtNi41IDI0dC01IDE4LjVxLTM3IDEyOCAtMzcgMjU1dDM3IDI1NXExIDQgNSAxOC41dDYuNSAyNHQ3LjUgMjB0MTEgMTUuNXExMyAxMSA0NyAxMXEyNCAwIDcxIC01LjV0NzAgLTUuNQ0KcTM2IDAgNTEgMTRxOSA4IDEzLjUgMjEuNXQ4LjUgMzZ0NSAyNi41cTIgOSAyMCA2OXQzMSAxMDd0MTMgNThxMCAyMiAtNDMuNSA1Mi41dC03NS41IDQyLjVxLTIwIDggLTQ1IDhxLTM0IDAgLTk4IC0xOHEtNTcgLTE3IC05Ni41IC00MC41dC03MSAtNjZ0LTQ2IC03MHQtNDUuNSAtOTQuNXEtNiAtMTIgLTkgLTE5cS00OSAtMTA3IC02OCAtMjE2dC0xOSAtMjQ0dDE5IC0yNDR0NjggLTIxNnE1NiAtMTIyIDgzIC0xNjFxNjMgLTkxIDE3OSAtMTI3DQpsNiAtMnE2NCAtMTggOTggLTE4cTI1IDAgNDUgOHEzMiAxMiA3NS41IDQyLjV0NDMuNSA1Mi41ek03NzYgNzYwcS0yNiAwIC00NSAxOXQtMTkgNDUuNXQxOSA0NS41cTM3IDM3IDM3IDkwcTAgNTIgLTM3IDkxcS0xOSAxOSAtMTkgNDV0MTkgNDV0NDUgMTl0NDUgLTE5cTc1IC03NSA3NSAtMTgxdC03NSAtMTgxcS0yMSAtMTkgLTQ1IC0xOXpNOTU3IDU3OXEtMjcgMCAtNDUgMTlxLTE5IDE5IC0xOSA0NXQxOSA0NXExMTIgMTE0IDExMiAyNzINCnQtMTEyIDI3MnEtMTkgMTkgLTE5IDQ1dDE5IDQ1dDQ1IDE5dDQ1IC0xOXExNTAgLTE1MCAxNTAgLTM2MnQtMTUwIC0zNjJxLTE4IC0xOSAtNDUgLTE5ek0xMTM4IDM5OHEtMjcgMCAtNDUgMTlxLTE5IDE5IC0xOSA0NXQxOSA0NXE5MCA5MSAxMzguNSAyMDh0NDguNSAyNDV0LTQ4LjUgMjQ1dC0xMzguNSAyMDhxLTE5IDE5IC0xOSA0NXQxOSA0NXQ0NSAxOXQ0NSAtMTlxMTA5IC0xMDkgMTY3IC0yNDl0NTggLTI5NHQtNTggLTI5NHQtMTY3IC0yNDkNCnEtMTggLTE5IC00NSAtMTl6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkExIiB1bmljb2RlPSImI3hmMmExOyIgaG9yaXotYWR2LXg9IjIxNzYiIA0KZD0iTTE5MiAzNTJxLTY2IDAgLTExMyAtNDd0LTQ3IC0xMTN0NDcgLTExM3QxMTMgLTQ3dDExMyA0N3Q0NyAxMTN0LTQ3IDExM3QtMTEzIDQ3ek03MDQgMzUycS02NiAwIC0xMTMgLTQ3dC00NyAtMTEzdDQ3IC0xMTN0MTEzIC00N3QxMTMgNDd0NDcgMTEzdC00NyAxMTN0LTExMyA0N3pNNzA0IDg2NHEtNjYgMCAtMTEzIC00N3QtNDcgLTExM3Q0NyAtMTEzdDExMyAtNDd0MTEzIDQ3dDQ3IDExM3QtNDcgMTEzdC0xMTMgNDd6TTE0NzIgMzUyDQpxLTY2IDAgLTExMyAtNDd0LTQ3IC0xMTN0NDcgLTExM3QxMTMgLTQ3dDExMyA0N3Q0NyAxMTN0LTQ3IDExM3QtMTEzIDQ3ek0xOTg0IDM1MnEtNjYgMCAtMTEzIC00N3QtNDcgLTExM3Q0NyAtMTEzdDExMyAtNDd0MTEzIDQ3dDQ3IDExM3QtNDcgMTEzdC0xMTMgNDd6TTE0NzIgODY0cS02NiAwIC0xMTMgLTQ3dC00NyAtMTEzdDQ3IC0xMTN0MTEzIC00N3QxMTMgNDd0NDcgMTEzdC00NyAxMTN0LTExMyA0N3pNMTk4NCA4NjQNCnEtNjYgMCAtMTEzIC00N3QtNDcgLTExM3Q0NyAtMTEzdDExMyAtNDd0MTEzIDQ3dDQ3IDExM3QtNDcgMTEzdC0xMTMgNDd6TTE5ODQgMTM3NnEtNjYgMCAtMTEzIC00N3QtNDcgLTExM3Q0NyAtMTEzdDExMyAtNDd0MTEzIDQ3dDQ3IDExM3QtNDcgMTEzdC0xMTMgNDd6TTM4NCAxOTJxMCAtODAgLTU2IC0xMzZ0LTEzNiAtNTZ0LTEzNiA1NnQtNTYgMTM2dDU2IDEzNnQxMzYgNTZ0MTM2IC01NnQ1NiAtMTM2ek04OTYgMTkycTAgLTgwIC01NiAtMTM2DQp0LTEzNiAtNTZ0LTEzNiA1NnQtNTYgMTM2dDU2IDEzNnQxMzYgNTZ0MTM2IC01NnQ1NiAtMTM2ek0zODQgNzA0cTAgLTgwIC01NiAtMTM2dC0xMzYgLTU2dC0xMzYgNTZ0LTU2IDEzNnQ1NiAxMzZ0MTM2IDU2dDEzNiAtNTZ0NTYgLTEzNnpNODk2IDcwNHEwIC04MCAtNTYgLTEzNnQtMTM2IC01NnQtMTM2IDU2dC01NiAxMzZ0NTYgMTM2dDEzNiA1NnQxMzYgLTU2dDU2IC0xMzZ6TTM4NCAxMjE2cTAgLTgwIC01NiAtMTM2dC0xMzYgLTU2DQp0LTEzNiA1NnQtNTYgMTM2dDU2IDEzNnQxMzYgNTZ0MTM2IC01NnQ1NiAtMTM2ek0xNjY0IDE5MnEwIC04MCAtNTYgLTEzNnQtMTM2IC01NnQtMTM2IDU2dC01NiAxMzZ0NTYgMTM2dDEzNiA1NnQxMzYgLTU2dDU2IC0xMzZ6TTg5NiAxMjE2cTAgLTgwIC01NiAtMTM2dC0xMzYgLTU2dC0xMzYgNTZ0LTU2IDEzNnQ1NiAxMzZ0MTM2IDU2dDEzNiAtNTZ0NTYgLTEzNnpNMjE3NiAxOTJxMCAtODAgLTU2IC0xMzZ0LTEzNiAtNTZ0LTEzNiA1Ng0KdC01NiAxMzZ0NTYgMTM2dDEzNiA1NnQxMzYgLTU2dDU2IC0xMzZ6TTE2NjQgNzA0cTAgLTgwIC01NiAtMTM2dC0xMzYgLTU2dC0xMzYgNTZ0LTU2IDEzNnQ1NiAxMzZ0MTM2IDU2dDEzNiAtNTZ0NTYgLTEzNnpNMjE3NiA3MDRxMCAtODAgLTU2IC0xMzZ0LTEzNiAtNTZ0LTEzNiA1NnQtNTYgMTM2dDU2IDEzNnQxMzYgNTZ0MTM2IC01NnQ1NiAtMTM2ek0xNjY0IDEyMTZxMCAtODAgLTU2IC0xMzZ0LTEzNiAtNTZ0LTEzNiA1NnQtNTYgMTM2DQp0NTYgMTM2dDEzNiA1NnQxMzYgLTU2dDU2IC0xMzZ6TTIxNzYgMTIxNnEwIC04MCAtNTYgLTEzNnQtMTM2IC01NnQtMTM2IDU2dC01NiAxMzZ0NTYgMTM2dDEzNiA1NnQxMzYgLTU2dDU2IC0xMzZ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkEyIiB1bmljb2RlPSImI3hmMmEyOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTEyOCAtMTkycTAgLTI2IC0xOSAtNDV0LTQ1IC0xOXQtNDUgMTl0LTE5IDQ1dDE5IDQ1dDQ1IDE5dDQ1IC0xOXQxOSAtNDV6TTMyMCAwcTAgLTI2IC0xOSAtNDV0LTQ1IC0xOXQtNDUgMTl0LTE5IDQ1dDE5IDQ1dDQ1IDE5dDQ1IC0xOXQxOSAtNDV6TTM2NSAzNjVsMjU2IC0yNTZsLTkwIC05MGwtMjU2IDI1NnpNNzA0IDM4NHEwIC0yNiAtMTkgLTQ1dC00NSAtMTl0LTQ1IDE5dC0xOSA0NXQxOSA0NXQ0NSAxOXQ0NSAtMTl0MTkgLTQ1eg0KTTE0MTEgNzA0cTAgLTU5IC0xMS41IC0xMDguNXQtMzcuNSAtOTMuNXQtNDQgLTY3LjV0LTUzIC02NC41cS0zMSAtMzUgLTQ1LjUgLTU0dC0zMy41IC01MHQtMjYuNSAtNjR0LTcuNSAtNzRxMCAtMTU5IC0xMTIuNSAtMjcxLjV0LTI3MS41IC0xMTIuNXEtMjYgMCAtNDUgMTl0LTE5IDQ1dDE5IDQ1dDQ1IDE5cTEwNiAwIDE4MSA3NXQ3NSAxODFxMCA1NyAxMS41IDEwNS41dDM3IDkxdDQzLjUgNjYuNXQ1MiA2M3E0MCA0NiA1OS41IDcyDQp0MzcuNSA3NC41dDE4IDEwMy41cTAgMTg1IC0xMzEuNSAzMTYuNXQtMzE2LjUgMTMxLjV0LTMxNi41IC0xMzEuNXQtMTMxLjUgLTMxNi41cTAgLTI2IC0xOSAtNDV0LTQ1IC0xOXQtNDUgMTl0LTE5IDQ1cTAgMTE3IDQ1LjUgMjIzLjV0MTIzIDE4NHQxODQgMTIzdDIyMy41IDQ1LjV0MjIzLjUgLTQ1LjV0MTg0IC0xMjN0MTIzIC0xODR0NDUuNSAtMjIzLjV6TTg5NiA1NzZxMCAtMjYgLTE5IC00NXQtNDUgLTE5dC00NSAxOXQtMTkgNDV0MTkgNDUNCnQ0NSAxOXQ0NSAtMTl0MTkgLTQ1ek0xMTg0IDcwNHEwIC0yNiAtMTkgLTQ1dC00NSAtMTl0LTQ1IDE5dC0xOSA0NXEwIDkzIC02NS41IDE1OC41dC0xNTguNSA2NS41cS05MiAwIC0xNTggLTY1LjV0LTY2IC0xNTguNXEwIC0yNiAtMTkgLTQ1dC00NSAtMTl0LTQ1IDE5dC0xOSA0NXEwIDE0NiAxMDMgMjQ5dDI0OSAxMDN0MjQ5IC0xMDN0MTAzIC0yNDl6TTE1NzggOTkzcTEwIC0yNSAtMSAtNDl0LTM2IC0zNHEtOSAtNCAtMjMgLTQNCnEtMTkgMCAtMzUuNSAxMXQtMjMuNSAzMHEtNjggMTc4IC0yMjQgMjk1cS0yMSAxNiAtMjUgNDJ0MTIgNDdxMTcgMjEgNDMgMjV0NDcgLTEycTE4MyAtMTM3IDI2NiAtMzUxek0xNzg4IDEwNzRxOSAtMjUgLTEuNSAtNDl0LTM1LjUgLTM0cS0xMSAtNCAtMjMgLTRxLTQ0IDAgLTYwIDQxcS05MiAyMzggLTI5NyAzOTNxLTIyIDE2IC0yNS41IDQydDEyLjUgNDdxMTYgMjIgNDIgMjUuNXQ0NyAtMTIuNXEyMzUgLTE3NSAzNDEgLTQ0OXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyQTMiIHVuaWNvZGU9IiYjeGYyYTM7IiBob3Jpei1hZHYteD0iMjMwNCIgDQpkPSJNMTAzMiA1NzZxLTU5IDIgLTg0IDU1cS0xNyAzNCAtNDggNTMuNXQtNjggMTkuNXEtNTMgMCAtOTAuNSAtMzcuNXQtMzcuNSAtOTAuNXEwIC01NiAzNiAtODlsMTAgLThxMzQgLTMxIDgyIC0zMXEzNyAwIDY4IDE5LjV0NDggNTMuNXEyNSA1MyA4NCA1NXpNMTYwMCA3MDRxMCA1NiAtMzYgODlsLTEwIDhxLTM0IDMxIC04MiAzMXEtMzcgMCAtNjggLTE5LjV0LTQ4IC01My41cS0yNSAtNTMgLTg0IC01NXE1OSAtMiA4NCAtNTUNCnExNyAtMzQgNDggLTUzLjV0NjggLTE5LjVxNTMgMCA5MC41IDM3LjV0MzcuNSA5MC41ek0xMTc0IDkyNXEtMTcgLTM1IC01NSAtNDh0LTczIDRxLTYyIDMxIC0xMzQgMzFxLTUxIDAgLTk5IC0xN3EzIDAgOS41IDAuNXQ5LjUgMC41cTkyIDAgMTcwLjUgLTUwdDExOC41IC0xMzNxMTcgLTM2IDMuNSAtNzMuNXQtNDkuNSAtNTQuNXEtMTggLTkgLTM5IC05cTIxIDAgMzkgLTlxMzYgLTE3IDQ5LjUgLTU0LjV0LTMuNSAtNzMuNQ0KcS00MCAtODMgLTExOC41IC0xMzN0LTE3MC41IC01MGgtNnEtMTYgMiAtNDQgNGwtMjkwIDI3bC0yMzkgLTEyMHEtMTQgLTcgLTI5IC03cS00MCAwIC01NyAzNWwtMTYwIDMyMHEtMTEgMjMgLTQgNDcuNXQyOSAzNy41bDIwOSAxMTlsMTQ4IDI2N3ExNyAxNTUgOTEuNSAyOTEuNXQxOTUuNSAyMzYuNXEzMSAyNSA3MC41IDIxLjV0NjQuNSAtMzQuNXQyMS41IC03MHQtMzQuNSAtNjVxLTcwIC01OSAtMTE3IC0xMjhxMTIzIDg0IDI2NyAxMDENCnE0MCA1IDcxLjUgLTE5dDM1LjUgLTY0cTUgLTQwIC0xOSAtNzEuNXQtNjQgLTM1LjVxLTg0IC0xMCAtMTU5IC01NXE0NiAxMCA5OSAxMHExMTUgMCAyMTggLTUwcTM2IC0xOCA0OSAtNTUuNXQtNSAtNzMuNXpNMjEzNyAxMDg1bDE2MCAtMzIwcTExIC0yMyA0IC00Ny41dC0yOSAtMzcuNWwtMjA5IC0xMTlsLTE0OCAtMjY3cS0xNyAtMTU1IC05MS41IC0yOTEuNXQtMTk1LjUgLTIzNi41cS0yNiAtMjIgLTYxIC0yMnEtNDUgMCAtNzQgMzUNCnEtMjUgMzEgLTIxLjUgNzB0MzQuNSA2NXE3MCA1OSAxMTcgMTI4cS0xMjMgLTg0IC0yNjcgLTEwMXEtNCAtMSAtMTIgLTFxLTM2IDAgLTYzLjUgMjR0LTMxLjUgNjBxLTUgNDAgMTkgNzEuNXQ2NCAzNS41cTg0IDEwIDE1OSA1NXEtNDYgLTEwIC05OSAtMTBxLTExNSAwIC0yMTggNTBxLTM2IDE4IC00OSA1NS41dDUgNzMuNXExNyAzNSA1NSA0OHQ3MyAtNHE2MiAtMzEgMTM0IC0zMXE1MSAwIDk5IDE3cS0zIDAgLTkuNSAtMC41dC05LjUgLTAuNQ0KcS05MiAwIC0xNzAuNSA1MHQtMTE4LjUgMTMzcS0xNyAzNiAtMy41IDczLjV0NDkuNSA1NC41cTE4IDkgMzkgOXEtMjEgMCAtMzkgOXEtMzYgMTcgLTQ5LjUgNTQuNXQzLjUgNzMuNXE0MCA4MyAxMTguNSAxMzN0MTcwLjUgNTBoNmgxcTE0IC0yIDQyIC00bDI5MSAtMjdsMjM5IDEyMHExNCA3IDI5IDdxNDAgMCA1NyAtMzV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkE0IiB1bmljb2RlPSImI3hmMmE0OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTEwNTYgNzA0cTAgLTI2IDE5IC00NXQ0NSAtMTl0NDUgMTl0MTkgNDVxMCAxNDYgLTEwMyAyNDl0LTI0OSAxMDN0LTI0OSAtMTAzdC0xMDMgLTI0OXEwIC0yNiAxOSAtNDV0NDUgLTE5dDQ1IDE5dDE5IDQ1cTAgOTMgNjYgMTU4LjV0MTU4IDY1LjV0MTU4IC02NS41dDY2IC0xNTguNXpNODM1IDEyODBxLTExNyAwIC0yMjMuNSAtNDUuNXQtMTg0IC0xMjN0LTEyMyAtMTg0dC00NS41IC0yMjMuNXEwIC0yNiAxOSAtNDV0NDUgLTE5dDQ1IDE5DQp0MTkgNDVxMCAxODUgMTMxLjUgMzE2LjV0MzE2LjUgMTMxLjV0MzE2LjUgLTEzMS41dDEzMS41IC0zMTYuNXEwIC01NSAtMTggLTEwMy41dC0zNy41IC03NC41dC01OS41IC03MnEtMzQgLTM5IC01MiAtNjN0LTQzLjUgLTY2LjV0LTM3IC05MXQtMTEuNSAtMTA1LjVxMCAtMTA2IC03NSAtMTgxdC0xODEgLTc1cS0yNiAwIC00NSAtMTl0LTE5IC00NXQxOSAtNDV0NDUgLTE5cTE1OSAwIDI3MS41IDExMi41dDExMi41IDI3MS41cTAgNDEgNy41IDc0DQp0MjYuNSA2NHQzMy41IDUwdDQ1LjUgNTRxMzUgNDEgNTMgNjQuNXQ0NCA2Ny41dDM3LjUgOTMuNXQxMS41IDEwOC41cTAgMTE3IC00NS41IDIyMy41dC0xMjMgMTg0dC0xODQgMTIzdC0yMjMuNSA0NS41ek01OTEgNTYxbDIyNiAtMjI2bC01NzkgLTU3OXEtMTIgLTEyIC0yOSAtMTJ0LTI5IDEybC0xNjggMTY4cS0xMiAxMiAtMTIgMjl0MTIgMjl6TTE2MTIgMTUyNGwxNjggLTE2OHExMiAtMTIgMTIgLTI5dC0xMiAtMzBsLTIzMyAtMjMzDQpsLTI2IC0yNWwtNzEgLTcxcS02NiAxNTMgLTE5NSAyNThsOTEgOTFsMjA3IDIwN3ExMyAxMiAzMCAxMnQyOSAtMTJ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkE1IiB1bmljb2RlPSImI3hmMmE1OyIgDQpkPSJNODY2IDEwMjFxMCAtMjcgLTEzIC05NHEtMTEgLTUwIC0zMS41IC0xNTB0LTMwLjUgLTE1MHEtMiAtMTEgLTQuNSAtMTIuNXQtMTMuNSAtMi41cS0yMCAtMiAtMzEgLTJxLTU4IDAgLTg0IDQ5LjV0LTI2IDExMy41cTAgODggMzUgMTc0dDEwMyAxMjRxMjggMTQgNTEgMTRxMjggMCAzNi41IC0xNi41dDguNSAtNDcuNXpNMTM1MiA1OTdxMCAxNCAtMzkgNzUuNXQtNTIgNjYuNXEtMjEgOCAtMzQgOHEtOTEgMCAtMjI2IC03N2wtMiAyDQpxMyAyMiAyNy41IDEzNXQyNC41IDE3OHEwIDIzMyAtMjQyIDIzM3EtMjQgMCAtNjggLTZxLTk0IC0xNyAtMTY4LjUgLTg5LjV0LTExMS41IC0xNjYuNXQtMzcgLTE4OXEwIC0xNDYgODAuNSAtMjI1dDIyNy41IC03OXEyNSAwIDI1IC0zdC0xIC01cS00IC0zNCAtMjYgLTExN3EtMTQgLTUyIC01MS41IC0xMDF0LTgyLjUgLTQ5cS00MiAwIC00MiA0N3EwIDI0IDEwLjUgNDcuNXQyNSAzOS41dDI5LjUgMjguNXQyNiAyMHQxMSA4LjVxMCAzIC03IDEwDQpxLTI0IDIyIC01OC41IDM2LjV0LTY1LjUgMTQuNXEtMzUgMCAtNjMuNSAtMzR0LTQxIC03NXQtMTIuNSAtNzVxMCAtODggNTEuNSAtMTQydDEzOC41IC01NHE4MiAwIDE1NSA1M3QxMTcuNSAxMjZ0NjUuNSAxNTNxNiAyMiAxNS41IDY2LjV0MTQuNSA2Ni41cTMgMTIgMTQgMThxMTE4IDYwIDIyNyA2MHE0OCAwIDEyNyAtMThxMSAtMSA0IC0xcTUgMCA5LjUgNC41dDQuNSA4LjV6TTE1MzYgMTEyMHYtOTYwcTAgLTExOSAtODQuNSAtMjAzLjUNCnQtMjAzLjUgLTg0LjVoLTk2MHEtMTE5IDAgLTIwMy41IDg0LjV0LTg0LjUgMjAzLjV2OTYwcTAgMTE5IDg0LjUgMjAzLjV0MjAzLjUgODQuNWg5NjBxMTE5IDAgMjAzLjUgLTg0LjV0ODQuNSAtMjAzLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkE2IiB1bmljb2RlPSImI3hmMmE2OyIgaG9yaXotYWR2LXg9IjE1MzUiIA0KZD0iTTc0NCAxMjMxcTAgMjQgLTIgMzguNXQtOC41IDMwdC0yMSAyM3QtMzcuNSA3LjVxLTM5IDAgLTc4IC0yM3EtMTA1IC01OCAtMTU5IC0xOTAuNXQtNTQgLTI2OS41cTAgLTQ0IDguNSAtODUuNXQyNi41IC04MC41dDUyLjUgLTYyLjV0ODEuNSAtMjMuNXE0IDAgMTggLTAuNXQyMCAwdDE2IDN0MTUgOC41dDcgMTZxMTYgNzcgNDggMjMxLjV0NDggMjMxLjVxMTkgOTEgMTkgMTQ2ek0xNDk4IDU3NXEwIC03IC03LjUgLTEzLjV0LTE1LjUgLTYuNQ0KbC02IDFxLTIyIDMgLTYyIDExdC03MiAxMi41dC02MyA0LjVxLTE2NyAwIC0zNTEgLTkzcS0xNSAtOCAtMjEgLTI3cS0xMCAtMzYgLTI0LjUgLTEwNS41dC0yMi41IC0xMDAuNXEtMjMgLTkxIC03MCAtMTc5LjV0LTExMi41IC0xNjQuNXQtMTU0LjUgLTEyM3QtMTg1IC00N3EtMTM1IDAgLTIxNC41IDgzLjV0LTc5LjUgMjE5LjVxMCA1MyAxOS41IDExN3Q2MyAxMTYuNXQ5Ny41IDUyLjVxMzggMCAxMjAgLTMzLjV0ODMgLTYxLjUNCnEwIC0xIC0xNi41IC0xMi41dC0zOS41IC0zMXQtNDYgLTQ0LjV0LTM5IC02MXQtMTYgLTc0cTAgLTMzIDE2LjUgLTUzdDQ4LjUgLTIwcTQ1IDAgODUgMzEuNXQ2Ni41IDc4dDQ4IDEwNS41dDMyLjUgMTA3dDE2IDkwdjlxMCAyIC0zLjUgMy41dC04LjUgMS41aC0xMHQtMTAgLTAuNXQtNiAtMC41cS0yMjcgMCAtMzUyIDEyMi41dC0xMjUgMzQ4LjVxMCAxMDggMzQuNSAyMjF0OTYgMjEwdDE1NiAxNjcuNXQyMDQuNSA4OS41cTUyIDkgMTA2IDkNCnEzNzQgMCAzNzQgLTM2MHEwIC05OCAtMzggLTI3M3QtNDMgLTIxMWwzIC0zcTEwMSA1NyAxODIuNSA4OHQxNjcuNSAzMXEyMiAwIDUzIC0xM3ExOSAtNyA4MCAtMTAyLjV0NjEgLTExNi41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJBNyIgdW5pY29kZT0iJiN4ZjJhNzsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik04MzEgODYzcTMyIDAgNTkgLTE4bDIyMiAtMTQ4cTYxIC00MCAxMTAgLTk3bDE0NiAtMTcwcTQwIC00NiAyOSAtMTA2bC03MiAtNDEzcS02IC0zMiAtMjkuNSAtNTMuNXQtNTUuNSAtMjUuNWwtNTI3IC01NmwtMzUyIC0zMmgtOXEtMzkgMCAtNjcuNSAyOHQtMjguNSA2OHEwIDM3IDI3IDY0dDY1IDMybDI2MCAzMmgtNDQ4cS00MSAwIC02OS41IDMwdC0yNi41IDcxcTIgMzkgMzIgNjV0NjkgMjZsNDQyIDFsLTUyMSA2NHEtNDEgNSAtNjYgMzcNCnQtMTkgNzNxNiAzNSAzNC41IDU3LjV0NjUuNSAyMi41aDEwbDQ4MSAtNjBsLTM1MSA5NHEtMzggMTAgLTYyIDQxLjV0LTE4IDY4LjVxNiAzNiAzMyA1OC41dDYyIDIyLjVxNiAwIDIwIC0ybDQ0OCAtOTZsMjE3IC0zN3ExIDAgMyAtMC41dDMgLTAuNXEyMyAwIDMwLjUgMjN0LTEyLjUgMzZsLTE4NiAxMjVxLTM1IDIzIC00MiA2My41dDE4IDczLjVxMjcgMzggNzYgMzh6TTc2MSA2NjFsMTg2IC0xMjVsLTIxOCAzN2wtNSAybC0zNiAzOA0KbC0yMzggMjYycS0xIDEgLTIuNSAzLjV0LTIuNSAzLjVxLTI0IDMxIC0xOC41IDcwdDM3LjUgNjRxMzEgMjMgNjggMTcuNXQ2NCAtMzMuNWwxNDIgLTE0N3EtMiAtMSAtNSAtMy41dC00IC00LjVxLTMyIC00NSAtMjMgLTk5dDU1IC04NXpNMTY0OCAxMTE1bDE1IC0yNjZxNCAtNzMgLTExIC0xNDdsLTQ4IC0yMTlxLTEyIC01OSAtNjcgLTg3bC0xMDYgLTU0cTIgNjIgLTM5IDEwOWwtMTQ2IDE3MHEtNTMgNjEgLTExNyAxMDNsLTIyMiAxNDgNCnEtMzQgMjMgLTc2IDIzcS01MSAwIC04OCAtMzdsLTIzNSAzMTJxLTI1IDMzIC0xOCA3My41dDQxIDYzLjVxMzMgMjIgNzEuNSAxNHQ2Mi41IC00MGwyNjYgLTM1MmwtMjYyIDQ1NXEtMjEgMzUgLTEwLjUgNzV0NDcuNSA1OXEzNSAxOCA3Mi41IDZ0NTcuNSAtNDZsMjQxIC00MjBsLTEzNiAzMzdxLTE1IDM1IC00LjUgNzR0NDQuNSA1NnEzNyAxOSA3NiA2dDU2IC01MWwxOTMgLTQxNWwxMDEgLTE5NnE4IC0xNSAyMyAtMTcuNXQyNyA3LjV0MTEgMjYNCmwtMTIgMjI0cS0yIDQxIDI2IDcxdDY5IDMxcTM5IDAgNjcgLTI4LjV0MzAgLTY3LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkE4IiB1bmljb2RlPSImI3hmMmE4OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTMzNSAxODBxLTIgMCAtNiAycS04NiA1NyAtMTY4LjUgMTQ1dC0xMzkuNSAxODBxLTIxIDMwIC0yMSA2OXEwIDkgMiAxOXQ0IDE4dDcgMTh0OC41IDE2dDEwLjUgMTd0MTAgMTV0MTIgMTUuNXQxMSAxNC41cTE4NCAyNTEgNDUyIDM2NXEtMTEwIDE5OCAtMTEwIDIxMXEwIDE5IDE3IDI5cTExNiA2NCAxMjggNjRxMTggMCAyOCAtMTZsMTI0IC0yMjlxOTIgMTkgMTkyIDE5cTI2NiAwIDQ5Ny41IC0xMzcuNXQzNzguNSAtMzY5LjUNCnEyMCAtMzEgMjAgLTY5dC0yMCAtNjlxLTkxIC0xNDIgLTIxOC41IC0yNTMuNXQtMjc4LjUgLTE3NS41cTExMCAtMTk4IDExMCAtMjExcTAgLTIwIC0xNyAtMjlxLTExNiAtNjQgLTEyNyAtNjRxLTE5IDAgLTI5IDE2bC0xMjQgMjI5bC02NCAxMTlsLTQ0NCA4MjBsNyA3cS01OCAtMjQgLTk5IC00N3EzIC01IDEyNyAtMjM0dDI0MyAtNDQ5dDExOSAtMjIzcTAgLTcgLTkgLTlxLTEzIC0zIC03MiAtM3EtNTcgMCAtNjAgN2wtNDU2IDg0MQ0KcS0zOSAtMjggLTgyIC02OHEyNCAtNDMgMjE0IC0zOTMuNXQxOTAgLTM1NC41cTAgLTEwIC0xMSAtMTBxLTE0IDAgLTgyLjUgMjJ0LTcyLjUgMjhsLTEwNiAxOTdsLTIyNCA0MTNxLTQ0IC01MyAtNzggLTEwNnEyIC0zIDE4IC0yNXQyMyAtMzRsMTc2IC0zMjdxMCAtMTAgLTEwIC0xMHpNMTE2NSAyODJsNDkgLTkxcTI3MyAxMTEgNDUwIDM4NXEtMTgwIDI3NyAtNDU5IDM4OXE2NyAtNjQgMTAzIC0xNDguNXQzNiAtMTc2LjUNCnEwIC0xMDYgLTQ3IC0yMDAuNXQtMTMyIC0xNTcuNXpNODQ4IDg5NnEwIC0yMCAxNCAtMzR0MzQgLTE0cTg2IDAgMTQ3IC02MXQ2MSAtMTQ3cTAgLTIwIDE0IC0zNHQzNCAtMTR0MzQgMTR0MTQgMzRxMCAxMjYgLTg5IDIxNXQtMjE1IDg5cS0yMCAwIC0zNCAtMTR0LTE0IC0zNHpNMTIxNCA5NjFsLTkgNGw3IC03eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJBOSIgdW5pY29kZT0iJiN4ZjJhOTsiIGhvcml6LWFkdi14PSIxMjgwIiANCmQ9Ik0xMDUwIDQzMHEwIC0yMTUgLTE0NyAtMzc0cS0xNDggLTE2MSAtMzc4IC0xNjFxLTIzMiAwIC0zNzggMTYxcS0xNDcgMTU5IC0xNDcgMzc0cTAgMTQ3IDY4IDI3MC41dDE4OSAxOTYuNXQyNjggNzNxOTYgMCAxODIgLTMxcS0zMiAtNjIgLTM5IC0xMjZxLTY2IDI4IC0xNDMgMjhxLTE2NyAwIC0yODAuNSAtMTIzdC0xMTMuNSAtMjkxcTAgLTE3MCAxMTIuNSAtMjg4LjV0MjgxLjUgLTExOC41dDI4MSAxMTguNXQxMTIgMjg4LjUNCnEwIDg5IC0zMiAxNjZxNjYgMTMgMTIzIDQ5cTQxIC05OCA0MSAtMjEyek04NDYgNjE5cTAgLTE5MiAtNzkuNSAtMzQ1dC0yMzguNSAtMjUzbC0xNCAtMXEtMjkgMCAtNjIgNXE4MyAzMiAxNDYuNSAxMDIuNXQ5OS41IDE1NC41dDU4LjUgMTg5dDMwIDE5Mi41dDcuNSAxNzguNXEwIDY5IC0zIDEwM3E1NSAtMTYwIDU1IC0zMjZ6TTc5MSA5NDd2LTJxLTczIDIxNCAtMjA2IDQ0MHE4OCAtNTkgMTQyLjUgLTE4Ni41dDYzLjUgLTI1MS41eg0KTTEwMzUgNzQ0cS04MyAwIC0xNjAgNzVxMjE4IDEyMCAyOTAgMjQ3cTE5IDM3IDIxIDU2cS00MiAtOTQgLTEzOS41IC0xNjYuNXQtMjA0LjUgLTk3LjVxLTM1IDU0IC0zNSAxMTNxMCAzNyAxNyA3OXQ0MyA2OHE0NiA0NCAxNTcgNzRxNTkgMTYgMTA2IDU4LjV0NzQgMTAwLjVxNzQgLTEwNSA3NCAtMjUzcTAgLTEwOSAtMjQgLTE3MHEtMzIgLTc3IC04OC41IC0xMzAuNXQtMTMwLjUgLTUzLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkFBIiB1bmljb2RlPSImI3hmMmFhOyIgDQpkPSJNMTA1MCA0OTVxMCA3OCAtMjggMTQ3cS00MSAtMjUgLTg1IC0zNHEyMiAtNTAgMjIgLTExNHEwIC0xMTcgLTc3IC0xOTguNXQtMTkzIC04MS41dC0xOTMuNSA4MS41dC03Ny41IDE5OC41cTAgMTE1IDc4IDE5OS41dDE5MyA4NC41cTUzIDAgOTggLTE5cTQgNDMgMjcgODdxLTYwIDIxIC0xMjUgMjFxLTE1NCAwIC0yNTcuNSAtMTA4LjV0LTEwMy41IC0yNjMuNXQxMDMuNSAtMjYxdDI1Ny41IC0xMDZ0MjU3LjUgMTA2LjV0MTAzLjUgMjYwLjV6DQpNODcyIDg1MHEyIC0yNCAyIC03MXEwIC02MyAtNSAtMTIzdC0yMC41IC0xMzIuNXQtNDAuNSAtMTMwdC02OC41IC0xMDZ0LTEwMC41IC03MC41cTIxIC0zIDQyIC0zaDEwcTIxOSAxMzkgMjE5IDQxMXEwIDExNiAtMzggMjI1ek04NzIgODUwcS00IDgwIC00NCAxNzEuNXQtOTggMTMwLjVxOTIgLTE1NiAxNDIgLTMwMnpNMTIwNyA5NTVxMCAxMDIgLTUxIDE3NHEtNDEgLTg2IC0xMjQgLTEwOXEtNjkgLTE5IC0xMDkgLTUzLjV0LTQwIC05OS41DQpxMCAtNDAgMjQgLTc3cTc0IDE3IDE0MC41IDY3dDk1LjUgMTE1cS00IC01MiAtNzQuNSAtMTExLjV0LTEzOC41IC05Ny41cTUyIC01MiAxMTAgLTUycTUxIDAgOTAgMzd0NjAgOTBxMTcgNDIgMTcgMTE3ek0xNTM2IDExMjB2LTk2MHEwIC0xMTkgLTg0LjUgLTIwMy41dC0yMDMuNSAtODQuNWgtOTYwcS0xMTkgMCAtMjAzLjUgODQuNXQtODQuNSAyMDMuNXY5NjBxMCAxMTkgODQuNSAyMDMuNXQyMDMuNSA4NC41aDk2MHExMTkgMCAyMDMuNSAtODQuNQ0KdDg0LjUgLTIwMy41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJBQiIgdW5pY29kZT0iJiN4ZjJhYjsiIA0KZD0iTTEyNzkgMzg4cTAgMjIgLTIyIDI3cS02NyAxNSAtMTE4IDU5dC04MCAxMDhxLTcgMTkgLTcgMjVxMCAxNSAxOS41IDI2dDQzIDE3dDQzIDIwLjV0MTkuNSAzNi41cTAgMTkgLTE4LjUgMzEuNXQtMzguNSAxMi41cS0xMiAwIC0zMiAtOHQtMzEgLThxLTQgMCAtMTIgMnE1IDk1IDUgMTE0cTAgNzkgLTE3IDExNHEtMzYgNzggLTEwMyAxMjEuNXQtMTUyIDQzLjVxLTE5OSAwIC0yNzUgLTE2NXEtMTcgLTM1IC0xNyAtMTE0cTAgLTE5IDUgLTExNA0KcS00IC0yIC0xNCAtMnEtMTIgMCAtMzIgNy41dC0zMCA3LjVxLTIxIDAgLTM4LjUgLTEydC0xNy41IC0zMnEwIC0yMSAxOS41IC0zNS41dDQzIC0yMC41dDQzIC0xN3QxOS41IC0yNnEwIC02IC03IC0yNXEtNjQgLTEzOCAtMTk4IC0xNjdxLTIyIC01IC0yMiAtMjdxMCAtNDYgMTM3IC02OHEyIC01IDYgLTI2dDExLjUgLTMwLjV0MjMuNSAtOS41cTEyIDAgMzcuNSA0LjV0MzkuNSA0LjVxMzUgMCA2NyAtMTV0NTQgLTMyLjV0NTcuNSAtMzIuNQ0KdDc2LjUgLTE1cTQzIDAgNzkgMTV0NTcuNSAzMi41dDUzLjUgMzIuNXQ2NyAxNXExNCAwIDM5LjUgLTR0MzguNSAtNHExNiAwIDIzIDEwdDExIDMwdDYgMjVxMTM3IDIyIDEzNyA2OHpNMTUzNiA2NDBxMCAtMjA5IC0xMDMgLTM4NS41dC0yNzkuNSAtMjc5LjV0LTM4NS41IC0xMDN0LTM4NS41IDEwM3QtMjc5LjUgMjc5LjV0LTEwMyAzODUuNXQxMDMgMzg1LjV0Mjc5LjUgMjc5LjV0Mzg1LjUgMTAzdDM4NS41IC0xMDN0Mjc5LjUgLTI3OS41DQp0MTAzIC0zODUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyQUMiIHVuaWNvZGU9IiYjeGYyYWM7IiBob3Jpei1hZHYteD0iMTY2NCIgDQpkPSJNODQ4IDE0MDhxMTM0IDEgMjQwLjUgLTY4LjV0MTYzLjUgLTE5Mi41cTI3IC01OCAyNyAtMTc5cTAgLTQ3IC05IC0xOTFxMTQgLTcgMjggLTdxMTggMCA1MSAxMy41dDUxIDEzLjVxMjkgMCA1NiAtMTh0MjcgLTQ2cTAgLTMyIC0zMS41IC01NHQtNjkgLTMxLjV0LTY5IC0yOXQtMzEuNSAtNDcuNXEwIC0xNSAxMiAtNDNxMzcgLTgyIDEwMi41IC0xNTB0MTQ0LjUgLTEwMXEyOCAtMTIgODAgLTIzcTI4IC02IDI4IC0zNQ0KcTAgLTcwIC0yMTkgLTEwM3EtNyAtMTEgLTExIC0zOXQtMTQgLTQ2LjV0LTMzIC0xOC41cS0yMCAwIC02MiA2LjV0LTY0IDYuNXEtMzcgMCAtNjIgLTVxLTMyIC01IC02MyAtMjIuNXQtNTggLTM4dC01OCAtNDAuNXQtNzYgLTMzLjV0LTk5IC0xMy41cS01MiAwIC05Ni41IDEzLjV0LTc1IDMzLjV0LTU3LjUgNDAuNXQtNTggMzh0LTYyIDIyLjVxLTI2IDUgLTYzIDVxLTI0IDAgLTY1LjUgLTcuNXQtNTguNSAtNy41cS0yNSAwIC0zNSAxOC41DQp0LTE0IDQ3LjV0LTExIDQwcS0yMTkgMzMgLTIxOSAxMDNxMCAyOSAyOCAzNXE1MiAxMSA4MCAyM3E3OCAzMiAxNDQuNSAxMDF0MTAyLjUgMTUwcTEyIDI4IDEyIDQzcTAgMjggLTMxLjUgNDcuNXQtNjkuNSAyOS41dC02OS41IDMxLjV0LTMxLjUgNTIuNXEwIDI3IDI2IDQ1LjV0NTUgMTguNXExNSAwIDQ4IC0xM3Q1MyAtMTNxMTggMCAzMiA3cS05IDE0MiAtOSAxOTBxMCAxMjIgMjcgMTgwcTY0IDEzNyAxNzIgMTk4dDI2NCA2M3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyQUQiIHVuaWNvZGU9IiYjeGYyYWQ7IiANCmQ9Ik0xMjgwIDM4OHEwIDIyIC0yMiAyN3EtNjcgMTQgLTExOCA1OHQtODAgMTA5cS03IDE0IC03IDI1cTAgMTUgMTkuNSAyNnQ0Mi41IDE3dDQyLjUgMjAuNXQxOS41IDM2LjVxMCAxOSAtMTguNSAzMS41dC0zOC41IDEyLjVxLTExIDAgLTMxIC04dC0zMiAtOHEtNCAwIC0xMiAycTUgNjMgNSAxMTVxMCA3OCAtMTcgMTE0cS0zNiA3OCAtMTAyLjUgMTIxLjV0LTE1Mi41IDQzLjVxLTE5OCAwIC0yNzUgLTE2NXEtMTggLTM4IC0xOCAtMTE1DQpxMCAtMzggNiAtMTE0cS0xMCAtMiAtMTUgLTJxLTExIDAgLTMxLjUgOHQtMzAuNSA4cS0yMCAwIC0zNy41IC0xMi41dC0xNy41IC0zMi41cTAgLTIxIDE5LjUgLTM1LjV0NDIuNSAtMjAuNXQ0Mi41IC0xN3QxOS41IC0yNnEwIC0xMSAtNyAtMjVxLTY0IC0xMzggLTE5OCAtMTY3cS0yMiAtNSAtMjIgLTI3cTAgLTQ3IDEzOCAtNjlxMiAtNSA2IC0yNnQxMSAtMzAuNXQyMyAtOS41cTEzIDAgMzguNSA1dDM4LjUgNXEzNSAwIDY3LjUgLTE1DQp0NTQuNSAtMzIuNXQ1Ny41IC0zMi41dDc2LjUgLTE1cTQzIDAgNzkgMTV0NTcuNSAzMi41dDU0IDMyLjV0NjcuNSAxNXExMyAwIDM5IC00LjV0MzkgLTQuNXExNSAwIDIyLjUgOS41dDExLjUgMzF0NSAyNC41cTEzOCAyMiAxMzggNjl6TTE1MzYgMTEyMHYtOTYwcTAgLTExOSAtODQuNSAtMjAzLjV0LTIwMy41IC04NC41aC05NjBxLTExOSAwIC0yMDMuNSA4NC41dC04NC41IDIwMy41djk2MHEwIDExOSA4NC41IDIwMy41dDIwMy41IDg0LjVoOTYwDQpxMTE5IDAgMjAzLjUgLTg0LjV0ODQuNSAtMjAzLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkFFIiB1bmljb2RlPSImI3hmMmFlOyIgaG9yaXotYWR2LXg9IjIzMDQiIA0KZD0iTTIzMDQgMTUzNnEtNjkgLTQ2IC0xMjUgLTkydC04OSAtODF0LTU5LjUgLTcxLjV0LTM3LjUgLTU3LjV0LTIyIC00NC41dC0xNCAtMjkuNXEtMTAgLTE4IC0zNS41IC0xMzYuNXQtNDguNSAtMTY0LjVxLTE1IC0yOSAtNTAgLTYwLjV0LTY3LjUgLTUwLjV0LTcyLjUgLTQxdC00OCAtMjhxLTQ3IC0zMSAtMTUxIC0yMzFxLTM0MSAxNCAtNjMwIC0xNThxLTkyIC01MyAtMzAzIC0xNzlxNDcgMTYgODYgMzF0NTUgMjJsMTUgNw0KcTcxIDI3IDE2MyA2NC41dDEzMy41IDUzLjV0MTA4IDM0LjV0MTQyLjUgMzEuNXExODYgMzEgNDY1IC03cTEgMCAxMCAtM3ExMSAtNiAxNCAtMTd0LTMgLTIybC0xOTQgLTM0NXEtMTUgLTI5IC00NyAtMjJxLTEyOCAyNCAtMzU0IDI0cS0xNDYgMCAtNDAyIC00NC41dC0zOTIgLTQ2LjVxLTgyIC0xIC0xNDkgMTN0LTEwNyAzN3QtNjEgNDB0LTMzIDM0bC0xIDF2MnEwIDYgNiA2cTEzOCAwIDM3MSA1NXExOTIgMzY2IDM3NC41IDUyNHQzODMuNSAxNTgNCnE1IDAgMTQuNSAtMC41dDM4IC01dDU1IC0xMnQ2MS41IC0yNC41dDYzIC0zOS41dDU0IC01OXQ0MCAtODIuNWwxMDIgMTc3cTIgNCAyMSA0Mi41dDQ0LjUgODYuNXQ2MSAxMDkuNXQ4NCAxMzMuNXQxMDAuNSAxMzdxNjYgODIgMTI4IDE0MS41dDEyMS41IDk2LjV0OTIuNSA1My41dDg4IDM5LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkIwIiB1bmljb2RlPSImI3hmMmIwOyIgDQpkPSJNMTMyMiA2NDBxMCAtNDUgLTUgLTc2bC0yMzYgMTRsMjI0IC03OHEtMTkgLTczIC01OCAtMTQxbC0yMTQgMTAzbDE3NyAtMTU4cS00NCAtNjEgLTEwNyAtMTA4bC0xNTcgMTc4bDEwMyAtMjE1cS02MSAtMzcgLTE0MCAtNTlsLTc5IDIyOGwxNCAtMjQwcS0zOCAtNiAtNzYgLTZ0LTc2IDZsMTQgMjM4bC03OCAtMjI2cS03NCAxOSAtMTQwIDU5bDEwMyAyMTVsLTE1NyAtMTc4cS01OSA0MyAtMTA4IDEwOGwxNzggMTU4bC0yMTQgLTEwNA0KcS0zOSA2OSAtNTggMTQxbDIyNCA3OWwtMjM3IC0xNHEtNSA0MiAtNSA3NnEwIDM1IDUgNzdsMjM4IC0xNGwtMjI1IDc5cTE5IDczIDU4IDE0MGwyMTQgLTEwNGwtMTc3IDE1OXE0NiA2MSAxMDcgMTA4bDE1OCAtMTc4bC0xMDMgMjE1cTY3IDM5IDE0MCA1OGw3NyAtMjI0bC0xMyAyMzZxMzYgNiA3NSA2cTM4IDAgNzYgLTZsLTE0IC0yMzdsNzggMjI1cTc0IC0xOSAxNDAgLTU5bC0xMDMgLTIxNGwxNTggMTc4cTYxIC00NyAxMDcgLTEwOA0KbC0xNzcgLTE1OWwyMTMgMTA0cTM3IC02MiA1OCAtMTQxbC0yMjQgLTc4bDIzNyAxNHE1IC0zMSA1IC03N3pNMTM1MiA2NDBxMCAxNjAgLTc4LjUgMjk1LjV0LTIxMyAyMTR0LTI5Mi41IDc4LjVxLTExOSAwIC0yMjcgLTQ2LjV0LTE4Ni41IC0xMjV0LTEyNC41IC0xODcuNXQtNDYgLTIyOXEwIC0xMTkgNDYgLTIyOHQxMjQuNSAtMTg3LjV0MTg2LjUgLTEyNXQyMjcgLTQ2LjVxMTU4IDAgMjkyLjUgNzguNXQyMTMgMjE0dDc4LjUgMjk0LjV6DQpNMTQyNSAxMDIzdi03NjZsLTY1NyAtMzgzbC02NTcgMzgzdjc2Nmw2NTcgMzgzek03NjggLTE4M2w3MDggNDEydjgyM2wtNzA4IDQxMWwtNzA4IC00MTF2LTgyM3pNMTUzNiAxMDg4di04OTZsLTc2OCAtNDQ4bC03NjggNDQ4djg5Nmw3NjggNDQ4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJCMSIgdW5pY29kZT0iJiN4ZjJiMTsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik0zMzkgMTMxOGg2OTFsLTI2IC03MmgtNjY1cS0xMTAgMCAtMTg4LjUgLTc5dC03OC41IC0xODl2LTc3MXEwIC05NSA2MC41IC0xNjkuNXQxNTMuNSAtOTMuNXEyMyAtNSA5OCAtNXYtNzJoLTQ1cS0xNDAgMCAtMjM5LjUgMTAwdC05OS41IDI0MHY3NzFxMCAxNDAgOTkuNSAyNDB0MjM5LjUgMTAwek0xMTkwIDE1MzZoMjQ3bC00ODIgLTEyOTRxLTIzIC02MSAtNDAuNSAtMTAzLjV0LTQ1IC05OHQtNTQgLTkzLjV0LTY0LjUgLTc4LjUNCnQtNzkuNSAtNjV0LTk1LjUgLTQxdC0xMTYgLTE4LjV2MTk1cTE2MyAyNiAyMjAgMTgycTIwIDUyIDIwIDEwNXEwIDU0IC0yMCAxMDZsLTI4NSA3MzNoMjI4bDE4NyAtNTg1ek0xNjY0IDk3OHYtMTExMWgtNzk1cTM3IDU1IDQ1IDczaDY3OHYxMDM4cTAgODUgLTQ5LjUgMTU1dC0xMjkuNSA5OWwyNSA2N3ExMDEgLTM0IDE2My41IC0xMjMuNXQ2Mi41IC0xOTcuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyQjIiIHVuaWNvZGU9IiYjeGYyYjI7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNODUyIDEyMjdxMCAtMjkgLTE3IC01Mi41dC00NSAtMjMuNXQtNDUgMjMuNXQtMTcgNTIuNXQxNyA1Mi41dDQ1IDIzLjV0NDUgLTIzLjV0MTcgLTUyLjV6TTY4OCAtMTQ5djExNHEwIDMwIC0yMC41IDUxLjV0LTUwLjUgMjEuNXQtNTAgLTIxLjV0LTIwIC01MS41di0xMTRxMCAtMzAgMjAuNSAtNTJ0NDkuNSAtMjJxMzAgMCA1MC41IDIydDIwLjUgNTJ6TTg2MCAtMTQ5djExNHEwIDMwIC0yMCA1MS41dC01MCAyMS41dC01MC41IC0yMS41DQp0LTIwLjUgLTUxLjV2LTExNHEwIC0zMCAyMC41IC01MnQ1MC41IC0yMnEyOSAwIDQ5LjUgMjJ0MjAuNSA1MnpNMTAzNCAtMTQ5djExNHEwIDMwIC0yMC41IDUxLjV0LTUwLjUgMjEuNXQtNTAuNSAtMjEuNXQtMjAuNSAtNTEuNXYtMTE0cTAgLTMwIDIwLjUgLTUydDUwLjUgLTIydDUwLjUgMjJ0MjAuNSA1MnpNMTIwOCAtMTQ5djExNHEwIDMwIC0yMC41IDUxLjV0LTUwLjUgMjEuNXQtNTAuNSAtMjEuNXQtMjAuNSAtNTEuNXYtMTE0DQpxMCAtMzAgMjAuNSAtNTJ0NTAuNSAtMjJ0NTAuNSAyMnQyMC41IDUyek0xNDc2IDUzNXEtODQgLTE2MCAtMjMyIC0yNTkuNXQtMzIzIC05OS41cS0xMjMgMCAtMjI5LjUgNTEuNXQtMTc4LjUgMTM3dC0xMTMgMTk3LjV0LTQxIDIzMnEwIDg4IDIxIDE3NHEtMTA0IC0xNzUgLTEwNCAtMzkwcTAgLTE2MiA2NSAtMzEydDE4NSAtMjUxcTMwIDU3IDkxIDU3cTU2IDAgODYgLTUwcTMyIDUwIDg3IDUwcTU2IDAgODYgLTUwcTMyIDUwIDg3IDUwdDg3IC01MA0KcTMwIDUwIDg2IDUwcTI4IDAgNTIuNSAtMTUuNXQzNy41IC00MC41cTExMiA5NCAxNzcgMjMxLjV0NzMgMjg3LjV6TTEzMjYgNTY0cTAgNzUgLTcyIDc1cS0xNyAwIC00NyAtNnEtOTUgLTE5IC0xNDkgLTE5cS0yMjYgMCAtMjI2IDI0M3EwIDg2IDMwIDIwNHEtODMgLTEyNyAtODMgLTI3NXEwIC0xNTAgODkgLTI2MC41dDIzNSAtMTEwLjVxMTExIDAgMjEwIDcwcTEzIDQ4IDEzIDc5ek04ODQgMTIyM3EwIDUwIC0zMiA4OS41dC04MSAzOS41DQp0LTgxIC0zOS41dC0zMiAtODkuNXEwIC01MSAzMS41IC05MC41dDgxLjUgLTM5LjV0ODEuNSAzOS41dDMxLjUgOTAuNXpNMTUxMyA4ODRxMCA5NiAtMzcuNSAxNzl0LTExMyAxMzd0LTE3My41IDU0cS03NyAwIC0xNDkgLTM1dC0xMjcgLTk0cS00OCAtMTU5IC00OCAtMjY4cTAgLTEwNCA0NS41IC0xNTd0MTQ3LjUgLTUzcTUzIDAgMTQyIDE5cTM2IDYgNTMgNnE1MSAwIDc3LjUgLTI4dDI2LjUgLTgwcTAgLTI2IC00IC00Ng0KcTc1IDY4IDExNy41IDE2NS41dDQyLjUgMjAwLjV6TTE3OTIgNjY3cTAgLTExMSAtMzMuNSAtMjQ5LjV0LTkzLjUgLTIwNC41cS01OCAtNjQgLTE5NSAtMTQyLjV0LTIyOCAtMTA0LjVsLTQgLTF2LTExNHEwIC00MyAtMjkuNSAtNzV0LTcyLjUgLTMycS01NiAwIC04NiA1MHEtMzIgLTUwIC04NyAtNTB0LTg3IDUwcS0zMCAtNTAgLTg2IC01MHEtNTUgMCAtODcgNTBxLTMwIC01MCAtODYgLTUwcS00NyAwIC03NSAzMy41dC0yOCA4MS41DQpxLTkwIC02OCAtMTk4IC02OHEtMTE4IDAgLTIxMSA4MHE1NCAxIDEwNiAyMHEtMTEzIDMxIC0xODIgMTI3cTMyIC03IDcxIC03cTg5IDAgMTY0IDQ2cS0xOTIgMTkyIC0yNDAgMzA2cS0yNCA1NiAtMjQgMTYwcTAgNTcgOSAxMjUuNXQzMS41IDE0Ni41dDU1IDE0MXQ4Ni41IDEwNXQxMjAgNDJxNTkgMCA4MSAtNTJxMTkgMjkgNDIgNTRxMiAzIDEyIDEzdDEzIDE2cTEwIDE1IDIzIDM4dDI1IDQydDI4IDM5cTg3IDExMSAyMTEuNSAxNzcNCnQyNjAuNSA2NnEzNSAwIDYyIC00cTU5IDY0IDE0NiA2NHE4MyAwIDE0MCAtNTdxNSAtNSA1IC0xMnEwIC01IC02IC0xMy41dC0xMi41IC0xNnQtMTYgLTE3bC0xMC41IC0xMC41cTE3IC02IDM2IC0xOHQxOSAtMjRxMCAtNiAtMTYgLTI1cTE1NyAtMTM4IDE5NyAtMzc4cTI1IDMwIDYwIDMwcTQ1IDAgMTAwIC00OXE5MCAtODAgOTAgLTI3OXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyQjMiIHVuaWNvZGU9IiYjeGYyYjM7IiANCmQ9Ik05MTcgNjMxcTAgMzMgLTYgNjRoLTM2MnYtMTMyaDIxN3EtMTIgLTc2IC03NC41IC0xMjAuNXQtMTQyLjUgLTQ0LjVxLTk5IDAgLTE2OSA3MS41dC03MCAxNzAuNXQ3MCAxNzAuNXQxNjkgNzEuNXE5MyAwIDE1MyAtNTlsMTA0IDEwMXEtMTA4IDEwMCAtMjU3IDEwMHEtMTYwIDAgLTI3MiAtMTEyLjV0LTExMiAtMjcxLjV0MTEyIC0yNzEuNXQyNzIgLTExMi41cTE2NSAwIDI2Ni41IDEwNXQxMDEuNSAyNzB6TTEyNjIgNTg1aDEwOXYxMTANCmgtMTA5djExMGgtMTEwdi0xMTBoLTExMHYtMTEwaDExMHYtMTEwaDExMHYxMTB6TTE1MzYgNjQwcTAgLTIwOSAtMTAzIC0zODUuNXQtMjc5LjUgLTI3OS41dC0zODUuNSAtMTAzdC0zODUuNSAxMDN0LTI3OS41IDI3OS41dC0xMDMgMzg1LjV0MTAzIDM4NS41dDI3OS41IDI3OS41dDM4NS41IDEwM3QzODUuNSAtMTAzdDI3OS41IC0yNzkuNXQxMDMgLTM4NS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJCNCIgdW5pY29kZT0iJiN4ZjJiNDsiIA0KZD0iTTE1MzYgMTAyNHYtODM5cTAgLTQ4IC00OSAtNjJxLTE3NCAtNTIgLTMzOCAtNTJxLTczIDAgLTIxNS41IDI5LjV0LTIyNy41IDI5LjVxLTE2NCAwIC0zNzAgLTQ4di0zMzhoLTE2MHYxMzY4cS02MyAyNSAtMTAxIDgxdC0zOCAxMjRxMCA5MSA2NCAxNTV0MTU1IDY0dDE1NSAtNjR0NjQgLTE1NXEwIC02OCAtMzggLTEyNHQtMTAxIC04MXYtNjhxMTkwIDQ0IDM0MyA0NHE5OSAwIDE5OCAtMTVxMTQgLTIgMTExLjUgLTIyLjV0MTQ5LjUgLTIwLjUNCnE3NyAwIDE2NSAxOHExMSAyIDgwIDIxdDg5IDE5cTI2IDAgNDUgLTE5dDE5IC00NXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyQjUiIHVuaWNvZGU9IiYjeGYyYjU7IiBob3Jpei1hZHYteD0iMjMwNCIgDQpkPSJNMTkyIDM4NHE0MCAwIDU2IDMydDAgNjR0LTU2IDMydC01NiAtMzJ0MCAtNjR0NTYgLTMyek0xNjY1IDQ0MnEtMTAgMTMgLTM4LjUgNTB0LTQxLjUgNTR0LTM4IDQ5dC00Mi41IDUzdC00MC41IDQ3dC00NSA0OWwtMTI1IC0xNDBxLTgzIC05NCAtMjA4LjUgLTkydC0yMDUuNSA5OHEtNTcgNjkgLTU2LjUgMTU4dDU4LjUgMTU3bDE3NyAyMDZxLTIyIDExIC01MSAxNi41dC00Ny41IDZ0LTU2LjUgLTAuNXQtNDkgLTFxLTkyIDAgLTE1OCAtNjYNCmwtMTU4IC0xNThoLTE1NXYtNTQ0cTUgMCAyMSAwLjV0MjIgMHQxOS41IC0ydDIwLjUgLTQuNXQxNy41IC04LjV0MTguNSAtMTMuNWwyOTcgLTI5MnExMTUgLTExMSAyMjcgLTExMXE3OCAwIDEyNSA0N3E1NyAtMjAgMTEyLjUgOHQ3Mi41IDg1cTc0IC02IDEyNyA0NHEyMCAxOCAzNiA0NS41dDE0IDUwLjVxMTAgLTEwIDQzIC0xMHE0MyAwIDc3IDIxdDQ5LjUgNTN0MTIgNzEuNXQtMzAuNSA3My41ek0xODI0IDM4NGg5NnY1MTJoLTkzbC0xNTcgMTgwDQpxLTY2IDc2IC0xNjkgNzZoLTE2N3EtODkgMCAtMTQ2IC02N2wtMjA5IC0yNDNxLTI4IC0zMyAtMjggLTc1dDI3IC03NXE0MyAtNTEgMTEwIC01MnQxMTEgNDlsMTkzIDIxOHEyNSAyMyA1My41IDIxLjV0NDcgLTI3dDguNSAtNTYuNXExNiAtMTkgNTYgLTYzdDYwIC02OHEyOSAtMzYgODIuNSAtMTA1LjV0NjQuNSAtODQuNXE1MiAtNjYgNjAgLTE0MHpNMjExMiAzODRxNDAgMCA1NiAzMnQwIDY0dC01NiAzMnQtNTYgLTMydDAgLTY0dDU2IC0zMnoNCk0yMzA0IDk2MHYtNjQwcTAgLTI2IC0xOSAtNDV0LTQ1IC0xOWgtNDM0cS0yNyAtNjUgLTgyIC0xMDYuNXQtMTI1IC01MS41cS0zMyAtNDggLTgwLjUgLTgxLjV0LTEwMi41IC00NS41cS00MiAtNTMgLTEwNC41IC04MS41dC0xMjguNSAtMjQuNXEtNjAgLTM0IC0xMjYgLTM5LjV0LTEyNy41IDE0dC0xMTcgNTMuNXQtMTAzLjUgODFsLTI4NyAyODJoLTM1OHEtMjYgMCAtNDUgMTl0LTE5IDQ1djY3MnEwIDI2IDE5IDQ1dDQ1IDE5aDQyMQ0KcTE0IDE0IDQ3IDQ4dDQ3LjUgNDh0NDQgNDB0NTAuNSAzNy41dDUxIDI1LjV0NjIgMTkuNXQ2OCA1LjVoMTE3cTk5IDAgMTgxIC01NnE4MiA1NiAxODEgNTZoMTY3cTM1IDAgNjcgLTZ0NTYuNSAtMTQuNXQ1MS41IC0yNi41dDQ0LjUgLTMxdDQzIC0zOS41dDM5IC00MnQ0MSAtNDh0NDEuNSAtNDguNWgzNTVxMjYgMCA0NSAtMTl0MTkgLTQ1eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJCNiIgdW5pY29kZT0iJiN4ZjJiNjsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNzkyIDg4MnYtOTc4cTAgLTY2IC00NyAtMTEzdC0xMTMgLTQ3aC0xNDcycS02NiAwIC0xMTMgNDd0LTQ3IDExM3Y5NzhxMCAxNSAxMSAyNHE4IDcgMzkgMzQuNXQ0MS41IDM2dDQ1LjUgMzcuNXQ3MCA1NS41dDk2IDczdDE0My41IDEwN3QxOTIuNSAxNDAuNXE1IDQgNTIuNSA0MHQ3MS41IDUyLjV0NjQgMzV0NjkgMTguNXQ2OSAtMTguNXQ2NSAtMzUuNXQ3MSAtNTJ0NTIgLTQwcTExMCAtODAgMTkyLjUgLTE0MC41dDE0My41IC0xMDcNCnQ5NiAtNzN0NzAgLTU1LjV0NDUuNSAtMzcuNXQ0MS41IC0zNnQzOSAtMzQuNXExMSAtOSAxMSAtMjR6TTEyMjggMjk3cTI2MyAxOTEgMzQ1IDI1MnExMSA4IDEyLjUgMjAuNXQtNi41IDIzLjVsLTM4IDUycS04IDExIC0yMSAxMi41dC0yNCAtNi41cS0yMzEgLTE2OSAtMzQzIC0yNTBxLTUgLTMgLTUyIC0zOXQtNzEuNSAtNTIuNXQtNjQuNSAtMzV0LTY5IC0xOC41dC02OSAxOC41dC02NC41IDM1dC03MS41IDUyLjV0LTUyIDM5DQpxLTE4NiAxMzQgLTM0MyAyNTBxLTExIDggLTI0IDYuNXQtMjEgLTEyLjVsLTM4IC01MnEtOCAtMTEgLTYuNSAtMjMuNXQxMi41IC0yMC41cTgyIC02MSAzNDUgLTI1MnExMCAtOCA1MCAtMzh0NjUgLTQ3dDY0IC0zOS41dDc3LjUgLTMzLjV0NzUuNSAtMTF0NzUuNSAxMXQ3OSAzNC41dDY0LjUgMzkuNXQ2NSA0Ny41dDQ4IDM2LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkI3IiB1bmljb2RlPSImI3hmMmI3OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE0NzQgNjIzbDM5IC01MXE4IC0xMSA2LjUgLTIzLjV0LTExLjUgLTIwLjVxLTQzIC0zNCAtMTI2LjUgLTk4LjV0LTE0Ni41IC0xMTN0LTY3IC01MS41cS0zOSAtMzIgLTYwIC00OHQtNjAuNSAtNDF0LTc2LjUgLTM2LjV0LTc0IC0xMS41aC0xaC0xcS0zNyAwIC03NCAxMS41dC03NiAzNi41dC02MSA0MS41dC02MCA0Ny41cS01IDQgLTY1IDUwLjV0LTE0My41IDExMXQtMTIyLjUgOTQuNXEtMTEgOCAtMTIuNSAyMC41dDYuNSAyMy41DQpsMzcgNTJxOCAxMSAyMS41IDEzdDI0LjUgLTdxOTQgLTczIDMwNiAtMjM2cTUgLTQgNDMuNSAtMzV0NjAuNSAtNDYuNXQ1Ni41IC0zMi41dDU4LjUgLTE3aDFoMXEyNCAwIDU4LjUgMTd0NTYuNSAzMi41dDYwLjUgNDYuNXQ0My41IDM1cTI1OCAxOTggMzEzIDI0MnExMSA4IDI0IDYuNXQyMSAtMTIuNXpNMTY2NCAtOTZ2OTI4cS05MCA4MyAtMTU5IDEzOXEtOTEgNzQgLTM4OSAzMDRxLTMgMiAtNDMgMzV0LTYxIDQ4dC01NiAzMi41dC01OSAxNy41DQpoLTFoLTFxLTI0IDAgLTU5IC0xNy41dC01NiAtMzIuNXQtNjEgLTQ4dC00MyAtMzVxLTIxNSAtMTY2IC0zMTUuNSAtMjQ1LjV0LTEyOS41IC0xMDR0LTgyIC03NC41cS0xNCAtMTIgLTIxIC0xOXYtOTI4cTAgLTEzIDkuNSAtMjIuNXQyMi41IC05LjVoMTQ3MnExMyAwIDIyLjUgOS41dDkuNSAyMi41ek0xNzkyIDgzMnYtOTI4cTAgLTY2IC00NyAtMTEzdC0xMTMgLTQ3aC0xNDcycS02NiAwIC0xMTMgNDd0LTQ3IDExM3Y5MjhxMCA1NiA0MSA5NA0KcTEyMyAxMTQgMzUwIDI5MC41dDIzMyAxODEuNXEzNiAzMCA1OSA0Ny41dDYxLjUgNDJ0NzYgMzYuNXQ3NC41IDEyaDFoMXEzNyAwIDc0LjUgLTEydDc2IC0zNi41dDYxLjUgLTQydDU5IC00Ny41cTQzIC0zNiAxNTYgLTEyMnQyMjYgLTE3N3QyMDEgLTE3M3E0MSAtMzggNDEgLTk0eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJCOCIgdW5pY29kZT0iJiN4ZjJiODsiIA0KZD0iTTMzMCAxbDIwMiAtMjE0bC0zNCAyMzZsLTIxNiAyMTN6TTU1NiAtMjI1bDI3NCAyMThsLTExIDI0NWwtMzAwIC0yMTV6TTI0NSA0MTNsMjI3IC0yMTNsLTQ4IDMyN2wtMjQ1IDIwNHpNNDk1IDE4OWwzMTcgMjE0bC0xNCAzMjRsLTM1MiAtMjAwek04NDMgMTc4bDk1IC04MGwtMiAyMzlsLTEwMyA3OXEwIC0xIDEgLTguNXQwIC0xMnQtNSAtNy41bC03OCAtNTJsODUgLTcwcTcgLTYgNyAtODh6TTEzOCA5MzBsMjU2IC0yMDBsLTY4IDQ2NQ0KbC0yNzkgMTczek0xMTczIDI2N2wxNSAyMzRsLTIzMCAtMTY0bDIgLTI0MHpNNDE3IDcyMmwzNzMgMTk0bC0xOSA0NDFsLTQyMyAtMTYzek0xMjcwIDM1N2wyMCAyMzNsLTIyNiAxNDJsLTIgLTEwNWwxNDQgLTk1cTYgLTQgNCAtOWwtNyAtMTE5ek0xNDYxIDQ5NmwzMCAyMjJsLTE3OSAtMTI4bC0yMCAtMjI4ek0xMjczIDMyOWwtNzEgNDlsLTggLTExN3EwIC01IC00IC04bC0yMzQgLTE4N3EtNyAtNSAtMTQgMGwtOTggODNsNyAtMTYxDQpxMCAtNSAtNCAtOGwtMjkzIC0yMzRxLTQgLTIgLTYgLTJxLTggMiAtOCAzbC0yMjggMjQycS00IDQgLTU5IDI3N3EtMiA3IDUgMTFsNjEgMzdxLTk0IDg2IC05NSA5MmwtNzIgMzUxcS0yIDcgNiAxMmw5NCA0NXEtMTMzIDEwMCAtMTM1IDEwOGwtOTYgNDY2cS0yIDEwIDcgMTNsNDMzIDEzNXE1IDAgOCAtMWwzMTcgLTE1M3E2IC00IDYgLTlsMjAgLTQ2M3EwIC03IC02IC0xMGwtMTE4IC02MWwxMjYgLTg1cTUgLTIgNSAtOGw1IC0xMjNsMTIxIDc0DQpxNSA0IDExIDBsODQgLTU2bDMgMTEwcTAgNiA1IDlsMjA2IDEyNnE2IDMgMTEgMGwyNDUgLTEzNXE0IC00IDUgLTd0LTYuNSAtNjB0LTE3LjUgLTEyNC41dC0xMCAtNzAuNXEwIC01IC00IC03bC0xOTEgLTE1M3EtNiAtNSAtMTMgMHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyQjkiIHVuaWNvZGU9IiYjeGYyYjk7IiBob3Jpei1hZHYteD0iMTY2NCIgDQpkPSJNMTIwMSAyOThxMCA1NyAtNS41IDEwN3QtMjEgMTAwLjV0LTM5LjUgODZ0LTY0IDU4dC05MSAyMi41cS02IC00IC0zMy41IC0yMC41dC00Mi41IC0yNC41dC00MC41IC0yMHQtNDkgLTE3dC00Ni41IC01dC00Ni41IDV0LTQ5IDE3dC00MC41IDIwdC00Mi41IDI0LjV0LTMzLjUgMjAuNXEtNTEgMCAtOTEgLTIyLjV0LTY0IC01OHQtMzkuNSAtODZ0LTIxIC0xMDAuNXQtNS41IC0xMDdxMCAtNzMgNDIgLTEyMS41dDEwMyAtNDguNWg1NzYNCnE2MSAwIDEwMyA0OC41dDQyIDEyMS41ek0xMDI4IDg5MnEwIDEwOCAtNzYuNSAxODR0LTE4My41IDc2dC0xODMuNSAtNzZ0LTc2LjUgLTE4NHEwIC0xMDcgNzYuNSAtMTgzdDE4My41IC03NnQxODMuNSA3NnQ3Ni41IDE4M3pNMTY2NCAzNTJ2LTE5MnEwIC0xNCAtOSAtMjN0LTIzIC05aC05NnYtMjI0cTAgLTY2IC00NyAtMTEzdC0xMTMgLTQ3aC0xMjE2cS02NiAwIC0xMTMgNDd0LTQ3IDExM3YxNDcycTAgNjYgNDcgMTEzdDExMyA0N2gxMjE2DQpxNjYgMCAxMTMgLTQ3dDQ3IC0xMTN2LTIyNGg5NnExNCAwIDIzIC05dDkgLTIzdi0xOTJxMCAtMTQgLTkgLTIzdC0yMyAtOWgtOTZ2LTEyOGg5NnExNCAwIDIzIC05dDkgLTIzdi0xOTJxMCAtMTQgLTkgLTIzdC0yMyAtOWgtOTZ2LTEyOGg5NnExNCAwIDIzIC05dDkgLTIzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJCQSIgdW5pY29kZT0iJiN4ZjJiYTsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik0xMDI4IDg5MnEwIC0xMDcgLTc2LjUgLTE4M3QtMTgzLjUgLTc2dC0xODMuNSA3NnQtNzYuNSAxODNxMCAxMDggNzYuNSAxODR0MTgzLjUgNzZ0MTgzLjUgLTc2dDc2LjUgLTE4NHpNOTgwIDY3MnE0NiAwIDgyLjUgLTE3dDYwIC00Ny41dDM5LjUgLTY3dDI0IC04MXQxMS41IC04Mi41dDMuNSAtNzlxMCAtNjcgLTM5LjUgLTExOC41dC0xMDUuNSAtNTEuNWgtNTc2cS02NiAwIC0xMDUuNSA1MS41dC0zOS41IDExOC41cTAgNDggNC41IDkzLjUNCnQxOC41IDk4LjV0MzYuNSA5MS41dDYzIDY0LjV0OTMuNSAyNmg1cTcgLTQgMzIgLTE5LjV0MzUuNSAtMjF0MzMgLTE3dDM3IC0xNnQzNSAtOXQzOS41IC00LjV0MzkuNSA0LjV0MzUgOXQzNyAxNnQzMyAxN3QzNS41IDIxdDMyIDE5LjV6TTE2NjQgOTI4cTAgLTEzIC05LjUgLTIyLjV0LTIyLjUgLTkuNWgtOTZ2LTEyOGg5NnExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV2LTE5MnEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTk2di0xMjhoOTYNCnExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV2LTE5MnEwIC0xMyAtOS41IC0yMi41dC0yMi41IC05LjVoLTk2di0yMjRxMCAtNjYgLTQ3IC0xMTN0LTExMyAtNDdoLTEyMTZxLTY2IDAgLTExMyA0N3QtNDcgMTEzdjE0NzJxMCA2NiA0NyAxMTN0MTEzIDQ3aDEyMTZxNjYgMCAxMTMgLTQ3dDQ3IC0xMTN2LTIyNGg5NnExMyAwIDIyLjUgLTkuNXQ5LjUgLTIyLjV2LTE5MnpNMTQwOCAtOTZ2MTQ3MnEwIDEzIC05LjUgMjIuNXQtMjIuNSA5LjVoLTEyMTYNCnEtMTMgMCAtMjIuNSAtOS41dC05LjUgLTIyLjV2LTE0NzJxMCAtMTMgOS41IC0yMi41dDIyLjUgLTkuNWgxMjE2cTEzIDAgMjIuNSA5LjV0OS41IDIyLjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkJCIiB1bmljb2RlPSImI3hmMmJiOyIgaG9yaXotYWR2LXg9IjIwNDgiIA0KZD0iTTEwMjQgNDA1cTAgNjQgLTkgMTE3LjV0LTI5LjUgMTAzdC02MC41IDc4dC05NyAyOC41cS02IC00IC0zMCAtMTh0LTM3LjUgLTIxLjV0LTM1LjUgLTE3LjV0LTQzIC0xNC41dC00MiAtNC41dC00MiA0LjV0LTQzIDE0LjV0LTM1LjUgMTcuNXQtMzcuNSAyMS41dC0zMCAxOHEtNTcgMCAtOTcgLTI4LjV0LTYwLjUgLTc4dC0yOS41IC0xMDN0LTkgLTExNy41dDM3IC0xMDYuNXQ5MSAtNDIuNWg1MTJxNTQgMCA5MSA0Mi41dDM3IDEwNi41eg0KTTg2NyA5MjVxMCA5NCAtNjYuNSAxNjAuNXQtMTYwLjUgNjYuNXQtMTYwLjUgLTY2LjV0LTY2LjUgLTE2MC41dDY2LjUgLTE2MC41dDE2MC41IC02Ni41dDE2MC41IDY2LjV0NjYuNSAxNjAuNXpNMTc5MiA0MTZ2NjRxMCAxNCAtOSAyM3QtMjMgOWgtNTc2cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTY0cTAgLTE0IDkgLTIzdDIzIC05aDU3NnExNCAwIDIzIDl0OSAyM3pNMTc5MiA2NzZ2NTZxMCAxNSAtMTAuNSAyNS41dC0yNS41IDEwLjVoLTU2OA0KcS0xNSAwIC0yNS41IC0xMC41dC0xMC41IC0yNS41di01NnEwIC0xNSAxMC41IC0yNS41dDI1LjUgLTEwLjVoNTY4cTE1IDAgMjUuNSAxMC41dDEwLjUgMjUuNXpNMTc5MiA5Mjh2NjRxMCAxNCAtOSAyM3QtMjMgOWgtNTc2cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTY0cTAgLTE0IDkgLTIzdDIzIC05aDU3NnExNCAwIDIzIDl0OSAyM3pNMjA0OCAxMjQ4di0xMjE2cTAgLTY2IC00NyAtMTEzdC0xMTMgLTQ3aC0zNTJ2OTZxMCAxNCAtOSAyM3QtMjMgOQ0KaC02NHEtMTQgMCAtMjMgLTl0LTkgLTIzdi05NmgtNzY4djk2cTAgMTQgLTkgMjN0LTIzIDloLTY0cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTk2aC0zNTJxLTY2IDAgLTExMyA0N3QtNDcgMTEzdjEyMTZxMCA2NiA0NyAxMTN0MTEzIDQ3aDE3MjhxNjYgMCAxMTMgLTQ3dDQ3IC0xMTN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkJDIiB1bmljb2RlPSImI3hmMmJjOyIgaG9yaXotYWR2LXg9IjIwNDgiIA0KZD0iTTEwMjQgNDA1cTAgLTY0IC0zNyAtMTA2LjV0LTkxIC00Mi41aC01MTJxLTU0IDAgLTkxIDQyLjV0LTM3IDEwNi41dDkgMTE3LjV0MjkuNSAxMDN0NjAuNSA3OHQ5NyAyOC41cTYgLTQgMzAgLTE4dDM3LjUgLTIxLjV0MzUuNSAtMTcuNXQ0MyAtMTQuNXQ0MiAtNC41dDQyIDQuNXQ0MyAxNC41dDM1LjUgMTcuNXQzNy41IDIxLjV0MzAgMThxNTcgMCA5NyAtMjguNXQ2MC41IC03OHQyOS41IC0xMDN0OSAtMTE3LjV6TTg2NyA5MjUNCnEwIC05NCAtNjYuNSAtMTYwLjV0LTE2MC41IC02Ni41dC0xNjAuNSA2Ni41dC02Ni41IDE2MC41dDY2LjUgMTYwLjV0MTYwLjUgNjYuNXQxNjAuNSAtNjYuNXQ2Ni41IC0xNjAuNXpNMTc5MiA0ODB2LTY0cTAgLTE0IC05IC0yM3QtMjMgLTloLTU3NnEtMTQgMCAtMjMgOXQtOSAyM3Y2NHEwIDE0IDkgMjN0MjMgOWg1NzZxMTQgMCAyMyAtOXQ5IC0yM3pNMTc5MiA3MzJ2LTU2cTAgLTE1IC0xMC41IC0yNS41dC0yNS41IC0xMC41aC01NjgNCnEtMTUgMCAtMjUuNSAxMC41dC0xMC41IDI1LjV2NTZxMCAxNSAxMC41IDI1LjV0MjUuNSAxMC41aDU2OHExNSAwIDI1LjUgLTEwLjV0MTAuNSAtMjUuNXpNMTc5MiA5OTJ2LTY0cTAgLTE0IC05IC0yM3QtMjMgLTloLTU3NnEtMTQgMCAtMjMgOXQtOSAyM3Y2NHEwIDE0IDkgMjN0MjMgOWg1NzZxMTQgMCAyMyAtOXQ5IC0yM3pNMTkyMCAzMnYxMjE2cTAgMTMgLTkuNSAyMi41dC0yMi41IDkuNWgtMTcyOHEtMTMgMCAtMjIuNSAtOS41DQp0LTkuNSAtMjIuNXYtMTIxNnEwIC0xMyA5LjUgLTIyLjV0MjIuNSAtOS41aDM1MnY5NnEwIDE0IDkgMjN0MjMgOWg2NHExNCAwIDIzIC05dDkgLTIzdi05Nmg3Njh2OTZxMCAxNCA5IDIzdDIzIDloNjRxMTQgMCAyMyAtOXQ5IC0yM3YtOTZoMzUycTEzIDAgMjIuNSA5LjV0OS41IDIyLjV6TTIwNDggMTI0OHYtMTIxNnEwIC02NiAtNDcgLTExM3QtMTEzIC00N2gtMTcyOHEtNjYgMCAtMTEzIDQ3dC00NyAxMTN2MTIxNnEwIDY2IDQ3IDExMw0KdDExMyA0N2gxNzI4cTY2IDAgMTEzIC00N3Q0NyAtMTEzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJCRCIgdW5pY29kZT0iJiN4ZjJiZDsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNTIzIDE5N3EtMjIgMTU1IC04Ny41IDI1Ny41dC0xODQuNSAxMTguNXEtNjcgLTc0IC0xNTkuNSAtMTE1LjV0LTE5NS41IC00MS41dC0xOTUuNSA0MS41dC0xNTkuNSAxMTUuNXEtMTE5IC0xNiAtMTg0LjUgLTExOC41dC04Ny41IC0yNTcuNXExMDYgLTE1MCAyNzEgLTIzNy41dDM1NiAtODcuNXQzNTYgODcuNXQyNzEgMjM3LjV6TTEyODAgODk2cTAgMTU5IC0xMTIuNSAyNzEuNXQtMjcxLjUgMTEyLjV0LTI3MS41IC0xMTIuNQ0KdC0xMTIuNSAtMjcxLjV0MTEyLjUgLTI3MS41dDI3MS41IC0xMTIuNXQyNzEuNSAxMTIuNXQxMTIuNSAyNzEuNXpNMTc5MiA2NDBxMCAtMTgyIC03MSAtMzQ3LjV0LTE5MC41IC0yODZ0LTI4NS41IC0xOTEuNXQtMzQ5IC03MXEtMTgyIDAgLTM0OCA3MXQtMjg2IDE5MXQtMTkxIDI4NnQtNzEgMzQ4dDcxIDM0OHQxOTEgMjg2dDI4NiAxOTF0MzQ4IDcxdDM0OCAtNzF0Mjg2IC0xOTF0MTkxIC0yODZ0NzEgLTM0OHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyQkUiIHVuaWNvZGU9IiYjeGYyYmU7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNODk2IDE1MzZxMTgyIDAgMzQ4IC03MXQyODYgLTE5MXQxOTEgLTI4NnQ3MSAtMzQ4cTAgLTE4MSAtNzAuNSAtMzQ3dC0xOTAuNSAtMjg2dC0yODYgLTE5MS41dC0zNDkgLTcxLjV0LTM0OSA3MXQtMjg1LjUgMTkxLjV0LTE5MC41IDI4NnQtNzEgMzQ3LjV0NzEgMzQ4dDE5MSAyODZ0Mjg2IDE5MXQzNDggNzF6TTE1MTUgMTg1cTE0OSAyMDUgMTQ5IDQ1NXEwIDE1NiAtNjEgMjk4dC0xNjQgMjQ1dC0yNDUgMTY0dC0yOTggNjF0LTI5OCAtNjENCnQtMjQ1IC0xNjR0LTE2NCAtMjQ1dC02MSAtMjk4cTAgLTI1MCAxNDkgLTQ1NXE2NiAzMjcgMzA2IDMyN3ExMzEgLTEyOCAzMTMgLTEyOHQzMTMgMTI4cTI0MCAwIDMwNiAtMzI3ek0xMjgwIDgzMnEwIDE1OSAtMTEyLjUgMjcxLjV0LTI3MS41IDExMi41dC0yNzEuNSAtMTEyLjV0LTExMi41IC0yNzEuNXQxMTIuNSAtMjcxLjV0MjcxLjUgLTExMi41dDI3MS41IDExMi41dDExMi41IDI3MS41eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJDMCIgdW5pY29kZT0iJiN4ZjJjMDsiIA0KZD0iTTEyMDEgNzUycTQ3IC0xNCA4OS41IC0zOHQ4OSAtNzN0NzkuNSAtMTE1LjV0NTUgLTE3MnQyMiAtMjM2LjVxMCAtMTU0IC0xMDAgLTI2My41dC0yNDEgLTEwOS41aC04NTRxLTE0MSAwIC0yNDEgMTA5LjV0LTEwMCAyNjMuNXEwIDEzMSAyMiAyMzYuNXQ1NSAxNzJ0NzkuNSAxMTUuNXQ4OSA3M3Q4OS41IDM4cS03OSAxMjUgLTc5IDI3MnEwIDEwNCA0MC41IDE5OC41dDEwOS41IDE2My41dDE2My41IDEwOS41dDE5OC41IDQwLjUNCnQxOTguNSAtNDAuNXQxNjMuNSAtMTA5LjV0MTA5LjUgLTE2My41dDQwLjUgLTE5OC41cTAgLTE0NyAtNzkgLTI3MnpNNzY4IDE0MDhxLTE1OSAwIC0yNzEuNSAtMTEyLjV0LTExMi41IC0yNzEuNXQxMTIuNSAtMjcxLjV0MjcxLjUgLTExMi41dDI3MS41IDExMi41dDExMi41IDI3MS41dC0xMTIuNSAyNzEuNXQtMjcxLjUgMTEyLjV6TTExOTUgLTEyOHE4OCAwIDE1MC41IDcxLjV0NjIuNSAxNzMuNXEwIDIzOSAtNzguNSAzNzd0LTIyNS41IDE0NQ0KcS0xNDUgLTEyNyAtMzM2IC0xMjd0LTMzNiAxMjdxLTE0NyAtNyAtMjI1LjUgLTE0NXQtNzguNSAtMzc3cTAgLTEwMiA2Mi41IC0xNzMuNXQxNTAuNSAtNzEuNWg4NTR6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkMxIiB1bmljb2RlPSImI3hmMmMxOyIgaG9yaXotYWR2LXg9IjEyODAiIA0KZD0iTTEwMjQgMjc4cTAgLTY0IC0zNyAtMTA3dC05MSAtNDNoLTUxMnEtNTQgMCAtOTEgNDN0LTM3IDEwN3Q5IDExOHQyOS41IDEwNHQ2MSA3OC41dDk2LjUgMjguNXE4MCAtNzUgMTg4IC03NXQxODggNzVxNTYgMCA5Ni41IC0yOC41dDYxIC03OC41dDI5LjUgLTEwNHQ5IC0xMTh6TTg3MCA3OTdxMCAtOTQgLTY3LjUgLTE2MC41dC0xNjIuNSAtNjYuNXQtMTYyLjUgNjYuNXQtNjcuNSAxNjAuNXQ2Ny41IDE2MC41dDE2Mi41IDY2LjUNCnQxNjIuNSAtNjYuNXQ2Ny41IC0xNjAuNXpNMTE1MiAtOTZ2MTM3NmgtMTAyNHYtMTM3NnEwIC0xMyA5LjUgLTIyLjV0MjIuNSAtOS41aDk2MHExMyAwIDIyLjUgOS41dDkuNSAyMi41ek0xMjgwIDEzNzZ2LTE0NzJxMCAtNjYgLTQ3IC0xMTN0LTExMyAtNDdoLTk2MHEtNjYgMCAtMTEzIDQ3dC00NyAxMTN2MTQ3MnEwIDY2IDQ3IDExM3QxMTMgNDdoMzUydi05NnEwIC0xNCA5IC0yM3QyMyAtOWgxOTJxMTQgMCAyMyA5dDkgMjN2OTZoMzUyDQpxNjYgMCAxMTMgLTQ3dDQ3IC0xMTN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkMyIiB1bmljb2RlPSImI3hmMmMyOyIgaG9yaXotYWR2LXg9IjIwNDgiIA0KZD0iTTg5NiAzMjRxMCA1NCAtNy41IDEwMC41dC0yNC41IDkwdC01MSA2OC41dC04MSAyNXEtNjQgLTY0IC0xNTYgLTY0dC0xNTYgNjRxLTQ3IDAgLTgxIC0yNXQtNTEgLTY4LjV0LTI0LjUgLTkwdC03LjUgLTEwMC41cTAgLTU1IDMxLjUgLTkzLjV0NzUuNSAtMzguNWg0MjZxNDQgMCA3NS41IDM4LjV0MzEuNSA5My41ek03NjggNzY4cTAgODAgLTU2IDEzNnQtMTM2IDU2dC0xMzYgLTU2dC01NiAtMTM2dDU2IC0xMzZ0MTM2IC01NnQxMzYgNTYNCnQ1NiAxMzZ6TTE3OTIgMjg4djY0cTAgMTQgLTkgMjN0LTIzIDloLTcwNHEtMTQgMCAtMjMgLTl0LTkgLTIzdi02NHEwIC0xNCA5IC0yM3QyMyAtOWg3MDRxMTQgMCAyMyA5dDkgMjN6TTE0MDggNTQ0djY0cTAgMTQgLTkgMjN0LTIzIDloLTMyMHEtMTQgMCAtMjMgLTl0LTkgLTIzdi02NHEwIC0xNCA5IC0yM3QyMyAtOWgzMjBxMTQgMCAyMyA5dDkgMjN6TTE3OTIgNTQ0djY0cTAgMTQgLTkgMjN0LTIzIDloLTE5MnEtMTQgMCAtMjMgLTl0LTkgLTIzDQp2LTY0cTAgLTE0IDkgLTIzdDIzIC05aDE5MnExNCAwIDIzIDl0OSAyM3pNMTc5MiA4MDB2NjRxMCAxNCAtOSAyM3QtMjMgOWgtNzA0cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTY0cTAgLTE0IDkgLTIzdDIzIC05aDcwNHExNCAwIDIzIDl0OSAyM3pNMTI4IDExNTJoMTc5MnY5NnEwIDE0IC05IDIzdC0yMyA5aC0xNzI4cS0xNCAwIC0yMyAtOXQtOSAtMjN2LTk2ek0yMDQ4IDEyNDh2LTEyMTZxMCAtNjYgLTQ3IC0xMTN0LTExMyAtNDdoLTE3MjgNCnEtNjYgMCAtMTEzIDQ3dC00NyAxMTN2MTIxNnEwIDY2IDQ3IDExM3QxMTMgNDdoMTcyOHE2NiAwIDExMyAtNDd0NDcgLTExM3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyQzMiIHVuaWNvZGU9IiYjeGYyYzM7IiBob3Jpei1hZHYteD0iMjA0OCIgDQpkPSJNODk2IDMyNHEwIC01NSAtMzEuNSAtOTMuNXQtNzUuNSAtMzguNWgtNDI2cS00NCAwIC03NS41IDM4LjV0LTMxLjUgOTMuNXEwIDU0IDcuNSAxMDAuNXQyNC41IDkwdDUxIDY4LjV0ODEgMjVxNjQgLTY0IDE1NiAtNjR0MTU2IDY0cTQ3IDAgODEgLTI1dDUxIC02OC41dDI0LjUgLTkwdDcuNSAtMTAwLjV6TTc2OCA3NjhxMCAtODAgLTU2IC0xMzZ0LTEzNiAtNTZ0LTEzNiA1NnQtNTYgMTM2dDU2IDEzNnQxMzYgNTZ0MTM2IC01NnQ1NiAtMTM2eg0KTTE3OTIgMzUydi02NHEwIC0xNCAtOSAtMjN0LTIzIC05aC03MDRxLTE0IDAgLTIzIDl0LTkgMjN2NjRxMCAxNCA5IDIzdDIzIDloNzA0cTE0IDAgMjMgLTl0OSAtMjN6TTE0MDggNjA4di02NHEwIC0xNCAtOSAtMjN0LTIzIC05aC0zMjBxLTE0IDAgLTIzIDl0LTkgMjN2NjRxMCAxNCA5IDIzdDIzIDloMzIwcTE0IDAgMjMgLTl0OSAtMjN6TTE3OTIgNjA4di02NHEwIC0xNCAtOSAtMjN0LTIzIC05aC0xOTJxLTE0IDAgLTIzIDl0LTkgMjN2NjQNCnEwIDE0IDkgMjN0MjMgOWgxOTJxMTQgMCAyMyAtOXQ5IC0yM3pNMTc5MiA4NjR2LTY0cTAgLTE0IC05IC0yM3QtMjMgLTloLTcwNHEtMTQgMCAtMjMgOXQtOSAyM3Y2NHEwIDE0IDkgMjN0MjMgOWg3MDRxMTQgMCAyMyAtOXQ5IC0yM3pNMTkyMCAzMnYxMTIwaC0xNzkydi0xMTIwcTAgLTEzIDkuNSAtMjIuNXQyMi41IC05LjVoMTcyOHExMyAwIDIyLjUgOS41dDkuNSAyMi41ek0yMDQ4IDEyNDh2LTEyMTZxMCAtNjYgLTQ3IC0xMTN0LTExMyAtNDcNCmgtMTcyOHEtNjYgMCAtMTEzIDQ3dC00NyAxMTN2MTIxNnEwIDY2IDQ3IDExM3QxMTMgNDdoMTcyOHE2NiAwIDExMyAtNDd0NDcgLTExM3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyQzQiIHVuaWNvZGU9IiYjeGYyYzQ7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTI1NSA3NDlxMCAzMTggLTEwNSA0NzQuNXQtMzMwIDE1Ni41cS0yMjIgMCAtMzI2IC0xNTd0LTEwNCAtNDc0cTAgLTMxNiAxMDQgLTQ3MS41dDMyNiAtMTU1LjVxNzQgMCAxMzEgMTdxLTIyIDQzIC0zOSA3M3QtNDQgNjV0LTUzLjUgNTYuNXQtNjMgMzZ0LTc3LjUgMTQuNXEtNDYgMCAtNzkgLTE2bC00OSA5N3ExMDUgOTEgMjc2IDkxcTEzMiAwIDIxNS41IC01NHQxNTAuNSAtMTU1cTY3IDE0OSA2NyA0MDJ6TTE2NDUgMTE3aDExNw0KcTMgLTI3IC0yIC02N3QtMjYuNSAtOTV0LTU4IC0xMDAuNXQtMTA3IC03OHQtMTYyLjUgLTMyLjVxLTcxIDAgLTEzMC41IDE5dC0xMDUuNSA1NnQtNzkgNzh0LTY2IDk2cS05NyAtMjcgLTIwNSAtMjdxLTE1MCAwIC0yOTIuNSA1OHQtMjUzIDE1OC41dC0xNzggMjQ5dC02Ny41IDMxNy41cTAgMTcwIDY3LjUgMzE5LjV0MTc4LjUgMjUwLjV0MjUzLjUgMTU5dDI5MS41IDU4cTEyMSAwIDIzOC41IC0zNnQyMTcgLTEwNnQxNzYgLTE2NC41DQp0MTE5LjUgLTIxOXQ0MyAtMjYxLjVxMCAtMTkwIC04MC41IC0zNDcuNXQtMjE4LjUgLTI2NC41cTQ3IC03MCA5My41IC0xMDYuNXQxMDQuNSAtMzYuNXE2MSAwIDk0IDM3LjV0MzggODUuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyQzUiIHVuaWNvZGU9IiYjeGYyYzU7IiBob3Jpei1hZHYteD0iMjMwNCIgDQpkPSJNNDUzIC0xMDFxMCAtMjEgLTE2IC0zNy41dC0zNyAtMTYuNXEtMSAwIC0xMyAzcS02MyAxNSAtMTYyIDE0MHEtMjI1IDI4NCAtMjI1IDY3NnEwIDM0MSAyMTMgNjE0cTM5IDUxIDk1IDEwMy41dDk0IDUyLjVxMTkgMCAzNSAtMTMuNXQxNiAtMzIuNXEwIC0yNyAtNjMgLTkwcS05OCAtMTAyIC0xNDcgLTE4NHEtMTE5IC0xOTkgLTExOSAtNDQ5cTAgLTI4MSAxMjMgLTQ5MXE1MCAtODUgMTM2IC0xNzNxMiAtMyAxNC41IC0xNnQxOS41IC0yMQ0KdDE3IC0yMC41dDE0LjUgLTIzLjV0NC41IC0yMXpNMTc5NiAzM3EwIC0yOSAtMTcuNSAtNDguNXQtNDYuNSAtMTkuNWgtMTA4MXEtMjYgMCAtNDUgMTl0LTE5IDQ1cTAgMjkgMTcuNSA0OC41dDQ2LjUgMTkuNWgxMDgxcTI2IDAgNDUgLTE5dDE5IC00NXpNMTU4MSA2NDRxMCAtMTM0IC02NyAtMjMzcS0yNSAtMzggLTY5LjUgLTc4LjV0LTgzLjUgLTYwLjVxLTE2IC0xMCAtMjcgLTEwcS03IDAgLTE1IDZ0LTggMTJxMCA5IDE5IDMwdDQyIDQ2DQp0NDIgNjcuNXQxOSA4OC41cTAgNzYgLTM1IDEzMHEtMjkgNDIgLTQ2IDQycS0zIDAgLTMgLTVxMCAtMTIgNy41IC0zNS41dDcuNSAtMzYuNXEwIC0yMiAtMjEuNSAtMzV0LTQ0LjUgLTEzcS02NiAwIC02NiA3NnEwIDE1IDEuNSA0NHQxLjUgNDRxMCAyNSAtMTAgNDZxLTEzIDI1IC00MiA1My41dC01MSAyOC41cS01IDAgLTcgLTAuNXQtMy41IC0yLjV0LTEuNSAtNnEwIC0yIDE2IC0yNnQxNiAtNTRxMCAtMzcgLTE5IC02OHQtNDYgLTU0DQp0LTUzLjUgLTQ2dC00NS41IC01NHQtMTkgLTY4cTAgLTk4IDQyIC0xNjBxMjkgLTQzIDc5IC02M3ExNiAtNSAxNyAtMTBxMSAtMiAxIC01cTAgLTE2IC0xOCAtMTZxLTYgMCAtMzMgMTFxLTExOSA0MyAtMTk1IDEzOS41dC03NiAyMTguNXEwIDU1IDI0LjUgMTE1LjV0NjAgMTE1dDcwLjUgMTA4LjV0NTkuNSAxMTMuNXQyNC41IDExMS41cTAgNTMgLTI1IDk0cS0yOSA0OCAtNTYgNjRxLTE5IDkgLTE5IDIxcTAgMjAgNDEgMjBxNTAgMCAxMTAgLTI5DQpxNDEgLTE5IDcxIC00NC41dDQ5LjUgLTUxdDMzLjUgLTYyLjV0MjIgLTY5dDE2IC04MHEwIC0xIDMgLTE3LjV0NC41IC0yNXQ1LjUgLTI1dDkgLTI3dDExIC0yMS41dDE0LjUgLTE2LjV0MTguNSAtNS41cTIzIDAgMzcgMTR0MTQgMzdxMCAyNSAtMjAgNjd0LTIwIDUydDEwIDEwcTI3IDAgOTMgLTcwcTcyIC03NiAxMDIuNSAtMTU2dDMwLjUgLTE4NnpNMjMwNCA2MTVxMCAtMjc0IC0xMzggLTUwM3EtMTkgLTMyIC00OCAtNzJ0LTY4IC04Ni41DQp0LTgxIC03N3QtNzQgLTMwLjVxLTE2IDAgLTMxIDE1LjV0LTE1IDMxLjVxMCAxNSAyOSA1MC41dDY4LjUgNzd0NDguNSA1Mi41cTE4MyAyMzAgMTgzIDUzMXEwIDEzMSAtMjAuNSAyMzV0LTcyLjUgMjExcS01OCAxMTkgLTE2MyAyMjhxLTIgMyAtMTMgMTMuNXQtMTYuNSAxNi41dC0xNSAxNy41dC0xNSAyMHQtOS41IDE4LjV0LTQgMTlxMCAxOSAxNiAzNS41dDM1IDE2LjVxNzAgMCAxOTYgLTE2OXE5OCAtMTMxIDE0NiAtMjczdDYwIC0zMTQNCnEyIC00MiAyIC02NHoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyQzYiIHVuaWNvZGU9IiYjeGYyYzY7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTE4OSAyMjlsMTQ3IDY5M3E5IDQ0IC0xMC41IDYzdC01MS41IDdsLTg2NCAtMzMzcS0yOSAtMTEgLTM5LjUgLTI1dC0yLjUgLTI2LjV0MzIgLTE5LjVsMjIxIC02OWw1MTMgMzIzcTIxIDE0IDMyIDZxNyAtNSAtNCAtMTVsLTQxNSAtMzc1djB2MGwtMTYgLTIyOHEyMyAwIDQ1IDIybDEwOCAxMDRsMjI0IC0xNjVxNjQgLTM2IDgxIDM4ek0xNzkyIDY0MHEwIC0xODIgLTcxIC0zNDh0LTE5MSAtMjg2dC0yODYgLTE5MXQtMzQ4IC03MQ0KdC0zNDggNzF0LTI4NiAxOTF0LTE5MSAyODZ0LTcxIDM0OHQ3MSAzNDh0MTkxIDI4NnQyODYgMTkxdDM0OCA3MXQzNDggLTcxdDI4NiAtMTkxdDE5MSAtMjg2dDcxIC0zNDh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkM3IiB1bmljb2RlPSImI3hmMmM3OyIgaG9yaXotYWR2LXg9IjEwMjQiIA0KZD0iTTY0MCAxOTJxMCAtODAgLTU2IC0xMzZ0LTEzNiAtNTZ0LTEzNiA1NnQtNTYgMTM2cTAgNjAgMzUgMTEwdDkzIDcxdjkwN2gxMjh2LTkwN3E1OCAtMjEgOTMgLTcxdDM1IC0xMTB6TTc2OCAxOTJxMCA3NyAtMzQgMTQ0dC05NCAxMTJ2NzY4cTAgODAgLTU2IDEzNnQtMTM2IDU2dC0xMzYgLTU2dC01NiAtMTM2di03NjhxLTYwIC00NSAtOTQgLTExMnQtMzQgLTE0NHEwIC0xMzMgOTMuNSAtMjI2LjV0MjI2LjUgLTkzLjV0MjI2LjUgOTMuNQ0KdDkzLjUgMjI2LjV6TTg5NiAxOTJxMCAtMTg1IC0xMzEuNSAtMzE2LjV0LTMxNi41IC0xMzEuNXQtMzE2LjUgMTMxLjV0LTEzMS41IDMxNi41cTAgMTgyIDEyOCAzMTN2NzExcTAgMTMzIDkzLjUgMjI2LjV0MjI2LjUgOTMuNXQyMjYuNSAtOTMuNXQ5My41IC0yMjYuNXYtNzExcTEyOCAtMTMxIDEyOCAtMzEzek0xMDI0IDc2OHYtMTI4aC0xOTJ2MTI4aDE5MnpNMTAyNCAxMDI0di0xMjhoLTE5MnYxMjhoMTkyek0xMDI0IDEyODB2LTEyOGgtMTkyDQp2MTI4aDE5MnoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyQzgiIHVuaWNvZGU9IiYjeGYyYzg7IiBob3Jpei1hZHYteD0iMTAyNCIgDQpkPSJNNjQwIDE5MnEwIC04MCAtNTYgLTEzNnQtMTM2IC01NnQtMTM2IDU2dC01NiAxMzZxMCA2MCAzNSAxMTB0OTMgNzF2NjUxaDEyOHYtNjUxcTU4IC0yMSA5MyAtNzF0MzUgLTExMHpNNzY4IDE5MnEwIDc3IC0zNCAxNDR0LTk0IDExMnY3NjhxMCA4MCAtNTYgMTM2dC0xMzYgNTZ0LTEzNiAtNTZ0LTU2IC0xMzZ2LTc2OHEtNjAgLTQ1IC05NCAtMTEydC0zNCAtMTQ0cTAgLTEzMyA5My41IC0yMjYuNXQyMjYuNSAtOTMuNXQyMjYuNSA5My41DQp0OTMuNSAyMjYuNXpNODk2IDE5MnEwIC0xODUgLTEzMS41IC0zMTYuNXQtMzE2LjUgLTEzMS41dC0zMTYuNSAxMzEuNXQtMTMxLjUgMzE2LjVxMCAxODIgMTI4IDMxM3Y3MTFxMCAxMzMgOTMuNSAyMjYuNXQyMjYuNSA5My41dDIyNi41IC05My41dDkzLjUgLTIyNi41di03MTFxMTI4IC0xMzEgMTI4IC0zMTN6TTEwMjQgNzY4di0xMjhoLTE5MnYxMjhoMTkyek0xMDI0IDEwMjR2LTEyOGgtMTkydjEyOGgxOTJ6TTEwMjQgMTI4MHYtMTI4aC0xOTINCnYxMjhoMTkyeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJDOSIgdW5pY29kZT0iJiN4ZjJjOTsiIGhvcml6LWFkdi14PSIxMDI0IiANCmQ9Ik02NDAgMTkycTAgLTgwIC01NiAtMTM2dC0xMzYgLTU2dC0xMzYgNTZ0LTU2IDEzNnEwIDYwIDM1IDExMHQ5MyA3MXYzOTVoMTI4di0zOTVxNTggLTIxIDkzIC03MXQzNSAtMTEwek03NjggMTkycTAgNzcgLTM0IDE0NHQtOTQgMTEydjc2OHEwIDgwIC01NiAxMzZ0LTEzNiA1NnQtMTM2IC01NnQtNTYgLTEzNnYtNzY4cS02MCAtNDUgLTk0IC0xMTJ0LTM0IC0xNDRxMCAtMTMzIDkzLjUgLTIyNi41dDIyNi41IC05My41dDIyNi41IDkzLjUNCnQ5My41IDIyNi41ek04OTYgMTkycTAgLTE4NSAtMTMxLjUgLTMxNi41dC0zMTYuNSAtMTMxLjV0LTMxNi41IDEzMS41dC0xMzEuNSAzMTYuNXEwIDE4MiAxMjggMzEzdjcxMXEwIDEzMyA5My41IDIyNi41dDIyNi41IDkzLjV0MjI2LjUgLTkzLjV0OTMuNSAtMjI2LjV2LTcxMXExMjggLTEzMSAxMjggLTMxM3pNMTAyNCA3Njh2LTEyOGgtMTkydjEyOGgxOTJ6TTEwMjQgMTAyNHYtMTI4aC0xOTJ2MTI4aDE5MnpNMTAyNCAxMjgwdi0xMjhoLTE5Mg0KdjEyOGgxOTJ6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkNBIiB1bmljb2RlPSImI3hmMmNhOyIgaG9yaXotYWR2LXg9IjEwMjQiIA0KZD0iTTY0MCAxOTJxMCAtODAgLTU2IC0xMzZ0LTEzNiAtNTZ0LTEzNiA1NnQtNTYgMTM2cTAgNjAgMzUgMTEwdDkzIDcxdjEzOWgxMjh2LTEzOXE1OCAtMjEgOTMgLTcxdDM1IC0xMTB6TTc2OCAxOTJxMCA3NyAtMzQgMTQ0dC05NCAxMTJ2NzY4cTAgODAgLTU2IDEzNnQtMTM2IDU2dC0xMzYgLTU2dC01NiAtMTM2di03NjhxLTYwIC00NSAtOTQgLTExMnQtMzQgLTE0NHEwIC0xMzMgOTMuNSAtMjI2LjV0MjI2LjUgLTkzLjV0MjI2LjUgOTMuNQ0KdDkzLjUgMjI2LjV6TTg5NiAxOTJxMCAtMTg1IC0xMzEuNSAtMzE2LjV0LTMxNi41IC0xMzEuNXQtMzE2LjUgMTMxLjV0LTEzMS41IDMxNi41cTAgMTgyIDEyOCAzMTN2NzExcTAgMTMzIDkzLjUgMjI2LjV0MjI2LjUgOTMuNXQyMjYuNSAtOTMuNXQ5My41IC0yMjYuNXYtNzExcTEyOCAtMTMxIDEyOCAtMzEzek0xMDI0IDc2OHYtMTI4aC0xOTJ2MTI4aDE5MnpNMTAyNCAxMDI0di0xMjhoLTE5MnYxMjhoMTkyek0xMDI0IDEyODB2LTEyOGgtMTkyDQp2MTI4aDE5MnoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyQ0IiIHVuaWNvZGU9IiYjeGYyY2I7IiBob3Jpei1hZHYteD0iMTAyNCIgDQpkPSJNNjQwIDE5MnEwIC04MCAtNTYgLTEzNnQtMTM2IC01NnQtMTM2IDU2dC01NiAxMzZxMCA3OSA1NiAxMzUuNXQxMzYgNTYuNXQxMzYgLTU2LjV0NTYgLTEzNS41ek03NjggMTkycTAgNzcgLTM0IDE0NHQtOTQgMTEydjc2OHEwIDgwIC01NiAxMzZ0LTEzNiA1NnQtMTM2IC01NnQtNTYgLTEzNnYtNzY4cS02MCAtNDUgLTk0IC0xMTJ0LTM0IC0xNDRxMCAtMTMzIDkzLjUgLTIyNi41dDIyNi41IC05My41dDIyNi41IDkzLjV0OTMuNSAyMjYuNXoNCk04OTYgMTkycTAgLTE4NSAtMTMxLjUgLTMxNi41dC0zMTYuNSAtMTMxLjV0LTMxNi41IDEzMS41dC0xMzEuNSAzMTYuNXEwIDE4MiAxMjggMzEzdjcxMXEwIDEzMyA5My41IDIyNi41dDIyNi41IDkzLjV0MjI2LjUgLTkzLjV0OTMuNSAtMjI2LjV2LTcxMXExMjggLTEzMSAxMjggLTMxM3pNMTAyNCA3Njh2LTEyOGgtMTkydjEyOGgxOTJ6TTEwMjQgMTAyNHYtMTI4aC0xOTJ2MTI4aDE5MnpNMTAyNCAxMjgwdi0xMjhoLTE5MnYxMjhoMTkyeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJDQyIgdW5pY29kZT0iJiN4ZjJjYzsiIGhvcml6LWFkdi14PSIxOTIwIiANCmQ9Ik0xNDMzIDEyODdxMTAgLTEwIDEwIC0yM3QtMTAgLTIzbC02MjYgLTYyNnEtMTAgLTEwIC0yMyAtMTB0LTIzIDEwbC04MiA4MnEtMTAgMTAgLTEwIDIzdDEwIDIzbDQ0IDQ0cS03MiA5MSAtODEuNSAyMDd0NDYuNSAyMTVxLTc0IDcxIC0xNzYgNzFxLTEwNiAwIC0xODEgLTc1dC03NSAtMTgxdi0xMjgwaC0yNTZ2MTI4MHEwIDEwNCA0MC41IDE5OC41dDEwOS41IDE2My41dDE2My41IDEwOS41dDE5OC41IDQwLjVxMTA2IDAgMjAxIC00MQ0KdDE2NiAtMTE1cTk0IDM5IDE5NyAyNC41dDE4NSAtNzkuNWw0NCA0NHExMCAxMCAyMyAxMHQyMyAtMTB6TTEzNDQgMTAyNHEyNiAwIDQ1IC0xOXQxOSAtNDV0LTE5IC00NXQtNDUgLTE5dC00NSAxOXQtMTkgNDV0MTkgNDV0NDUgMTl6TTE2MDAgODk2cS0yNiAwIC00NSAxOXQtMTkgNDV0MTkgNDV0NDUgMTl0NDUgLTE5dDE5IC00NXQtMTkgLTQ1dC00NSAtMTl6TTE4NTYgMTAyNHEyNiAwIDQ1IC0xOXQxOSAtNDV0LTE5IC00NXQtNDUgLTE5DQp0LTQ1IDE5dC0xOSA0NXQxOSA0NXQ0NSAxOXpNMTIxNiA4OTZxMjYgMCA0NSAtMTl0MTkgLTQ1dC0xOSAtNDV0LTQ1IC0xOXQtNDUgMTl0LTE5IDQ1dDE5IDQ1dDQ1IDE5ek0xNDA4IDgzMnEwIDI2IDE5IDQ1dDQ1IDE5dDQ1IC0xOXQxOSAtNDV0LTE5IC00NXQtNDUgLTE5dC00NSAxOXQtMTkgNDV6TTE3MjggODk2cTI2IDAgNDUgLTE5dDE5IC00NXQtMTkgLTQ1dC00NSAtMTl0LTQ1IDE5dC0xOSA0NXQxOSA0NXQ0NSAxOXpNMTA4OCA3NjgNCnEyNiAwIDQ1IC0xOXQxOSAtNDV0LTE5IC00NXQtNDUgLTE5dC00NSAxOXQtMTkgNDV0MTkgNDV0NDUgMTl6TTEzNDQgNjQwcS0yNiAwIC00NSAxOXQtMTkgNDV0MTkgNDV0NDUgMTl0NDUgLTE5dDE5IC00NXQtMTkgLTQ1dC00NSAtMTl6TTE2MDAgNzY4cTI2IDAgNDUgLTE5dDE5IC00NXQtMTkgLTQ1dC00NSAtMTl0LTQ1IDE5dC0xOSA0NXQxOSA0NXQ0NSAxOXpNMTIxNiA1MTJxLTI2IDAgLTQ1IDE5dC0xOSA0NXQxOSA0NXQ0NSAxOXQ0NSAtMTkNCnQxOSAtNDV0LTE5IC00NXQtNDUgLTE5ek0xNDcyIDY0MHEyNiAwIDQ1IC0xOXQxOSAtNDV0LTE5IC00NXQtNDUgLTE5dC00NSAxOXQtMTkgNDV0MTkgNDV0NDUgMTl6TTEwODggNTEycTI2IDAgNDUgLTE5dDE5IC00NXQtMTkgLTQ1dC00NSAtMTl0LTQ1IDE5dC0xOSA0NXQxOSA0NXQ0NSAxOXpNMTM0NCA1MTJxMjYgMCA0NSAtMTl0MTkgLTQ1dC0xOSAtNDV0LTQ1IC0xOXQtNDUgMTl0LTE5IDQ1dDE5IDQ1dDQ1IDE5ek0xMjE2IDM4NA0KcTI2IDAgNDUgLTE5dDE5IC00NXQtMTkgLTQ1dC00NSAtMTl0LTQ1IDE5dC0xOSA0NXQxOSA0NXQ0NSAxOXpNMTA4OCAyNTZxMjYgMCA0NSAtMTl0MTkgLTQ1dC0xOSAtNDV0LTQ1IC0xOXQtNDUgMTl0LTE5IDQ1dDE5IDQ1dDQ1IDE5eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJDRCIgdW5pY29kZT0iJiN4ZjJjZDsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xNjY0IDQ0OHYtMTkycTAgLTE2OSAtMTI4IC0yODZ2LTE5NHEwIC0xNCAtOSAtMjN0LTIzIC05aC02NHEtMTQgMCAtMjMgOXQtOSAyM3YxMThxLTYzIC0yMiAtMTI4IC0yMmgtNzY4cS02NSAwIC0xMjggMjJ2LTExMHEwIC0xNyAtOS41IC0yOC41dC0yMi41IC0xMS41aC02NHEtMTMgMCAtMjIuNSAxMS41dC05LjUgMjguNXYxODZxLTEyOCAxMTcgLTEyOCAyODZ2MTkyaDE1MzZ6TTcwNCA4NjRxMCAtMTQgLTkgLTIzdC0yMyAtOXQtMjMgOQ0KdC05IDIzdDkgMjN0MjMgOXQyMyAtOXQ5IC0yM3pNNzY4IDkyOHEwIC0xNCAtOSAtMjN0LTIzIC05dC0yMyA5dC05IDIzdDkgMjN0MjMgOXQyMyAtOXQ5IC0yM3pNNzA0IDk5MnEwIC0xNCAtOSAtMjN0LTIzIC05dC0yMyA5dC05IDIzdDkgMjN0MjMgOXQyMyAtOXQ5IC0yM3pNODMyIDk5MnEwIC0xNCAtOSAtMjN0LTIzIC05dC0yMyA5dC05IDIzdDkgMjN0MjMgOXQyMyAtOXQ5IC0yM3pNNzY4IDEwNTZxMCAtMTQgLTkgLTIzdC0yMyAtOXQtMjMgOQ0KdC05IDIzdDkgMjN0MjMgOXQyMyAtOXQ5IC0yM3pNNzA0IDExMjBxMCAtMTQgLTkgLTIzdC0yMyAtOXQtMjMgOXQtOSAyM3Q5IDIzdDIzIDl0MjMgLTl0OSAtMjN6TTE3OTIgNjA4di02NHEwIC0xNCAtOSAtMjN0LTIzIC05aC0xNzI4cS0xNCAwIC0yMyA5dC05IDIzdjY0cTAgMTQgOSAyM3QyMyA5aDk2djY0MHEwIDEwNiA3NSAxODF0MTgxIDc1cTEwOCAwIDE4NCAtNzhxNDYgMTkgOTggMTJ0OTMgLTM5bDIyIDIycTExIDExIDIyIDBsNDIgLTQyDQpxMTEgLTExIDAgLTIybC0zMTQgLTMxNHEtMTEgLTExIC0yMiAwbC00MiA0MnEtMTEgMTEgMCAyMmwyMiAyMnEtMzYgNDYgLTQwLjUgMTA0dDIzLjUgMTA4cS0zNyAzNSAtODggMzVxLTUzIDAgLTkwLjUgLTM3LjV0LTM3LjUgLTkwLjV2LTY0MGgxNTA0cTE0IDAgMjMgLTl0OSAtMjN6TTg5NiAxMDU2cTAgLTE0IC05IC0yM3QtMjMgLTl0LTIzIDl0LTkgMjN0OSAyM3QyMyA5dDIzIC05dDkgLTIzek04MzIgMTEyMHEwIC0xNCAtOSAtMjN0LTIzIC05DQp0LTIzIDl0LTkgMjN0OSAyM3QyMyA5dDIzIC05dDkgLTIzek03NjggMTE4NHEwIC0xNCAtOSAtMjN0LTIzIC05dC0yMyA5dC05IDIzdDkgMjN0MjMgOXQyMyAtOXQ5IC0yM3pNOTYwIDExMjBxMCAtMTQgLTkgLTIzdC0yMyAtOXQtMjMgOXQtOSAyM3Q5IDIzdDIzIDl0MjMgLTl0OSAtMjN6TTg5NiAxMTg0cTAgLTE0IC05IC0yM3QtMjMgLTl0LTIzIDl0LTkgMjN0OSAyM3QyMyA5dDIzIC05dDkgLTIzek04MzIgMTI0OHEwIC0xNCAtOSAtMjMNCnQtMjMgLTl0LTIzIDl0LTkgMjN0OSAyM3QyMyA5dDIzIC05dDkgLTIzek0xMDI0IDExODRxMCAtMTQgLTkgLTIzdC0yMyAtOXQtMjMgOXQtOSAyM3Q5IDIzdDIzIDl0MjMgLTl0OSAtMjN6TTk2MCAxMjQ4cTAgLTE0IC05IC0yM3QtMjMgLTl0LTIzIDl0LTkgMjN0OSAyM3QyMyA5dDIzIC05dDkgLTIzek0xMDg4IDEyNDhxMCAtMTQgLTkgLTIzdC0yMyAtOXQtMjMgOXQtOSAyM3Q5IDIzdDIzIDl0MjMgLTl0OSAtMjN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkNFIiB1bmljb2RlPSImI3hmMmNlOyIgDQpkPSJNOTk0IDM0NHEwIC04NiAtMTcgLTE5N3EtMzEgLTIxNSAtNTUgLTMxM3EtMjIgLTkwIC0xNTIgLTkwdC0xNTIgOTBxLTI0IDk4IC01NSAzMTNxLTE3IDExMCAtMTcgMTk3cTAgMTY4IDIyNCAxNjh0MjI0IC0xNjh6TTE1MzYgNzY4cTAgLTI0MCAtMTM0IC00MzR0LTM1MCAtMjgwcS04IC0zIC0xNSAzdC02IDE1cTcgNDggMTAgNjZxNCAzMiA2IDQ3cTEgOSA5IDEycTE1OSA4MSAyNTUuNSAyMzR0OTYuNSAzMzdxMCAxODAgLTkxIDMzMC41DQp0LTI0NyAyMzQuNXQtMzM3IDc0cS0xMjQgLTcgLTIzNyAtNjF0LTE5My41IC0xNDAuNXQtMTI4IC0yMDJ0LTQ2LjUgLTI0MC41cTEgLTE4NCA5OSAtMzM2LjV0MjU3IC0yMzEuNXE3IC0zIDkgLTEycTMgLTIxIDYgLTQ1cTEgLTkgNSAtMzIuNXQ2IC0zNS41cTEgLTkgLTYuNSAtMTV0LTE1LjUgLTJxLTE0OCA1OCAtMjYxIDE2OS41dC0xNzMuNSAyNjR0LTUyLjUgMzE5LjVxNyAxNDMgNjYgMjczLjV0MTU0LjUgMjI3dDIyNSAxNTcuNXQyNzIuNSA3MA0KcTE2NCAxMCAzMTUuNSAtNDYuNXQyNjEgLTE2MC41dDE3NSAtMjUwLjV0NjUuNSAtMzA4LjV6TTk5NCA4MDBxMCAtOTMgLTY1LjUgLTE1OC41dC0xNTguNSAtNjUuNXQtMTU4LjUgNjUuNXQtNjUuNSAxNTguNXQ2NS41IDE1OC41dDE1OC41IDY1LjV0MTU4LjUgLTY1LjV0NjUuNSAtMTU4LjV6TTEyODIgNzY4cTAgLTEyMiAtNTMuNSAtMjI4LjV0LTE0Ni41IC0xNzcuNXEtOCAtNiAtMTYgLTJ0LTEwIDE0cS02IDUyIC0yOSA5MnEtNyAxMCAzIDIwDQpxNTggNTQgOTEgMTI3dDMzIDE1NXEwIDExMSAtNTguNSAyMDR0LTE1Ny41IDE0MS41dC0yMTIgMzYuNXEtMTMzIC0xNSAtMjI5IC0xMTN0LTEwOSAtMjMxcS0xMCAtOTIgMjMuNSAtMTc2dDk4LjUgLTE0NHExMCAtMTAgMyAtMjBxLTI0IC00MSAtMjkgLTkzcS0yIC05IC0xMCAtMTN0LTE2IDJxLTk1IDc0IC0xNDguNSAxODN0LTUxLjUgMjM0cTMgMTMxIDY5IDI0NHQxNzcgMTgxLjV0MjQxIDc0LjVxMTQ0IDcgMjY4IC02MHQxOTYuNSAtMTg3LjUNCnQ3Mi41IC0yNjMuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyRDAiIHVuaWNvZGU9IiYjeGYyZDA7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMjU2IDEyOGgxMjgwdjc2OGgtMTI4MHYtNzY4ek0xNzkyIDEyNDh2LTEyMTZxMCAtNjYgLTQ3IC0xMTN0LTExMyAtNDdoLTE0NzJxLTY2IDAgLTExMyA0N3QtNDcgMTEzdjEyMTZxMCA2NiA0NyAxMTN0MTEzIDQ3aDE0NzJxNjYgMCAxMTMgLTQ3dDQ3IC0xMTN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkQxIiB1bmljb2RlPSImI3hmMmQxOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTE3OTIgMjI0di0xOTJxMCAtNjYgLTQ3IC0xMTN0LTExMyAtNDdoLTE0NzJxLTY2IDAgLTExMyA0N3QtNDcgMTEzdjE5MnEwIDY2IDQ3IDExM3QxMTMgNDdoMTQ3MnE2NiAwIDExMyAtNDd0NDcgLTExM3oiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyRDIiIHVuaWNvZGU9IiYjeGYyZDI7IiBob3Jpei1hZHYteD0iMjA0OCIgDQpkPSJNMjU2IDBoNzY4djUxMmgtNzY4di01MTJ6TTEyODAgNTEyaDUxMnY3NjhoLTc2OHYtMjU2aDk2cTY2IDAgMTEzIC00N3Q0NyAtMTEzdi0zNTJ6TTIwNDggMTM3NnYtOTYwcTAgLTY2IC00NyAtMTEzdC0xMTMgLTQ3aC02MDh2LTM1MnEwIC02NiAtNDcgLTExM3QtMTEzIC00N2gtOTYwcS02NiAwIC0xMTMgNDd0LTQ3IDExM3Y5NjBxMCA2NiA0NyAxMTN0MTEzIDQ3aDYwOHYzNTJxMCA2NiA0NyAxMTN0MTEzIDQ3aDk2MHE2NiAwIDExMyAtNDcNCnQ0NyAtMTEzeiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJEMyIgdW5pY29kZT0iJiN4ZjJkMzsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik0xMTc1IDIxNWwxNDYgMTQ2cTEwIDEwIDEwIDIzdC0xMCAyM2wtMjMzIDIzM2wyMzMgMjMzcTEwIDEwIDEwIDIzdC0xMCAyM2wtMTQ2IDE0NnEtMTAgMTAgLTIzIDEwdC0yMyAtMTBsLTIzMyAtMjMzbC0yMzMgMjMzcS0xMCAxMCAtMjMgMTB0LTIzIC0xMGwtMTQ2IC0xNDZxLTEwIC0xMCAtMTAgLTIzdDEwIC0yM2wyMzMgLTIzM2wtMjMzIC0yMzNxLTEwIC0xMCAtMTAgLTIzdDEwIC0yM2wxNDYgLTE0NnExMCAtMTAgMjMgLTEwdDIzIDEwDQpsMjMzIDIzM2wyMzMgLTIzM3ExMCAtMTAgMjMgLTEwdDIzIDEwek0xNzkyIDEyNDh2LTEyMTZxMCAtNjYgLTQ3IC0xMTN0LTExMyAtNDdoLTE0NzJxLTY2IDAgLTExMyA0N3QtNDcgMTEzdjEyMTZxMCA2NiA0NyAxMTN0MTEzIDQ3aDE0NzJxNjYgMCAxMTMgLTQ3dDQ3IC0xMTN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkQ0IiB1bmljb2RlPSImI3hmMmQ0OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTEyNTcgNDI1bC0xNDYgLTE0NnEtMTAgLTEwIC0yMyAtMTB0LTIzIDEwbC0xNjkgMTY5bC0xNjkgLTE2OXEtMTAgLTEwIC0yMyAtMTB0LTIzIDEwbC0xNDYgMTQ2cS0xMCAxMCAtMTAgMjN0MTAgMjNsMTY5IDE2OWwtMTY5IDE2OXEtMTAgMTAgLTEwIDIzdDEwIDIzbDE0NiAxNDZxMTAgMTAgMjMgMTB0MjMgLTEwbDE2OSAtMTY5bDE2OSAxNjlxMTAgMTAgMjMgMTB0MjMgLTEwbDE0NiAtMTQ2cTEwIC0xMCAxMCAtMjN0LTEwIC0yMw0KbC0xNjkgLTE2OWwxNjkgLTE2OXExMCAtMTAgMTAgLTIzdC0xMCAtMjN6TTI1NiAxMjhoMTI4MHYxMDI0aC0xMjgwdi0xMDI0ek0xNzkyIDEyNDh2LTEyMTZxMCAtNjYgLTQ3IC0xMTN0LTExMyAtNDdoLTE0NzJxLTY2IDAgLTExMyA0N3QtNDcgMTEzdjEyMTZxMCA2NiA0NyAxMTN0MTEzIDQ3aDE0NzJxNjYgMCAxMTMgLTQ3dDQ3IC0xMTN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkQ1IiB1bmljb2RlPSImI3hmMmQ1OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTEwNzAgMzU4bDMwNiA1NjRoLTY1NGwtMzA2IC01NjRoNjU0ek0xNzkyIDY0MHEwIC0xODIgLTcxIC0zNDh0LTE5MSAtMjg2dC0yODYgLTE5MXQtMzQ4IC03MXQtMzQ4IDcxdC0yODYgMTkxdC0xOTEgMjg2dC03MSAzNDh0NzEgMzQ4dDE5MSAyODZ0Mjg2IDE5MXQzNDggNzF0MzQ4IC03MXQyODYgLTE5MXQxOTEgLTI4NnQ3MSAtMzQ4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJENiIgdW5pY29kZT0iJiN4ZjJkNjsiIGhvcml6LWFkdi14PSIxNzk0IiANCmQ9Ik0xMjkxIDEwNjBxLTE1IDE3IC0zNSA4LjV0LTI2IC0yOC41dDUgLTM4cTE0IC0xNyA0MCAtMTQuNXQzNCAyMC41dC0xOCA1MnpNODk1IDgxNHEtOCAtOCAtMTkuNSAtOHQtMTguNSA4cS04IDggLTggMTl0OCAxOHE3IDggMTguNSA4dDE5LjUgLThxNyAtNyA3IC0xOHQtNyAtMTl6TTEwNjAgNzQwbC0zNSAtMzVxLTEyIC0xMyAtMjkuNSAtMTN0LTMwLjUgMTNsLTM4IDM4cS0xMiAxMyAtMTIgMzB0MTIgMzBsMzUgMzVxMTIgMTIgMjkuNSAxMg0KdDMwLjUgLTEybDM4IC0zOXExMiAtMTIgMTIgLTI5LjV0LTEyIC0yOS41ek05NTEgODcwcS03IC04IC0xOC41IC04dC0xOS41IDhxLTcgOCAtNyAxOXQ3IDE5cTggOCAxOSA4dDE5IC04dDggLTE5dC04IC0xOXpNMTM1NCA5NjhxLTM0IC02NCAtMTA3LjUgLTg1LjV0LTEyNy41IDE2LjVxLTM4IDI4IC02MSA2Ni41dC0yMSA4Ny41dDM5IDkydDc1LjUgNTN0NzAuNSAtNXQ3MCAtNTFxMiAtMiAxMyAtMTIuNXQxNC41IC0xMy41dDEzIC0xMy41DQp0MTIuNSAtMTUuNXQxMCAtMTUuNXQ4LjUgLTE4dDQgLTE4LjV0MSAtMjF0LTUgLTIydC05LjUgLTI0ek0xNTU1IDQ4NnEzIDIwIC04LjUgMzQuNXQtMjcuNSAyMS41dC0zMyAxN3QtMjMgMjBxLTQwIDcxIC04NCA5OC41dC0xMTMgMTEuNXExOSAxMyA0MCAxOC41dDMzIDQuNWwxMiAtMXEyIDQ1IC0zNCA5MHE2IDIwIDYuNSA0MC41dC0yLjUgMzAuNWwtMyAxMHE0MyAyNCA3MSA2NXQzNCA5MXExMCA4NCAtNDMgMTUwLjV0LTEzNyA3Ni41DQpxLTYwIDcgLTExNCAtMTguNXQtODIgLTc0LjVxLTMwIC01MSAtMzMuNSAtMTAxdDE0LjUgLTg3dDQzLjUgLTY0dDU2LjUgLTQycS00NSA0IC04OCAzNnQtNTcgODhxLTI4IDEwOCAzMiAyMjJxLTE2IDIxIC0yOSAzMnEtNTAgMCAtODkgLTE5cTE5IDI0IDQyIDM3dDM2IDE0bDEzIDFxMCA1MCAtMTMgNzhxLTEwIDIxIC0zMi41IDI4LjV0LTQ3IC0zLjV0LTM3LjUgLTQwcTIgNCA0IDdxLTcgLTI4IC02LjUgLTc1LjV0MTkgLTExN3Q0OC41IC0xMjIuNQ0KcS0yNSAtMTQgLTQ3IC0zNnEtMzUgLTE2IC04NS41IC03MC41dC04NC41IC0xMDEuNWwtMzMgLTQ2cS05MCAtMzQgLTE4MSAtMTI1LjV0LTc1IC0xNjIuNXExIC0xNiAxMSAtMjdxLTE1IC0xMiAtMzAgLTMwcS0yMSAtMjUgLTIxIC01NHQyMS41IC00MHQ2My41IDZxNDEgMTkgNzcgNDkuNXQ1NSA2MC41cS0yIDIgLTYuNSA1dC0yMC41IDcuNXQtMzMgMy41cTIzIDUgNTEgMTIuNXQ0MCAxMHQyNy41IDZ0MjYgNHQyMy41IDAuNXExNCAtNyAyMiAzNA0KcTcgMzcgNyA5MHEwIDEwMiAtNDAgMTUwcTEwNiAtMTAzIDEwMSAtMjE5cS0xIC0yOSAtMTUgLTUwdC0yNyAtMjdsLTEzIC02cS00IC03IC0xOSAtMzJ0LTI2IC00NS41dC0yNi41IC01MnQtMjUgLTYxdC0xNyAtNjN0LTYuNSAtNjYuNXQxMCAtNjNxLTM1IDU0IC0zNyA4MHEtMjIgLTI0IC0zNC41IC0zOXQtMzMuNSAtNDJ0LTMwLjUgLTQ2dC0xNi41IC00MXQtMC41IC0zOHQyNS41IC0yN3E0NSAtMjUgMTQ0IDY0dDE5MC41IDIyMS41DQp0MTIyLjUgMjI4LjVxODYgNTIgMTQ1IDExNS41dDg2IDExOS41cTQ3IC05MyAxNTQgLTE3OHExMDQgLTgzIDE2NyAtODBxMzkgMiA0NiA0M3pNMTc5NCA2NDBxMCAtMTgyIC03MSAtMzQ4dC0xOTEgLTI4NnQtMjg2LjUgLTE5MXQtMzQ4LjUgLTcxdC0zNDguNSA3MXQtMjg2LjUgMTkxdC0xOTEgMjg2dC03MSAzNDh0NzEgMzQ4dDE5MSAyODZ0Mjg2LjUgMTkxdDM0OC41IDcxdDM0OC41IC03MXQyODYuNSAtMTkxdDE5MSAtMjg2dDcxIC0zNDh6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkQ3IiB1bmljb2RlPSImI3hmMmQ3OyIgDQpkPSJNNTE4IDEzNTN2LTY1NXExMDMgLTEgMTkxLjUgMS41dDEyNS41IDUuNWwzNyAzcTY4IDIgOTAuNSAyNC41dDM5LjUgOTQuNWwzMyAxNDJoMTAzbC0xNCAtMzIybDcgLTMxOWgtMTAzbC0yOSAxMjdxLTE1IDY4IC00NSA5M3QtODQgMjZxLTg3IDggLTM1MiA4di01NTZxMCAtNzggNDMuNSAtMTE1LjV0MTMzLjUgLTM3LjVoMzU3cTM1IDAgNTkuNSAydDU1IDcuNXQ1NCAxOHQ0OC41IDMydDQ2IDUwLjV0MzkgNzNsOTMgMjE2aDg5DQpxLTYgLTM3IC0zMS41IC0yNTJ0LTMwLjUgLTI3NnEtMTQ2IDUgLTI2My41IDh0LTE2Mi41IDRoLTQ0aC02MjhsLTM3NiAtMTJ2MTAybDEyNyAyNXE2NyAxMyA5MS41IDM3dDI1LjUgNzlsOCA2NDNxMyA0MDIgLTggNjQ1cS0yIDYxIC0yNS41IDg0dC05MS41IDM2bC0xMjcgMjR2MTAybDM3NiAtMTJoNzAycTEzOSAwIDM3NCAyN3EtNiAtNjggLTE0IC0xOTQuNXQtMTIgLTIxOS41bC01IC05MmgtOTNsLTMyIDEyNHEtMzEgMTIxIC03NCAxNzkuNQ0KdC0xMTMgNTguNWgtNTQ4cS0yOCAwIC0zNS41IC04LjV0LTcuNSAtMzAuNXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyRDgiIHVuaWNvZGU9IiYjeGYyZDg7IiANCmQ9Ik05MjIgNzM5di0xODJxMCAtNCAwLjUgLTE1dDAgLTE1bC0xLjUgLTEydC0zLjUgLTExLjV0LTYuNSAtNy41dC0xMSAtNS41dC0xNiAtMS41djMwOXE5IDAgMTYgLTF0MTEgLTV0Ni41IC01LjV0My41IC05LjV0MSAtMTAuNXYtMTMuNXYtMTR6TTEyMzggNjQzdi0xMjFxMCAtMSAwLjUgLTEyLjV0MCAtMTUuNXQtMi41IC0xMS41dC03LjUgLTEwLjV0LTEzLjUgLTNxLTkgMCAtMTQgOXEtNCAxMCAtNCAxNjV2N3Y4LjV2OXQxLjUgOC41bDMuNSA3DQp0NSA1LjV0OCAxLjVxNiAwIDEwIC0xLjV0Ni41IC00LjV0NCAtNnQyIC04LjV0MC41IC04di05LjV2LTl6TTE4MCA0MDdoMTIydjQ3MmgtMTIydi00NzJ6TTYxNCA0MDdoMTA2djQ3MmgtMTU5bC0yOCAtMjIxcS0yMCAxNDggLTMyIDIyMWgtMTU4di00NzJoMTA3djMxMmw0NSAtMzEyaDc2bDQzIDMxOXYtMzE5ek0xMDM5IDcxMnEwIDY3IC01IDkwcS0zIDE2IC0xMSAyOC41dC0xNyAyMC41dC0yNSAxNHQtMjYuNSA4LjV0LTMxIDR0LTI5IDEuNQ0KaC0yOS41aC0xMmgtOTF2LTQ3Mmg1NnExNjkgLTEgMTk3IDI0LjV0MjUgMTgwLjVxLTEgNjIgLTEgMTAwek0xMzU2IDUxNXYxMzNxMCAyOSAtMiA0NXQtOS41IDMzLjV0LTI0LjUgMjV0LTQ2IDcuNXEtNDYgMCAtNzcgLTM0djE1NGgtMTE3di00NzJoMTEwbDcgMzBxMzAgLTM2IDc3IC0zNnE1MCAwIDY2IDMwLjV0MTYgODMuNXpNMTUzNiAxMjQ4di0xMjE2cTAgLTY2IC00NyAtMTEzdC0xMTMgLTQ3aC0xMjE2cS02NiAwIC0xMTMgNDd0LTQ3IDExMw0KdjEyMTZxMCA2NiA0NyAxMTN0MTEzIDQ3aDEyMTZxNjYgMCAxMTMgLTQ3dDQ3IC0xMTN6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkQ5IiB1bmljb2RlPSImI3hmMmQ5OyIgaG9yaXotYWR2LXg9IjIxNzYiIA0KZD0iTTExNDMgLTE5N3EtNiAxIC0xMSA0cS0xMyA4IC0zNiAyM3QtODYgNjV0LTExNi41IDEwNC41dC0xMTIgMTQwdC04OS41IDE3Mi41cS0xNyAzIC0xNzUgMzdxNjYgLTIxMyAyMzUgLTM2MnQzOTEgLTE4NHpNNTAyIDQwOWwxNjggLTI4cS0yNSA3NiAtNDEgMTY3LjV0LTE5IDE0NS41bC00IDUzcS04NCAtODIgLTEyMSAtMjI0cTUgLTY1IDE3IC0xMTR6TTYxMiAxMDE4cS00MyAtNjQgLTc3IC0xNDhxNDQgNDYgNzQgNjh6TTIwNDkgNTg0DQpxMCAxNjEgLTYyIDMwN3QtMTY3LjUgMjUydC0yNTAuNSAxNjguNXQtMzA0IDYyLjVxLTE0NyAwIC0yODEgLTUyLjV0LTI0MCAtMTQ4LjVxLTMwIC01OCAtNDUgLTE2MHE2MCA1MSAxNDMgODMuNXQxNTguNSA0M3QxNDMgMTMuNXQxMDguNSAtMWw0MCAtM3EzMyAtMSA1MyAtMTUuNXQyNC41IC0zM3Q2LjUgLTM3dC0xIC0yOC41cS0xMjYgMTEgLTIyNy41IDAuNXQtMTgzIC00My41dC0xNDIuNSAtNzEuNXQtMTMxIC05OC41DQpxNCAtMzYgMTEuNSAtOTIuNXQzNS41IC0xNzh0NjIgLTE3OS41cTEyMyAtNiAyNDcuNSAxNC41dDIxNC41IDUzLjV0MTYyLjUgNjd0MTA5LjUgNTlsMzcgMjRxMjIgMTYgMzkuNSAyMC41dDMwLjUgLTV0MTcgLTM0LjVxMTQgLTk3IC0zOSAtMTIxcS0yMDggLTk3IC00NjcgLTEzNHEtMTM1IC0yMCAtMzE3IC0xNnE0MSAtOTYgMTEwIC0xNzYuNXQxMzcgLTEyN3QxMzAuNSAtNzl0MTAxLjUgLTQzLjVsMzkgLTEycTE0MyAtMjMgMjYzIDE1DQpxMTk1IDk5IDMxNCAyODl0MTE5IDQxOHpNMjEyMyA2MjFxLTE0IC0xMzUgLTQwIC0yMTJxLTcwIC0yMDggLTE4MS41IC0zNDYuNXQtMzE4LjUgLTI1My41cS00OCAtMzMgLTgyIC00NHEtNzIgLTI2IC0xNjMgLTE2cS0zNiAtMyAtNzMgLTNxLTI4MyAwIC01MDQuNSAxNzN0LTI5NS41IDQ0MnEtMSAwIC00IDAuNXQtNSAwLjVxLTYgLTUwIDIuNSAtMTEyLjV0MjYgLTExNXQzNiAtOTh0MzEuNSAtNzEuNWwxNCAtMjZxOCAtMTIgNTQgLTgyDQpxLTcxIDM4IC0xMjQuNSAxMDYuNXQtNzguNSAxNDB0LTM5LjUgMTM3dC0xNy41IDEwNy41bC0yIDQycS01IDIgLTMzLjUgMTIuNXQtNDguNSAxOHQtNTMgMjAuNXQtNTcuNSAyNXQtNTAgMjUuNXQtNDIuNSAyN3QtMjUgMjUuNXExOSAtMTAgNTAuNSAtMjUuNXQxMTMgLTQ1LjV0MTQ1LjUgLTM4bDIgMzJxMTEgMTQ5IDk0IDI5MHE0MSAyMDIgMTc2IDM2NXEyOCAxMTUgODEgMjE0cTE1IDI4IDMyIDQ1dDQ5IDMycTE1OCA3NCAzMDMuNSAxMDQNCnQzMDIgMTF0MzA2LjUgLTk3cTIyMCAtMTE1IDMzMyAtMzM2dDg3IC00NzR6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkRBIiB1bmljb2RlPSImI3hmMmRhOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KZD0iTTEzNDEgNzUycTI5IDQ0IC02LjUgMTI5LjV0LTEyMS41IDE0Mi41cS01OCAzOSAtMTI1LjUgNTMuNXQtMTE4IDQuNXQtNjguNSAtMzdxLTEyIC0yMyAtNC41IC0yOHQ0Mi41IC0xMHEyMyAtMyAzOC41IC01dDQ0LjUgLTkuNXQ1NiAtMTcuNXEzNiAtMTMgNjcuNSAtMzEuNXQ1MyAtMzd0NDAgLTM4LjV0MzAuNSAtMzh0MjIgLTM0LjV0MTYuNSAtMjguNXQxMiAtMTguNXQxMC41IC02dDExIDkuNXpNMTcwNCAxNzgNCnEtNTIgLTEyNyAtMTQ4LjUgLTIyMHQtMjE0LjUgLTE0MS41dC0yNTMgLTYwLjV0LTI2NiAxMy41dC0yNTEgOTF0LTIxMCAxNjEuNXQtMTQxLjUgMjM1LjV0LTQ2LjUgMzAzLjVxMSA0MSA4LjUgODQuNXQxMi41IDY0dDI0IDgwLjV0MjMgNzNxLTUxIC0yMDggMSAtMzk3dDE3MyAtMzE4dDI5MSAtMjA2dDM0NiAtODN0MzQ5IDc0LjV0Mjg5IDI0NC41cTIwIDI3IDE4IDE0cTAgLTQgLTQgLTE0ek0xNDY1IDYyN3EwIC0xMDQgLTQwLjUgLTE5OQ0KdC0xMDguNSAtMTY0dC0xNjIgLTEwOS41dC0xOTggLTQwLjV0LTE5OCA0MC41dC0xNjIgMTA5LjV0LTEwOC41IDE2NHQtNDAuNSAxOTl0NDAuNSAxOTl0MTA4LjUgMTY0dDE2MiAxMDkuNXQxOTggNDAuNXQxOTggLTQwLjV0MTYyIC0xMDkuNXQxMDguNSAtMTY0dDQwLjUgLTE5OXpNMTc1MiA5MTVxLTY1IDE0NyAtMTgwLjUgMjUxdC0yNTMgMTUzLjV0LTI5MiA1My41dC0zMDEgLTM2LjV0LTI3NS41IC0xMjl0LTIyMCAtMjExLjV0LTEzMSAtMjk3DQp0LTEwIC0zNzNxLTQ5IDE2MSAtNTEuNSAzMTEuNXQzNS41IDI3Mi41dDEwOSAyMjd0MTY1LjUgMTgwLjV0MjA3IDEyNnQyMzIgNzF0MjQyLjUgOXQyMzYgLTU0dDIxNiAtMTI0LjV0MTc4IC0xOTdxMzMgLTUwIDYyIC0xMjF0MzEgLTExMnpNMTY5MCA1NzNxMTIgMjQ0IC0xMzYuNSA0MTZ0LTM5Ni41IDI0MHEtOCAwIC0xMCA1dDI0IDhxMTI1IC00IDIzMCAtNTB0MTczIC0xMjB0MTE2IC0xNjguNXQ1OC41IC0xOTl0LTEgLTIwOA0KdC02MS41IC0xOTcuNXQtMTIyLjUgLTE2N3QtMTg1IC0xMTcuNXQtMjQ4LjUgLTQ2LjVxMTA4IDMwIDIwMS41IDgwdDE3NCAxMjN0MTI5LjUgMTc2LjV0NTUgMjI1LjV6IiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkRCIiB1bmljb2RlPSImI3hmMmRiOyIgDQpkPSJNMTkyIDI1NnYtMTI4aC0xMTJxLTE2IDAgLTE2IDE2djE2aC00OHEtMTYgMCAtMTYgMTZ2MzJxMCAxNiAxNiAxNmg0OHYxNnEwIDE2IDE2IDE2aDExMnpNMTkyIDUxMnYtMTI4aC0xMTJxLTE2IDAgLTE2IDE2djE2aC00OHEtMTYgMCAtMTYgMTZ2MzJxMCAxNiAxNiAxNmg0OHYxNnEwIDE2IDE2IDE2aDExMnpNMTkyIDc2OHYtMTI4aC0xMTJxLTE2IDAgLTE2IDE2djE2aC00OHEtMTYgMCAtMTYgMTZ2MzJxMCAxNiAxNiAxNmg0OHYxNg0KcTAgMTYgMTYgMTZoMTEyek0xOTIgMTAyNHYtMTI4aC0xMTJxLTE2IDAgLTE2IDE2djE2aC00OHEtMTYgMCAtMTYgMTZ2MzJxMCAxNiAxNiAxNmg0OHYxNnEwIDE2IDE2IDE2aDExMnpNMTkyIDEyODB2LTEyOGgtMTEycS0xNiAwIC0xNiAxNnYxNmgtNDhxLTE2IDAgLTE2IDE2djMycTAgMTYgMTYgMTZoNDh2MTZxMCAxNiAxNiAxNmgxMTJ6TTEyODAgMTQ0MHYtMTQ3MnEwIC00MCAtMjggLTY4dC02OCAtMjhoLTgzMnEtNDAgMCAtNjggMjgNCnQtMjggNjh2MTQ3MnEwIDQwIDI4IDY4dDY4IDI4aDgzMnE0MCAwIDY4IC0yOHQyOCAtNjh6TTE1MzYgMjA4di0zMnEwIC0xNiAtMTYgLTE2aC00OHYtMTZxMCAtMTYgLTE2IC0xNmgtMTEydjEyOGgxMTJxMTYgMCAxNiAtMTZ2LTE2aDQ4cTE2IDAgMTYgLTE2ek0xNTM2IDQ2NHYtMzJxMCAtMTYgLTE2IC0xNmgtNDh2LTE2cTAgLTE2IC0xNiAtMTZoLTExMnYxMjhoMTEycTE2IDAgMTYgLTE2di0xNmg0OHExNiAwIDE2IC0xNnpNMTUzNiA3MjB2LTMyDQpxMCAtMTYgLTE2IC0xNmgtNDh2LTE2cTAgLTE2IC0xNiAtMTZoLTExMnYxMjhoMTEycTE2IDAgMTYgLTE2di0xNmg0OHExNiAwIDE2IC0xNnpNMTUzNiA5NzZ2LTMycTAgLTE2IC0xNiAtMTZoLTQ4di0xNnEwIC0xNiAtMTYgLTE2aC0xMTJ2MTI4aDExMnExNiAwIDE2IC0xNnYtMTZoNDhxMTYgMCAxNiAtMTZ6TTE1MzYgMTIzMnYtMzJxMCAtMTYgLTE2IC0xNmgtNDh2LTE2cTAgLTE2IC0xNiAtMTZoLTExMnYxMjhoMTEycTE2IDAgMTYgLTE2di0xNg0KaDQ4cTE2IDAgMTYgLTE2eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJEQyIgdW5pY29kZT0iJiN4ZjJkYzsiIGhvcml6LWFkdi14PSIxNjY0IiANCmQ9Ik0xNTY2IDQxOWwtMTY3IC0zM2wxODYgLTEwN3EyMyAtMTMgMjkuNSAtMzguNXQtNi41IC00OC41cS0xNCAtMjMgLTM5IC0yOS41dC00OCA2LjVsLTE4NiAxMDZsNTUgLTE2MHExMyAtMzggLTEyIC02My41dC02MC41IC0yMC41dC00OC41IDQybC0xMDIgMzAwbC0yNzEgMTU2di0zMTNsMjA4IC0yMzhxMTYgLTE4IDE3IC0zOXQtMTEgLTM2LjV0LTI4LjUgLTI1dC0zNyAtNS41dC0zNi41IDIybC0xMTIgMTI4di0yMTRxMCAtMjYgLTE5IC00NQ0KdC00NSAtMTl0LTQ1IDE5dC0xOSA0NXYyMTRsLTExMiAtMTI4cS0xNiAtMTggLTM2LjUgLTIydC0zNyA1LjV0LTI4LjUgMjV0LTExIDM2LjV0MTcgMzlsMjA4IDIzOHYzMTNsLTI3MSAtMTU2bC0xMDIgLTMwMHEtMTMgLTM3IC00OC41IC00MnQtNjAuNSAyMC41dC0xMiA2My41bDU1IDE2MGwtMTg2IC0xMDZxLTIzIC0xMyAtNDggLTYuNXQtMzkgMjkuNXEtMTMgMjMgLTYuNSA0OC41dDI5LjUgMzguNWwxODYgMTA3bC0xNjcgMzMNCnEtMjkgNiAtNDIgMjl0LTguNSA0Ni41dDI1LjUgNDB0NTAgMTAuNWwzMTAgLTYybDI3MSAxNTdsLTI3MSAxNTdsLTMxMCAtNjJxLTQgLTEgLTEzIC0xcS0yNyAwIC00NCAxOHQtMTkgNDB0MTEgNDN0NDAgMjZsMTY3IDMzbC0xODYgMTA3cS0yMyAxMyAtMjkuNSAzOC41dDYuNSA0OC41dDM5IDMwdDQ4IC03bDE4NiAtMTA2bC01NSAxNjBxLTEzIDM4IDEyIDYzLjV0NjAuNSAyMC41dDQ4LjUgLTQybDEwMiAtMzAwbDI3MSAtMTU2djMxMw0KbC0yMDggMjM4cS0xNiAxOCAtMTcgMzl0MTEgMzYuNXQyOC41IDI1dDM3IDUuNXQzNi41IC0yMmwxMTIgLTEyOHYyMTRxMCAyNiAxOSA0NXQ0NSAxOXQ0NSAtMTl0MTkgLTQ1di0yMTRsMTEyIDEyOHExNiAxOCAzNi41IDIydDM3IC01LjV0MjguNSAtMjV0MTEgLTM2LjV0LTE3IC0zOWwtMjA4IC0yMzh2LTMxM2wyNzEgMTU2bDEwMiAzMDBxMTMgMzcgNDguNSA0MnQ2MC41IC0yMC41dDEyIC02My41bC01NSAtMTYwbDE4NiAxMDYNCnEyMyAxMyA0OCA2LjV0MzkgLTI5LjVxMTMgLTIzIDYuNSAtNDguNXQtMjkuNSAtMzguNWwtMTg2IC0xMDdsMTY3IC0zM3EyNyAtNSA0MCAtMjZ0MTEgLTQzdC0xOSAtNDB0LTQ0IC0xOHEtOSAwIC0xMyAxbC0zMTAgNjJsLTI3MSAtMTU3bDI3MSAtMTU3bDMxMCA2MnEyOSA2IDUwIC0xMC41dDI1LjUgLTQwdC04LjUgLTQ2LjV0LTQyIC0yOXoiIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyREQiIHVuaWNvZGU9IiYjeGYyZGQ7IiBob3Jpei1hZHYteD0iMTc5MiIgDQpkPSJNMTQ3MyA2MDdxNyAxMTggLTMzIDIyNi41dC0xMTMgMTg5dC0xNzcgMTMxdC0yMjEgNTcuNXEtMTE2IDcgLTIyNS41IC0zMnQtMTkyIC0xMTAuNXQtMTM1IC0xNzV0LTU5LjUgLTIyMC41cS03IC0xMTggMzMgLTIyNi41dDExMyAtMTg5dDE3Ny41IC0xMzF0MjIxLjUgLTU3LjVxMTU1IC05IDI5MyA1OXQyMjQgMTk1LjV0OTQgMjgzLjV6TTE3OTIgMTUzNmwtMzQ5IC0zNDhxMTIwIC0xMTcgMTgwLjUgLTI3MnQ1MC41IC0zMjENCnEtMTEgLTE4MyAtMTAyIC0zMzl0LTI0MSAtMjU1LjV0LTMzMiAtMTI0LjVsLTk5OSAtMTMybDM0NyAzNDdxLTEyMCAxMTYgLTE4MC41IDI3MS41dC01MC41IDMyMS41cTExIDE4NCAxMDIgMzQwdDI0MS41IDI1NS41dDMzMi41IDEyNC41cTE2NyAyMiA1MDAgNjZ0NTAwIDY2eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJERSIgdW5pY29kZT0iJiN4ZjJkZTsiIGhvcml6LWFkdi14PSIxNzkyIiANCmQ9Ik05NDggNTA4bDE2MyAtMzI5aC01MWwtMTc1IDM1MGwtMTcxIC0zNTBoLTQ5bDE3OSAzNzRsLTc4IDMzbDIxIDQ5bDI0MCAtMTAybC0yMSAtNTB6TTU2MyAxMTAwbDMwNCAtMTMwbC0xMzAgLTMwNGwtMzA0IDEzMHpNOTA3IDkxNWwyNDAgLTEwM2wtMTAzIC0yMzlsLTIzOSAxMDJ6TTExODggNzY1bDE5MSAtODFsLTgyIC0xOTBsLTE5MCA4MXpNMTY4MCA2NDBxMCAxNTkgLTYyIDMwNHQtMTY3LjUgMjUwLjV0LTI1MC41IDE2Ny41dC0zMDQgNjINCnQtMzA0IC02MnQtMjUwLjUgLTE2Ny41dC0xNjcuNSAtMjUwLjV0LTYyIC0zMDR0NjIgLTMwNHQxNjcuNSAtMjUwLjV0MjUwLjUgLTE2Ny41dDMwNCAtNjJ0MzA0IDYydDI1MC41IDE2Ny41dDE2Ny41IDI1MC41dDYyIDMwNHpNMTc5MiA2NDBxMCAtMTgyIC03MSAtMzQ4dC0xOTEgLTI4NnQtMjg2IC0xOTF0LTM0OCAtNzF0LTM0OCA3MXQtMjg2IDE5MXQtMTkxIDI4NnQtNzEgMzQ4dDcxIDM0OHQxOTEgMjg2dDI4NiAxOTF0MzQ4IDcxdDM0OCAtNzENCnQyODYgLTE5MXQxOTEgLTI4NnQ3MSAtMzQ4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJFMCIgdW5pY29kZT0iJiN4ZjJlMDsiIGhvcml6LWFkdi14PSIxOTIwIiANCmQ9Ik0xMzM0IDMwMnEtNCAyNCAtMjcuNSAzNHQtNDkuNSAxMC41dC00OC41IDEyLjV0LTI1LjUgMzhxLTUgNDcgMzMgMTM5LjV0NzUgMTgxdDMyIDEyNy41cS0xNCAxMDEgLTExNyAxMDNxLTQ1IDEgLTc1IC0xNmwtMyAtMmwtNSAtMi41dC00LjUgLTJ0LTUgLTJ0LTUgLTAuNXQtNiAxLjV0LTYgMy41dC02LjUgNXEtMyAyIC05IDguNXQtOSA5dC04LjUgNy41dC05LjUgNy41dC05LjUgNS41dC0xMSA0LjV0LTExLjUgMi41cS0zMCA1IC00OCAtMw0KdC00NSAtMzFxLTEgLTEgLTkgLTguNXQtMTIuNSAtMTF0LTE1IC0xMHQtMTYuNSAtNS41dC0xNyAzcS01NCAyNyAtODQgNDBxLTQxIDE4IC05NCAtNXQtNzYgLTY1cS0xNiAtMjggLTQxIC05OC41dC00My41IC0xMzIuNXQtNDAgLTEzNHQtMjEuNSAtNzNxLTIyIC02OSAxOC41IC0xMTl0MTEwLjUgLTQ2cTMwIDIgNTAuNSAxNXQzOC41IDQ2cTcgMTMgNzkgMTk5LjV0NzcgMTk0LjVxNiAxMSAyMS41IDE4dDI5LjUgMHEyNyAtMTUgMjEgLTUzDQpxLTIgLTE4IC01MSAtMTM5LjV0LTUwIC0xMzIuNXEtNiAtMzggMTkuNSAtNTYuNXQ2MC41IC03dDU1IDQ5LjVxNCA4IDQ1LjUgOTJ0ODEuNSAxNjMuNXQ0NiA4OC41cTIwIDI5IDQxIDI4cTI5IDAgMjUgLTM4cS0yIC0xNiAtNjUuNSAtMTQ3LjV0LTcwLjUgLTE1OS41cS0xMiAtNTMgMTMgLTEwM3Q3NCAtNzRxMTcgLTkgNTEgLTE1LjV0NzEuNSAtOHQ2Mi41IDE0dDIwIDQ4LjV6TTM4MyA4NnEzIC0xNSAtNSAtMjcuNXQtMjMgLTE1LjUNCnEtMTQgLTMgLTI2LjUgNXQtMTUuNSAyM3EtMyAxNCA1IDI3dDIyIDE2dDI3IC01dDE2IC0yM3pNOTUzIC0xNzdxMTIgLTE3IDguNSAtMzcuNXQtMjAuNSAtMzIuNXQtMzcuNSAtOHQtMzIuNSAyMXEtMTEgMTcgLTcuNSAzNy41dDIwLjUgMzIuNXQzNy41IDh0MzEuNSAtMjF6TTE3NyA2MzVxLTE4IC0yNyAtNDkuNSAtMzN0LTU3LjUgMTNxLTI2IDE4IC0zMiA1MHQxMiA1OHExOCAyNyA0OS41IDMzdDU3LjUgLTEycTI2IC0xOSAzMiAtNTAuNQ0KdC0xMiAtNTguNXpNMTQ2NyAtNDJxMTkgLTI4IDEzIC02MS41dC0zNCAtNTIuNXQtNjAuNSAtMTN0LTUxLjUgMzR0LTEzIDYxdDMzIDUzcTI4IDE5IDYwLjUgMTN0NTIuNSAtMzR6TTE1NzkgNTYycTY5IC0xMTMgNDIuNSAtMjQ0LjV0LTEzNC41IC0yMDcuNXEtOTAgLTYzIC0xOTkgLTYwcS0yMCAtODAgLTg0LjUgLTEyN3QtMTQzLjUgLTQ0LjV0LTE0MCA1Ny41cS0xMiAtOSAtMTMgLTEwcS0xMDMgLTcxIC0yMjUgLTQ4LjV0LTE5MyAxMjYuNQ0KcS01MCA3MyAtNTMgMTY0cS04MyAxNCAtMTQyLjUgNzAuNXQtODAuNSAxMjh0LTIgMTUydDgxIDEzOC41cS0zNiA2MCAtMzggMTI4dDI0LjUgMTI1dDc5LjUgOTguNXQxMjEgNTAuNXEzMiA4NSA5OSAxNDh0MTQ2LjUgOTEuNXQxNjggMTd0MTU5LjUgLTY2LjVxNzIgMjEgMTQwIDE3LjV0MTI4LjUgLTM2dDEwNC41IC04MHQ2Ny41IC0xMTV0MTcuNSAtMTQwLjVxNTIgLTE2IDg3IC01N3Q0NS41IC04OXQtNS41IC05OS41dC01OCAtODcuNXoNCk00NTUgMTIyMnExNCAtMjAgOS41IC00NC41dC0yNC41IC0zOC41cS0xOSAtMTQgLTQzLjUgLTkuNXQtMzcuNSAyNC41cS0xNCAyMCAtOS41IDQ0LjV0MjQuNSAzOC41cTE5IDE0IDQzLjUgOS41dDM3LjUgLTI0LjV6TTYxNCAxNTAzcTQgLTE2IC01IC0zMC41dC0yNiAtMTguNXQtMzEgNS41dC0xOCAyNi41cS0zIDE3IDYuNSAzMXQyNS41IDE4cTE3IDQgMzEgLTUuNXQxNyAtMjYuNXpNMTgwMCA1NTVxNCAtMjAgLTYuNSAtMzd0LTMwLjUgLTIxDQpxLTE5IC00IC0zNiA2LjV0LTIxIDMwLjV0Ni41IDM3dDMwLjUgMjJxMjAgNCAzNi41IC03LjV0MjAuNSAtMzAuNXpNMTEzNiAxNDQ4cTE2IC0yNyA4LjUgLTU4LjV0LTM1LjUgLTQ3LjVxLTI3IC0xNiAtNTcuNSAtOC41dC00Ni41IDM0LjVxLTE2IDI4IC04LjUgNTl0MzQuNSA0OHQ1OCA5dDQ3IC0zNnpNMTg4MiA3OTJxNCAtMTUgLTQgLTI3LjV0LTIzIC0xNi41cS0xNSAtMyAtMjcuNSA1LjV0LTE1LjUgMjIuNXEtMyAxNSA1IDI4dDIzIDE2DQpxMTQgMyAyNi41IC01dDE1LjUgLTIzek0xNjkxIDEwMzNxMTUgLTIyIDEwLjUgLTQ5dC0yNi41IC00M3EtMjIgLTE1IC00OSAtMTB0LTQyIDI3dC0xMCA0OXQyNyA0M3Q0OC41IDExdDQxLjUgLTI4eiIgLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJFMSIgdW5pY29kZT0iJiN4ZjJlMTsiIGhvcml6LWFkdi14PSIxNzkyIiANCiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkUyIiB1bmljb2RlPSImI3hmMmUyOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyRTMiIHVuaWNvZGU9IiYjeGYyZTM7IiBob3Jpei1hZHYteD0iMTc5MiIgDQogLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJFNCIgdW5pY29kZT0iJiN4ZjJlNDsiIGhvcml6LWFkdi14PSIxNzkyIiANCiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkU1IiB1bmljb2RlPSImI3hmMmU1OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyRTYiIHVuaWNvZGU9IiYjeGYyZTY7IiBob3Jpei1hZHYteD0iMTc5MiIgDQogLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJFNyIgdW5pY29kZT0iJiN4ZjJlNzsiIGhvcml6LWFkdi14PSIxNzkyIiANCiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJfNjk4IiB1bmljb2RlPSImI3hmMmU4OyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyRTkiIHVuaWNvZGU9IiYjeGYyZTk7IiBob3Jpei1hZHYteD0iMTc5MiIgDQogLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJFQSIgdW5pY29kZT0iJiN4ZjJlYTsiIGhvcml6LWFkdi14PSIxNzkyIiANCiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkVCIiB1bmljb2RlPSImI3hmMmViOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9InVuaUYyRUMiIHVuaWNvZGU9IiYjeGYyZWM7IiBob3Jpei1hZHYteD0iMTc5MiIgDQogLz4NCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0idW5pRjJFRCIgdW5pY29kZT0iJiN4ZjJlZDsiIGhvcml6LWFkdi14PSIxNzkyIiANCiAvPg0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ1bmlGMkVFIiB1bmljb2RlPSImI3hmMmVlOyIgaG9yaXotYWR2LXg9IjE3OTIiIA0KIC8+DQogICAgPGdseXBoIGdseXBoLW5hbWU9Imxlc3NlcXVhbCIgdW5pY29kZT0iJiN4ZjUwMDsiIGhvcml6LWFkdi14PSIxNzkyIiANCiAvPg0KICA8L2ZvbnQ+DQo8L2RlZnM+PC9zdmc+DQo="},function(A,M,t){var g=t(56),I=t(215);"string"==typeof(I=I.__esModule?I.default:I)&&(I=[[A.i,I,""]]);var e={insert:"head",singleton:!1};g(I,e);A.exports=I.locals||{}},function(A,M,t){var g=t(57),I=t(109),e=t(216),i=t(217),T=t(218),E=t(219);M=g(!1);var N=I(e),n=I(e,{hash:"#iefix"}),D=I(i),C=I(T),r=I(E,{hash:"#iconfont"});M.push([A.i,'@font-face {font-family: "iconfont";\r\n src: url('+N+"); /* IE9 */\r\n src: url("+n+") format('embedded-opentype'), \r\n url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAWwAAsAAAAAC7QAAAViAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDdAqJTIdvATYCJAMgCxIABCAFhG0Hexv8CREVpMGRfXFgnrR3yGVcg8V7C46keQ4e/t+p3qf3BHkKSAphCVmp03abk9oFYA3IHpDtMaEzwr+d4wSQoVrUadshHY+ufeGv7xj+gOy6BnmyMmqAgLS4DMZg4tb/3LvX5vHA5rVnucyxbVk4JgcU0N7UiaIwC/8A/ML4F/MqdnCqyZn82l2HADRN9EdMnzk/xEEB8wQgNm9Y14ETUaEKCAJHamVsVYjzMHGMM43XwbnO74tfSDhgYErgpsVrZySZnDc775dRIXKcjIjaMwDXsSCB/oCifXamKYX0Qv2R6Hei4gTAE39RQt7MB/nWfCyf6ryr8/5CIVFKRaJWEf0XDywUNiYSAwHCAkTFDJ8M25A3FQIL8gECBflWVrE4RoXgxCmKQELnXQgM6Lyfs7Mix2zxgFoQF4J4D5CMd0TujhgEiuapSruuJrAcMTqoKK+Snu8HvudVwInr0DVuQ3SRXl7vCt2cy7luJtNLBSpCHYvxIYTRRRUXZauqJlJGfMOJuVymsjUmdItbr+trOXQqK7KYT2msJUY6v54yUgStXnw607tG4yptt0QEU6PYWlOq4KKI3hdlhajrTGfCCn/liVpns9XVOXdGGqJDIUJ/8Uk19oturItfdlt1xw1XrA9vfrTOvOzSnu03Xlofy7kdmVtqj4viatehsg8+aFXvvx+WfvjhcvOjj+Ju+ra24m5N3VyB1unRLStcMrckRfrO3xJSVAsE+5cK0S1xN3c1i+ytj5H73BtuqVUX39xd33hrj7VuW80TY1zzwQz3pgqJPXXqViEufYR2uC991M1LrCG6WevruwltEtd+OJMm9nDTt8Algwjt1M+/30JD9eLE29wWjg+yRodINDnrrG1CrQmSjVabFV+cqtfqzWxjae05tcYCpz1dZExXI1uW3jvt4idurTDm1hht1r7VJw6bNveekWztF5MTZNhsxsydcm2RMdPaYG1qP5QurkwYiSZ7vbXdTMpkg7v/34r9ap2dPqRmlyesw01GEEYZH3moKT/pL1/UyclmRTBV7rCsCbJy8cmPd02YW2yr/+Be5U8EcrtlH5GM26WOm7Ou6LK5xwbO3//465JDInx6Mf417347ao4v6w+LhR2K9v+/mzb7srIhvN0TnvEpfPV07zhl06ZUzxPJJPPqmGer5oknSqDFMuLFf3jDy/4o9ev9kvfeo6gGZ9t77xfz4J9lw70/HtaVf5fkSEqRDn/uH28XC0q/hKA+mDmMm76Ktv9uL7F/3+7UeojYnfx+VDlr1pLIO7376V4U7Asisb/7/tCPlsyaVRmIRAazqEV+iHsOx4BCAQrxBgrvy+fkWADZx/gahaflk3KqQxepUwBke1bkA3mb7HeeTGPrX/1+ipeN/89x/zrezybtPcHhF8D4tKJ+Gg4GoNlnhUT1yvtwQeWol7pQcCooFpricC3uFwxAo4FzwRbwZZuSSpYJAofKAAxsGkHi0B2t8P3BxGU4WDhMAk0/Zu12KWcnEqGKgL6cSkDgcRcYlPIUSDzeQCv8F2BSxW9g4QkJ0m2fnRy547RtnzG2ggN0f2B6CkpqZdvNfkU9upbDXIf4iZyMF6q8jEevMCC3sSJ961pEgWLycEluQ+cIItMSe8kXInFeFCrrSXlPfrLtDEMtgQag8wcYPRIo01tsJ37+FdJGTosLyr5pPyGWmMFBJVci0FeigCo7lsLkm1YTFlCAumYSD1wyA3IiREDMnrSEeiK3qBGI5gpWTWFFef8Sf6T7AA0ec0qljXWeH9s3Qf+a+6erJ3+MI02rrambza05OQnFQ/w1kqnRuSz1jBg2/tu9LAh682tsQ3SVkwkAAA==') format('woff2'),\r\n url("+D+") format('woff'),\r\n url("+C+") format('truetype'), \r\n url("+r+') format(\'svg\'); /* iOS 4.1- */\r\n}\r\n\r\n.iconfont {\r\n font-family: "iconfont" !important;\r\n font-size: 16px;\r\n font-style: normal;\r\n -webkit-font-smoothing: antialiased;\r\n -moz-osx-font-smoothing: grayscale;\r\n}\r\n\r\n.iconqingxiLOGO:before {\r\n content: "\\e7b4";\r\n}\r\n\r\n.iconqingxilogo:before {\r\n content: "\\e7af";\r\n}\r\n\r\n.iconai23:before {\r\n content: "\\e68a";\r\n}\r\n\r\n.iconiconstop:before {\r\n content: "\\e612";\r\n}\r\n\r\n.iconexit-full-screen:before {\r\n content: "\\e657";\r\n}\r\n\r\n.iconzanting:before {\r\n content: "\\e605";\r\n}\r\n\r\n.iconquanping:before {\r\n content: "\\e655";\r\n}\r\n\r\n',""]),A.exports=M},function(A,M,t){"use strict";t.r(M),M.default="data:application/vnd.ms-fontobject;base64,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AQEAAikC4P1AAWAAAAEAAP/AA74DQgAPAAATIR4BFxEOAQchLgEnET4B9AIXTGUBAWVM/elMZQICZQNBAmVM/eZMZQICZUwCGkxlAAAEAAD/wAPAA0EAEAAhADMARAAAEzMyNj0BNCYiBgcVIyIGFBYTMxUeATI2PQE0JisBIgYUFgUyNjc1MzI2NCYrASIGHQEUFhMzPgE0JisBNS4BIgYdARQWYO8QFBIbEgHTDhISDtMBEhsSFBDvDhISAlsNEgHTDhISDu8QFBIS7w4SEg7TARIbEhQCDRUP7w4SEg7TEhwS/qfUDRISDfAPFRIcEvMSDtMSHBIVD+8OEgJLARIbEtQNExMN8A8UAAACAAAAAANgAuAADwAfAAABMx4BFxEOAQcjLgEnET4BJTMeARcRDgEHIy4BJxE+AQKogBgfAQEfGIAYHwEBH/5IgBgfAQEfGIAYHwEBHwLgAR8Y/a8YHwEBHxgCURgfAQEfGP2vGB8BAR8YAlEYHwAABAAA/9UDqwMrABMAJwA7AE8AADcVHgEXMz4BNCYnIyImPQEuASIGATM+ATc1LgEiBgcVFAYrAQ4BFBYBNS4BJyMOARQWFzMyFh0BHgEyNgEjDgEHFR4BMjY3NTQ2OwE+ATQmVQJINoASGRkSgBIYARgkGAJVgDZIAgEYJBgBGBKAEhkZARICSDaAEhkZEoASGAEYJBj9q4A2SAIBGCQYARgSgBIZGdWANkgCARgkGAEYEoASGRn+7gJINoASGRkSgBIYARgkGAJVgDZIAgEYJBgBGBKAEhkZARICSDaAEhkZEoASGAEYJBgAAAAAABIA3gABAAAAAAAAABUAAAABAAAAAAABAAgAFQABAAAAAAACAAcAHQABAAAAAAADAAgAJAABAAAAAAAEAAgALAABAAAAAAAFAAsANAABAAAAAAAGAAgAPwABAAAAAAAKACsARwABAAAAAAALABMAcgADAAEECQAAACoAhQADAAEECQABABAArwADAAEECQACAA4AvwADAAEECQADABAAzQADAAEECQAEABAA3QADAAEECQAFABYA7QADAAEECQAGABABAwADAAEECQAKAFYBEwADAAEECQALACYBaQpDcmVhdGVkIGJ5IGljb25mb250Cmljb25mb250UmVndWxhcmljb25mb250aWNvbmZvbnRWZXJzaW9uIDEuMGljb25mb250R2VuZXJhdGVkIGJ5IHN2ZzJ0dGYgZnJvbSBGb250ZWxsbyBwcm9qZWN0Lmh0dHA6Ly9mb250ZWxsby5jb20ACgBDAHIAZQBhAHQAZQBkACAAYgB5ACAAaQBjAG8AbgBmAG8AbgB0AAoAaQBjAG8AbgBmAG8AbgB0AFIAZQBnAHUAbABhAHIAaQBjAG8AbgBmAG8AbgB0AGkAYwBvAG4AZgBvAG4AdABWAGUAcgBzAGkAbwBuACAAMQAuADAAaQBjAG8AbgBmAG8AbgB0AEcAZQBuAGUAcgBhAHQAZQBkACAAYgB5ACAAcwB2AGcAMgB0AHQAZgAgAGYAcgBvAG0AIABGAG8AbgB0AGUAbABsAG8AIABwAHIAbwBqAGUAYwB0AC4AaAB0AHQAcAA6AC8ALwBmAG8AbgB0AGUAbABsAG8ALgBjAG8AbQAAAAACAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgBAgEDAQQBBQEGAQcBCAEJAApxaW5neGlMT0dPCnFpbmd4aWxvZ28EYWkyMwhpY29uc3RvcBBleGl0LWZ1bGwtc2NyZWVuB3phbnRpbmcIcXVhbnBpbmcAAAA="},function(A,M,t){"use strict";t.r(M),M.default="data:font/woff;base64,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"},function(A,M,t){"use strict";t.r(M),M.default="data:font/ttf;base64,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"},function(A,M,t){"use strict";t.r(M),M.default="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pg0KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIiA+DQo8IS0tDQoyMDEzLTktMzA6IENyZWF0ZWQuDQotLT4NCjxzdmc+DQo8bWV0YWRhdGE+DQpDcmVhdGVkIGJ5IGljb25mb250DQo8L21ldGFkYXRhPg0KPGRlZnM+DQoNCjxmb250IGlkPSJpY29uZm9udCIgaG9yaXotYWR2LXg9IjEwMjQiID4NCiAgPGZvbnQtZmFjZQ0KICAgIGZvbnQtZmFtaWx5PSJpY29uZm9udCINCiAgICBmb250LXdlaWdodD0iNTAwIg0KICAgIGZvbnQtc3RyZXRjaD0ibm9ybWFsIg0KICAgIHVuaXRzLXBlci1lbT0iMTAyNCINCiAgICBhc2NlbnQ9Ijg5NiINCiAgICBkZXNjZW50PSItMTI4Ig0KICAvPg0KICAgIDxtaXNzaW5nLWdseXBoIC8+DQogICAgDQogICAgPGdseXBoIGdseXBoLW5hbWU9InFpbmd4aUxPR08iIHVuaWNvZGU9IiYjNTkzMTY7IiBkPSJNMjc1LjQgNDA3bC0wLjEtMS42aC05Ny4ybC0wLjgtMTguN2g5OWwtMS45LTQ2LjhjLTAuMi01LjEtMS40LTkuMS0zLjYtMTItMi4yLTIuNy01LjQtNC05LjUtNGwtMTEyIDEuOSAwLjkgMjEuM2gxMDAuNWwwLjcgMTguMWgtMTAwbDEuOSA0NS41YzAuMiA0LjYgMS40IDguNCAzLjYgMTEuNCAyLjIgMy4xIDUuNCA0LjYgOS42IDQuNmg5NC40YzguNCAwIDE0LjktOC44IDE0LjUtMTkuN3pNMjk0LjIgNDI2LjdoOS44YzguNCAwIDE0LjgtOC44IDE0LjQtMTkuNEwzMTUgMzI0aC0yNWw0LjIgMTAyLjd6TTQzMi41IDM1MC4xbDMuMiA3Ny41aDguNGM5LjIgMCAxNi4yLTkuNSAxNS44LTIxLjNsLTIuOC02OS41Yy0wLjEtMi42LTAuOS01LTIuNC03LjQtMS4zLTItMi44LTMuNi00LjMtNC42LTQuNC0xLjgtOC42LTIuMi0xMi41LTEuMy00LjEgMC43LTcuOSAyLjUtMTEuMyA1LjMtMy4xIDMuMS02LjMgNi4yLTkuNSA5LjMtMy4yIDMuMS02LjYgNi41LTEwLjEgMTAuMi0zLjYgMy43LTcuMyA3LjctMTEuMyAxMS45LTguMSA4LjQtMTUuNyAxNi40LTIzIDI0LTcuNCA3LjUtMTEuNyAxMi42LTEyLjkgMTUuNGwtMy4xLTc0LjhoLTI0bDMuNyA4OS4zYzAuMSAzLjUgMSA3LjcgMy41IDEwLjQgMi43IDIuNyA1LjIgNC4yIDcuOSA1LjEgMy44IDEuMiAxMC45IDEgMTYuMS00LjhsNjguNi03NC43ek02MTAuMyA0MDUuNHYtMC4ySDQ5OS45bC0yLjUtNjBoODYuNWwwLjggMjBoLTYwLjVsMC44IDIwaDg0LjRsLTItNDhjLTAuMy03LjMtNS4yLTEzLjMtMTAuOS0xMy4zSDQ4MS45Yy0yIDAtNC4yIDEuMS02LjYgMy40LTIuMyAyLjItMy40IDYuNS0zLjEgMTIuNmwyLjkgNzAuOGMwLjIgNC41IDEuNCA4LjIgMy42IDExLjIgMi4yIDMuMiA1LjQgNC44IDkuNiA0LjhoMTA2LjJjOS4yIDAgMTYuMi05LjUgMTUuOC0yMS4zek03NTAuNCA0MjcuMmwtMC45LTIxLjNoLTk4LjlsLTAuOC0xOC43aDk5bC0xLjktNDYuOGMtMC4yLTUuMS0xLjQtOS4xLTMuNi0xMi0yLjItMi43LTUuNC00LTkuNS00SDYyMi4zbDAuOSAyMS4zaDEwMGwwLjggMjBINjI0bDEuOSA0NS41YzAuMiA0LjYgMS40IDguNCAzLjYgMTEuNCAyLjIgMy4xIDUuNCA0LjYgOS42IDQuNmgxMTEuM3pNODgzLjcgNDA1LjlINzY0bDAuOSAyMS4zSDg2OGM5LjIgMCAxNi4yLTkuNSAxNS43LTIxLjN6TTg2MC4xIDM4Ny4ybC0wLjktMjEuNWgtNzEuOGwtMC44LTE4LjZoOTlsLTAuOS0yMi43aC0xMjRsMi41IDYyLjh6TTEwMjEuNSA0MDUuOUg5MDEuOGwwLjkgMjEuM2gxMDMuMWM5LjIgMCAxNi4yLTkuNSAxNS43LTIxLjN6TTk5Ny45IDM4Ny4ybC0wLjktMjEuNWgtNzEuOWwtMC43LTE4LjZoOTlsLTEtMjIuN0g4OTguNWwyLjUgNjIuOHpNMTU4LjIgNDUxLjNsLTE2LjUtMTguNmMtMC44LTAuOS0xLjktMS40LTMtMS40SDExM2MtMS4yIDAtMC41LTcyLjEtMS4zLTczLjJsLTE3LjMtMjEuNGMtMi43LTMuNC03LjUtMS4xLTcuNSAzLjZsMC4xIDExM2MwLjEgNC4yIDMuNCA3IDYuNSA3bDYxLjgtMC4xYzMuOSAwLjEgNS44LTUuNyAyLjktOC45ek03OSAzMjIuNGwtMTEuNy0xMy4xYy0zLjktNC40LTEwLjMtMS0xMC4zIDUuNGwwLjQgMTA5LjVjMCA0LTIuNyA3LjMtNi4xIDcuM2wtNDQuNi0wLjFjLTUuNCAwLTguMSA3LjctNC40IDEyLjNsMTIuMSAxNC44YzEuMiAxLjQgMi43IDIuMiA0LjQgMi4ybDU2LjItMC4zYzMuNCAwIDYuMS0zLjMgNi4xLTcuM0w4MSAzMjcuN2MwLTItMC43LTMuOS0yLTUuM3oiICBob3Jpei1hZHYteD0iMTAyNCIgLz4NCg0KICAgIA0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJxaW5neGlsb2dvIiB1bmljb2RlPSImIzU5MzExOyIgZD0iTTk1MS4yIDc3Mi40bC05Mi44LTc0LjVjLTQuNS01LjEtMTAuNS04LTE2LjgtOEg2ODYuN2MtNi43IDAtMi42LTQzNS4xLTcuNC00NDEuM2wtNzcuMS0xMjAuNGMtMTUuMi0xOS00Mi4xLTYuMS00Mi4xIDIwLjJsMC44IDYzNS41YzAuNiAyMy4zIDE4LjkgMzkuNiAzNi4zIDM5LjZsMzM3LjItMC42YzIyLjItMC4xIDMyLjktMzIuMyAxNi44LTUwLjV6TTQ5NiA0Ny42bC01NS44LTczLjhjLTIyLTI0LjctNTMtNS45LTUyLjggMzAuMmwtMC44IDYzNS41YzAuMiAyMi44IDYuNCA1MC45LTI2LjMgNTEuM2wtMjYwLjgtMC41Yy0zMC4zLTAuMS00NS44IDIzLjUtMjQuNyA0OS40TDE0Mi43IDgxM2M2LjUgOCAxNS40IDEyLjUgMjQuOCAxMi40bDMwNi4xLTEuN2MxOC45LTAuMSAzNC4xLTE4LjQgMzQuMS00MC45bC0wLjgtNzA1LjRjLTAuMS0xMS4zLTQtMjIuMS0xMC45LTI5Ljh6IiAgaG9yaXotYWR2LXg9IjEwMjQiIC8+DQoNCiAgICANCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0iYWkyMyIgdW5pY29kZT0iJiM1OTAxODsiIGQ9Ik0yNTYuMDc5ODE4IDczNS42MzQ2MTYgMjU2LjA3OTgxOCAzMi4zNjY0MDggODA4LjY0OTgxNSAzODQuMDE2MzczWiIgIGhvcml6LWFkdi14PSIxMDI0IiAvPg0KDQogICAgDQogICAgPGdseXBoIGdseXBoLW5hbWU9Imljb25zdG9wIiB1bmljb2RlPSImIzU4ODk4OyIgZD0iTTI0My42MTEzNDQgODMzLjQwMjMxM2w1MzUuNDAzMDEzIDBjOTguNTY1ODc2IDAgMTc4LjQ2NDYwMS04MC4yNjUwNjggMTc4LjQ2NDYwMS0xNzkuMjgzMjQ2bDAtNTM3Ljg0OTczOGMwLTk5LjAwODk2OC03OS44OTg3MjUtMTc5LjI4MzI0Ni0xNzguNDY0NjAxLTE3OS4yODMyNDZMMjQzLjYxMTM0NC02My4wMTM5MTdjLTk4LjU1NjY2NyAwLTE3OC40NTUzOTEgODAuMjc0Mjc4LTE3OC40NTUzOTEgMTc5LjI4MzI0NmwwIDUzNy44NDk3MzhDNjUuMTU1OTUyIDc1My4xMzcyNDUgMTQ1LjA1NDY3NyA4MzMuNDAyMzEzIDI0My42MTEzNDQgODMzLjQwMjMxM3oiICBob3Jpei1hZHYteD0iMTAyNCIgLz4NCg0KICAgIA0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJleGl0LWZ1bGwtc2NyZWVuIiB1bmljb2RlPSImIzU4OTY3OyIgZD0iTTk1Ljk0NCA1MjQuOTE4aDIzOS40ODdjMTkuODUxIDAgMzYgMTYuMTQ5IDM2IDM2djIzOS40ODhjMCAxNy42NzMtMTQuMzI3IDMyLTMyIDMycy0zMi0xNC4zMjctMzItMzJ2LTIxMS40ODhoLTIxMS40ODdjLTE3LjY3MyAwLTMyLTE0LjMyNy0zMi0zMiAwLTE3LjY3MiAxNC4zMjctMzIgMzItMzJ6TTk1Ljk0NCAxNzkuOTg0aDIxMS40ODd2LTIxMS40ODdjMC0xNy42NzMgMTQuMzI3LTMyIDMyLTMyczMyIDE0LjMyNyAzMiAzMnYyMzkuNDg3YzAgMTkuODUxLTE2LjE0OSAzNi0zNiAzNmgtMjM5LjQ4N2MtMTcuNjczIDAtMzItMTQuMzI3LTMyLTMyczE0LjMyNy0zMiAzMi0zMnpNNjg0LjUxMy02My40MDJjMTcuNjczIDAgMzIgMTQuMzI3IDMyIDMydjIxMS40ODdoMjExLjQ4N2MxNy42NzMgMCAzMiAxNC4zMjcgMzIgMzJzLTE0LjMyNyAzMi0zMiAzMmgtMjM5LjQ4N2MtMTkuODUxIDAtMzYtMTYuMTQ5LTM2LTM2di0yMzkuNDg3YzAtMTcuNjczIDE0LjMyNy0zMiAzMi0zMnpNNjg4LjUxMyA1MjQuMzYxaDIzOS40ODdjMTcuNjczIDAgMzIgMTQuMzI3IDMyIDMycy0xNC4zMjcgMzItMzIgMzJoLTIxMS40ODd2MjExLjQ4OGMwIDE3LjY3My0xNC4zMjcgMzItMzIgMzJzLTMyLTE0LjMyNy0zMi0zMnYtMjM5LjQ4OGMwLTE5Ljg1IDE2LjE0OS0zNiAzNi0zNnoiICBob3Jpei1hZHYteD0iMTAyNCIgLz4NCg0KICAgIA0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ6YW50aW5nIiB1bmljb2RlPSImIzU4ODg1OyIgZD0iTTY4MCA3MzZoMTI4YzMwLjkyOCAwIDU2LTI1LjA3MiA1Ni01NnYtNTkzYzAtMzAuOTI4LTI1LjA3Mi01Ni01Ni01Nkg2ODBjLTMwLjkyOCAwLTU2IDI1LjA3Mi01NiA1NlY2ODBjMCAzMC45MjggMjUuMDcyIDU2IDU2IDU2eiBtLTQ2NCAwaDEyOGMzMC45MjggMCA1Ni0yNS4wNzIgNTYtNTZ2LTU5M2MwLTMwLjkyOC0yNS4wNzItNTYtNTYtNTZIMjE2Yy0zMC45MjggMC01NiAyNS4wNzItNTYgNTZWNjgwYzAgMzAuOTI4IDI1LjA3MiA1NiA1NiA1NnoiICBob3Jpei1hZHYteD0iMTAyNCIgLz4NCg0KICAgIA0KICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJxdWFucGluZyIgdW5pY29kZT0iJiM1ODk2NTsiIGQ9Ik04NS4zMzMzMzMgMjEzLjMzMzMzM3YtMTI4YTEyOCAxMjggMCAwIDEgMTI4LTEyOGgxMjhhNDIuNjY2NjY3IDQyLjY2NjY2NyAwIDAgMSAwIDg1LjMzMzMzNEgyMTMuMzMzMzMzYTQyLjY2NjY2NyA0Mi42NjY2NjcgMCAwIDAtNDIuNjY2NjY2IDQyLjY2NjY2NnYxMjhhNDIuNjY2NjY3IDQyLjY2NjY2NyAwIDAgMS04NS4zMzMzMzQgMHogbTU5Ny4zMzMzMzQtMjU2aDEyOGExMjggMTI4IDAgMCAxIDEyOCAxMjh2MTI4YTQyLjY2NjY2NyA0Mi42NjY2NjcgMCAwIDEtODUuMzMzMzM0IDB2LTEyOGE0Mi42NjY2NjcgNDIuNjY2NjY3IDAgMCAwLTQyLjY2NjY2Ni00Mi42NjY2NjZoLTEyOGE0Mi42NjY2NjcgNDIuNjY2NjY3IDAgMCAxIDAtODUuMzMzMzM0eiBtMjU2IDU5Ny4zMzMzMzRWNjgyLjY2NjY2N2ExMjggMTI4IDAgMCAxLTEyOCAxMjhoLTEyOGE0Mi42NjY2NjcgNDIuNjY2NjY3IDAgMCAxIDAtODUuMzMzMzM0aDEyOGE0Mi42NjY2NjcgNDIuNjY2NjY3IDAgMCAwIDQyLjY2NjY2Ni00Mi42NjY2NjZ2LTEyOGE0Mi42NjY2NjcgNDIuNjY2NjY3IDAgMCAxIDg1LjMzMzMzNCAwek0zNDEuMzMzMzMzIDgxMC42NjY2NjdIMjEzLjMzMzMzM2ExMjggMTI4IDAgMCAxLTEyOC0xMjh2LTEyOGE0Mi42NjY2NjcgNDIuNjY2NjY3IDAgMCAxIDg1LjMzMzMzNCAwVjY4Mi42NjY2NjdhNDIuNjY2NjY3IDQyLjY2NjY2NyAwIDAgMCA0Mi42NjY2NjYgNDIuNjY2NjY2aDEyOGE0Mi42NjY2NjcgNDIuNjY2NjY3IDAgMCAxIDAgODUuMzMzMzM0eiIgIGhvcml6LWFkdi14PSIxMDI0IiAvPg0KDQogICAgDQoNCg0KICA8L2ZvbnQ+DQo8L2RlZnM+PC9zdmc+DQo="},function(A,M,t){var g=t(56),I=t(221);"string"==typeof(I=I.__esModule?I.default:I)&&(I=[[A.i,I,""]]);var e={insert:"head",singleton:!1};g(I,e);A.exports=I.locals||{}},function(A,M,t){(M=t(57)(!1)).push([A.i,"input[type=range] {\r\n -webkit-appearance: none;\r\n}\r\ninput[type=range]::-webkit-slider-thumb {\r\n -webkit-appearance: none;\r\n} \r\ninput[type=range]::-webkit-slider-runnable-track {\r\n height: 15px;\r\n border-radius: 10px; /*将轨道设为圆角的*/\r\n}\r\ninput[type=range]:focus {\r\n outline: none;\r\n}\r\ninput[type=range]::-webkit-slider-thumb {\r\n -webkit-appearance: none;\r\n height: 15px;\r\n width: 15px;\r\n margin-top: -2px; /*使滑块超出轨道部分的偏移量相等*/\r\n background: #ffffff; \r\n border-radius: 50%; /*外观设置为圆形*/\r\n}\r\n.conter{\r\n position: absolute;\r\n left: 50%;\r\n top: 50%;\r\n transform: translate(-50%,-50%);\r\n}\r\n.Loding{\r\n margin: 0 auto;\r\n margin-bottom: 5px;\r\n border:3px solid #f3f3f3;\r\n border-radius:50%;\r\n border-top:3px solid #2e8e9a;\r\n width:25px;\r\n height:25px;\r\n /* animation-name:load; */\r\n animation:load .5s linear infinite;\r\n\r\n}\r\n.LodingTitle {\r\n margin-top: 20px;\r\n color: #fff;\r\n font-size: 14px;\r\n}\r\n@keyframes load{\r\n 0%{\r\n transform: rotate(0deg);\r\n }\r\n 100%{\r\n transform:rotate(360deg);\r\n }\r\n}\r\n/* 中间大播放按钮样式 */\r\n.bigPlayBox{\r\n position: absolute;\r\n top: 50%;\r\n left: 50%;\r\n transform: translate(-50%,-50%);\r\n background-color: rgba(0,0,0,.5);\r\n cursor: pointer;\r\n width: 80px;\r\n height:80px;\r\n border-radius: 40px;\r\n text-align: center;\r\n line-height: 80px;\r\n font-size: 36px;\r\n color: #fff;\r\n z-index: 9;\r\n}",""]),A.exports=M},function(A,M,t){"use strict";t.r(M),t.d(M,"default",(function(){return hA}));var g=function(){function A(A){this.gl=A,this.texture=null}var M=A.prototype;return M.create=function(A,M){var t=this.gl;this.texture=t.createTexture(),t.bindTexture(t.TEXTURE_2D,this.texture),1==A&&t.texImage2D(t.TEXTURE_2D,1,t.RGBA,t.RGBA,t.UNSIGNED_BYTE,M),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)},M.bind=function(A,M,t){var g=this.gl;g.activeTexture([g.TEXTURE0,g.TEXTURE1,g.TEXTURE2,g.TEXTURE3][A]),g.bindTexture(g.TEXTURE_2D,this.texture),g.uniform1i(g.getUniformLocation(M,t),A)},M.fill=function(A,M,t){var g=this.gl;g.bindTexture(g.TEXTURE_2D,this.texture),g.texImage2D(g.TEXTURE_2D,0,g.LUMINANCE,A,M,0,g.LUMINANCE,g.UNSIGNED_BYTE,t)},A}(),I=function(){function A(A){this.enableTextTexture=0,this.canvas=A,this.gl=A.getContext("webgl")||A.getContext("experimental-webgl"),this.textCtx=null}var M=A.prototype;return M.destroy=function(){this.gl&&this.gl.commit&&this.gl.commit()},M.makeTextCanvas=function(A,M,t){return textCtx.canvas.width=M,textCtx.canvas.height=t,textCtx.font="10px monospace",textCtx.textAlign="center",textCtx.textBaseline="middle",textCtx.fillStyle="rgba(100, 0, 0, 0.6)",textCtx.clearRect(0,0,textCtx.canvas.width,textCtx.canvas.height),textCtx.fillText(A,M/2,t/2),textCtx.canvas},M.initGL=function(A){if(this.gl){var M=this.gl;M.pixelStorei(M.UNPACK_ALIGNMENT,1);var t=M.createProgram(),I=["attribute highp vec4 aVertexPosition;","attribute vec2 aTextureCoord;","varying highp vec2 vTextureCoord;","void main(void) {"," gl_Position = aVertexPosition;"," vTextureCoord = aTextureCoord;","}"].join("\n"),e=M.createShader(M.VERTEX_SHADER);M.shaderSource(e,I),M.compileShader(e);var i=["precision highp float;","varying lowp vec2 vTextureCoord;","uniform sampler2D YTexture;","uniform sampler2D UTexture;","uniform sampler2D VTexture;","uniform sampler2D TTexture;","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) {"," gl_FragColor = mix(vec4( texture2D(YTexture, vTextureCoord).x, texture2D(UTexture, vTextureCoord).x, texture2D(VTexture, vTextureCoord).x, 1) * YUV2RGB, texture2D(TTexture, vTextureCoord), 0.1);","}"].join("\n"),T=M.createShader(M.FRAGMENT_SHADER);M.shaderSource(T,i),M.compileShader(T),M.attachShader(t,e),M.attachShader(t,T),M.linkProgram(t),M.useProgram(t),M.getProgramParameter(t,M.LINK_STATUS)||console.log("[ER] Shader link failed.");var E=M.getAttribLocation(t,"aVertexPosition");M.enableVertexAttribArray(E);var N=M.getAttribLocation(t,"aTextureCoord");M.enableVertexAttribArray(N);var n=M.createBuffer();M.bindBuffer(M.ARRAY_BUFFER,n),M.bufferData(M.ARRAY_BUFFER,new Float32Array([1,1,0,-1,1,0,1,-1,0,-1,-1,0]),M.STATIC_DRAW),M.vertexAttribPointer(E,3,M.FLOAT,!1,0,0);var D=M.createBuffer();M.bindBuffer(M.ARRAY_BUFFER,D),M.bufferData(M.ARRAY_BUFFER,new Float32Array([1,0,0,0,1,1,0,1]),M.STATIC_DRAW),M.vertexAttribPointer(N,2,M.FLOAT,!1,0,0),1==this.enableTextTexture&&(void 0!==document&&(this.textCtx=document.createElement("canvas").getContext("2d")),console.log("textCtx:"+this.textCtx),null!=this.textCtx?(this.textCtx.canvas.width=256,this.textCtx.canvas.height=256,this.textCtx.font="10px monospace",this.textCtx.textAlign="center",this.textCtx.textBaseline="middle",this.textCtx.fillStyle="rgba(100, 0, 0, 0.6)",this.textCtx.clearRect(0,0,this.textCtx.canvas.width,this.textCtx.canvas.height),this.textCtx.fillText("Hello",this.textCtx.canvas.width/2,this.textCtx.canvas.height/2),M.t=new g(M),M.t.create(1,this.textCtx.canvas),console.log("create textctx ok.")):console.log("create textctx fail.")),M.y=new g(M),M.y.create(0,null),M.u=new g(M),M.u.create(0,null),M.v=new g(M),M.v.create(0,null),M.y.bind(0,t,"YTexture"),M.u.bind(1,t,"UTexture"),M.v.bind(2,t,"VTexture"),1==this.enableTextTexture&&this.textCtx&&M.t.bind(3,t,"TTexture")}else console.log("[ER] WebGL not supported.")},M.renderFrame=function(A,M,t,g,I){if(this.gl){var e=this.gl;e.viewport(0,0,e.canvas.width,e.canvas.height),e.clearColor(0,0,0,0),e.clear(e.COLOR_BUFFER_BIT),A&&(e.y.fill(M,t,A.subarray(0,g)),e.u.fill(M>>1,t>>1,A.subarray(g,g+I)),e.v.fill(M>>1,t>>1,A.subarray(g+I,A.length))),1==this.enableTextTexture&&this.textCtx&&e.texImage2D(e.TEXTURE_2D,0,e.RGBA,e.RGBA,e.UNSIGNED_BYTE,this.textCtx.canvas),e.drawArrays(e.TRIANGLE_STRIP,0,4)}else console.log("[ER] Render frame failed due to WebGL not supported.")},M.renderVideoFrame=function(A,M,t){if(M<1||t<1){var g=this.gl,I=new Uint8Array(g.canvas.width*g.canvas.height*3/2),e=0,i=g.canvas.width*g.canvas.height,T=g.canvas.width*g.canvas.height*5/4,E=I.byteLength;for(e=i;e=0&&/(rv)(?::| )([\w.]+)/.exec(A)||A.indexOf("compatible")<0&&/(firefox)[ \/]([\w.]+)/.exec(A)||[],t=/(ipad)/.exec(A)||/(ipod)/.exec(A)||/(windows phone)/.exec(A)||/(iphone)/.exec(A)||/(kindle)/.exec(A)||/(android)/.exec(A)||/(windows)/.exec(A)||/(mac)/.exec(A)||/(linux)/.exec(A)||/(cros)/.exec(A)||[],g={browser:M[5]||M[3]||M[1]||"",version:M[2]||M[4]||"0",majorVersion:M[4]||M[2]||"0",platform:t[0]||""},I={};if(g.browser){I[g.browser]=!0;var e=g.majorVersion.split(".");I.version={major:parseInt(g.majorVersion,10),string:g.version},e.length>1&&(I.version.minor=parseInt(e[1],10)),e.length>2&&(I.version.build=parseInt(e[2],10))}if(g.platform&&(I[g.platform]=!0),(I.chrome||I.opr||I.safari)&&(I.webkit=!0),I.rv||I.iemobile){I.rv&&delete I.rv;g.browser="msie",I.msie=!0}if(I.edge){delete I.edge;g.browser="msedge",I.msedge=!0}if(I.opr){g.browser="opera",I.opera=!0}if(I.safari&&I.android){g.browser="android",I.android=!0}for(var i in I.name=g.browser,I.platform=g.platform,D)D.hasOwnProperty(i)&&delete D[i];Object.assign(D,I)}();var C=D,r={ERROR:"error",LOADING_COMPLETE:"loading_complete",RECOVERED_EARLY_EOF:"recovered_early_eof",MEDIA_INFO:"media_info",METADATA_ARRIVED:"metadata_arrived",SCRIPTDATA_ARRIVED:"scriptdata_arrived",STATISTICS_INFO:"statistics_info"};function c(A,M){for(var t=0;t0){var t=A.getConfig();M.emit("change",t)}},A.registerListener=function(M){A.emitter.addListener("change",M)},A.removeListener=function(M){A.emitter.removeListener("change",M)},A.addLogListener=function(M){n.a.emitter.addListener("log",M),n.a.emitter.listenerCount("log")>0&&(n.a.ENABLE_CALLBACK=!0,A._notifyChange())},A.removeLogListener=function(M){n.a.emitter.removeListener("log",M),0===n.a.emitter.listenerCount("log")&&(n.a.ENABLE_CALLBACK=!1,A._notifyChange())},M=A,g=[{key:"forceGlobalTag",get:function(){return n.a.FORCE_GLOBAL_TAG},set:function(M){n.a.FORCE_GLOBAL_TAG=M,A._notifyChange()}},{key:"globalTag",get:function(){return n.a.GLOBAL_TAG},set:function(M){n.a.GLOBAL_TAG=M,A._notifyChange()}},{key:"enableAll",get:function(){return n.a.ENABLE_VERBOSE&&n.a.ENABLE_DEBUG&&n.a.ENABLE_INFO&&n.a.ENABLE_WARN&&n.a.ENABLE_ERROR},set:function(M){n.a.ENABLE_VERBOSE=M,n.a.ENABLE_DEBUG=M,n.a.ENABLE_INFO=M,n.a.ENABLE_WARN=M,n.a.ENABLE_ERROR=M,A._notifyChange()}},{key:"enableDebug",get:function(){return n.a.ENABLE_DEBUG},set:function(M){n.a.ENABLE_DEBUG=M,A._notifyChange()}},{key:"enableVerbose",get:function(){return n.a.ENABLE_VERBOSE},set:function(M){n.a.ENABLE_VERBOSE=M,A._notifyChange()}},{key:"enableInfo",get:function(){return n.a.ENABLE_INFO},set:function(M){n.a.ENABLE_INFO=M,A._notifyChange()}},{key:"enableWarn",get:function(){return n.a.ENABLE_WARN},set:function(M){n.a.ENABLE_WARN=M,A._notifyChange()}},{key:"enableError",get:function(){return n.a.ENABLE_ERROR},set:function(M){n.a.ENABLE_ERROR=M,A._notifyChange()}}],(t=null)&&c(M.prototype,t),g&&c(M,g),A}();o.emitter=new N.a;var B=o,Q=t(13),a=t(36),h=function(){function A(){}return A.init=function(){for(var M in A.types={avc1:[],avcC:[],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:[],".mp3":[]},A.types)A.types.hasOwnProperty(M)&&(A.types[M]=[M.charCodeAt(0),M.charCodeAt(1),M.charCodeAt(2),M.charCodeAt(3)]);var t=A.constants={};t.FTYP=new Uint8Array([105,115,111,109,0,0,0,1,105,115,111,109,97,118,99,49]),t.STSD_PREFIX=new Uint8Array([0,0,0,0,0,0,0,1]),t.STTS=new Uint8Array([0,0,0,0,0,0,0,0]),t.STSC=t.STCO=t.STTS,t.STSZ=new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0]),t.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]),t.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]),t.DREF=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,12,117,114,108,32,0,0,0,1]),t.SMHD=new Uint8Array([0,0,0,0,0,0,0,0]),t.VMHD=new Uint8Array([0,0,0,1,0,0,0,0,0,0,0,0])},A.box=function(A){for(var M=8,t=null,g=Array.prototype.slice.call(arguments,1),I=g.length,e=0;e>>24&255,t[1]=M>>>16&255,t[2]=M>>>8&255,t[3]=255&M,t.set(A,4);for(var i=8,T=0;T>>24&255,M>>>16&255,M>>>8&255,255&M,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]))},A.trak=function(M){return A.box(A.types.trak,A.tkhd(M),A.mdia(M))},A.tkhd=function(M){var t=M.id,g=M.duration,I=M.presentWidth,e=M.presentHeight;return A.box(A.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,g>>>24&255,g>>>16&255,g>>>8&255,255&g,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,I>>>8&255,255&I,0,0,e>>>8&255,255&e,0,0]))},A.mdia=function(M){return A.box(A.types.mdia,A.mdhd(M),A.hdlr(M),A.minf(M))},A.mdhd=function(M){var t=M.timescale,g=M.duration;return A.box(A.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,g>>>24&255,g>>>16&255,g>>>8&255,255&g,85,196,0,0]))},A.hdlr=function(M){var t=null;return t="audio"===M.type?A.constants.HDLR_AUDIO:A.constants.HDLR_VIDEO,A.box(A.types.hdlr,t)},A.minf=function(M){var t=null;return t="audio"===M.type?A.box(A.types.smhd,A.constants.SMHD):A.box(A.types.vmhd,A.constants.VMHD),A.box(A.types.minf,t,A.dinf(),A.stbl(M))},A.dinf=function(){return A.box(A.types.dinf,A.box(A.types.dref,A.constants.DREF))},A.stbl=function(M){return A.box(A.types.stbl,A.stsd(M),A.box(A.types.stts,A.constants.STTS),A.box(A.types.stsc,A.constants.STSC),A.box(A.types.stsz,A.constants.STSZ),A.box(A.types.stco,A.constants.STCO))},A.stsd=function(M){return"audio"===M.type?"mp3"===M.codec?A.box(A.types.stsd,A.constants.STSD_PREFIX,A.mp3(M)):A.box(A.types.stsd,A.constants.STSD_PREFIX,A.mp4a(M)):A.box(A.types.stsd,A.constants.STSD_PREFIX,A.avc1(M))},A.mp3=function(M){var t=M.channelCount,g=M.audioSampleRate,I=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,g>>>8&255,255&g,0,0]);return A.box(A.types[".mp3"],I)},A.mp4a=function(M){var t=M.channelCount,g=M.audioSampleRate,I=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,g>>>8&255,255&g,0,0]);return A.box(A.types.mp4a,I,A.esds(M))},A.esds=function(M){var t=M.config||[],g=t.length,I=new Uint8Array([0,0,0,0,3,23+g,0,1,0,4,15+g,64,21,0,0,0,0,0,0,0,0,0,0,0,5].concat([g]).concat(t).concat([6,1,2]));return A.box(A.types.esds,I)},A.avc1=function(M){var t=M.avcc,g=M.codecWidth,I=M.codecHeight,e=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,g>>>8&255,255&g,I>>>8&255,255&I,0,72,0,0,0,72,0,0,0,0,0,0,0,1,10,120,113,113,47,102,108,118,46,106,115,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 A.box(A.types.avc1,e,A.box(A.types.avcC,t))},A.mvex=function(M){return A.box(A.types.mvex,A.trex(M))},A.trex=function(M){var t=M.id,g=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 A.box(A.types.trex,g)},A.moof=function(M,t){return A.box(A.types.moof,A.mfhd(M.sequenceNumber),A.traf(M,t))},A.mfhd=function(M){var t=new Uint8Array([0,0,0,0,M>>>24&255,M>>>16&255,M>>>8&255,255&M]);return A.box(A.types.mfhd,t)},A.traf=function(M,t){var g=M.id,I=A.box(A.types.tfhd,new Uint8Array([0,0,0,0,g>>>24&255,g>>>16&255,g>>>8&255,255&g])),e=A.box(A.types.tfdt,new Uint8Array([0,0,0,0,t>>>24&255,t>>>16&255,t>>>8&255,255&t])),i=A.sdtp(M),T=A.trun(M,i.byteLength+16+16+8+16+8+8);return A.box(A.types.traf,I,e,T,i)},A.sdtp=function(M){for(var t=M.samples||[],g=t.length,I=new Uint8Array(4+g),e=0;e>>24&255,I>>>16&255,I>>>8&255,255&I,t>>>24&255,t>>>16&255,t>>>8&255,255&t],0);for(var T=0;T>>24&255,E>>>16&255,E>>>8&255,255&E,N>>>24&255,N>>>16&255,N>>>8&255,255&N,n.isLeading<<2|n.dependsOn,n.isDependedOn<<6|n.hasRedundancy<<4|n.isNonSync,0,0,D>>>24&255,D>>>16&255,D>>>8&255,255&D],12+16*T)}return A.box(A.types.trun,i)},A.mdat=function(M){return A.box(A.types.mdat,M)},A}();h.init();var s=h,y=function(){function A(){}return A.getSilentFrame=function(A,M){if("mp4a.40.2"===A){if(1===M)return new Uint8Array([0,200,0,128,35,128]);if(2===M)return new Uint8Array([33,0,73,144,2,25,0,35,128]);if(3===M)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,142]);if(4===M)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,128,44,128,8,2,56]);if(5===M)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,130,48,4,153,0,33,144,2,56]);if(6===M)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,130,48,4,153,0,33,144,2,0,178,0,32,8,224])}else{if(1===M)return new Uint8Array([1,64,34,128,163,78,230,128,186,8,0,0,0,28,6,241,193,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94]);if(2===M)return new Uint8Array([1,64,34,128,163,94,230,128,186,8,0,0,0,0,149,0,6,241,161,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94]);if(3===M)return new Uint8Array([1,64,34,128,163,94,230,128,186,8,0,0,0,0,149,0,6,241,161,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94])}return null},A}();function w(A,M){for(var t=0;t0&&A[0].originalDts=M[I].dts&&AM[g].lastSample.originalDts&&A=M[g].lastSample.originalDts&&(g===M.length-1||g0&&(I=this._searchNearestSegmentBefore(t.originalBeginDts)+1),this._lastAppendLocation=I,this._list.splice(I,0,t)},I.getLastSegmentBefore=function(A){var M=this._searchNearestSegmentBefore(A);return M>=0?this._list[M]:null},I.getLastSampleBefore=function(A){var M=this.getLastSegmentBefore(A);return null!=M?M.lastSample:null},I.getLastSyncPointBefore=function(A){for(var M=this._searchNearestSegmentBefore(A),t=this._list[M].syncPoints;0===t.length&&M>0;)M--,t=this._list[M].syncPoints;return t.length>0?t[t.length-1]:null},M=A,(t=[{key:"type",get:function(){return this._type}},{key:"length",get:function(){return this._list.length}}])&&w(M.prototype,t),g&&w(M,g),A}(),Y=t(2);function u(A,M){for(var t=0;t1&&(o-=(B=I.pop()).length),null!=this._audioStashedLastSample){var Q=this._audioStashedLastSample;this._audioStashedLastSample=null,I.unshift(Q),o+=Q.length}null!=B&&(this._audioStashedLastSample=B);var a=I[0].dts-this._dtsBase;if(this._audioNextDts)e=a-this._audioNextDts;else if(this._audioSegmentInfoList.isEmpty())e=0,this._fillSilentAfterSeek&&!this._videoSegmentInfoList.isEmpty()&&"mp3"!==this._audioMeta.originalCodec&&(D=!0);else{var h=this._audioSegmentInfoList.getLastSampleBefore(a);if(null!=h){var w=a-(h.originalDts+h.duration);w<=3&&(w=0),e=a-(h.dts+h.duration+w)}else e=0}if(D){var d=a-e,x=this._videoSegmentInfoList.getLastSegmentBefore(a);if(null!=x&&x.beginDts=1?l[l.length-1].duration:Math.floor(T);var R=!1,k=null;if(m>1.5*T&&"mp3"!==this._audioMeta.codec&&this._fillAudioTimestampGap&&!C.safari){R=!0;var p=Math.abs(m-T),b=Math.ceil(p/T),G=F+T;n.a.w(this.TAG,"Large audio timestamp gap detected, may cause AV sync to drift. Silent frames will be generated to avoid unsync.\ndts: "+(F+m)+" ms, expected: "+(F+Math.round(T))+" ms, delta: "+Math.round(p)+" ms, generate: "+b+" framessampleDuration: "+m+" ms, refSampleDuration: "+T);var J=y.getSilentFrame(this._audioMeta.originalCodec,this._audioMeta.channelCount);null==J&&(n.a.w(this.TAG,"Unable to generate silent frame for "+this._audioMeta.originalCodec+" with "+this._audioMeta.channelCount+" channels, repeat last frame"),J=f),k=[];for(var H=0;H0){var v=k[k.length-1];v.duration=X-v.dts}var V={dts:X,pts:X,cts:0,unit:J,size:J.byteLength,duration:0,originalDts:O,flags:{isLeading:0,dependsOn:1,isDependedOn:0,hasRedundancy:0}};k.push(V),o+=V.size,G+=T}var W=k[k.length-1];W.duration=F+m-W.dts,m=Math.round(T)}l.push({dts:F,pts:F,cts:0,unit:U.unit,size:U.unit.byteLength,duration:m,originalDts:O,flags:{isLeading:0,dependsOn:1,isDependedOn:0,hasRedundancy:0}}),R&&l.push.apply(l,k)}E?c=new Uint8Array(o):((c=new Uint8Array(o))[0]=o>>>24&255,c[1]=o>>>16&255,c[2]=o>>>8&255,c[3]=255&o,c.set(s.types.mdat,4));for(var P=0;P1&&(D-=(C=e.pop()).length),null!=this._videoStashedLastSample){var r=this._videoStashedLastSample;this._videoStashedLastSample=null,e.unshift(r),D+=r.length}null!=C&&(this._videoStashedLastSample=C);var c=e[0].dts-this._dtsBase;if(this._videoNextDts)i=c-this._videoNextDts;else if(this._videoSegmentInfoList.isEmpty())i=0;else{var o=this._videoSegmentInfoList.getLastSampleBefore(c);if(null!=o){var B=c-(o.originalDts+o.duration);B<=3&&(B=0),i=c-(o.dts+o.duration+B)}else i=0}for(var Q=new L,a=[],h=0;h=1?a[a.length-1].duration:Math.floor(this._videoMeta.refSampleDuration);if(d){var l=new j(x,u,S,y.dts,!0);l.fileposition=y.fileposition,Q.appendSyncPoint(l)}a.push({dts:x,pts:u,cts:Y,units:y.units,size:y.length,isKeyframe:d,duration:S,originalDts:w,flags:{isLeading:0,dependsOn:d?2:1,isDependedOn:d?1:0,hasRedundancy:0,isNonSync:d?0:1}})}(n=new Uint8Array(D))[0]=D>>>24&255,n[1]=D>>>16&255,n[2]=D>>>8&255,n[3]=255&D,n.set(s.types.mdat,4);for(var z=0;z=500?this.currentKBps:0}},{key:"averageKBps",get:function(){var A=(this._now()-this._firstCheckpoint)/1e3;return this._totalBytes/A/1024}}])&&z(M.prototype,t),g&&z(M,g),A}();function f(A,M){for(var t=0;t=15048,M=!C.msedge||A;return self.fetch&&self.ReadableStream&&M}catch(A){return!1}};var I=g.prototype;return I.destroy=function(){this.isWorking()&&this.abort(),A.prototype.destroy.call(this)},I.fetchStream=function(){var A=this,M=this._fetchParam;this._retryConnectTimes++,this._triggerReconnectingNotify&&(this._triggerReconnectingNotify=!1,this._onDataArrival&&this._onDataArrival(null,-this._retryConnectTimes,0)),this._triggerReconnectSuccessNotify=!0,self.fetch(this._fetchUrl,M).then((function(M){if(A._requestAbort)return A._requestAbort=!1,void(A._status=O);if(M.ok&&M.status>=200&&M.status<=299){if(M.url!==A._fetchUrl&&A._onURLRedirect){console.log("full");var t=A._seekHandler.removeURLParameters(M.url);A._onURLRedirect(t)}var g=M.headers.get("Content-Length");return null!=g&&(A._contentLength=parseInt(g),0!==A._contentLength&&A._onContentLengthKnown&&A._onContentLengthKnown(A._contentLength)),A._triggerReconnectingNotify=!0,A._pump.call(A,M.body.getReader())}A._status=R,A._onError,e.d("fetch-stream: code:"+M.status+" msg:"+M.statusText),A._callbackFunc({code:M.status,msg:M.statusText});var I=A;e.i(3e3).then((function(){console.log("Reconnecting..."),I.fetchStream()}))})).catch((function(M){if(A._status=R,!A._onError)throw M;A._onError(p,{code:-1,msg:M.message});var t=A;e.i(3e3).then((function(){t.fetchStream()}))}))},I.open=function(A,M){this._dataSource=A,this._range=M;var t=A.url;this._config.reuseRedirectedURL&&null!=A.redirectedURL&&(t=A.redirectedURL);var g=this._seekHandler.getConfig(t,M),I=new self.Headers;if("object"==typeof g.headers){var e=g.headers;for(var i in e)e.hasOwnProperty(i)&&I.append(i,e[i])}var T={method:"GET",headers:I,mode:"cors",cache:"default",referrerPolicy:"no-referrer-when-downgrade"};if("object"==typeof this._config.headers)for(var E in this._config.headers)I.append(E,this._config.headers[E]);!1===A.cors&&(T.mode="same-origin"),A.withCredentials&&(T.credentials="include"),A.referrerPolicy&&(T.referrerPolicy=A.referrerPolicy),this._status=F,this._fetchUrl=g.url,this._fetchParam=T,this.fetchStream()},I.abort=function(){this._requestAbort=!0},I._pump=function(A){var M=this;return A.read().then((function(t){if(t.done)if(null!==M._contentLength&&M._receivedLength0?(e.h(!1),M._onComplete(M._range.from,M._range.from+M._receivedLength-1)):M._onComplete(-1,-1)}else M._status=k,M._onComplete&&(null!==M._contentLength&&M._receivedLength===M._contentLength&&M._receivedLength>0?(e.h(!1),M._onComplete(M._range.from,M._range.from+M._receivedLength-1)):M._onComplete(-1,-1));else{if(!0===M._requestAbort)return M._requestAbort=!1,M._status=k,A.cancel();M._status=m;var i=t.value.buffer,T=M._range.from+M._receivedLength;M._receivedLength+=i.byteLength,M._onDataArrival&&M._onDataArrival(i,T,M._receivedLength),M._triggerReconnectSuccessNotify&&(M._triggerReconnectSuccessNotify=!1,M._onDataArrival&&(M._onDataArrival(null,M._retryConnectTimes,0),M._retryConnectTimes++)),M._pump(A)}})).catch((function(A){console.log("fetch-stream-loader:: errCode:"+A.code+" msg:"+A.message),M._onComplete(-1,-1)}))},g}(H);var v=function(A){var M,t;function g(M,t){var g;return(g=A.call(this,"xhr-moz-chunked-loader")||this).TAG="MozChunkedLoader",g._seekHandler=M,g._config=t,g._needStash=!0,g._xhr=null,g._requestAbort=!1,g._contentLength=null,g._receivedLength=0,g}t=A,(M=g).prototype=Object.create(t.prototype),M.prototype.constructor=M,M.__proto__=t,g.isSupported=function(){try{var A=new XMLHttpRequest;return A.open("GET","https://example.com",!0),A.responseType="moz-chunked-arraybuffer","moz-chunked-arraybuffer"===A.responseType}catch(A){return n.a.w("MozChunkedLoader",A.message),!1}};var I=g.prototype;return I.destroy=function(){this.isWorking()&&this.abort(),this._xhr&&(this._xhr.onreadystatechange=null,this._xhr.onprogress=null,this._xhr.onloadend=null,this._xhr.onerror=null,this._xhr=null),A.prototype.destroy.call(this)},I.open=function(A,M){this._dataSource=A,this._range=M;var t=A.url;this._config.reuseRedirectedURL&&null!=A.redirectedURL&&(t=A.redirectedURL);var g=this._seekHandler.getConfig(t,M);this._requestURL=g.url;var I=this._xhr=new XMLHttpRequest;if(I.open("GET",g.url,!0),I.responseType="moz-chunked-arraybuffer",I.onreadystatechange=this._onReadyStateChange.bind(this),I.onprogress=this._onProgress.bind(this),I.onloadend=this._onLoadEnd.bind(this),I.onerror=this._onXhrError.bind(this),A.withCredentials&&(I.withCredentials=!0),"object"==typeof g.headers){var e=g.headers;for(var i in e)e.hasOwnProperty(i)&&I.setRequestHeader(i,e[i])}if("object"==typeof this._config.headers){var T=this._config.headers;for(var E in T)T.hasOwnProperty(E)&&I.setRequestHeader(E,T[E])}this._status=F,I.send()},I.abort=function(){this._requestAbort=!0,this._xhr&&this._xhr.abort(),this._status=k},I._onReadyStateChange=function(A){var M=A.target;if(2===M.readyState){if(null!=M.responseURL&&M.responseURL!==this._requestURL&&this._onURLRedirect){var t=this._seekHandler.removeURLParameters(M.responseURL);this._onURLRedirect(t)}if(0!==M.status&&(M.status<200||M.status>299)){if(this._status=R,!this._onError)throw new Y.d("MozChunkedLoader: Http code invalid, "+M.status+" "+M.statusText);this._onError(b,{code:M.status,msg:M.statusText})}else this._status=m}},I._onProgress=function(A){if(this._status!==R){null===this._contentLength&&null!==A.total&&0!==A.total&&(this._contentLength=A.total,this._onContentLengthKnown&&this._onContentLengthKnown(this._contentLength));var M=A.target.response,t=this._range.from+this._receivedLength;this._receivedLength+=M.byteLength,this._onDataArrival&&this._onDataArrival(M,t,this._receivedLength)}},I._onLoadEnd=function(A){!0!==this._requestAbort?this._status!==R&&(this._status=k,this._onComplete&&this._onComplete(this._range.from,this._range.from+this._receivedLength-1)):this._requestAbort=!1},I._onXhrError=function(A){this._status=R;var M=0,t=null;if(this._contentLength&&A.loaded=this._contentLength&&(t=this._range.from+this._contentLength-1),this._currentRequestRange={from:M,to:t},this._internalOpen(this._dataSource,this._currentRequestRange)},T._internalOpen=function(A,M){this._lastTimeLoaded=0;var t=A.url;this._config.reuseRedirectedURL&&(null!=this._currentRedirectedURL?t=this._currentRedirectedURL:null!=A.redirectedURL&&(t=A.redirectedURL));var g=this._seekHandler.getConfig(t,M);this._currentRequestURL=g.url;var I=this._xhr=new XMLHttpRequest;if(I.open("GET",g.url,!0),I.responseType="arraybuffer",I.onreadystatechange=this._onReadyStateChange.bind(this),I.onprogress=this._onProgress.bind(this),I.onload=this._onLoad.bind(this),I.onerror=this._onXhrError.bind(this),A.withCredentials&&(I.withCredentials=!0),"object"==typeof g.headers){var e=g.headers;for(var i in e)e.hasOwnProperty(i)&&I.setRequestHeader(i,e[i])}if("object"==typeof this._config.headers){var T=this._config.headers;for(var E in T)T.hasOwnProperty(E)&&I.setRequestHeader(E,T[E])}I.send()},T.abort=function(){this._requestAbort=!0,this._internalAbort(),this._status=k},T._internalAbort=function(){this._xhr&&(this._xhr.onreadystatechange=null,this._xhr.onprogress=null,this._xhr.onload=null,this._xhr.onerror=null,this._xhr.abort(),this._xhr=null)},T._onReadyStateChange=function(A){var M=A.target;if(2===M.readyState){if(null!=M.responseURL){var t=this._seekHandler.removeURLParameters(M.responseURL);M.responseURL!==this._currentRequestURL&&t!==this._currentRedirectedURL&&(this._currentRedirectedURL=t,this._onURLRedirect&&this._onURLRedirect(t))}if(M.status>=200&&M.status<=299){if(this._waitForTotalLength)return;this._status=m}else{if(this._status=R,!this._onError)throw new Y.d("RangeLoader: Http code invalid, "+M.status+" "+M.statusText);this._onError(b,{code:M.status,msg:M.statusText})}}},T._onProgress=function(A){if(this._status!==R){if(null===this._contentLength){var M=!1;if(this._waitForTotalLength){this._waitForTotalLength=!1,this._totalLengthReceived=!0,M=!0;var t=A.total;this._internalAbort(),null!=t&0!==t&&(this._totalLength=t)}if(-1===this._range.to?this._contentLength=this._totalLength-this._range.from:this._contentLength=this._range.to-this._range.from+1,M)return void this._openSubRange();this._onContentLengthKnown&&this._onContentLengthKnown(this._contentLength)}var g=A.loaded-this._lastTimeLoaded;this._lastTimeLoaded=A.loaded,this._speedSampler.addBytes(g)}},T._normalizeSpeed=function(A){var M=this._chunkSizeKBList,t=M.length-1,g=0,I=0,e=t;if(A=M[g]&&A=3&&(M=this._speedSampler.currentKBps)),0!==M){var t=this._normalizeSpeed(M);this._currentSpeedNormalized!==t&&(this._currentSpeedNormalized=t,this._currentChunkSizeKB=t)}var g=A.target.response,I=this._range.from+this._receivedLength;this._receivedLength+=g.byteLength;var e=!1;null!=this._contentLength&&this._receivedLength0&&this._receivedLength0)for(var e=t.split("&"),i=0;i0;T[0]!==this._startName&&T[0]!==this._endName&&(E&&(I+="&"),I+=e[i])}return 0===I.length?M:M+"?"+I},A}();var q=function(A){var M,t;function g(M,t){var g;return(g=A.call(this,"m3u8-loader")||this).TAG="M3U8Loader",g._needStash=!0,g.url="",g.startPlayTime=0,g._requestAbort=!1,g._receivedLength=0,g._callbackFunc=t.callbackFunc,g.getNextFileFlag=!0,g.pauseDisplay=!1,g.totalDuration=0,g.seeking=!1,g}t=A,(M=g).prototype=Object.create(t.prototype),M.prototype.constructor=M,M.__proto__=t,g.isSupported=function(){return!0};var I=g.prototype;return I.destroy=function(){this.abort(),A.prototype.destroy.call(this)},I.GetM3u8TotalDuration=function(){return this.totalDuration},I.createDownloadWorker=function(){var A;if(!this.downloadWorker)if("undefined"!=typeof Worker)try{A=this.downloadWorker=i(223),this.onwmsg=this.onWorkerMessage.bind(this);var M={cmd:e.u,url:this.url,time:this.startPlayTime};A.postMessage(M),A.addEventListener("message",this.onwmsg),A.onerror=function(A){}}catch(A){console.log("Create download Worker fail: "+A),this.downloadWorker=void 0}else console.log("Unsupport worker...")},I.setStartPlaySecs=function(A){this.startPlayTime=A},I.open=function(A){try{this.url=A.url,this.createDownloadWorker(),this._status=F}catch(A){this._status=R;var M={code:A.code,msg:A.message};if(!this._onError)throw new Y.d(M.msg);this._onError(p,M)}},I.abort=function(){var A={cmd:e.k};this.downloadWorker&&void 0!==this.downloadWorker&&this.downloadWorker.postMessage(A),this._status=k},I._onWebSocketOpen=function(A){this._status=m},I._onWebSocketClose=function(A){!0!==this._requestAbort?(this._status=k,this._onComplete&&this._onComplete(0,this._receivedLength-1)):this._requestAbort=!1},I.seek=function(A,M){this.pauseDisplay=!1;var t={cmd:e.z,type:M,time:A};this.downloadWorker&&void 0!==this.downloadWorker&&(this.downloadWorker.postMessage(t),this.seeking=!0)},I.pause=function(){this.pauseDisplay=!0;var A={cmd:e.v};this.downloadWorker&&void 0!==this.downloadWorker&&this.downloadWorker.postMessage(A)},I.resume=function(){this.pauseDisplay=!1;var A={cmd:e.y};this.downloadWorker&&void 0!==this.downloadWorker&&this.downloadWorker.postMessage(A)},I.onWorkerMessage=function(A){var M=A.data;switch(M.cmd){case e.u:var t={cmd:e.q,t:0};this.downloadWorker.postMessage(t);break;case e.r:this.getNextFileFlag=!0;break;case e.p:this.playComplete=!0;break;case e.n:console.log("disconnect..");t={cmd:e.w};this.downloadWorker.postMessage(t);break;case e.k:M.workerId==e.b?(this.downloadWorker.terminate(),this.downloadWorker=null):M.workerId==e.a?(this.decodeWorker.terminate(),this.decodeWorker=null,this.soundPlayer&&(this.soundPlayer.destroy(),delete this.soundPlayer,this.soundPlayer=null),this.webGLPlayer&&this.webGLPlayer.renderVideoFrame(null,0,0)):M.workerId==e.e&&(this.renderWorker.terminate(),this.renderWorker=null);break;case e.o:if(M.workerId==e.b){var g=M.dat,I=this._receivedLength;this._receivedLength+=g.byteLength,this._onDataArrival&&this._onDataArrival(g,I,this._receivedLength);break}if(M.workerId==e.a){if(this.pauseDisplay)break;1==M.mediaType?(this.canvas.width=M.width,this.canvas.height=M.height,this.webGLPlayer.renderVideoFrame(M.dat,M.width,M.height),this.OnUpdatePlayInfo(M.playTimeSec,M.frameNum)):2==M.mediaType&&(null==this.soundPlayer&&this.OpenAudioPlayer(M.fmt,M.ch,M.samplerate),null!=this.soundPlayer&&this.soundPlayer.play(M.dat))}break;case e.s:this.totalDuration=M.duration,e.h(M.live);break;case e.q:this.pauseDisplay||this.getNextFileFlag&&(this.downloadWorker.postMessage(M),this.getNextFileFlag=!1);break;case e.z:this.seeking=!1;break;case 410:this._callbackFunc(M);break;case e.x:this._onDataArrival&&(this._receivedLength=0,this._onDataArrival(null,M.fileSequence,0))}},I._dispatchArrayBuffer=function(A){var M=A,t=this._receivedLength;this._receivedLength+=M.byteLength,this._onDataArrival&&this._onDataArrival(M,t,this._receivedLength)},I._onWebSocketError=function(A){this._status=R;var M={code:A.code,msg:A.message};if(!this._onError)throw new Y.d(M.msg);this._onError(p,M)},g}(H);function _(A,M){for(var t=0;t0&&(this._stashInitialSize=M.stashInitialSize),this._stashUsed=0,this._stashSize=this._stashInitialSize,this._bufferSize=3145728,this._stashBuffer=new ArrayBuffer(this._bufferSize),this._stashByteStart=0,this._enableStash=!0,!1===M.enableStashBuffer&&(this._enableStash=!1),"soft"===M.decodeType&&(this._enableStash=!1),this._loader=null,this._loaderClass=null,this._seekHandler=null,this._dataSource=A,this._isWebSocketURL=/wss?:\/\/(.+?)/.test(A.url),this._refTotalLength=A.filesize?A.filesize:null,this._totalLength=this._refTotalLength,this._fullRequestFlag=!1,this._currentRange=null,this._redirectedURL=null,this._speedNormalized=0,this._speedSampler=new U,this._speedNormalizeList=[64,128,256,384,512,768,1024,1536,2048,3072,4096],this._isEarlyEofReconnecting=!1,this._paused=!1,this._resumeFrom=0,this._onDataArrival=null,this._onSeeked=null,this._onError=null,this._onComplete=null,this._onRedirect=null,this._onRecoveredEarlyEof=null,this._selectSeekHandler(),this._selectLoader(),this._createLoader(g)}var M,t,g,I=A.prototype;return I.destroy=function(){this._loader.isWorking()&&this._loader.abort(),this._loader.destroy(),this._loader=null,this._loaderClass=null,this._dataSource=null,this._stashBuffer=null,this._stashUsed=this._stashSize=this._bufferSize=this._stashByteStart=0,this._currentRange=null,this._speedSampler=null,this._isEarlyEofReconnecting=!1,this._onDataArrival=null,this._onSeeked=null,this._onError=null,this._onComplete=null,this._onRedirect=null,this._onRecoveredEarlyEof=null,this._extraData=null},I.isWorking=function(){return this._loader&&this._loader.isWorking()&&!this._paused},I.isPaused=function(){return this._paused},I._selectSeekHandler=function(){var A=this._config;if("range"===A.seekType)this._seekHandler=new Z(this._config.rangeLoadZeroStart);else if("param"===A.seekType){var M=A.seekParamStart||"bstart",t=A.seekParamEnd||"bend";this._seekHandler=new K(M,t)}else{if("custom"!==A.seekType)throw new Y.b("Invalid seekType in config: "+A.seekType);if("function"!=typeof A.customSeekHandler)throw new Y.b("Custom seekType specified in config but invalid customSeekHandler!");this._seekHandler=new A.customSeekHandler}},I._selectLoader=function(){if(null!=this._config.customLoader)this._loaderClass=this._config.customLoader;else if("m3u8"===this._config.streamType)this._loaderClass=q;else if(this._isWebSocketURL)this._loaderClass=P;else if(X.isSupported())this._loaderClass=X;else if(v.isSupported())this._loaderClass=v;else{if(!W.isSupported())throw new Y.d("Your browser doesn't support xhr with arraybuffer responseType!");this._loaderClass=W}},I._IsM3u8Loader=function(){return this._loaderClass==q},I._GetM3u8Loader=function(){return this._loaderClass==q?this._loader:null},I._createLoader=function(A){this._loader=new this._loaderClass(this._seekHandler,this._config),!1===this._loader.needStashBuffer&&(this._enableStash=!1),this._loaderClass==q&&this._loader.setStartPlaySecs(A),this._loader.onContentLengthKnown=this._onContentLengthKnown.bind(this),this._loader.onURLRedirect=this._onURLRedirect.bind(this),this._loader.onDataArrival=this._onLoaderChunkArrival.bind(this),this._loader.onComplete=this._onLoaderComplete.bind(this),this._loader.onError=this._onLoaderError.bind(this)},I.open=function(A){this._currentRange={from:0,to:-1},A&&(this._currentRange.from=A),this._speedSampler.reset(),A||(this._fullRequestFlag=!0),this._loader.open(this._dataSource,Object.assign({},this._currentRange))},I.abort=function(){this._loader.abort(),this._paused&&(this._paused=!1,this._resumeFrom=0)},I.pause=function(){this.isWorking()&&(this._loader.abort(),0!==this._stashUsed?(this._resumeFrom=this._stashByteStart,this._currentRange.to=this._stashByteStart-1):this._resumeFrom=this._currentRange.to+1,this._stashUsed=0,this._stashByteStart=0,this._paused=!0)},I.resume=function(){if(this._paused){this._paused=!1;var A=this._resumeFrom;this._resumeFrom=0,this._internalSeek(A,!0)}},I.seek=function(A){this._paused=!1,this._stashUsed=0,this._stashByteStart=0,this._internalSeek(A,!0)},I._internalSeek=function(A,M){this._loader.isWorking()&&this._loader.abort(),this._flushStashBuffer(M),this._loader.destroy(),this._loader=null;var t={from:A,to:-1};this._currentRange={from:t.from,to:-1},this._speedSampler.reset(),this._stashSize=this._stashInitialSize,this._createLoader(),this._loader.open(this._dataSource,t),this._onSeeked&&this._onSeeked()},I.updateUrl=function(A){if(!A||"string"!=typeof A||0===A.length)throw new Y.b("Url must be a non-empty string!");this._dataSource.url=A},I._expandBuffer=function(A){for(var M=this._stashSize;M+10485760){var g=new Uint8Array(this._stashBuffer,0,this._stashUsed);new Uint8Array(t,0,M).set(g,0)}this._stashBuffer=t,this._bufferSize=M}},I._normalizeSpeed=function(A){var M=this._speedNormalizeList,t=M.length-1,g=0,I=0,e=t;if(A=M[g]&&A=512&&A<=1024?Math.floor(1.5*A):2*A)>8192&&(M=8192);var t=1024*M+1048576;this._bufferSize1&&this._onComplete&&this._onComplete(-1)):this._onDataArrival(A,M):(this._currentRange.to=M+A.byteLength-1,this._onDataArrival(A,M))},I._onURLRedirect=function(A){this._redirectedURL=A,this._onRedirect&&this._onRedirect(A)},I._onContentLengthKnown=function(A){A&&this._fullRequestFlag&&(this._totalLength=A,this._fullRequestFlag=!1)},I._onLoaderChunkArrival=function(A,M,t){if(!this._onDataArrival)throw new Y.a("IOController: No existing consumer (onDataArrival) callback!");if(!this._paused)if(this._isEarlyEofReconnecting&&(this._isEarlyEofReconnecting=!1,this._onRecoveredEarlyEof&&this._onRecoveredEarlyEof()),null===A&&t<1)this._dispatchChunks(A,M);else{this._speedSampler.addBytes(A.byteLength);var g=this._speedSampler.lastSecondKBps;if(0!==g){var I=this._normalizeSpeed(g);this._speedNormalized!==I&&(this._speedNormalized=I,this._adjustStashSize(I))}if(this._enableStash)if(0===this._stashUsed&&0===this._stashByteStart&&(this._stashByteStart=M),this._stashUsed+A.byteLength<=this._stashSize){new Uint8Array(this._stashBuffer,0,this._stashSize).set(new Uint8Array(A),this._stashUsed),this._stashUsed+=A.byteLength}else{var e=new Uint8Array(this._stashBuffer,0,this._bufferSize);if(this._stashUsed>0){var i=this._stashBuffer.slice(0,this._stashUsed),T=this._dispatchChunks(i,this._stashByteStart);if(T0){var E=new Uint8Array(i,T);e.set(E,0),this._stashUsed=E.byteLength,this._stashByteStart+=T}}else this._stashUsed=0,this._stashByteStart+=T;this._stashUsed+A.byteLength>this._bufferSize&&(this._expandBuffer(this._stashUsed+A.byteLength),e=new Uint8Array(this._stashBuffer,0,this._bufferSize)),e.set(new Uint8Array(A),this._stashUsed),this._stashUsed+=A.byteLength}else{var N=this._dispatchChunks(A,M);if(Nthis._bufferSize&&(this._expandBuffer(n),e=new Uint8Array(this._stashBuffer,0,this._bufferSize)),e.set(new Uint8Array(A,N),0),this._stashUsed+=n,this._stashByteStart=M+N}}}else if(0===this._stashUsed){var D=this._dispatchChunks(A,M);if(Dthis._bufferSize&&this._expandBuffer(C),new Uint8Array(this._stashBuffer,0,this._bufferSize).set(new Uint8Array(A,D),0),this._stashUsed+=C,this._stashByteStart=M+D}}else{this._stashUsed+A.byteLength>this._bufferSize&&this._expandBuffer(this._stashUsed+A.byteLength);var r=new Uint8Array(this._stashBuffer,0,this._bufferSize);r.set(new Uint8Array(A),this._stashUsed),this._stashUsed+=A.byteLength;var c=this._dispatchChunks(this._stashBuffer.slice(0,this._stashUsed),this._stashByteStart);if(c0){var o=new Uint8Array(this._stashBuffer,c);r.set(o,0)}this._stashUsed-=c,this._stashByteStart+=c}}},I._flushStashBuffer=function(A){if(this._stashUsed>0){var M=this._stashBuffer.slice(0,this._stashUsed),t=this._dispatchChunks(M,this._stashByteStart),g=M.byteLength-t;if(t0){var I=new Uint8Array(this._stashBuffer,0,this._bufferSize),e=new Uint8Array(M,t);I.set(e,0),this._stashUsed=e.byteLength,this._stashByteStart+=t}return 0}n.a.w(this.TAG,g+" bytes unconsumed data remain when flush buffer, dropped")}return this._stashUsed=0,this._stashByteStart=0,g}return 0},I._onLoaderComplete=function(A,M){this._flushStashBuffer(!0),A!=M||-1!=M?this._onComplete&&this._onComplete(this._extraData):this._onComplete&&this._onComplete(-1)},I._onLoaderError=function(A,M){switch(n.a.e(this.TAG,"Loader error, code = "+M.code+", msg = "+M.msg),this._flushStashBuffer(!1),this._isEarlyEofReconnecting&&(this._isEarlyEofReconnecting=!1,A=J),A){case G:if(!this._config.isLive&&this._totalLength){var t=this._currentRange.to+1;return void(t0)return;var g=0;if(M>0)this._demuxer.bindDataSource(this._ioctl),this._demuxer.timestampBase=this._mediaDataSource.segments[this._currentSegmentIndex].timestampBase,this._demuxer.append(A,M,0),g=A.length;else{this._demuxer=new MA.a(this._config,this._callbackMediaData,this._callbackUserPtr),this._remuxer||(this._remuxer=new S(this._config));var I=this._mediaDataSource;null==I.duration||isNaN(I.duration)||(this._demuxer.overridedDuration=I.duration),"boolean"==typeof I.hasAudio&&(this._demuxer.overridedHasAudio=I.hasAudio),"boolean"==typeof I.hasVideo&&(this._demuxer.overridedHasVideo=I.hasVideo),this._demuxer.timestampBase=I.segments[this._currentSegmentIndex].timestampBase,this._demuxer.onError=this._onDemuxException.bind(this),this._demuxer.onMediaInfo=this._onMediaInfo.bind(this),this._demuxer.onMetaDataArrived=this._onMetaDataArrived.bind(this),this._demuxer.onScriptDataArrived=this._onScriptDataArrived.bind(this),this._remuxer.bindDataSource(this._demuxer.bindDataSource(this._ioctl)),this._remuxer.onInitSegment=this._onRemuxerInitSegmentArrival.bind(this),this._remuxer.onMediaSegment=this._onRemuxerMediaSegmentArrival.bind(this),this._demuxer.append(A,M,0),g=A.length}return g}var e=null,i=0;if(null==A&&M<0)this._emitter.emit(AA.RECONNECT_ING,M);else if(!(null==A&&M>0)){if(M>0)this._demuxer.bindDataSource(this._ioctl),this._demuxer.timestampBase=this._mediaDataSource.segments[this._currentSegmentIndex].timestampBase,i=this._demuxer.parseChunks(A,M);else if((e=a.a.probe(A)).match){this._demuxer=new a.a(e,this._config,this._callbackMediaData,this._callbackUserPtr),this._remuxer||(this._remuxer=new S(this._config));var T=this._mediaDataSource;null==T.duration||isNaN(T.duration)||(this._demuxer.overridedDuration=T.duration),"boolean"==typeof T.hasAudio&&(this._demuxer.overridedHasAudio=T.hasAudio),"boolean"==typeof T.hasVideo&&(this._demuxer.overridedHasVideo=T.hasVideo),this._demuxer.timestampBase=T.segments[this._currentSegmentIndex].timestampBase,this._demuxer.onError=this._onDemuxException.bind(this),this._demuxer.onMediaInfo=this._onMediaInfo.bind(this),this._demuxer.onMetaDataArrived=this._onMetaDataArrived.bind(this),this._demuxer.onScriptDataArrived=this._onScriptDataArrived.bind(this),this._remuxer.bindDataSource(this._demuxer.bindDataSource(this._ioctl)),this._remuxer.onInitSegment=this._onRemuxerInitSegmentArrival.bind(this),this._remuxer.onMediaSegment=this._onRemuxerMediaSegmentArrival.bind(this),i=this._demuxer.parseChunks(A,M),this._reconnectTimes++,this._reconnectTimes>0&&this._emitter.emit(AA.RECONNECT_SUCCESS,this._reconnectTimes)}else e=null,n.a.e(this.TAG,"Non-FLV, Unsupported media type!"),Promise.resolve().then((function(){t._internalAbort()})),this._emitter.emit(AA.DEMUX_ERROR,l.a.FORMAT_UNSUPPORTED,"Non-FLV, Unsupported media type"),i=0;return i}},M._GetM3u8Loader=function(){if(null==this._ioctl)return null;var A=this._ioctl._GetM3u8Loader();return A||null},M._GetM3u8TotalDuration=function(){if(null==this._ioctl)return 0;var A=this._ioctl._GetM3u8Loader();return A?A.GetM3u8TotalDuration():0},M._onMediaInfo=function(A){var M=this;null==this._mediaInfo&&(this._mediaInfo=Object.assign({},A),this._mediaInfo.keyframesIndex=null,this._mediaInfo.segments=[],this._mediaInfo.segmentCount=this._mediaDataSource.segments.length,Object.setPrototypeOf(this._mediaInfo,Q.a.prototype));var t=Object.assign({},A);Object.setPrototypeOf(t,Q.a.prototype),this._mediaInfo.segments[this._currentSegmentIndex]=t,this._reportSegmentMediaInfo(this._currentSegmentIndex),null!=this._pendingSeekTime&&Promise.resolve().then((function(){var A=M._pendingSeekTime;M._pendingSeekTime=null,M.seek(A)}))},M._onMetaDataArrived=function(A){this._emitter.emit(AA.METADATA_ARRIVED,A)},M._onScriptDataArrived=function(A){this._emitter.emit(AA.SCRIPTDATA_ARRIVED,A)},M._onIOSeeked=function(){this._remuxer.insertDiscontinuity()},M._onIOComplete=function(A){var M=A+1;if(this._ioctl&&this._ioctl._IsM3u8Loader()){this._ioctl._GetM3u8Loader();e.c()&&(console.log("M3u8: Reconnect Success"),this._emitter.emit(AA.RECONNECT_SUCCESS,2),this._demuxer&&(this._demuxer.destroy(),this._demuxer=null),this._remuxer&&(this._remuxer.destroy(),this._remuxer=null),this._ioctl&&(this._ioctl.destroy(),this._ioctl=null),this.start())}else if(M0&&t[0].originalDts===g&&(g=t[0].pts),this._emitter.emit(AA.RECOMMEND_SEEKPOINT,g)}},M._enableStatisticsReporter=function(){null==this._statisticsReporter&&(this._statisticsReporter=self.setInterval(this._reportStatisticsInfo.bind(this),this._config.statisticsInfoReportInterval))},M._disableStatisticsReporter=function(){this._statisticsReporter&&(self.clearInterval(this._statisticsReporter),this._statisticsReporter=null)},M._reportSegmentMediaInfo=function(A){var M=this._mediaInfo.segments[A],t=Object.assign({},M);t.duration=this._mediaInfo.duration,t.segmentCount=this._mediaInfo.segmentCount,delete t.segments,delete t.keyframesIndex,this._emitter.emit(AA.MEDIA_INFO,t)},M._reportStatisticsInfo=function(){var A={};null!=this._ioctl&&(A.url=this._ioctl.currentURL,A.hasRedirect=this._ioctl.hasRedirect,A.hasRedirect&&(A.redirectedURL=this._ioctl.currentRedirectedURL),A.speed=this._ioctl.currentSpeed,A.loaderType=this._ioctl.loaderType,A.currentSegmentIndex=this._currentSegmentIndex,A.totalSegmentCount=this._mediaDataSource.segments.length,this._emitter.emit(AA.STATISTICS_INFO,A))},A}(),gA=function(){function A(){}return A.install=function(){Object.setPrototypeOf=Object.setPrototypeOf||function(A,M){return A.__proto__=M,A},Object.assign=Object.assign||function(A){if(null==A)throw new TypeError("Cannot convert undefined or null to object");for(var M=Object(A),t=1;t-1||i.indexOf("Adr")>-1,this.isiOS=!!i.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/),this.isiPad=!1;var T=navigator.userAgent;if(("iPad"==T.match(/iPad/i)||T.indexOf("Macintosh;")>-1&&T.indexOf("AppleWebKit/"))&&(this.isiPad=!0),this._callbackUserPtr=I,this._callbackWasmTypePtr=e,M.enableWorker&&"undefined"!=typeof Worker)try{var E=t(115);this._worker=E(eA),this._workerDestroying=!1,this._worker.addEventListener("message",this._onWorkerMessage.bind(this)),this._worker.postMessage({cmd:"init",param:[A,M]}),this.e={onLoggingConfigChanged:this._onLoggingConfigChanged.bind(this)},B.registerListener(this.e.onLoggingConfigChanged),this._worker.postMessage({cmd:"logging_config",param:B.getConfig()})}catch(t){n.a.e(this.TAG,"Error while initialize transmuxing worker, fallback to inline transmuxing"),this._worker=null,this._controller=new tA(A,M)}else this._controller=new tA(A,M);if(this._controller){var D=this._controller;D.on(AA.IO_ERROR,this._onIOError.bind(this)),D.on(AA.DEMUX_ERROR,this._onDemuxError.bind(this)),D.on(AA.INIT_SEGMENT,this._onInitSegment.bind(this)),D.on(AA.MEDIA_SEGMENT,this._onMediaSegment.bind(this)),D.on(AA.LOADING_COMPLETE,this._onLoadingComplete.bind(this)),D.on(AA.RECOVERED_EARLY_EOF,this._onRecoveredEarlyEof.bind(this)),D.on(AA.MEDIA_INFO,this._onMediaInfo.bind(this)),D.on(AA.METADATA_ARRIVED,this._onMetaDataArrived.bind(this)),D.on(AA.SCRIPTDATA_ARRIVED,this._onScriptDataArrived.bind(this)),D.on(AA.STATISTICS_INFO,this._onStatisticsInfo.bind(this)),D.on(AA.RECOMMEND_SEEKPOINT,this._onRecommendSeekpoint.bind(this)),D.on(AA.RECONNECT_ING,this._onReconnecting.bind(this)),D.on(AA.RECONNECT_SUCCESS,this._onReconnectSuccess.bind(this))}}var M=A.prototype;return M.destroy=function(){this._worker?this._workerDestroying||(this._workerDestroying=!0,this._worker.postMessage({cmd:"destroy"}),B.removeListener(this.e.onLoggingConfigChanged),this.e=null):(this._controller.destroy(),this._controller=null),this._emitter.removeAllListeners(),this._emitter=null,this.soundPlayer&&(this.soundPlayer.destroy(),delete this.soundPlayer,this.soundPlayer=null)},M.on=function(A,M){this._emitter.addListener(A,M)},M.off=function(A,M){this._emitter.removeListener(A,M)},M.hasWorker=function(){return null!=this._worker},M.OpenAudioPlayer=function(A,M,t){if(null==this.soundPlayer){1;var g="16bitInt";g="16bitInt",this.soundPlayer=new T.a,this.soundPlayer.init({encoding:g,channels:M,sampleRate:t,flushingTime:1e3}),this.initAudioPlayer=!0}},M.createDecodeWorker=function(){var A;if(this.decodeWorker=null,"undefined"!=typeof Worker)try{A=this.decodeWorker=i(60),this.onwmsg=this.onWorkerMessage.bind(this);var M={cmd:e.u,url:e.f(),isFlv:!this._controller._isM3u8};A.postMessage(M),A.addEventListener("message",this.onwmsg),A.onerror=function(A){}}catch(A){console.log("Create Decode Worker fail: "+A),this.w=void 0}else this.decodeWorker=null;this.decodeWorker&&null==this._webGLPlayer&&(this._webGLPlayer=new I(this._mediaCanvas),this._webGLPlayer.initGL({preserveDrawingBuffer:!1}))},M.getCurrentPlayTime=function(){return this.currentPlayTime},M.getTotalDuration=function(){return this._controller?this._controller._GetM3u8TotalDuration():this.totalDuration},M._onMediaDataCallback=function(A,M,t,g,I,i,T,E,N,n){if(g<1&&!M&&1===T)console.log("_onMediaDataCallback... Exception...");else if(M){null==A.decodeWorker&&(A.createDecodeWorker(),A._callbackWasmTypePtr&&A._callbackWasmTypePtr(A._callbackUserPtr,!0));var D=new Uint8Array(i);if(A.decodeWorker){var C={cmd:e.o,mediaType:t,codecId:g,frameType:I,samplerate:E,channelNum:N,bitPerSample:n,playTimeSec:E,dat:D};A.decodeWorker.postMessage(C,[C.dat.buffer])}}else A._callbackWasmTypePtr&&A._callbackWasmTypePtr(A._callbackUserPtr,!1)},M.onWorkerMessage=function(A){var M=A.data;switch(M.cmd){case e.n:var t={cmd:e.w};this.downloadWorker.postMessage(t);break;case e.k:M.workerId==e.b?(this.downloadWorker.terminate(),this.downloadWorker=null):M.workerId==e.a?(this.decodeWorker.terminate(),this.decodeWorker=null,this.soundPlayer&&(this.soundPlayer.destroy(),delete this.soundPlayer,this.soundPlayer=null),this._webGLPlayer&&this._webGLPlayer.renderVideoFrame(null,0,0)):M.workerId==e.e&&(this.renderWorker.terminate(),this.renderWorker=null);break;case e.m:this.enableAudio&&this.openAudio();break;case e.o:if(M.workerId==e.b)this.decodeWorker;else if(M.workerId==e.a){if(this.pauseDisplay)break;if(1==M.mediaType){if(this._mediaCanvas.width=M.width,this._mediaCanvas.height=M.height,M.playTimeSec>0){var g=Math.floor((M.playTimeSec+1e3)/1e3);this.currentPlayTime!==g&&(this.initDuration>0&&(this.currentPlayTime+=g-this.initDuration),this.initDuration!==g&&(this.initDuration=g,this._emitter&&this._emitter.emit(iA.CB_PLAY_INFO)))}this._webGLPlayer.renderVideoFrame(M.dat,M.width,M.height)}else 2==M.mediaType&&(null==this.soundPlayer&&(null!=this.soundPlayer||this.isiOS||this.isiPad?(this.audioFmt=M.fmt,this.audioSamplerate=M.samplerate,this.audioChannelNum=M.ch,44100==this.audioSamplerate&&(this.audioFmt=1)):this.OpenAudioPlayer(M.fmt,M.samplerate>=16e3?2:M.ch,M.samplerate)),this.enableAudio&&this.soundPlayer&&this.initAudioPlayer&&this.soundPlayer.play(M.dat))}break;case e.l:this.avQueue.length>0&&this.pauseDisplay}},M.openAudio=function(){var A={cmd:e.t};if(this.decodeWorker&&void 0!==this.decodeWorker&&this.decodeWorker.postMessage(A),this.enableAudio=!0,null==this.soundPlayer&&(this.isiOS||this.isiPad)){if(!(this.audioSamplerate>0))return!1;this.OpenAudioPlayer(this.audioFmt,this.audioSamplerate>=16e3?2:this.audioChannelNum,this.audioSamplerate)}return!0},M.closeAudio=function(){var A={cmd:e.j};return this.decodeWorker&&void 0!==this.decodeWorker&&this.decodeWorker.postMessage(A),this.soundPlayer&&this.initAudioPlayer&&this.soundPlayer.clearAudioBuffer(),this.enableAudio=!1,!0},M.open=function(){this._worker?this._worker.postMessage({cmd:"start"}):(this._controller.setMediaSourceCallback(this._onMediaDataCallback,this),this._controller.start())},M.close=function(){var A={cmd:e.k};this.decodeWorker&&void 0!==this.decodeWorker&&this.decodeWorker.postMessage(A),this._webGLPlayer&&this._webGLPlayer.renderVideoFrame(null,0,0),this._worker?this._worker.postMessage({cmd:"stop"}):this._controller.stop()},M.changeSpeed=function(A){this._controller&&this._controller.changeSpeed(A)},M.seekToSecs=function(A,M){var t={cmd:e.z,type:M,time:A};this.decodeWorker&&void 0!==this.decodeWorker&&this.decodeWorker.postMessage(t),this._controller.seekToSecs(A,M)},M.seek=function(A){this._worker?this._worker.postMessage({cmd:"seek",param:A}):this._controller.seek(A)},M.isPaused=function(){return this.pauseDisplay},M.pause=function(){this.pauseDisplay=!0;var A={cmd:e.v};if(this.decodeWorker&&void 0!==this.decodeWorker&&this.decodeWorker.postMessage(A),this._controller){var M=this._controller._GetM3u8Loader();M?M.pause():this._controller.pauseOrResume(!0)}this._worker&&this._worker.postMessage({cmd:"pause"})},M.resume=function(){this.pauseDisplay=!1;var A={cmd:e.y};if(this.decodeWorker&&void 0!==this.decodeWorker&&this.decodeWorker.postMessage(A),this._controller){var M=this._controller._GetM3u8Loader();M?M.resume():this._controller.pauseOrResume(!1)}this._worker?this._worker.postMessage({cmd:"resume"}):this._controller.resume()},M._onReconnecting=function(A){var M=this;Promise.resolve().then((function(){M._emitter.emit(AA.RECONNECT_ING,A)}))},M._onReconnectSuccess=function(A){var M=this;Promise.resolve().then((function(){M._emitter.emit(AA.RECONNECT_SUCCESS,A)}))},M._onInitSegment=function(A,M){var t=this;Promise.resolve().then((function(){t._emitter.emit(AA.INIT_SEGMENT,A,M)}))},M._onMediaSegment=function(A,M){var t=this;Promise.resolve().then((function(){t._emitter.emit(AA.MEDIA_SEGMENT,A,M)}))},M._onLoadingComplete=function(){var A=this;Promise.resolve().then((function(){A._emitter.emit(AA.LOADING_COMPLETE)}))},M._onRecoveredEarlyEof=function(){var A=this;Promise.resolve().then((function(){A._emitter.emit(AA.RECOVERED_EARLY_EOF)}))},M._onMediaInfo=function(A){var M=this;Promise.resolve().then((function(){M._emitter.emit(AA.MEDIA_INFO,A)}))},M._onMetaDataArrived=function(A){var M=this;Promise.resolve().then((function(){M._emitter.emit(AA.METADATA_ARRIVED,A)}))},M._onScriptDataArrived=function(A){var M=this;Promise.resolve().then((function(){M._emitter.emit(AA.SCRIPTDATA_ARRIVED,A)}))},M._onStatisticsInfo=function(A){var M=this;Promise.resolve().then((function(){M._emitter.emit(AA.STATISTICS_INFO,A)}))},M._onIOError=function(A,M){var t=this;Promise.resolve().then((function(){t._emitter.emit(AA.IO_ERROR,A,M)}))},M._onDemuxError=function(A,M){var t=this;Promise.resolve().then((function(){t._emitter.emit(AA.DEMUX_ERROR,A,M)}))},M._onRecommendSeekpoint=function(A){var M=this;Promise.resolve().then((function(){M._emitter.emit(AA.RECOMMEND_SEEKPOINT,A)}))},M._onLoggingConfigChanged=function(A){this._worker&&this._worker.postMessage({cmd:"logging_config",param:A})},M._onWorkerMessage=function(A){var M=A.data,t=M.data;if("destroyed"===M.msg||this._workerDestroying)return this._workerDestroying=!1,this._worker.terminate(),void(this._worker=null);switch(M.msg){case AA.INIT_SEGMENT:case AA.MEDIA_SEGMENT:this._emitter.emit(M.msg,t.type,t.data);break;case AA.LOADING_COMPLETE:case AA.RECOVERED_EARLY_EOF:this._emitter.emit(M.msg);break;case AA.MEDIA_INFO:Object.setPrototypeOf(t,Q.a.prototype),this._emitter.emit(M.msg,t);break;case AA.METADATA_ARRIVED:case AA.SCRIPTDATA_ARRIVED:case AA.STATISTICS_INFO:this._emitter.emit(M.msg,t);break;case AA.IO_ERROR:case AA.DEMUX_ERROR:this._emitter.emit(M.msg,t.type,t.info);break;case AA.RECOMMEND_SEEKPOINT:this._emitter.emit(M.msg,t);break;case"logcat_callback":n.a.emitter.emit("log",t.type,t.logcat);break;case AA.RECONNECT_ING:case AA.RECONNECT_SUCCESS:console.log("no process... reconnectSuccess")}},A}(),EA=function(){function A(A){this.TAG="MSEController",this._config=A,this._emitter=new N.a,this._currentPlayTime=0,this._hlsSeekTime=0,this._lastPlayTime=0,this._pause=!1,this._config.isLive&&null==this._config.autoCleanupSourceBuffer&&(this._config.autoCleanupSourceBuffer=!0),this.e={onSourceOpen:this._onSourceOpen.bind(this),onSourceEnded:this._onSourceEnded.bind(this),onSourceClose:this._onSourceClose.bind(this),onSourceBufferError:this._onSourceBufferError.bind(this),onSourceBufferUpdateEnd:this._onSourceBufferUpdateEnd.bind(this)},this._mediaSource=null,this._mediaSourceObjectURL=null,this._mediaElement=null,this._isBufferFull=!1,this._hasPendingEos=!1,this._seek=!1,this._requireSetMediaDuration=!1,this._pendingMediaDuration=0,this._pendingSourceBufferInit=[],this._mimeTypes={video:null,audio:null},this._sourceBuffers={video:null,audio:null},this._lastInitSegments={video:null,audio:null},this._pendingSegments={video:[],audio:[]},this._pendingRemoveRanges={video:[],audio:[]},this._idrList=new d}var M=A.prototype;return M.destroy=function(){(this._mediaElement||this._mediaSource)&&this.detachMediaElement(),this.e=null,this._emitter.removeAllListeners(),this._emitter=null},M.on=function(A,M){this._emitter.addListener(A,M)},M.off=function(A,M){this._emitter.removeListener(A,M)},M.setSeekPlayTime=function(A){this._mediaElement&&(this._lastPlayTime=Math.floor(this._mediaElement.currentTime)),this._hlsSeekTime=A},M.pause=function(){this._pause=!0},M.resume=function(){this._pause=!1},M.attachMediaElement=function(A){if(this._mediaSource)throw new Y.a("MediaSource has been attached to an HTMLMediaElement!");var M=this._mediaSource=new window.MediaSource;M.addEventListener("sourceopen",this.e.onSourceOpen),M.addEventListener("sourceended",this.e.onSourceEnded),M.addEventListener("sourceclose",this.e.onSourceClose),this._mediaElement=A,this._mediaSourceObjectURL=window.URL.createObjectURL(this._mediaSource),A.src=this._mediaSourceObjectURL;var t=this;A.addEventListener("timeupdate",(function(){var M=Math.floor(A.currentTime);t._hlsSeekTime>0&&(M=M-t._lastPlayTime+t._hlsSeekTime),t._currentPlayTime!=M&&(t._currentPlayTime=M,t._emitter&&t._emitter.emit(iA.CB_PLAY_INFO))}),!1),A.addEventListener("pause",(function(){})),A.addEventListener("play",(function(){}))},M.getCurrentPlayTime=function(){return this._currentPlayTime},M.getTotalDuration=function(){return this._mediaElement.duration},M.detachMediaElement=function(){if(this._mediaSource){var A=this._mediaSource;for(var M in this._sourceBuffers){var t=this._pendingSegments[M];t.splice(0,t.length),this._pendingSegments[M]=null,this._pendingRemoveRanges[M]=null,this._lastInitSegments[M]=null;var g=this._sourceBuffers[M];if(g){if("closed"!==A.readyState){try{A.removeSourceBuffer(g)}catch(A){n.a.e(this.TAG,A.message)}g.removeEventListener("error",this.e.onSourceBufferError),g.removeEventListener("updateend",this.e.onSourceBufferUpdateEnd)}this._mimeTypes[M]=null,this._sourceBuffers[M]=null}}if("open"===A.readyState)try{A.endOfStream()}catch(A){n.a.e(this.TAG,A.message)}A.removeEventListener("sourceopen",this.e.onSourceOpen),A.removeEventListener("sourceended",this.e.onSourceEnded),A.removeEventListener("sourceclose",this.e.onSourceClose),this._pendingSourceBufferInit=[],this._isBufferFull=!1,this._idrList.clear(),this._mediaSource=null}this._mediaElement&&(this._mediaElement.src="",this._mediaElement.removeAttribute("src"),this._mediaElement=null),this._mediaSourceObjectURL&&(window.URL.revokeObjectURL(this._mediaSourceObjectURL),this._mediaSourceObjectURL=null)},M.appendInitSegment=function(A,M){if(!this._mediaSource||"open"!==this._mediaSource.readyState)return this._pendingSourceBufferInit.push(A),void this._pendingSegments[A.type].push(A);var t=A,g=""+t.container;t.codec&&t.codec.length>0&&(g+=";codecs="+t.codec);var I=!1;if(n.a.v(this.TAG,"Received Initialization Segment, mimeType: "+g),this._lastInitSegments[t.type]=t,g!==this._mimeTypes[t.type]){if(this._mimeTypes[t.type])n.a.v(this.TAG,"Notice: "+t.type+" mimeType changed, origin: "+this._mimeTypes[t.type]+", target: "+g);else{I=!0;try{var e=this._sourceBuffers[t.type]=this._mediaSource.addSourceBuffer(g);e.addEventListener("error",this.e.onSourceBufferError),e.addEventListener("updateend",this.e.onSourceBufferUpdateEnd)}catch(A){return n.a.e(this.TAG,A.message),void this._emitter.emit(iA.ERROR,{code:A.code,msg:A.message})}}this._mimeTypes[t.type]=g}M||this._pendingSegments[t.type].push(t),I||this._sourceBuffers[t.type]&&!this._sourceBuffers[t.type].updating&&this._doAppendSegments(),C.safari&&"audio/mpeg"===t.container&&t.mediaDuration>0&&(this._requireSetMediaDuration=!0,this._pendingMediaDuration=t.mediaDuration/1e3,this._updateMediaSourceDuration())},M.appendMediaSegment=function(A){var M=A;this._pendingSegments[M.type].push(M),(this._config.autoCleanupSourceBuffer&&this._needCleanupSourceBuffer()||this._seek)&&(this._doCleanupSourceBuffer(),this._seek=!1);var t=this._sourceBuffers[M.type];!t||t.updating||this._hasPendingRemoveRanges()||this._doAppendSegments()},M.reset=function(A){for(var M in this._sourceBuffers){var t=this._sourceBuffers[M];if(t){for(var g=t.buffered,I=!1,e=0;e=1&&A-g.start(0)>=this._config.autoCleanupMaxBackwardDuration)return!0}}return!1},M.AdjustPlayTimeToEnd=function(){if(null!=this._emitter&&null!=this._emitter){var A=this._mediaElement.currentTime;for(var M in this._sourceBuffers)if("video"===M){var t=this._sourceBuffers[M];if(t&&!t.updating){var g=t.buffered;if(g.length>0){var I=g.end(g.length-1);I-A>2&&(this._mediaElement.pause(),(I=A+(I-A)/2)>0&&(this._mediaElement.currentTime=I),this._mediaElement.play())}}}}},M.GetSourceBufferStartTime=function(){for(var A in this._sourceBuffers){var M=this._sourceBuffers[A];if(M)for(var t=M.buffered,g=0;g=this._config.autoCleanupMaxBackwardDuration){I=!0;var E=A-this._config.autoCleanupMinBackwardDuration;this._pendingRemoveRanges[M].push({start:i,end:E})}}else T0&&(isNaN(M)||t>M)&&(n.a.v(this.TAG,"Update MediaSource duration from "+M+" to "+t),this._mediaSource.duration=t),this._requireSetMediaDuration=!1,this._pendingMediaDuration=0}},M._doRemoveRanges=function(){for(var A in this._pendingRemoveRanges)if(this._sourceBuffers[A]&&!this._sourceBuffers[A].updating)for(var M=this._sourceBuffers[A],t=this._pendingRemoveRanges[A];t.length&&!M.updating;){var g=t.shift();M.remove(g.start,g.end)}},M._doAppendSegments=function(){var A=this._pendingSegments;for(var M in e.c()&&!this._pause&&this.AdjustPlayTimeToEnd(),A)if(this._sourceBuffers[M]&&!this._sourceBuffers[M].updating&&A[M].length>0){var t=A[M].shift();if(t.timestampOffset){var g=this._sourceBuffers[M].timestampOffset,I=t.timestampOffset/1e3;Math.abs(g-I)>.1&&(n.a.v(this.TAG,"Update MPEG audio timestampOffset from "+g+" to "+I),this._sourceBuffers[M].timestampOffset=I),delete t.timestampOffset}if(!t.data||0===t.data.byteLength)continue;try{this._sourceBuffers[M].appendBuffer(t.data),this._isBufferFull=!1,"video"===M&&t.hasOwnProperty("info")&&this._idrList.appendArray(t.info.syncPoints)}catch(A){this._pendingSegments[M].unshift(t),this.__ClearAllSourceBuffer(),22===A.code?(this._isBufferFull||this._emitter.emit(iA.BUFFER_FULL),this._isBufferFull=!0):(n.a.e(this.TAG,A.message),this._emitter.emit(iA.ERROR,{code:A.code,msg:A.message}))}}},M._onSourceOpen=function(){if(n.a.v(this.TAG,"MediaSource onSourceOpen"),this._mediaSource.removeEventListener("sourceopen",this.e.onSourceOpen),this._pendingSourceBufferInit.length>0)for(var A=this._pendingSourceBufferInit;A.length;){var M=A.shift();this.appendInitSegment(M,!0)}this._hasPendingSegments()&&this._doAppendSegments(),this._emitter.emit(iA.SOURCE_OPEN)},M._onSourceEnded=function(){n.a.v(this.TAG,"MediaSource onSourceEnded")},M._onSourceClose=function(){n.a.v(this.TAG,"MediaSource onSourceClose"),this._mediaSource&&null!=this.e&&(this._mediaSource.removeEventListener("sourceopen",this.e.onSourceOpen),this._mediaSource.removeEventListener("sourceended",this.e.onSourceEnded),this._mediaSource.removeEventListener("sourceclose",this.e.onSourceClose))},M._hasPendingSegments=function(){var A=this._pendingSegments;return A.video.length>0||A.audio.length>0},M._hasPendingRemoveRanges=function(){var A=this._pendingRemoveRanges;return A.video.length>0||A.audio.length>0},M._onSourceBufferUpdateEnd=function(){this._requireSetMediaDuration?this._updateMediaSourceDuration():this._hasPendingRemoveRanges()?this._doRemoveRanges():this._hasPendingSegments()?this._doAppendSegments():this._hasPendingEos&&this.endOfStream(),this._emitter.emit(iA.UPDATE_END)},M._onSourceBufferError=function(A){n.a.e(this.TAG,"SourceBuffer Error: "+A)},A}(),NA="NetworkError",nA="MediaError",DA=(l.a.FORMAT_ERROR,l.a.FORMAT_UNSUPPORTED,l.a.CODEC_UNSUPPORTED,{enableWorker:!1,enableStashBuffer:!0,stashInitialSize:void 0,isLive:!1,lazyLoad:!0,lazyLoadMaxDuration:180,lazyLoadRecoverDuration:30,deferLoadAfterSourceOpen:!0,autoCleanupMaxBackwardDuration:180,autoCleanupMinBackwardDuration:120,statisticsInfoReportInterval:600,fixAudioTimestampGap:!0,accurateSeek:!1,seekType:"range",seekParamStart:"bstart",seekParamEnd:"bend",rangeLoadZeroStart:!1,customSeekHandler:void 0,reuseRedirectedURL:!1,headers:void 0,customLoader:void 0});function CA(){return Object.assign({},DA)}function rA(A,M){for(var t=0;t0&&(this._requestSetTime=!0,this._mediaElement.currentTime=0)}this._transmuxer=new TA(this._mediaDataSource,this._config,this._mediaCanvas,this,this.callbackWasmType),this._transmuxer.on(AA.INIT_SEGMENT,(function(M,t){A._IsWasm||(A._initFlag=!0,A._msectl.appendInitSegment(t),"video"===M?A._msectlInitSegmentVideoParams=t:"audio"===M&&(A._msectlInitSegmentAudioParams=t))})),this._transmuxer.on(AA.MEDIA_SEGMENT,(function(M,t){if(A._IsWasm||A._msectlResetComplete&&A._msectl.appendMediaSegment(t),A._config.lazyLoad&&!A._config.isLive){var g=A._mediaElement.currentTime;t.info.endDts>=1e3*(g+A._config.lazyLoadMaxDuration)&&null==A._progressChecker&&(n.a.v(A.TAG,"Maximum buffering duration exceeded, suspend transmuxing task"),A._suspendTransmuxer())}})),this._transmuxer.on(AA.LOADING_COMPLETE,(function(){A._IsWasm||A._msectl.endOfStream(),A._emitter.emit(r.LOADING_COMPLETE)})),this._transmuxer.on(AA.RECOVERED_EARLY_EOF,(function(){A._emitter.emit(r.RECOVERED_EARLY_EOF)})),this._transmuxer.on(AA.IO_ERROR,(function(M,t){A._emitter.emit(r.ERROR,NA,M,t)})),this._transmuxer.on(AA.DEMUX_ERROR,(function(M,t){A._emitter.emit(r.ERROR,nA,M,{code:-1,msg:t})})),this._transmuxer.on(AA.MEDIA_INFO,(function(M){A._mediaInfo=M,A._emitter.emit(r.MEDIA_INFO,Object.assign({},M))})),this._transmuxer.on(AA.METADATA_ARRIVED,(function(M){A._emitter.emit(r.METADATA_ARRIVED,M)})),this._transmuxer.on(AA.SCRIPTDATA_ARRIVED,(function(M){A._emitter.emit(r.SCRIPTDATA_ARRIVED,M)})),this._transmuxer.on(AA.STATISTICS_INFO,(function(M){A._statisticsInfo=A._fillStatisticsInfo(M),A._emitter.emit(r.STATISTICS_INFO,Object.assign({},A._statisticsInfo))})),this._transmuxer.on(AA.RECOMMEND_SEEKPOINT,(function(M){A._mediaElement&&!A._config.accurateSeek&&(A._requestSetTime=!0,A._mediaElement.currentTime=M/1e3)})),this._transmuxer.on(AA.RECONNECT_ING,(function(M){A.callbackConnectStatusFunc&&A.callbackConnectStatusFunc(A.callbackUserPtr,0)})),this._transmuxer.on(AA.RECONNECT_SUCCESS,(function(M){A._IsWasm||(M>1&&A._msectl&&(A._transmuxer.seekToSecs(0,0),console.log("msectl.reset..."),A._msectl.reset(!0)),A._mediaElement),A.callbackConnectStatusFunc&&A.callbackConnectStatusFunc(A.callbackUserPtr,1)})),this._transmuxer.on(iA.CB_PLAY_INFO,this._onmseCbPlayInfo.bind(this)),this._transmuxer.open()},I.unload=function(){this._IsWasm||(this._mediaElement&&this._mediaElement.pause(),this._msectl&&this._msectl.seek(0)),this._transmuxer&&(this._transmuxer.close(),this._transmuxer.destroy(),this._transmuxer=null),this._initFlag=!1},I.play=function(){return null==this._transmuxer?(this.needCallPlay=!0,this._mediaElement&&(this._mediaElement.volume=0),!1):this._mediaElement?(this.needCallPlay=!1,this._mediaElement.play()):void 0},I.pause=function(){this._transmuxer&&this._transmuxer.pause(),this._mediaElement&&this._mediaElement.pause(),this._msectl&&this._msectl.pause()},I.resume=function(){this._transmuxer&&this._transmuxer.resume(),this._msectl&&(e.c()&&this._msectl.reset(!1),this._msectl.resume()),this._mediaElement&&this._mediaElement.play()},I.openAudio=function(){if(this._IsWasm){if(this._transmuxer)return this._transmuxer.openAudio()}else if(this._mediaElement)return this._mediaElement.volume=1,!0;return!1},I.closeAudio=function(){if(this._IsWasm){if(this._transmuxer)return this._transmuxer.closeAudio()}else if(this._mediaElement)return this._mediaElement.volume=0,!0;return!1},I.seek=function(A,M){if(!this._IsWasm&&this.isFlv&&this._mediaElement&&this._mediaElement.duration>0){var t=Math.floor(A/100*this._mediaElement.duration);return this._mediaElement?this._internalSeek(t):this._pendingSeekTime=t,!0}if(this._transmuxer){t=Math.floor(A/100*this._transmuxer.getTotalDuration());return this._msectl&&this._msectl.getCurrentPlayTime()==t||(this._transmuxer.seekToSecs(A,M),this._msectl&&(this._msectl.setSeekPlayTime(t),this._msectl.reset(!0)),this._mediaElement?(Math.floor(this._mediaElement.currentTime),Math.floor(t),this._progressChecker):this._pendingSeekTime=t),!0}},I._fillStatisticsInfo=function(A){if(A.playerType=this._type,!(this._mediaElement instanceof HTMLVideoElement))return A;var M=!0,t=0,g=0;if(this._mediaElement.getVideoPlaybackQuality){var I=this._mediaElement.getVideoPlaybackQuality();t=I.totalVideoFrames,g=I.droppedVideoFrames}else null!=this._mediaElement.webkitDecodedFrameCount?(t=this._mediaElement.webkitDecodedFrameCount,g=this._mediaElement.webkitDroppedFrameCount):M=!1;return M&&(A.decodedFrames=t,A.droppedFrames=g),A},I._onmseCbPlayInfo=function(){if(this._transmuxer){var A=0,M=0;!this._IsWasm&&this._msectl?(A=this._msectl.getCurrentPlayTime(),(M=this._msectl.getTotalDuration())!=1/0||e.c()||(M=this._transmuxer.getTotalDuration())):((A=this._transmuxer.getCurrentPlayTime())<0&&(A=0),M=this._transmuxer.getTotalDuration()),this.callbackPlayTimeFunc&&this.callbackPlayTimeFunc(this.callbackUserPtr,this.isFlv?"flv":"m3u8",A,M)}},I._onmseUpdateEnd=function(){if(this._config.lazyLoad&&!this._config.isLive){for(var A=this._mediaElement.buffered,M=this._mediaElement.currentTime,t=0,g=0;g=M+this._config.lazyLoadMaxDuration&&null==this._progressChecker&&(n.a.v(this.TAG,"onmseUpdateEnd::Maximum buffering duration exceeded, suspend transmuxing task"),this._suspendTransmuxer())}},I._onmseBufferFull=function(){n.a.v(this.TAG,"MSE SourceBuffer is full, suspend transmuxing task"),null==this._progressChecker&&this._suspendTransmuxer()},I._suspendTransmuxer=function(){this._transmuxer&&(this._transmuxer.pause(),null==this._progressChecker&&(this._progressChecker=window.setInterval(this._checkProgressAndResume.bind(this),1e3)))},I._checkProgressAndResume=function(){if(null!=this._mediaElement){for(var A=this._mediaElement.currentTime,M=this._mediaElement.buffered,t=!1,g=0;g=I&&A=e-this._config.lazyLoadRecoverDuration&&(t=!0);break}}t&&(window.clearInterval(this._progressChecker),this._progressChecker=null,t&&(n.a.v(this.TAG,"Continue loading from paused position"),this._transmuxer.resume()))}},I._isTimepointBuffered=function(A){for(var M=this._mediaElement.buffered,t=0;t=g&&A0){var I=this._mediaElement.buffered.start(0);(I<1&&A0&&e.c()){console.log("_checkAndApplyUnbufferedSeekpoint. RETURN..."),null!=this._progressChecker&&(window.clearTimeout(this._progressChecker),this._progressChecker=null);var A=!1;return this._transmuxer&&(A=this._transmuxer.isPaused()),void(this._mediaElement&&!A&&this._mediaElement.play())}if(this._seekpointRecord)if(this._seekpointRecord.recordTime<=this._now()-100){var M=this._mediaElement.currentTime;this._seekpointRecord=null,this._isTimepointBuffered(M)||(null!=this._progressChecker&&(window.clearTimeout(this._progressChecker),this._progressChecker=null),this._msectl.seek(M),this._transmuxer.seek(Math.floor(1e3*M)),this._config.accurateSeek&&(this._requestSetTime=!0,this._mediaElement.currentTime=M))}else window.setTimeout(this._checkAndApplyUnbufferedSeekpoint.bind(this),50)},I._checkAndResumeStuckPlayback=function(A){var M=this._mediaElement;if(A||!this._receivedCanPlay||M.readyState<2){var t=M.buffered;t.length>0&&M.currentTime0){var g=t.start(0);if(g<1&&M0;){this.avQueue[0];this.avQueue.shift()}},M.play=function(A,M){return this.pauseDisplay?this.resume():(this.url=A,null==this.flvPlayer&&(this.flvPlayer=new cA({type:this.streamType,isLive:!0,url:A,startPlaySecs:M},{callbackFunc:this.cbFunc,streamType:this.streamType,enableWorker:!1,lazyLoadMaxDuration:"flv"===this.streamType?1:180,lazyLoadRecoverDuration:"flv"===this.streamType?1:30,deferLoadAfterSourceOpen:!0,autoCleanupMaxBackwardDuration:"flv"===this.streamType?120:180,autoCleanupMinBackwardDuration:"flv"===this.streamType?60:120,seekType:"range",enableStashBuffer:!1,decodeType:this.decodeType}),"flv"===this.streamType&&e.h(!0)),this.flvPlayer.setCallback(this.callbackFunc,this.callbackPlayTimeFunc,this.callbackConnectStatusFunc,this.callbackUserPtr),this.flvPlayer.attachMediaElement(this.h5Video,this.canvas),this.flvPlayer.load(),this.flvPlayer.play()),!0},M.stop=function(){return this.pauseDisplay=!1,this.closeAudio(),this.flvPlayer&&(this.flvPlayer.unload(),this.flvPlayer.detachMediaElement()),!0},M.isPause=function(){return this.pauseDisplay},M.pause=function(){this.pauseDisplay=!0,this.flvPlayer&&this.flvPlayer.pause()},M.resume=function(){this.pauseDisplay=!1,this.flvPlayer&&this.flvPlayer.resume()},M.seek=function(A,M){this.pauseDisplay=!1,this.flvPlayer&&this.flvPlayer.seek(A,M)},M.setTrack=function(A,M){},M.openAudio=function(){return!!this.flvPlayer&&this.flvPlayer.openAudio()},M.closeAudio=function(){return!!this.flvPlayer&&this.flvPlayer.closeAudio()},M.openStatinfo=function(){return this.printStatInfo=!0,!0},M.closeStatinfo=function(){return this.printStatInfo=!1,!0},M.isFullScreen=function(){return this.fullScreenFlag},M.canvasFullscreen=function(){var A=this.canvas;A.RequestFullScreen?A.RequestFullScreen():A.webkitRequestFullScreen?A.webkitRequestFullScreen():A.mozRequestFullScreen?A.mozRequestFullScreen():A.msRequestFullscreen?A.msRequestFullscreen():e.d("This browser doesn't supporter fullscreen")},M.canvasExitFullscreen=function(){document.exitFullscreen?document.exitFullscreen():document.webkitExitFullscreen?document.webkitExitFullscreen():document.mozCancelFullScreen?document.mozCancelFullScreen():document.msExitFullscreen?document.msExitFullscreen():e.d("Exit fullscreen doesn't work")},M.fullscreen=function(A){if(1==A){if(!this.fullScreenFlag)return this.canvasFullscreen(),this.fullScreenFlag=!0,!0}else if(this.fullScreenFlag)return this.canvasExitFullscreen(),this.fullScreenFlag=!1,!0;return!1},A}(),BA=function(){function A(){}return A.supportMSEH264Playback=function(){return window.MediaSource&&window.MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"')},A.supportNetworkStreamIO=function(){var A=new $({},CA()),M=A.loaderType;return A.destroy(),"fetch-stream-loader"==M||"xhr-moz-chunked-loader"==M},A.getNetworkLoaderTypeName=function(){var A=new $({},CA()),M=A.loaderType;return A.destroy(),M},A.supportNativeMediaPlayback=function(M){null==A.videoElement&&(A.videoElement=window.document.createElement("video"));var t=A.videoElement.canPlayType(M);return"probably"===t||"maybe"==t},A.getFeatureList=function(){var M={mseFlvPlayback:!1,mseLiveFlvPlayback:!1,networkStreamIO:!1,networkLoaderName:"",nativeMP4H264Playback:!1,nativeWebmVP8Playback:!1,nativeWebmVP9Playback:!1};return M.mseFlvPlayback=A.supportMSEH264Playback(),M.networkStreamIO=A.supportNetworkStreamIO(),M.networkLoaderName=A.getNetworkLoaderTypeName(),M.mseLiveFlvPlayback=M.mseFlvPlayback&&M.networkStreamIO,M.nativeMP4H264Playback=A.supportNativeMediaPlayback('video/mp4; codecs="avc1.42001E, mp4a.40.2"'),M.nativeWebmVP8Playback=A.supportNativeMediaPlayback('video/webm; codecs="vp8.0, vorbis"'),M.nativeWebmVP9Playback=A.supportNativeMediaPlayback('video/webm; codecs="vp9"'),M},A}();function QA(A,M){for(var t=0;t0&&(this._mediaElement.currentTime=0),this._mediaElement.preload="auto",this._mediaElement.load(),this._statisticsReporter=window.setInterval(this._reportStatisticsInfo.bind(this),this._config.statisticsInfoReportInterval)},I.unload=function(){this._mediaElement&&(this._mediaElement.src="",this._mediaElement.removeAttribute("src")),null!=this._statisticsReporter&&(window.clearInterval(this._statisticsReporter),this._statisticsReporter=null)},I.play=function(){return this.pauseDisplay||(this.attachMediaElement(this.h5Video),this.load()),this.pauseDisplay=!1,this._mediaElement.play()},I.stop=function(){this.unload(),this.detachMediaElement()},I.pause=function(){this._mediaElement.pause(),this.pauseDisplay=!0},I.isPause=function(){return this.pauseDisplay},I.openAudio=function(){return this._mediaElement.volume=1,!0},I.closeAudio=function(){return this._mediaElement.volume=0,!0},I.seek=function(A,M){var t=A;1==M&&(t=A/100*this._mediaElement.duration),this._mediaElement?this._mediaElement.currentTime=t:this._pendingSeekTime=t},I._onvLoadedMetadata=function(A){null!=this._pendingSeekTime&&(this._mediaElement.currentTime=this._pendingSeekTime,this._pendingSeekTime=null),this._emitter.emit(r.MEDIA_INFO,this.mediaInfo)},I._reportStatisticsInfo=function(){this._emitter.emit(r.STATISTICS_INFO,this.statisticsInfo)},M=A,(t=[{key:"type",get:function(){return this._type}},{key:"buffered",get:function(){return this._mediaElement.buffered}},{key:"duration",get:function(){return this._mediaElement.duration}},{key:"volume",get:function(){return this._mediaElement.volume},set:function(A){this._mediaElement.volume=A}},{key:"muted",get:function(){return this._mediaElement.muted},set:function(A){this._mediaElement.muted=A}},{key:"currentTime",get:function(){return this._mediaElement?this._mediaElement.currentTime:0},set:function(A){this._mediaElement?this._mediaElement.currentTime=A:this._pendingSeekTime=A}},{key:"mediaInfo",get:function(){var A={mimeType:(this._mediaElement instanceof HTMLAudioElement?"audio/":"video/")+this._mediaDataSource.type};if(this._mediaElement){A.duration=Math.floor(1e3*this._mediaElement.duration),this._mediaElement instanceof HTMLVideoElement&&(A.width=this._mediaElement.videoWidth,A.height=this._mediaElement.videoHeight),this.callbackPlayTimeFunc&&this.callbackPlayTimeFunc(this.callbackUserPtr,"mp4",this._mediaElement.currentTime,A.duration/1e3);var M=this,t=0,g=A.duration/1e3;this._mediaElement.addEventListener("timeupdate",(function(){if(M._mediaElement){var A=M._mediaElement.currentTime;if(null!=A){var I=Math.floor(A);t!=I&&(t=I,M.callbackPlayTimeFunc(M.callbackUserPtr,"mp4",t,g))}}}),!1)}return A}},{key:"statisticsInfo",get:function(){var A={playerType:this._type,url:this._mediaDataSource.url};if(!(this._mediaElement instanceof HTMLVideoElement))return A;var M=!0,t=0,g=0;if(this._mediaElement.getVideoPlaybackQuality){var I=this._mediaElement.getVideoPlaybackQuality();t=I.totalVideoFrames,g=I.droppedVideoFrames}else null!=this._mediaElement.webkitDecodedFrameCount?(t=this._mediaElement.webkitDecodedFrameCount,g=this._mediaElement.webkitDroppedFrameCount):M=!1;return M&&(A.decodedFrames=t,A.droppedFrames=g),A}}])&&QA(M.prototype,t),g&&QA(M,g),A}(),hA=(t(206),t(214),t(220),function(){function A(A,M,t,g){void 0===g&&(g={});var I=g,e=I.cbUserPtr,i=I.decodeType,T=I.openAudio,E=I.BigPlay,N=I.Height,n=I.UnLogo,D=I.HideKbs,C=I.cfKbs;if(this.playerInstance=null,this.url=A,this.UnLogo=n,this.callbackFunc=t||function(){},this.fulls=!0,this.callbackUserPtr=e,this.decodeType="auto",this.version="v2.0.20200930",this.timeKbps=null,this.HideKbs=D,this.cfKbs=C||function(){},"auto"!==i&&"soft"!==i||(this.decodeType=i),BA.supportMSEH264Playback()||(this.decodeType="soft"),this.internalTriggerPlay=!1,this.showTimeLabel=!1,this.seeking=!1,this.callbackEnd=!1,this.initH5Flag=!1,this.currentH5Status=!1,this.seekTimeSecs=0,this.fullScreenFlag=!1,this.playerUI=document.getElementById(M),null!=this.playerUI){if(this.playerUI.style=N?"color:blue;position:relative;background-color:black;width:100%;height:100%;overflow: hidden;":"color:blue;position:relative;background-color:black;width:100%;padding-top:56.25%;overflow: hidden;",this.h5Video=null,this.h5Video=document.createElement("VIDEO"),this.h5Video.style="width:0%;height:0%;position:absolute;top:0px;left:0px;background: black;",this.h5Video.setAttribute("oncontextmenu","return false"),this.playerUI.appendChild(this.h5Video),null!=E&&E){this.bigPlayBox=document.createElement("div"),this.bigPlayBox.style="display:block",this.bigPlayBox.classList="bigPlayBox fa fa-play",this.playerUI.appendChild(this.bigPlayBox);var r=this;this.bigPlayBox.onclick=function(){""!==r.url&&(r.playFlag?(r.internalTriggerPlay=!1,r.play(r.url,1,r.seekTimeSecs)):(r.pause(),r.bigPlayBox.classList.remove("fa-pause"),r.bigPlayBox.classList.add("fa-play"),r.bigPlayBox.title="Play",r.playBtn.classList.remove("fa-pause"),r.playBtn.classList.add("fa-play"),r.bigPlayBox.style="display:block",r.playBtn.title="Play",r.playFlag=!0))}}this.lodingBox=document.createElement("div"),this.lodingBox.style="width:100%;height:100%;position: absolute;left:0px;top:0px;background:rgba(0,0,0,0.6);z-index:9;display:none",this.lodingBox.classList="lodingBox",this.playerUI.appendChild(this.lodingBox),this.conter=document.createElement("div"),this.conter.classList="conter",this.lodingBox.appendChild(this.conter),this.loding=document.createElement("div"),this.loding.classList="Loding",this.conter.appendChild(this.loding),this.loding=document.createElement("span"),this.loding.classList="LodingTitle",this.loding.innerHTML="加载中...",this.conter.appendChild(this.loding),this.offscreenCanvas=null,this.canvas=document.createElement("canvas"),this.canvas.style="width:100%;height:100%;position:absolute;top:0px;left:0px;background: black;",this.playerUI.appendChild(this.canvas),this.timeBox=document.createElement("div"),this.timeBox.style="position: absolute;left: 0px;bottom: 0px;display:flex;width: 100%;display:none;background-color: black;z-index:10;",this.playerUI.appendChild(this.timeBox),this.playBtn=document.createElement("span"),this.playFlag=!0,this.playBtn.classList="fa fa-play",this.playBtn.title="Play",this.playBtn.style="display:inine-block;width: 20px;color: #fff;line-height: 27px;margin: 0 10px;padding:0px 2px;cursor:pointer;text-align:center",this.timeRule=null,this.timeTrack=null,this.timeLabel=null;var c=this;this.playBtn.onclick=function(){""!==c.url&&(c.playFlag?(c.internalTriggerPlay=!1,c.play(c.url,1,c.seekTimeSecs)):(c.pause(),c.playBtn.classList.remove("fa-pause"),c.playBtn.classList.add("fa-play"),c.playBtn.title="Play",null!=c.bigPlayBox&&(c.bigPlayBox.classList.remove("fa-pause"),c.bigPlayBox.classList.add("fa-play"),c.bigPlayBox.style="display:block",c.bigPlayBox.title="Play"),c.playFlag=!0))},this.timeBox.appendChild(this.playBtn);var o=document.createElement("span");o.classList="fa fa-stop",o.title="Stop",o.onclick=function(){c.stop(),c.playBtn.classList.remove("fa-pause"),c.playBtn.classList.add("fa-play"),c.playBtn.title="Play",null!=c.bigPlayBox&&(c.bigPlayBox.classList.remove("fa-pause"),c.bigPlayBox.classList.add("fa-play"),c.bigPlayBox.style="display:block",c.bigPlayBox.title="Play"),c.playFlag=!0,c.timeTrack&&(c.timeTrack.value=0,c.timeLabel.innerHTML="00:00:00/00:00:00")},o.style="display:inine-block;width: 15px;color: #fff;line-height: 27px;margin-right:10px;padding:0px 2px;cursor:pointer;",this.timeBox.appendChild(o),this.soundButton=document.createElement("span"),this.defaultAudioStatus=1==T,this.enableAudio=1==T,this.enableAudio?this.soundButton.classList="fa fa-volume-up ":this.soundButton.classList="fa fa-volume-off ",this.soundButton.style="display:inine-block;width:23px;color:#fff;line-height: 27px;margin-right:10px; text-align:center;cursor:pointer;",this.soundButton.onclick=function(){c.enableAudio?c.closeAudio():c.openAudio()},this.timeBox.appendChild(this.soundButton);var B=document.createElement("span");B.classList="no-padding",B.style="display:inine-block;width:100%;display:flex;",this.timeBox.appendChild(B);var Q=document.createElement("input");Q.classList="timeTrack",Q.id="timeTrack",Q.type="range",Q.style="width:100%;-webkit-appearance: none;height:3px;border-radius:3px;cursor:pointer;margin:auto;display:none",Q.value=0,B.appendChild(Q),this.timeTrack=Q,this.timeRule=B;var a=document.createElement("span");a.classList="padding",a.style="display:inine-block;padding-left:10px;",this.timeBox.appendChild(a);var h=document.createElement("label");h.style="color: #fff;margin: 0 40px 0px 10px;line-height:27px;display:none",h.innerHTML="00:00:00/00:00:00",a.appendChild(h),this.timeLabel=h,this.timeTrack&&(this.timeTrack.oninput=function(){c.seeking||c.seekToPercent(c.timeTrack.value,1)},this.timeTrack.onchange=function(){}),n&&void 0!==n||(this.logo=document.createElement("a"),this.logo.classList="iconfont iconqingxiLOGO",this.logo.title="青犀视频",this.logo.href="http://www.tsingsee.com",this.logo.target="_blank",this.logo.style="display:inline-block;color: #fff;font-size:70px;margin-right:40px;line-height: 27px;text-align:center;position:absolute;top:1px;right:0px;cursor:pointer;overflow: hidden;opacity: 1;",this.timeBox.appendChild(this.logo),this.detectionLogo()),this.domKbps=document.createElement("div"),this.timeBox.appendChild(this.domKbps),this.full=document.createElement("span"),this.full.classList="fa fa-expand",this.full.title="Fullscreen",this.full.onclick=function(){c.fullScreen()},this.full.style="color: #fff;width:20px;margin-right: 10px;line-height: 27px;text-align:center;position:absolute;top:0px;right:0px;cursor:pointer",this.timeBox.appendChild(this.full),this.Menu=document.createElement("div"),this.Menu.style=" position: absolute;left: 0;top: 0;min-width:140px;border-radius: 4px;background:rgba(43, 51, 63, 0.7); color:#fff;font-size:12px;padding:5px 10px;display: none;z-index: 2;",this.playerUI.appendChild(this.Menu);var s=document.createElement("div");s.innerHTML="版本号:"+this.version,this.Menu.appendChild(s);var y=this;this.playerUI.oncontextmenu=function(A){var M=A.offsetX,t=A.offsetY;return y.Menu.style.display="block",y.Menu.style.left=M+"px",y.Menu.style.top=t+"px",console.dir(A),!1},this.playerUI.onclick=function(A){A=A||window.event;y.Menu.style.display="none"},y.Menu.onclick=function(A){(A=A||window.event).cancelBubble=!0},this.playerUI.onmousemove=function(){c.timeBox.style="position: absolute;left: 0px;bottom: 0px;display:flex;width: 100%;background-color: rgba(43,51,63,.7);z-index:10;",clearTimeout(this.Timer),this.Timer=setTimeout((function(){c.timeBox.style="position: absolute;left: 0px;bottom: 0px;display:flex;width: 100%;background-color: rgba(43,51,63,.7);z-index:10;opacity:0;"}),3e3)},this.playerUI.onmouseout=function(){clearTimeout(this.Timer),this.Timer=setTimeout((function(){c.timeBox.style="position: absolute;left: 0px;bottom: 0px;display:flex;width: 100%;background-color: rgba(43,51,63,.7);z-index:10;opacity:0;"}),3e3)},document.addEventListener("fullscreenchange",(function(A){document.fullscreenElement||(y.full.classList.remove("fa-compress"),y.full.classList.add("fa-expand"),document.exitFullScreen?document.exitFullScreen():document.mozCancelFullScreen?document.mozCancelFullScreen():document.webkitExitFullscreen?document.webkitExitFullscreen():element.msExitFullscreen&&element.msExitFullscreen(),y.fulls=!0,y.full.title="Fullscreen")}))}}var M=A.prototype;return M.normal=function(){this.timeRule.innerHTML="",this.timeRule.style="color:#fff;line-height: 27px;font-size=16px;width:100%;display:flex;",this.timeLabel.style=" color: #fff;margin: 0 120px 0px 10px;display:none"},M.live=function(){this.timeRule.innerHTML="LIVE",this.timeRule.style="color:#fff;line-height: 27px;font-size=16px;width:100%;display:flex;",this.timeLabel.style=" color: #fff;margin: 0 120px 0px 10px;display:none"},M.playback=function(){this.timeRule.innerHTML="",this.timeTrack.style="width:100%;-webkit-appearance:none;height:3px;border-radius:3px;cursor:pointer;margin:auto;",this.timeTrack.value=0,this.timeRule.appendChild(this.timeTrack),this.UnLogo||this.HideKbs?this.UnLogo?this.timeLabel.style="color: #fff;margin: 0 40px 0px 10px;dispplay:block;line-height:27px;":this.timeLabel.style="color: #fff;margin: 0 120px 0px 10px;dispplay:block;line-height:27px;":this.timeLabel.style="color: #fff;margin: 0 174px 0px 10px;dispplay:block;line-height:27px;"},M.onUpdateTrackPos=function(A){},M.removeAllChilds=function(A){for(var M=A.childNodes.length-1;M>=0;M--)this.removeAllChilds(A.childNodes[M]),A.removeChild(A.childNodes[M])},M.destroy=function(){this.playerUI&&this.removeAllChilds(this.playerUI),this.stop(),this.playerInstance&&(this.playerInstance.stop(),this.playerInstance.destroy(),delete this.playerInstance,this.playerInstance=null),clearInterval(this.LogoTimer),clearInterval(this.timeKbps)},M.changeToH5Video=function(A){if(this.initH5Flag){if(A==this.currentH5Status)return}else this.initH5Flag=!0;this.currentH5Status=A,A?(this.canvas&&(this.playerUI.removeChild(this.canvas),this.playerUI.removeChild(this.timeBox)),null==this.h5Video&&(this.h5Video=document.createElement("VIDEO")),this.h5Video.style="width:100%;height:100%;position:absolute;top:0px;left:0px;background: black;",this.h5Video.setAttribute("oncontextmenu","return false"),this.playerUI.appendChild(this.h5Video),this.playerUI.appendChild(this.timeBox)):(this.h5Video&&(this.playerUI.removeChild(this.h5Video),this.playerUI.removeChild(this.timeBox)),null==this.canvas&&(this.canvas=document.createElement("canvas")),this.canvas.style="width:100%;height:100%;position:absolute;top:0px;left:0px;background: black;",this.playerUI.appendChild(this.canvas),this.playerUI.appendChild(this.timeBox))},M._onConnectStatus=function(A,M){A.lodingBox.style=1==M?"display:none":"width:100%;height:100%;position: absolute;left:0px;top:0px;background:rgba(0,0,0,0.6);z-index:9;display:block"},M._onChangeTypeCallback=function(A,M,t){A.changeToH5Video(!t)},M.formatTime=function(A){return(Math.floor(A/3600)<10?"0"+Math.floor(A/3600):Math.floor(A/3600))+":"+(Math.floor(A/60%60)<10?"0"+Math.floor(A/60%60):Math.floor(A/60%60))+":"+(A=Math.floor(A%60)<10?"0"+Math.floor(A%60):Math.floor(A%60))},M._onUpdatePlayTime=function(A,M,t,g){if(A.showTimeLabel||(A.showTimeLabel=!0,g!==1/0&&g>0?A.playback():A.live()),g!==1/0&&!(g<1)&&A.showTimeLabel&&A.timeTrack&&g>0){var I=parseInt((t+1)/g*100);I!==parseInt(A.timeTrack.value)&&(A.timeTrack.value=I),A.onUpdateTrackPos(I),A.timeLabel&&void 0!==A.timeLabel&&(t<1?t=0:t>g&&(t=g),A.timeLabel.innerHTML=A.formatTime(t)+"/"+A.formatTime(g)),A.callbackFunc&&!this.callbackEnd&&(A.callbackFunc("playbackTime",parseInt(t)),100!=I||this.callbackEnd||(A.callbackFunc("ended",0),this.callbackEnd=!0))}},M.startLoding=function(){this.callbackFunc("startLoding"),this.lodingBox.style="width:100%;height:100%;position: absolute;left:0px;top:0px;background:rgba(0,0,0,0.6);z-index:9;"},M.endLoding=function(){this.callbackFunc("endLoding"),this.lodingBox.style="display:none"},M.detectionLogo=function(){var A=this;this.LogoTimer=setInterval((function(){null!=A.logo&&"青犀视频"==A.logo.title&&"http://www.tsingsee.com/"===A.logo.href&&"inline-block"==A.logo.style.display&&"1"==A.logo.style.opacity||A.stop()}),1e3)},M.play=function(A,M,t){var g=this;if(null!=this.playerUI){if(this.callbackFunc("play"),0==M&&null!=this.bigPlayBox&&(this.bigPlayBox.style="display:block"),A&&(this.url=A),null!=t&&(this.seekTimeSecs=t),!this.url||""===this.url||void 0===this.url)return console.log("url is empty..."),!1;if(-1!=this.url.indexOf("rtmp:"))return console.log("Unsupport rtmp stream."),!1;if(-1!=this.url.indexOf("rtsp:"))return console.log("Unsupport rtsp stream."),!1;if(-1==this.url.indexOf("http:")&&-1==this.url.indexOf("https:")&&-1==this.url.indexOf("ws://")&&-1==this.url.indexOf("wss://"))return console.log("Unsupport stream: "+this.url),!1;if(1!=M)return!0;null!=this.bigPlayBox&&(this.bigPlayBox.style="display:none"),this._onConnectStatus(this,1);var I=!1,e=!1,i=!1,T=this.url.substr(this.url.lastIndexOf(".")+1);if(T){var E=T.toLowerCase();E.indexOf("m3u8")>-1?I=!0:E.indexOf("flv")>-1?e=!0:E.indexOf("mp4")>-1&&(i=!0),I||e||i||(this.url.indexOf(".flv")>-1?e=!0:this.url.indexOf(".m3u8")>-1?I=!0:this.url.indexOf(".mp4")>-1&&(i=!0))}else e=!0;this.callbackEnd=!1;var N=!1;return I?(this.showTimeLabel=!1,null!=this.playerInstance&&this.playerInstance.isPause()||this.live(),this.isHLS=!1,null==this.playerInstance&&(this.playerInstance=new oA("m3u8",this.h5Video,null==this.offscreenCanvas?this.canvas:this.offscreenCanvas,this.decodeType,this._onChangeTypeCallback,this._onUpdatePlayTime,this._onConnectStatus,this,this.enableAudio,this.callbackFunc)),N=this.playerInstance.play(this.url,this.seekTimeSecs),this.enableAudio=this.defaultAudioStatus,!this.internalTriggerPlay&&this.enableAudio&&this.openAudio(),this.seekTimeSecs=0):i?(this.changeToH5Video(!0),this.showTimeLabel=!1,null!=this.playerInstance&&this.playerInstance.isPause()||this.live(),this.isHLS=!1,null==this.playerInstance&&(this.playerInstance=new aA({type:"mp4",url:A},this.h5Video,this._onUpdatePlayTime,this)),this.playerInstance.play(),this.enableAudio=this.defaultAudioStatus,!this.internalTriggerPlay&&this.enableAudio?this.openAudio():this.closeAudio(),N=!0):e&&("undefined"!=typeof OffscreenCanvas&&this.offscreenCanvas,this.isHLS,this.showTimeLabel=!1,this.live(),this.isHLS=!1,null==this.playerInstance&&(this.playerInstance=new oA("flv",this.h5Video,null==this.offscreenCanvas?this.canvas:this.offscreenCanvas,this.decodeType,this._onChangeTypeCallback,this._onUpdatePlayTime,this._onConnectStatus,this,this.enableAudio,this.callbackFunc)),N=this.playerInstance.play(this.url,0),this.enableAudio=this.defaultAudioStatus,!this.internalTriggerPlay&&this.enableAudio&&this.openAudio()),this.HideKbs||(this.domKbps.style="position:absolute;top:6px;right:115px;z-index: 19;font-size: 12px;color: #fff;",this.timeKbps=setInterval((function(){if(g.playerInstance.flvPlayer&&g.playerInstance.flvPlayer._transmuxer&&g.playerInstance.flvPlayer._transmuxer._controller&&g.playerInstance.flvPlayer._transmuxer._controller._ioctl){var A=parseInt(g.playerInstance.flvPlayer._transmuxer._controller._ioctl.currentSpeed),M=parseInt(g.playerInstance.flvPlayer._transmuxer._controller._ioctl.averageKBps);g.domKbps.innerText=8*M>=1024?(8*M/1024).toFixed(2)+"Mb/s":8*M+"Kb/s",g.cfKbs({currentKbs:8*A,averageKbs:8*M,currentKbps:A,averageKbps:M})}}),1e3)),null==this.playerInstance?(console.log("Unsupport url: "+A),!1):(N&&(this.playBtn.classList.remove("fa-play"),this.playBtn.classList.add("fa-pause"),this.playBtn.title="Pause",null!=this.bigPlayBox&&(this.bigPlayBox.classList.remove("fa-play"),this.bigPlayBox.classList.add("fa-pause"),this.bigPlayBox.style="display:none",this.bigPlayBox.title="Pause"),this.playFlag=!1),N)}},M.stop=function(){if(null!=this.playerUI)return this.callbackFunc("stop"),this.callbackEnd=!1,this._onConnectStatus(this,1),!!this.playerInstance&&(this.closeAudio(),this.playerInstance.stop(),this.showTimeLabel=!1,this.normal(),!0)},M.pause=function(){this.callbackFunc("pause"),this.playerInstance&&this.playerInstance.pause()},M.resume=function(){this.callbackFunc("resume"),this.playerInstance&&this.playerInstance.resume()},M.seekToSecs=function(A){this.playerInstance&&this.playerInstance.seek(A,0)},M.seekToPercent=function(A){this.playerInstance&&this.playerInstance.seek(A,1)},M.setTrack=function(A,M){this.playerInstance&&this.playerInstance.setTrack(A,M)},M.openAudio=function(){return this.callbackFunc("openAudio"),this.playerInstance&&(this.playerInstance.openAudio()?(this.soundButton.classList.add("fa-volume-up"),this.soundButton.classList.remove("fa-volume-off"),this.enableAudio=!0):(this.soundButton.classList.add("fa-volume-off"),this.soundButton.classList.remove("fa-volume-up"),this.enableAudio=!1)),this.enableAudio},M.closeAudio=function(){if(this.callbackFunc("closeAudio"),this.playerInstance){if(!this.enableAudio)return!0;this.playerInstance.closeAudio()?(this.soundButton.classList.add("fa-volume-off"),this.soundButton.classList.remove("fa-volume-up"),this.enableAudio=!1):(this.soundButton.classList.add("fa-volume-up"),this.soundButton.classList.remove("fa-volume-off"),this.enableAudio=!0)}return!this.enableAudio},M.showStaticsInfo=function(A){return null!=this.playerInstance&&(A?this.playerInstance.openStatinfo():this.playerInstance.closeStatinfo())},M.fullScreen=function(){if(this.fulls){this.callbackFunc("full"),this.full.classList.remove("fa-expand"),this.full.classList.add("fa-compress"),this.playerUI.requestFullscreen?this.playerUI.requestFullscreen():this.playerUI.mozRequestFullScreen?this.playerUI.mozRequestFullScreen():this.playerUI.webkitRequestFullscreen?this.playerUI.webkitRequestFullscreen():this.playerUI.msRequestFullscreen&&this.playerUI.msRequestFullscreen(),this.fulls=!1,this.full.title="Exit Fullscreen";var A=navigator.userAgent;A.indexOf("Android")>-1||A.indexOf("Adr"),A.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)}else this.callbackFunc("unFull"),this.full.classList.remove("fa-compress"),this.full.classList.add("fa-expand"),document.exitFullScreen?document.exitFullScreen():document.mozCancelFullScreen?document.mozCancelFullScreen():document.webkitExitFullscreen?document.webkitExitFullscreen():element.msExitFullscreen&&element.msExitFullscreen(),this.fulls=!0,this.full.title="Fullscreen"},A}())},function(A,M,t){"use strict";t.r(M);var g=t(0),I=(t(58),t(22)),e=t.n(I);var i=function(A){for(var M,t=A.split(new RegExp('(?:^|,)((?:[^=]*)=(?:"[^"]*"|[^,]*))')),g={},I=t.length;I--;)""!==t[I]&&((M=/([^=]*)=(.*)/.exec(t[I]).slice(1))[0]=M[0].replace(/^\s+|\s+$/g,""),M[1]=M[1].replace(/^\s+|\s+$/g,""),M[1]=M[1].replace(/^['"](.*)['"]$/g,"$1"),g[M[0]]=M[1]);return g},T=function(A){var M,t;function g(){var M;return(M=A.call(this)||this).customParsers=[],M.tagMappers=[],M}t=A,(M=g).prototype=Object.create(t.prototype),M.prototype.constructor=M,M.__proto__=t;var I=g.prototype;return I.push=function(A){var M,t,g=this;0!==(A=A.trim()).length&&("#"===A[0]?this.tagMappers.reduce((function(M,t){var g=t(A);return g===A?M:M.concat([g])}),[A]).forEach((function(A){for(var I=0;I-1;M=this.buffer.indexOf("\n"))this.trigger("data",this.buffer.substring(0,M)),this.buffer=this.buffer.substring(M+1)},g}(e.a),N=t(110),n=t.n(N);var D=function(A){var M,t;function g(){var M;(M=A.call(this)||this).lineStream=new E,M.parseStream=new T,M.lineStream.pipe(M.parseStream);var t,g,I=function(A){if(void 0===A)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return A}(M),e=[],i={},N=function(){},D={AUDIO:{},VIDEO:{},"CLOSED-CAPTIONS":{},SUBTITLES:{}},C=0;return M.manifest={allowCache:!0,discontinuityStarts:[],segments:[]},M.parseStream.on("data",(function(A){var M,T;({tag:function(){({"allow-cache":function(){this.manifest.allowCache=A.allowed,"allowed"in A||(this.trigger("info",{message:"defaulting allowCache to YES"}),this.manifest.allowCache=!0)},byterange:function(){var M={};"length"in A&&(i.byterange=M,M.length=A.length,"offset"in A||(this.trigger("info",{message:"defaulting offset to zero"}),A.offset=0)),"offset"in A&&(i.byterange=M,M.offset=A.offset)},endlist:function(){this.manifest.endList=!0},inf:function(){"mediaSequence"in this.manifest||(this.manifest.mediaSequence=0,this.trigger("info",{message:"defaulting media sequence to zero"})),"discontinuitySequence"in this.manifest||(this.manifest.discontinuitySequence=0,this.trigger("info",{message:"defaulting discontinuity sequence to zero"})),A.duration>0&&(i.duration=A.duration),0===A.duration&&(i.duration=.01,this.trigger("info",{message:"updating zero segment duration to a small value"})),this.manifest.segments=e},key:function(){if(A.attributes)if("NONE"!==A.attributes.METHOD)if(A.attributes.URI){if("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"===A.attributes.KEYFORMAT){return-1===["SAMPLE-AES","SAMPLE-AES-CTR","SAMPLE-AES-CENC"].indexOf(A.attributes.METHOD)?void this.trigger("warn",{message:"invalid key method provided for Widevine"}):("SAMPLE-AES-CENC"===A.attributes.METHOD&&this.trigger("warn",{message:"SAMPLE-AES-CENC is deprecated, please use SAMPLE-AES-CTR instead"}),"data:text/plain;base64,"!==A.attributes.URI.substring(0,23)?void this.trigger("warn",{message:"invalid key URI provided for Widevine"}):A.attributes.KEYID&&"0x"===A.attributes.KEYID.substring(0,2)?void(this.manifest.contentProtection={"com.widevine.alpha":{attributes:{schemeIdUri:A.attributes.KEYFORMAT,keyId:A.attributes.KEYID.substring(2)},pssh:n()(A.attributes.URI.split(",")[1])}}):void this.trigger("warn",{message:"invalid key ID provided for Widevine"}))}A.attributes.METHOD||this.trigger("warn",{message:"defaulting key method to AES-128"}),g={method:A.attributes.METHOD||"AES-128",uri:A.attributes.URI},void 0!==A.attributes.IV&&(g.iv=A.attributes.IV)}else this.trigger("warn",{message:"ignoring key declaration without URI"});else g=null;else this.trigger("warn",{message:"ignoring key declaration without attribute list"})},"media-sequence":function(){isFinite(A.number)?this.manifest.mediaSequence=A.number:this.trigger("warn",{message:"ignoring invalid media sequence: "+A.number})},"discontinuity-sequence":function(){isFinite(A.number)?(this.manifest.discontinuitySequence=A.number,C=A.number):this.trigger("warn",{message:"ignoring invalid discontinuity sequence: "+A.number})},"playlist-type":function(){/VOD|EVENT/.test(A.playlistType)?this.manifest.playlistType=A.playlistType:this.trigger("warn",{message:"ignoring unknown playlist type: "+A.playlist})},map:function(){t={},A.uri&&(t.uri=A.uri),A.byterange&&(t.byterange=A.byterange)},"stream-inf":function(){this.manifest.playlists=e,this.manifest.mediaGroups=this.manifest.mediaGroups||D,A.attributes?(i.attributes||(i.attributes={}),Object.assign(i.attributes,A.attributes)):this.trigger("warn",{message:"ignoring empty stream-inf attributes"})},media:function(){if(this.manifest.mediaGroups=this.manifest.mediaGroups||D,A.attributes&&A.attributes.TYPE&&A.attributes["GROUP-ID"]&&A.attributes.NAME){var t=this.manifest.mediaGroups[A.attributes.TYPE];t[A.attributes["GROUP-ID"]]=t[A.attributes["GROUP-ID"]]||{},M=t[A.attributes["GROUP-ID"]],(T={default:/yes/i.test(A.attributes.DEFAULT)}).default?T.autoselect=!0:T.autoselect=/yes/i.test(A.attributes.AUTOSELECT),A.attributes.LANGUAGE&&(T.language=A.attributes.LANGUAGE),A.attributes.URI&&(T.uri=A.attributes.URI),A.attributes["INSTREAM-ID"]&&(T.instreamId=A.attributes["INSTREAM-ID"]),A.attributes.CHARACTERISTICS&&(T.characteristics=A.attributes.CHARACTERISTICS),A.attributes.FORCED&&(T.forced=/yes/i.test(A.attributes.FORCED)),M[A.attributes.NAME]=T}else this.trigger("warn",{message:"ignoring incomplete or missing media group"})},discontinuity:function(){C+=1,i.discontinuity=!0,this.manifest.discontinuityStarts.push(e.length)},"program-date-time":function(){void 0===this.manifest.dateTimeString&&(this.manifest.dateTimeString=A.dateTimeString,this.manifest.dateTimeObject=A.dateTimeObject),i.dateTimeString=A.dateTimeString,i.dateTimeObject=A.dateTimeObject},targetduration:function(){!isFinite(A.duration)||A.duration<0?this.trigger("warn",{message:"ignoring invalid target duration: "+A.duration}):this.manifest.targetDuration=A.duration},totalduration:function(){!isFinite(A.duration)||A.duration<0?this.trigger("warn",{message:"ignoring invalid total duration: "+A.duration}):this.manifest.totalDuration=A.duration},start:function(){A.attributes&&!isNaN(A.attributes["TIME-OFFSET"])?this.manifest.start={timeOffset:A.attributes["TIME-OFFSET"],precise:A.attributes.PRECISE}:this.trigger("warn",{message:"ignoring start declaration without appropriate attribute list"})},"cue-out":function(){i.cueOut=A.data},"cue-out-cont":function(){i.cueOutCont=A.data},"cue-in":function(){i.cueIn=A.data}}[A.tagType]||N).call(I)},uri:function(){i.uri=A.uri,e.push(i),this.manifest.targetDuration&&!("duration"in i)&&(this.trigger("warn",{message:"defaulting segment duration to the target duration"}),i.duration=this.manifest.targetDuration),g&&(i.key=g),i.timeline=C,t&&(i.map=t),i={}},comment:function(){},custom:function(){A.segment?(i.custom=i.custom||{},i.custom[A.customType]=A.data):(this.manifest.custom=this.manifest.custom||{},this.manifest.custom[A.customType]=A.data)}})[A.type].call(I)})),M}t=A,(M=g).prototype=Object.create(t.prototype),M.prototype.constructor=M,M.__proto__=t;var I=g.prototype;return I.push=function(A){this.lineStream.push(A)},I.end=function(){this.lineStream.push("\n")},I.addParser=function(A){this.parseStream.addParser(A)},I.addTagMapper=function(A){this.parseStream.addTagMapper(A)},g}(e.a),C=new WebAssembly.Memory({initial:256,maximum:256});new WebAssembly.Table({initial:0,maximum:0,element:"anyfunc"}),C.buffer.byteLength,M.default=function(A){A.url="",A.canvas=null,A.webGLPlayer=null,A.videoSampleQueue=[],A.audioSampleQueue=[],A.videoInterval=40,A.lastRenderTime=0,A.intervalVal=0,A.renderFrameCount=0,A.requestAbort=!1,A.pause=!1,A.cbStreamData=!0,A.reConnect=!0,A.isLiveStream=!0,A.seekTime=0,A.totalDuration=0,A.playDuration=0,A.startVideoPts=0,A.startAudioPts=0,A.tsFileList=[],A.getFileIndex=0,A.lastTsFile="",A.isFirstFile=!0,A.lastTsFileSequence=0,A.sequenceExceptNum=0,A.tsDemuxer=null,A.getFileCount=100,A.intervalHandle=-1,A.intervalValue=40,A.startDownloadTimeSecs=0;var M=function(A,M){for(var t=M,g=0,I=0;I>=0;)(I=t.indexOf("/"))>=0?(t=t.substring(I+1),g+=I+1):t=M.substring(0,g);return""!==A?t+A:t},t=function(){var M={cmd:g.k,workerId:g.b};A.postMessage(M),A.getFileIndex=0},I=function I(e,i){if(A.url=e,1!=A._requestAbort){if(!A.pause&&(!(A.totalDuration>0)||A.isLiveStream)){var T=new XMLHttpRequest;T.open("get",e,!0),T.responseType="text",T.onreadystatechange=function(){if(4!=T.readyState||200!=T.status&&304!=T.status)4===T.readyState&&410===T.status&&A.postMessage({cmd:410,code:T.status,msg:T.responseText});else{var t=new D;t.push(T.responseText),t.end();var e=t.manifest;if("undefined"!==t.manifest.endList&&1==t.manifest.endList?A.isLiveStream=!1:A.isLiveStream=!0,e.segments.length<1){console.log("Not found ts file... waiting for reconnect...");var E=A;return void g.i(3e3).then((function(){I(E.url,1)}))}var N=0,n=0;if(A.isLiveStream)if(""==A.lastTsFile){for(N=0;N2&&(C=e.segments.length-1-1),A.getFileIndex=C,A.lastTsFile=e.segments[C].uri}else{C=-1;var r=!1;for(N=0;N=0){B={filename:c,duration:e.segments[N].duration};A.totalDuration+=e.segments[N].duration,A.tsFileList.push(B),r||(A.lastTsFile=e.segments[N].uri,r=!0)}else;else C=N,A.tsFileList.length=0,A.tsFileList=[],A.getFileIndex=0}if(C<0)for(A.tsFileList.length=0,A.tsFileList=[],A.getFileIndex=0,N=0;N0&&(A.getFileIndex=-1),N=0;N0&&A.totalDuration>=A.seekTime&&A.getFileIndex<0&&(A.getFileIndex=N);var B={filename:c,duration:e.segments[N].duration};A.totalDuration+=e.segments[N].duration,0!==N||A.isLiveStream||A.tsFileList.push(B),A.tsFileList.push(B)}}A.getFileIndex<0&&(A.getFileIndex=0);var Q={cmd:g.s,duration:A.totalDuration,live:A.isLiveStream,workerId:g.b};A.postMessage(Q)}if(A.tsFileList.length>0&&1==i){Q={cmd:g.u,workerId:g.b};A.postMessage(Q)}}},T.send()}}else t()},e=function M(){if(!(A.getFileCount++<1)){var e=!1;if(!A.pause)if(1!=A._requestAbort){if(A.isLiveStream){if(A.getFileCount=0,A.getFileIndex>=A.tsFileList.length||3==A.getFileCount)return void I(A.url,1);e=!0}else if(A.getFileIndex>=A.tsFileList.length){A.getFileIndex++;var i={cmd:g.p,workerId:g.b};return void A.postMessage(i)}var T=A.tsFileList[A.getFileIndex].filename;if(A.isLiveStream){var E=function(A){var M=A.split("-"),t=M.length;if(t>0){var g=M[t-1].split(".");if(g.length>0)return g[0]}return""}(T),N=parseInt(E);if(N>0&&N>A.lastTsFileSequence+1){A.sequenceExceptNum++;i={cmd:g.x,workerId:g.b,fileSequence:A.sequenceExceptNum};A.postMessage(i)}A.lastTsFileSequence=N}var n=new XMLHttpRequest;n.open("get",T,!0),n.responseType="arraybuffer",n.onreadystatechange=function(){if(404==n.status)return g.d("current file not found. refresh list..."),void I(A.url,1);if(4==n.readyState&&(200==n.status||304==n.status)){var t=new Uint8Array(n.response),i=0;for(i=0;i=0&&(clearInterval(A.intervalHandle),A.intervalHandle=-1),!A.pause){var E=A.getFileIndex;E>=0&&E900&&(A.intervalValue-=900)),A.intervalHandle=setInterval(M,A.intervalValue)}A.getFileCount=100,A.getFileIndex++,A.getFileIndex>=0&&(A.getFileIndex,A.tsFileList.length);T={cmd:g.r,workerId:g.b};if(A.postMessage(T),e)return void I(A.url,0)}},n.send()}else t()}};A.addEventListener("message",(function(M){var i=M.data;switch(i.cmd){case g.u:A.url=i.url,A.seekTime=i.time,I(i.url,1);break;case g.q:0==i.t&&e();break;case g.w:I(A.url,1);break;case g.z:var T=i.time;1==i.type&&(T=i.time/100*A.totalDuration);var E=!1,N=0,n=0;for(n=0;nT){A.getFileIndex=n,e();var D={cmd:g.z,type:i.type,time:T};A.postMessage(D),E=!0;break}if(!E){D={cmd:g.z,type:i.type,time:T};A.postMessage(D)}break;case g.v:A.pause=!0;break;case g.y:A.pause=!1,I(A.url,1);break;case g.k:A._requestAbort=!0,A.seekTime=0,t()}}))}}]).default})); diff --git a/web_src/static/js/ZLMRTCClient.js b/web_src/static/js/ZLMRTCClient.js new file mode 100644 index 000000000..a44a2ac15 --- /dev/null +++ b/web_src/static/js/ZLMRTCClient.js @@ -0,0 +1,7718 @@ +var ZLMRTCClient = (function (exports) { + 'use strict'; + + const Events$1 = { + WEBRTC_NOT_SUPPORT: 'WEBRTC_NOT_SUPPORT', + 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_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 = 'Thu Mar 24 2022 17:42:57 GMT+0800 (China Standard Time)'; + + // Copyright (C) <2018> Intel Corporation + // + // SPDX-License-Identifier: Apache-2.0 + // eslint-disable-next-line require-jsdoc + function isFirefox() { + return window.navigator.userAgent.match('Firefox') !== null; + } // eslint-disable-next-line require-jsdoc + + function isChrome() { + return window.navigator.userAgent.match('Chrome') !== null; + } // eslint-disable-next-line require-jsdoc + + function isEdge() { + return window.navigator.userAgent.match(/Edge\/(\d+).(\d+)$/) !== null; + } // eslint-disable-next-line require-jsdoc + + // Copyright (C) <2018> Intel Corporation + /** + * @class AudioSourceInfo + * @classDesc Source info about an audio track. Values: 'mic', 'screen-cast', 'file', 'mixed'. + * @memberOf Owt.Base + * @readonly + * @enum {string} + */ + + const AudioSourceInfo = { + MIC: 'mic', + SCREENCAST: 'screen-cast', + FILE: 'file', + MIXED: 'mixed' + }; + /** + * @class VideoSourceInfo + * @classDesc Source info about a video track. Values: 'camera', 'screen-cast', 'file', 'mixed'. + * @memberOf Owt.Base + * @readonly + * @enum {string} + */ + + const VideoSourceInfo = { + CAMERA: 'camera', + SCREENCAST: 'screen-cast', + FILE: 'file', + MIXED: 'mixed' + }; + /** + * @class TrackKind + * @classDesc Kind of a track. Values: 'audio' for audio track, 'video' for video track, 'av' for both audio and video tracks. + * @memberOf Owt.Base + * @readonly + * @enum {string} + */ + + const TrackKind = { + /** + * Audio tracks. + * @type string + */ + AUDIO: 'audio', + + /** + * Video tracks. + * @type string + */ + VIDEO: 'video', + + /** + * Both audio and video tracks. + * @type string + */ + AUDIO_AND_VIDEO: 'av' + }; + /** + * @class Resolution + * @memberOf Owt.Base + * @classDesc The Resolution defines the size of a rectangle. + * @constructor + * @param {number} width + * @param {number} height + */ + + class Resolution { + // eslint-disable-next-line require-jsdoc + constructor(width, height) { + /** + * @member {number} width + * @instance + * @memberof Owt.Base.Resolution + */ + this.width = width; + /** + * @member {number} height + * @instance + * @memberof Owt.Base.Resolution + */ + + this.height = height; + } + + } + + /* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + + let logDisabled_ = true; + let deprecationWarnings_ = true; + + /** + * Extract browser version out of the provided user agent string. + * + * @param {!string} uastring userAgent string. + * @param {!string} expr Regular expression used as match criteria. + * @param {!number} pos position in the version string to be returned. + * @return {!number} browser version. + */ + function extractVersion(uastring, expr, pos) { + const match = uastring.match(expr); + return match && match.length >= pos && parseInt(match[pos], 10); + } + + // Wraps the peerconnection event eventNameToWrap in a function + // which returns the modified event object (or false to prevent + // the event). + function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) { + if (!window.RTCPeerConnection) { + return; + } + const proto = window.RTCPeerConnection.prototype; + const nativeAddEventListener = proto.addEventListener; + proto.addEventListener = function(nativeEventName, cb) { + if (nativeEventName !== eventNameToWrap) { + return nativeAddEventListener.apply(this, arguments); + } + const wrappedCallback = (e) => { + const modifiedEvent = wrapper(e); + if (modifiedEvent) { + if (cb.handleEvent) { + cb.handleEvent(modifiedEvent); + } else { + cb(modifiedEvent); + } + } + }; + this._eventMap = this._eventMap || {}; + if (!this._eventMap[eventNameToWrap]) { + this._eventMap[eventNameToWrap] = new Map(); + } + this._eventMap[eventNameToWrap].set(cb, wrappedCallback); + return nativeAddEventListener.apply(this, [nativeEventName, + wrappedCallback]); + }; + + const nativeRemoveEventListener = proto.removeEventListener; + proto.removeEventListener = function(nativeEventName, cb) { + if (nativeEventName !== eventNameToWrap || !this._eventMap + || !this._eventMap[eventNameToWrap]) { + return nativeRemoveEventListener.apply(this, arguments); + } + if (!this._eventMap[eventNameToWrap].has(cb)) { + return nativeRemoveEventListener.apply(this, arguments); + } + const unwrappedCb = this._eventMap[eventNameToWrap].get(cb); + this._eventMap[eventNameToWrap].delete(cb); + if (this._eventMap[eventNameToWrap].size === 0) { + delete this._eventMap[eventNameToWrap]; + } + if (Object.keys(this._eventMap).length === 0) { + delete this._eventMap; + } + return nativeRemoveEventListener.apply(this, [nativeEventName, + unwrappedCb]); + }; + + Object.defineProperty(proto, 'on' + eventNameToWrap, { + get() { + return this['_on' + eventNameToWrap]; + }, + set(cb) { + if (this['_on' + eventNameToWrap]) { + this.removeEventListener(eventNameToWrap, + this['_on' + eventNameToWrap]); + delete this['_on' + eventNameToWrap]; + } + if (cb) { + this.addEventListener(eventNameToWrap, + this['_on' + eventNameToWrap] = cb); + } + }, + enumerable: true, + configurable: true + }); + } + + function disableLog(bool) { + if (typeof bool !== 'boolean') { + return new Error('Argument type: ' + typeof bool + + '. Please use a boolean.'); + } + logDisabled_ = bool; + return (bool) ? 'adapter.js logging disabled' : + 'adapter.js logging enabled'; + } + + /** + * Disable or enable deprecation warnings + * @param {!boolean} bool set to true to disable warnings. + */ + function disableWarnings(bool) { + if (typeof bool !== 'boolean') { + return new Error('Argument type: ' + typeof bool + + '. Please use a boolean.'); + } + deprecationWarnings_ = !bool; + return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled'); + } + + function log$1() { + if (typeof window === 'object') { + if (logDisabled_) { + return; + } + if (typeof console !== 'undefined' && typeof console.log === 'function') { + console.log.apply(console, arguments); + } + } + } + + /** + * Shows a deprecation warning suggesting the modern and spec-compatible API. + */ + function deprecated(oldMethod, newMethod) { + if (!deprecationWarnings_) { + return; + } + console.warn(oldMethod + ' is deprecated, please use ' + newMethod + + ' instead.'); + } + + /** + * Browser detector. + * + * @return {object} result containing browser and version + * properties. + */ + function detectBrowser(window) { + // Returned result object. + const result = {browser: null, version: null}; + + // Fail early if it's not a browser + if (typeof window === 'undefined' || !window.navigator) { + result.browser = 'Not a browser.'; + return result; + } + + const {navigator} = window; + + if (navigator.mozGetUserMedia) { // Firefox. + result.browser = 'firefox'; + result.version = extractVersion(navigator.userAgent, + /Firefox\/(\d+)\./, 1); + } else if (navigator.webkitGetUserMedia || + (window.isSecureContext === false && window.webkitRTCPeerConnection && + !window.RTCIceGatherer)) { + // Chrome, Chromium, Webview, Opera. + // Version matches Chrome/WebRTC version. + // Chrome 74 removed webkitGetUserMedia on http as well so we need the + // more complicated fallback to webkitRTCPeerConnection. + result.browser = 'chrome'; + result.version = extractVersion(navigator.userAgent, + /Chrom(e|ium)\/(\d+)\./, 2); + } else if (navigator.mediaDevices && + navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) { // Edge. + result.browser = 'edge'; + result.version = extractVersion(navigator.userAgent, + /Edge\/(\d+).(\d+)$/, 2); + } else if (window.RTCPeerConnection && + navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) { // Safari. + result.browser = 'safari'; + result.version = extractVersion(navigator.userAgent, + /AppleWebKit\/(\d+)\./, 1); + result.supportsUnifiedPlan = window.RTCRtpTransceiver && + 'currentDirection' in window.RTCRtpTransceiver.prototype; + } else { // Default fallthrough: not supported. + result.browser = 'Not a supported browser.'; + return result; + } + + return result; + } + + /** + * Checks if something is an object. + * + * @param {*} val The something you want to check. + * @return true if val is an object, false otherwise. + */ + function isObject$1(val) { + return Object.prototype.toString.call(val) === '[object Object]'; + } + + /** + * Remove all empty objects and undefined values + * from a nested object -- an enhanced and vanilla version + * of Lodash's `compact`. + */ + function compactObject(data) { + if (!isObject$1(data)) { + return data; + } + + return Object.keys(data).reduce(function(accumulator, key) { + const isObj = isObject$1(data[key]); + const value = isObj ? compactObject(data[key]) : data[key]; + const isEmptyObject = isObj && !Object.keys(value).length; + if (value === undefined || isEmptyObject) { + return accumulator; + } + return Object.assign(accumulator, {[key]: value}); + }, {}); + } + + /* iterates the stats graph recursively. */ + function walkStats(stats, base, resultSet) { + if (!base || resultSet.has(base.id)) { + return; + } + resultSet.set(base.id, base); + Object.keys(base).forEach(name => { + if (name.endsWith('Id')) { + walkStats(stats, stats.get(base[name]), resultSet); + } else if (name.endsWith('Ids')) { + base[name].forEach(id => { + walkStats(stats, stats.get(id), resultSet); + }); + } + }); + } + + /* filter getStats for a sender/receiver track. */ + function filterStats(result, track, outbound) { + const streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp'; + const filteredResult = new Map(); + if (track === null) { + return filteredResult; + } + const trackStats = []; + result.forEach(value => { + if (value.type === 'track' && + value.trackIdentifier === track.id) { + trackStats.push(value); + } + }); + trackStats.forEach(trackStat => { + result.forEach(stats => { + if (stats.type === streamStatsType && stats.trackId === trackStat.id) { + walkStats(result, stats, filteredResult); + } + }); + }); + return filteredResult; + } + + /* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + const logging = log$1; + + function shimGetUserMedia$3(window, browserDetails) { + const navigator = window && window.navigator; + + if (!navigator.mediaDevices) { + return; + } + + const constraintsToChrome_ = function(c) { + if (typeof c !== 'object' || c.mandatory || c.optional) { + return c; + } + const cc = {}; + Object.keys(c).forEach(key => { + if (key === 'require' || key === 'advanced' || key === 'mediaSource') { + return; + } + const r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]}; + if (r.exact !== undefined && typeof r.exact === 'number') { + r.min = r.max = r.exact; + } + const oldname_ = function(prefix, name) { + if (prefix) { + return prefix + name.charAt(0).toUpperCase() + name.slice(1); + } + return (name === 'deviceId') ? 'sourceId' : name; + }; + if (r.ideal !== undefined) { + cc.optional = cc.optional || []; + let oc = {}; + if (typeof r.ideal === 'number') { + oc[oldname_('min', key)] = r.ideal; + cc.optional.push(oc); + oc = {}; + oc[oldname_('max', key)] = r.ideal; + cc.optional.push(oc); + } else { + oc[oldname_('', key)] = r.ideal; + cc.optional.push(oc); + } + } + if (r.exact !== undefined && typeof r.exact !== 'number') { + cc.mandatory = cc.mandatory || {}; + cc.mandatory[oldname_('', key)] = r.exact; + } else { + ['min', 'max'].forEach(mix => { + if (r[mix] !== undefined) { + cc.mandatory = cc.mandatory || {}; + cc.mandatory[oldname_(mix, key)] = r[mix]; + } + }); + } + }); + if (c.advanced) { + cc.optional = (cc.optional || []).concat(c.advanced); + } + return cc; + }; + + const shimConstraints_ = function(constraints, func) { + if (browserDetails.version >= 61) { + return func(constraints); + } + constraints = JSON.parse(JSON.stringify(constraints)); + if (constraints && typeof constraints.audio === 'object') { + const remap = function(obj, a, b) { + if (a in obj && !(b in obj)) { + obj[b] = obj[a]; + delete obj[a]; + } + }; + constraints = JSON.parse(JSON.stringify(constraints)); + remap(constraints.audio, 'autoGainControl', 'googAutoGainControl'); + remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression'); + constraints.audio = constraintsToChrome_(constraints.audio); + } + if (constraints && typeof constraints.video === 'object') { + // Shim facingMode for mobile & surface pro. + let face = constraints.video.facingMode; + face = face && ((typeof face === 'object') ? face : {ideal: face}); + const getSupportedFacingModeLies = browserDetails.version < 66; + + if ((face && (face.exact === 'user' || face.exact === 'environment' || + face.ideal === 'user' || face.ideal === 'environment')) && + !(navigator.mediaDevices.getSupportedConstraints && + navigator.mediaDevices.getSupportedConstraints().facingMode && + !getSupportedFacingModeLies)) { + delete constraints.video.facingMode; + let matches; + if (face.exact === 'environment' || face.ideal === 'environment') { + matches = ['back', 'rear']; + } else if (face.exact === 'user' || face.ideal === 'user') { + matches = ['front']; + } + if (matches) { + // Look for matches in label, or use last cam for back (typical). + return navigator.mediaDevices.enumerateDevices() + .then(devices => { + devices = devices.filter(d => d.kind === 'videoinput'); + let dev = devices.find(d => matches.some(match => + d.label.toLowerCase().includes(match))); + if (!dev && devices.length && matches.includes('back')) { + dev = devices[devices.length - 1]; // more likely the back cam + } + if (dev) { + constraints.video.deviceId = face.exact ? {exact: dev.deviceId} : + {ideal: dev.deviceId}; + } + constraints.video = constraintsToChrome_(constraints.video); + logging('chrome: ' + JSON.stringify(constraints)); + return func(constraints); + }); + } + } + constraints.video = constraintsToChrome_(constraints.video); + } + logging('chrome: ' + JSON.stringify(constraints)); + return func(constraints); + }; + + const shimError_ = function(e) { + if (browserDetails.version >= 64) { + return e; + } + return { + name: { + PermissionDeniedError: 'NotAllowedError', + PermissionDismissedError: 'NotAllowedError', + InvalidStateError: 'NotAllowedError', + DevicesNotFoundError: 'NotFoundError', + ConstraintNotSatisfiedError: 'OverconstrainedError', + TrackStartError: 'NotReadableError', + MediaDeviceFailedDueToShutdown: 'NotAllowedError', + MediaDeviceKillSwitchOn: 'NotAllowedError', + TabCaptureError: 'AbortError', + ScreenCaptureError: 'AbortError', + DeviceCaptureError: 'AbortError' + }[e.name] || e.name, + message: e.message, + constraint: e.constraint || e.constraintName, + toString() { + return this.name + (this.message && ': ') + this.message; + } + }; + }; + + const getUserMedia_ = function(constraints, onSuccess, onError) { + shimConstraints_(constraints, c => { + navigator.webkitGetUserMedia(c, onSuccess, e => { + if (onError) { + onError(shimError_(e)); + } + }); + }); + }; + navigator.getUserMedia = getUserMedia_.bind(navigator); + + // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia + // function which returns a Promise, it does not accept spec-style + // constraints. + if (navigator.mediaDevices.getUserMedia) { + const origGetUserMedia = navigator.mediaDevices.getUserMedia. + bind(navigator.mediaDevices); + navigator.mediaDevices.getUserMedia = function(cs) { + return shimConstraints_(cs, c => origGetUserMedia(c).then(stream => { + if (c.audio && !stream.getAudioTracks().length || + c.video && !stream.getVideoTracks().length) { + stream.getTracks().forEach(track => { + track.stop(); + }); + throw new DOMException('', 'NotFoundError'); + } + return stream; + }, e => Promise.reject(shimError_(e)))); + }; + } + } + + /* + * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + function shimGetDisplayMedia$2(window, getSourceId) { + if (window.navigator.mediaDevices && + 'getDisplayMedia' in window.navigator.mediaDevices) { + return; + } + if (!(window.navigator.mediaDevices)) { + return; + } + // getSourceId is a function that returns a promise resolving with + // the sourceId of the screen/window/tab to be shared. + if (typeof getSourceId !== 'function') { + console.error('shimGetDisplayMedia: getSourceId argument is not ' + + 'a function'); + return; + } + window.navigator.mediaDevices.getDisplayMedia = + function getDisplayMedia(constraints) { + return getSourceId(constraints) + .then(sourceId => { + const widthSpecified = constraints.video && constraints.video.width; + const heightSpecified = constraints.video && + constraints.video.height; + const frameRateSpecified = constraints.video && + constraints.video.frameRate; + constraints.video = { + mandatory: { + chromeMediaSource: 'desktop', + chromeMediaSourceId: sourceId, + maxFrameRate: frameRateSpecified || 3 + } + }; + if (widthSpecified) { + constraints.video.mandatory.maxWidth = widthSpecified; + } + if (heightSpecified) { + constraints.video.mandatory.maxHeight = heightSpecified; + } + return window.navigator.mediaDevices.getUserMedia(constraints); + }); + }; + } + + /* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + + function shimMediaStream(window) { + window.MediaStream = window.MediaStream || window.webkitMediaStream; + } + + function shimOnTrack$1(window) { + if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in + window.RTCPeerConnection.prototype)) { + Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', { + get() { + return this._ontrack; + }, + set(f) { + if (this._ontrack) { + this.removeEventListener('track', this._ontrack); + } + this.addEventListener('track', this._ontrack = f); + }, + enumerable: true, + configurable: true + }); + const origSetRemoteDescription = + window.RTCPeerConnection.prototype.setRemoteDescription; + window.RTCPeerConnection.prototype.setRemoteDescription = + function setRemoteDescription() { + if (!this._ontrackpoly) { + this._ontrackpoly = (e) => { + // onaddstream does not fire when a track is added to an existing + // stream. But stream.onaddtrack is implemented so we use that. + e.stream.addEventListener('addtrack', te => { + let receiver; + if (window.RTCPeerConnection.prototype.getReceivers) { + receiver = this.getReceivers() + .find(r => r.track && r.track.id === te.track.id); + } else { + receiver = {track: te.track}; + } + + const event = new Event('track'); + event.track = te.track; + event.receiver = receiver; + event.transceiver = {receiver}; + event.streams = [e.stream]; + this.dispatchEvent(event); + }); + e.stream.getTracks().forEach(track => { + let receiver; + if (window.RTCPeerConnection.prototype.getReceivers) { + receiver = this.getReceivers() + .find(r => r.track && r.track.id === track.id); + } else { + receiver = {track}; + } + const event = new Event('track'); + event.track = track; + event.receiver = receiver; + event.transceiver = {receiver}; + event.streams = [e.stream]; + this.dispatchEvent(event); + }); + }; + this.addEventListener('addstream', this._ontrackpoly); + } + return origSetRemoteDescription.apply(this, arguments); + }; + } else { + // even if RTCRtpTransceiver is in window, it is only used and + // emitted in unified-plan. Unfortunately this means we need + // to unconditionally wrap the event. + wrapPeerConnectionEvent(window, 'track', e => { + if (!e.transceiver) { + Object.defineProperty(e, 'transceiver', + {value: {receiver: e.receiver}}); + } + return e; + }); + } + } + + function shimGetSendersWithDtmf(window) { + // Overrides addTrack/removeTrack, depends on shimAddTrackRemoveTrack. + if (typeof window === 'object' && window.RTCPeerConnection && + !('getSenders' in window.RTCPeerConnection.prototype) && + 'createDTMFSender' in window.RTCPeerConnection.prototype) { + const shimSenderWithDtmf = function(pc, track) { + return { + track, + get dtmf() { + if (this._dtmf === undefined) { + if (track.kind === 'audio') { + this._dtmf = pc.createDTMFSender(track); + } else { + this._dtmf = null; + } + } + return this._dtmf; + }, + _pc: pc + }; + }; + + // augment addTrack when getSenders is not available. + if (!window.RTCPeerConnection.prototype.getSenders) { + window.RTCPeerConnection.prototype.getSenders = function getSenders() { + this._senders = this._senders || []; + return this._senders.slice(); // return a copy of the internal state. + }; + const origAddTrack = window.RTCPeerConnection.prototype.addTrack; + window.RTCPeerConnection.prototype.addTrack = + function addTrack(track, stream) { + let sender = origAddTrack.apply(this, arguments); + if (!sender) { + sender = shimSenderWithDtmf(this, track); + this._senders.push(sender); + } + return sender; + }; + + const origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack; + window.RTCPeerConnection.prototype.removeTrack = + function removeTrack(sender) { + origRemoveTrack.apply(this, arguments); + const idx = this._senders.indexOf(sender); + if (idx !== -1) { + this._senders.splice(idx, 1); + } + }; + } + const origAddStream = window.RTCPeerConnection.prototype.addStream; + window.RTCPeerConnection.prototype.addStream = function addStream(stream) { + this._senders = this._senders || []; + origAddStream.apply(this, [stream]); + stream.getTracks().forEach(track => { + this._senders.push(shimSenderWithDtmf(this, track)); + }); + }; + + const origRemoveStream = window.RTCPeerConnection.prototype.removeStream; + window.RTCPeerConnection.prototype.removeStream = + function removeStream(stream) { + this._senders = this._senders || []; + origRemoveStream.apply(this, [stream]); + + stream.getTracks().forEach(track => { + const sender = this._senders.find(s => s.track === track); + if (sender) { // remove sender + this._senders.splice(this._senders.indexOf(sender), 1); + } + }); + }; + } else if (typeof window === 'object' && window.RTCPeerConnection && + 'getSenders' in window.RTCPeerConnection.prototype && + 'createDTMFSender' in window.RTCPeerConnection.prototype && + window.RTCRtpSender && + !('dtmf' in window.RTCRtpSender.prototype)) { + const origGetSenders = window.RTCPeerConnection.prototype.getSenders; + window.RTCPeerConnection.prototype.getSenders = function getSenders() { + const senders = origGetSenders.apply(this, []); + senders.forEach(sender => sender._pc = this); + return senders; + }; + + Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', { + get() { + if (this._dtmf === undefined) { + if (this.track.kind === 'audio') { + this._dtmf = this._pc.createDTMFSender(this.track); + } else { + this._dtmf = null; + } + } + return this._dtmf; + } + }); + } + } + + function shimGetStats(window) { + if (!window.RTCPeerConnection) { + return; + } + + const origGetStats = window.RTCPeerConnection.prototype.getStats; + window.RTCPeerConnection.prototype.getStats = function getStats() { + const [selector, onSucc, onErr] = arguments; + + // If selector is a function then we are in the old style stats so just + // pass back the original getStats format to avoid breaking old users. + if (arguments.length > 0 && typeof selector === 'function') { + return origGetStats.apply(this, arguments); + } + + // When spec-style getStats is supported, return those when called with + // either no arguments or the selector argument is null. + if (origGetStats.length === 0 && (arguments.length === 0 || + typeof selector !== 'function')) { + return origGetStats.apply(this, []); + } + + const fixChromeStats_ = function(response) { + const standardReport = {}; + const reports = response.result(); + reports.forEach(report => { + const standardStats = { + id: report.id, + timestamp: report.timestamp, + type: { + localcandidate: 'local-candidate', + remotecandidate: 'remote-candidate' + }[report.type] || report.type + }; + report.names().forEach(name => { + standardStats[name] = report.stat(name); + }); + standardReport[standardStats.id] = standardStats; + }); + + return standardReport; + }; + + // shim getStats with maplike support + const makeMapStats = function(stats) { + return new Map(Object.keys(stats).map(key => [key, stats[key]])); + }; + + if (arguments.length >= 2) { + const successCallbackWrapper_ = function(response) { + onSucc(makeMapStats(fixChromeStats_(response))); + }; + + return origGetStats.apply(this, [successCallbackWrapper_, + selector]); + } + + // promise-support + return new Promise((resolve, reject) => { + origGetStats.apply(this, [ + function(response) { + resolve(makeMapStats(fixChromeStats_(response))); + }, reject]); + }).then(onSucc, onErr); + }; + } + + function shimSenderReceiverGetStats(window) { + if (!(typeof window === 'object' && window.RTCPeerConnection && + window.RTCRtpSender && window.RTCRtpReceiver)) { + return; + } + + // shim sender stats. + if (!('getStats' in window.RTCRtpSender.prototype)) { + const origGetSenders = window.RTCPeerConnection.prototype.getSenders; + if (origGetSenders) { + window.RTCPeerConnection.prototype.getSenders = function getSenders() { + const senders = origGetSenders.apply(this, []); + senders.forEach(sender => sender._pc = this); + return senders; + }; + } + + const origAddTrack = window.RTCPeerConnection.prototype.addTrack; + if (origAddTrack) { + window.RTCPeerConnection.prototype.addTrack = function addTrack() { + const sender = origAddTrack.apply(this, arguments); + sender._pc = this; + return sender; + }; + } + window.RTCRtpSender.prototype.getStats = function getStats() { + const sender = this; + return this._pc.getStats().then(result => + /* Note: this will include stats of all senders that + * send a track with the same id as sender.track as + * it is not possible to identify the RTCRtpSender. + */ + filterStats(result, sender.track, true)); + }; + } + + // shim receiver stats. + if (!('getStats' in window.RTCRtpReceiver.prototype)) { + const origGetReceivers = window.RTCPeerConnection.prototype.getReceivers; + if (origGetReceivers) { + window.RTCPeerConnection.prototype.getReceivers = + function getReceivers() { + const receivers = origGetReceivers.apply(this, []); + receivers.forEach(receiver => receiver._pc = this); + return receivers; + }; + } + wrapPeerConnectionEvent(window, 'track', e => { + e.receiver._pc = e.srcElement; + return e; + }); + window.RTCRtpReceiver.prototype.getStats = function getStats() { + const receiver = this; + return this._pc.getStats().then(result => + filterStats(result, receiver.track, false)); + }; + } + + if (!('getStats' in window.RTCRtpSender.prototype && + 'getStats' in window.RTCRtpReceiver.prototype)) { + return; + } + + // shim RTCPeerConnection.getStats(track). + const origGetStats = window.RTCPeerConnection.prototype.getStats; + window.RTCPeerConnection.prototype.getStats = function getStats() { + if (arguments.length > 0 && + arguments[0] instanceof window.MediaStreamTrack) { + const track = arguments[0]; + let sender; + let receiver; + let err; + this.getSenders().forEach(s => { + if (s.track === track) { + if (sender) { + err = true; + } else { + sender = s; + } + } + }); + this.getReceivers().forEach(r => { + if (r.track === track) { + if (receiver) { + err = true; + } else { + receiver = r; + } + } + return r.track === track; + }); + if (err || (sender && receiver)) { + return Promise.reject(new DOMException( + 'There are more than one sender or receiver for the track.', + 'InvalidAccessError')); + } else if (sender) { + return sender.getStats(); + } else if (receiver) { + return receiver.getStats(); + } + return Promise.reject(new DOMException( + 'There is no sender or receiver for the track.', + 'InvalidAccessError')); + } + return origGetStats.apply(this, arguments); + }; + } + + function shimAddTrackRemoveTrackWithNative(window) { + // shim addTrack/removeTrack with native variants in order to make + // the interactions with legacy getLocalStreams behave as in other browsers. + // Keeps a mapping stream.id => [stream, rtpsenders...] + window.RTCPeerConnection.prototype.getLocalStreams = + function getLocalStreams() { + this._shimmedLocalStreams = this._shimmedLocalStreams || {}; + return Object.keys(this._shimmedLocalStreams) + .map(streamId => this._shimmedLocalStreams[streamId][0]); + }; + + const origAddTrack = window.RTCPeerConnection.prototype.addTrack; + window.RTCPeerConnection.prototype.addTrack = + function addTrack(track, stream) { + if (!stream) { + return origAddTrack.apply(this, arguments); + } + this._shimmedLocalStreams = this._shimmedLocalStreams || {}; + + const sender = origAddTrack.apply(this, arguments); + if (!this._shimmedLocalStreams[stream.id]) { + this._shimmedLocalStreams[stream.id] = [stream, sender]; + } else if (this._shimmedLocalStreams[stream.id].indexOf(sender) === -1) { + this._shimmedLocalStreams[stream.id].push(sender); + } + return sender; + }; + + const origAddStream = window.RTCPeerConnection.prototype.addStream; + window.RTCPeerConnection.prototype.addStream = function addStream(stream) { + this._shimmedLocalStreams = this._shimmedLocalStreams || {}; + + stream.getTracks().forEach(track => { + const alreadyExists = this.getSenders().find(s => s.track === track); + if (alreadyExists) { + throw new DOMException('Track already exists.', + 'InvalidAccessError'); + } + }); + const existingSenders = this.getSenders(); + origAddStream.apply(this, arguments); + const newSenders = this.getSenders() + .filter(newSender => existingSenders.indexOf(newSender) === -1); + this._shimmedLocalStreams[stream.id] = [stream].concat(newSenders); + }; + + const origRemoveStream = window.RTCPeerConnection.prototype.removeStream; + window.RTCPeerConnection.prototype.removeStream = + function removeStream(stream) { + this._shimmedLocalStreams = this._shimmedLocalStreams || {}; + delete this._shimmedLocalStreams[stream.id]; + return origRemoveStream.apply(this, arguments); + }; + + const origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack; + window.RTCPeerConnection.prototype.removeTrack = + function removeTrack(sender) { + this._shimmedLocalStreams = this._shimmedLocalStreams || {}; + if (sender) { + Object.keys(this._shimmedLocalStreams).forEach(streamId => { + const idx = this._shimmedLocalStreams[streamId].indexOf(sender); + if (idx !== -1) { + this._shimmedLocalStreams[streamId].splice(idx, 1); + } + if (this._shimmedLocalStreams[streamId].length === 1) { + delete this._shimmedLocalStreams[streamId]; + } + }); + } + return origRemoveTrack.apply(this, arguments); + }; + } + + function shimAddTrackRemoveTrack(window, browserDetails) { + if (!window.RTCPeerConnection) { + return; + } + // shim addTrack and removeTrack. + if (window.RTCPeerConnection.prototype.addTrack && + browserDetails.version >= 65) { + return shimAddTrackRemoveTrackWithNative(window); + } + + // also shim pc.getLocalStreams when addTrack is shimmed + // to return the original streams. + const origGetLocalStreams = window.RTCPeerConnection.prototype + .getLocalStreams; + window.RTCPeerConnection.prototype.getLocalStreams = + function getLocalStreams() { + const nativeStreams = origGetLocalStreams.apply(this); + this._reverseStreams = this._reverseStreams || {}; + return nativeStreams.map(stream => this._reverseStreams[stream.id]); + }; + + const origAddStream = window.RTCPeerConnection.prototype.addStream; + window.RTCPeerConnection.prototype.addStream = function addStream(stream) { + this._streams = this._streams || {}; + this._reverseStreams = this._reverseStreams || {}; + + stream.getTracks().forEach(track => { + const alreadyExists = this.getSenders().find(s => s.track === track); + if (alreadyExists) { + throw new DOMException('Track already exists.', + 'InvalidAccessError'); + } + }); + // Add identity mapping for consistency with addTrack. + // Unless this is being used with a stream from addTrack. + if (!this._reverseStreams[stream.id]) { + const newStream = new window.MediaStream(stream.getTracks()); + this._streams[stream.id] = newStream; + this._reverseStreams[newStream.id] = stream; + stream = newStream; + } + origAddStream.apply(this, [stream]); + }; + + const origRemoveStream = window.RTCPeerConnection.prototype.removeStream; + window.RTCPeerConnection.prototype.removeStream = + function removeStream(stream) { + this._streams = this._streams || {}; + this._reverseStreams = this._reverseStreams || {}; + + origRemoveStream.apply(this, [(this._streams[stream.id] || stream)]); + delete this._reverseStreams[(this._streams[stream.id] ? + this._streams[stream.id].id : stream.id)]; + delete this._streams[stream.id]; + }; + + window.RTCPeerConnection.prototype.addTrack = + function addTrack(track, stream) { + if (this.signalingState === 'closed') { + throw new DOMException( + 'The RTCPeerConnection\'s signalingState is \'closed\'.', + 'InvalidStateError'); + } + const streams = [].slice.call(arguments, 1); + if (streams.length !== 1 || + !streams[0].getTracks().find(t => t === track)) { + // this is not fully correct but all we can manage without + // [[associated MediaStreams]] internal slot. + throw new DOMException( + 'The adapter.js addTrack polyfill only supports a single ' + + ' stream which is associated with the specified track.', + 'NotSupportedError'); + } + + const alreadyExists = this.getSenders().find(s => s.track === track); + if (alreadyExists) { + throw new DOMException('Track already exists.', + 'InvalidAccessError'); + } + + this._streams = this._streams || {}; + this._reverseStreams = this._reverseStreams || {}; + const oldStream = this._streams[stream.id]; + if (oldStream) { + // this is using odd Chrome behaviour, use with caution: + // https://bugs.chromium.org/p/webrtc/issues/detail?id=7815 + // Note: we rely on the high-level addTrack/dtmf shim to + // create the sender with a dtmf sender. + oldStream.addTrack(track); + + // Trigger ONN async. + Promise.resolve().then(() => { + this.dispatchEvent(new Event('negotiationneeded')); + }); + } else { + const newStream = new window.MediaStream([track]); + this._streams[stream.id] = newStream; + this._reverseStreams[newStream.id] = stream; + this.addStream(newStream); + } + return this.getSenders().find(s => s.track === track); + }; + + // replace the internal stream id with the external one and + // vice versa. + function replaceInternalStreamId(pc, description) { + let sdp = description.sdp; + Object.keys(pc._reverseStreams || []).forEach(internalId => { + const externalStream = pc._reverseStreams[internalId]; + const internalStream = pc._streams[externalStream.id]; + sdp = sdp.replace(new RegExp(internalStream.id, 'g'), + externalStream.id); + }); + return new RTCSessionDescription({ + type: description.type, + sdp + }); + } + function replaceExternalStreamId(pc, description) { + let sdp = description.sdp; + Object.keys(pc._reverseStreams || []).forEach(internalId => { + const externalStream = pc._reverseStreams[internalId]; + const internalStream = pc._streams[externalStream.id]; + sdp = sdp.replace(new RegExp(externalStream.id, 'g'), + internalStream.id); + }); + return new RTCSessionDescription({ + type: description.type, + sdp + }); + } + ['createOffer', 'createAnswer'].forEach(function(method) { + const nativeMethod = window.RTCPeerConnection.prototype[method]; + const methodObj = {[method]() { + const args = arguments; + const isLegacyCall = arguments.length && + typeof arguments[0] === 'function'; + if (isLegacyCall) { + return nativeMethod.apply(this, [ + (description) => { + const desc = replaceInternalStreamId(this, description); + args[0].apply(null, [desc]); + }, + (err) => { + if (args[1]) { + args[1].apply(null, err); + } + }, arguments[2] + ]); + } + return nativeMethod.apply(this, arguments) + .then(description => replaceInternalStreamId(this, description)); + }}; + window.RTCPeerConnection.prototype[method] = methodObj[method]; + }); + + const origSetLocalDescription = + window.RTCPeerConnection.prototype.setLocalDescription; + window.RTCPeerConnection.prototype.setLocalDescription = + function setLocalDescription() { + if (!arguments.length || !arguments[0].type) { + return origSetLocalDescription.apply(this, arguments); + } + arguments[0] = replaceExternalStreamId(this, arguments[0]); + return origSetLocalDescription.apply(this, arguments); + }; + + // TODO: mangle getStats: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamstats-streamidentifier + + const origLocalDescription = Object.getOwnPropertyDescriptor( + window.RTCPeerConnection.prototype, 'localDescription'); + Object.defineProperty(window.RTCPeerConnection.prototype, + 'localDescription', { + get() { + const description = origLocalDescription.get.apply(this); + if (description.type === '') { + return description; + } + return replaceInternalStreamId(this, description); + } + }); + + window.RTCPeerConnection.prototype.removeTrack = + function removeTrack(sender) { + if (this.signalingState === 'closed') { + throw new DOMException( + 'The RTCPeerConnection\'s signalingState is \'closed\'.', + 'InvalidStateError'); + } + // We can not yet check for sender instanceof RTCRtpSender + // since we shim RTPSender. So we check if sender._pc is set. + if (!sender._pc) { + throw new DOMException('Argument 1 of RTCPeerConnection.removeTrack ' + + 'does not implement interface RTCRtpSender.', 'TypeError'); + } + const isLocal = sender._pc === this; + if (!isLocal) { + throw new DOMException('Sender was not created by this connection.', + 'InvalidAccessError'); + } + + // Search for the native stream the senders track belongs to. + this._streams = this._streams || {}; + let stream; + Object.keys(this._streams).forEach(streamid => { + const hasTrack = this._streams[streamid].getTracks() + .find(track => sender.track === track); + if (hasTrack) { + stream = this._streams[streamid]; + } + }); + + if (stream) { + if (stream.getTracks().length === 1) { + // if this is the last track of the stream, remove the stream. This + // takes care of any shimmed _senders. + this.removeStream(this._reverseStreams[stream.id]); + } else { + // relying on the same odd chrome behaviour as above. + stream.removeTrack(sender.track); + } + this.dispatchEvent(new Event('negotiationneeded')); + } + }; + } + + function shimPeerConnection$2(window, browserDetails) { + if (!window.RTCPeerConnection && window.webkitRTCPeerConnection) { + // very basic support for old versions. + window.RTCPeerConnection = window.webkitRTCPeerConnection; + } + if (!window.RTCPeerConnection) { + return; + } + + // shim implicit creation of RTCSessionDescription/RTCIceCandidate + if (browserDetails.version < 53) { + ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'] + .forEach(function(method) { + const nativeMethod = window.RTCPeerConnection.prototype[method]; + const methodObj = {[method]() { + arguments[0] = new ((method === 'addIceCandidate') ? + window.RTCIceCandidate : + window.RTCSessionDescription)(arguments[0]); + return nativeMethod.apply(this, arguments); + }}; + window.RTCPeerConnection.prototype[method] = methodObj[method]; + }); + } + } + + // Attempt to fix ONN in plan-b mode. + function fixNegotiationNeeded(window, browserDetails) { + wrapPeerConnectionEvent(window, 'negotiationneeded', e => { + const pc = e.target; + if (browserDetails.version < 72 || (pc.getConfiguration && + pc.getConfiguration().sdpSemantics === 'plan-b')) { + if (pc.signalingState !== 'stable') { + return; + } + } + return e; + }); + } + + var chromeShim = /*#__PURE__*/Object.freeze({ + __proto__: null, + shimMediaStream: shimMediaStream, + shimOnTrack: shimOnTrack$1, + shimGetSendersWithDtmf: shimGetSendersWithDtmf, + shimGetStats: shimGetStats, + shimSenderReceiverGetStats: shimSenderReceiverGetStats, + shimAddTrackRemoveTrackWithNative: shimAddTrackRemoveTrackWithNative, + shimAddTrackRemoveTrack: shimAddTrackRemoveTrack, + shimPeerConnection: shimPeerConnection$2, + fixNegotiationNeeded: fixNegotiationNeeded, + shimGetUserMedia: shimGetUserMedia$3, + shimGetDisplayMedia: shimGetDisplayMedia$2 + }); + + /* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + // Edge does not like + // 1) stun: filtered after 14393 unless ?transport=udp is present + // 2) turn: that does not have all of turn:host:port?transport=udp + // 3) turn: with ipv6 addresses + // 4) turn: occurring muliple times + function filterIceServers$1(iceServers, edgeVersion) { + let hasTurn = false; + iceServers = JSON.parse(JSON.stringify(iceServers)); + return iceServers.filter(server => { + if (server && (server.urls || server.url)) { + let urls = server.urls || server.url; + if (server.url && !server.urls) { + deprecated('RTCIceServer.url', 'RTCIceServer.urls'); + } + const isString = typeof urls === 'string'; + if (isString) { + urls = [urls]; + } + urls = urls.filter(url => { + // filter STUN unconditionally. + if (url.indexOf('stun:') === 0) { + return false; + } + + const validTurn = url.startsWith('turn') && + !url.startsWith('turn:[') && + url.includes('transport=udp'); + if (validTurn && !hasTurn) { + hasTurn = true; + return true; + } + return validTurn && !hasTurn; + }); + + delete server.url; + server.urls = isString ? urls[0] : urls; + return !!urls.length; + } + }); + } + + function createCommonjsModule(fn) { + var module = { exports: {} }; + return fn(module, module.exports), module.exports; + } + + /* eslint-env node */ + + var sdp = createCommonjsModule(function (module) { + + // SDP helpers. + var SDPUtils = {}; + + // Generate an alphanumeric identifier for cname or mids. + // TODO: use UUIDs instead? https://gist.github.com/jed/982883 + SDPUtils.generateIdentifier = function() { + return Math.random().toString(36).substr(2, 10); + }; + + // The RTCP CNAME used by all peerconnections from the same JS. + SDPUtils.localCName = SDPUtils.generateIdentifier(); + + // Splits SDP into lines, dealing with both CRLF and LF. + SDPUtils.splitLines = function(blob) { + return blob.trim().split('\n').map(function(line) { + return line.trim(); + }); + }; + // Splits SDP into sessionpart and mediasections. Ensures CRLF. + SDPUtils.splitSections = function(blob) { + var parts = blob.split('\nm='); + return parts.map(function(part, index) { + return (index > 0 ? 'm=' + part : part).trim() + '\r\n'; + }); + }; + + // returns the session description. + SDPUtils.getDescription = function(blob) { + var sections = SDPUtils.splitSections(blob); + return sections && sections[0]; + }; + + // returns the individual media sections. + SDPUtils.getMediaSections = function(blob) { + var sections = SDPUtils.splitSections(blob); + sections.shift(); + return sections; + }; + + // Returns lines that start with a certain prefix. + SDPUtils.matchPrefix = function(blob, prefix) { + return SDPUtils.splitLines(blob).filter(function(line) { + return line.indexOf(prefix) === 0; + }); + }; + + // Parses an ICE candidate line. Sample input: + // candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8 + // rport 55996" + SDPUtils.parseCandidate = function(line) { + var parts; + // Parse both variants. + if (line.indexOf('a=candidate:') === 0) { + parts = line.substring(12).split(' '); + } else { + parts = line.substring(10).split(' '); + } + + var candidate = { + foundation: parts[0], + component: parseInt(parts[1], 10), + protocol: parts[2].toLowerCase(), + priority: parseInt(parts[3], 10), + ip: parts[4], + address: parts[4], // address is an alias for ip. + port: parseInt(parts[5], 10), + // skip parts[6] == 'typ' + type: parts[7] + }; + + for (var i = 8; i < parts.length; i += 2) { + switch (parts[i]) { + case 'raddr': + candidate.relatedAddress = parts[i + 1]; + break; + case 'rport': + candidate.relatedPort = parseInt(parts[i + 1], 10); + break; + case 'tcptype': + candidate.tcpType = parts[i + 1]; + break; + case 'ufrag': + candidate.ufrag = parts[i + 1]; // for backward compability. + candidate.usernameFragment = parts[i + 1]; + break; + default: // extension handling, in particular ufrag + candidate[parts[i]] = parts[i + 1]; + break; + } + } + return candidate; + }; + + // Translates a candidate object into SDP candidate attribute. + SDPUtils.writeCandidate = function(candidate) { + var sdp = []; + sdp.push(candidate.foundation); + sdp.push(candidate.component); + sdp.push(candidate.protocol.toUpperCase()); + sdp.push(candidate.priority); + sdp.push(candidate.address || candidate.ip); + sdp.push(candidate.port); + + var type = candidate.type; + sdp.push('typ'); + sdp.push(type); + if (type !== 'host' && candidate.relatedAddress && + candidate.relatedPort) { + sdp.push('raddr'); + sdp.push(candidate.relatedAddress); + sdp.push('rport'); + sdp.push(candidate.relatedPort); + } + if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') { + sdp.push('tcptype'); + sdp.push(candidate.tcpType); + } + if (candidate.usernameFragment || candidate.ufrag) { + sdp.push('ufrag'); + sdp.push(candidate.usernameFragment || candidate.ufrag); + } + return 'candidate:' + sdp.join(' '); + }; + + // Parses an ice-options line, returns an array of option tags. + // a=ice-options:foo bar + SDPUtils.parseIceOptions = function(line) { + return line.substr(14).split(' '); + }; + + // Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input: + // a=rtpmap:111 opus/48000/2 + SDPUtils.parseRtpMap = function(line) { + var parts = line.substr(9).split(' '); + var parsed = { + payloadType: parseInt(parts.shift(), 10) // was: id + }; + + parts = parts[0].split('/'); + + parsed.name = parts[0]; + parsed.clockRate = parseInt(parts[1], 10); // was: clockrate + parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1; + // legacy alias, got renamed back to channels in ORTC. + parsed.numChannels = parsed.channels; + return parsed; + }; + + // Generate an a=rtpmap line from RTCRtpCodecCapability or + // RTCRtpCodecParameters. + SDPUtils.writeRtpMap = function(codec) { + var pt = codec.payloadType; + if (codec.preferredPayloadType !== undefined) { + pt = codec.preferredPayloadType; + } + var channels = codec.channels || codec.numChannels || 1; + return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate + + (channels !== 1 ? '/' + channels : '') + '\r\n'; + }; + + // Parses an a=extmap line (headerextension from RFC 5285). Sample input: + // a=extmap:2 urn:ietf:params:rtp-hdrext:toffset + // a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset + SDPUtils.parseExtmap = function(line) { + var parts = line.substr(9).split(' '); + return { + id: parseInt(parts[0], 10), + direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv', + uri: parts[1] + }; + }; + + // Generates a=extmap line from RTCRtpHeaderExtensionParameters or + // RTCRtpHeaderExtension. + SDPUtils.writeExtmap = function(headerExtension) { + return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) + + (headerExtension.direction && headerExtension.direction !== 'sendrecv' + ? '/' + headerExtension.direction + : '') + + ' ' + headerExtension.uri + '\r\n'; + }; + + // Parses an ftmp line, returns dictionary. Sample input: + // a=fmtp:96 vbr=on;cng=on + // Also deals with vbr=on; cng=on + SDPUtils.parseFmtp = function(line) { + var parsed = {}; + var kv; + var parts = line.substr(line.indexOf(' ') + 1).split(';'); + for (var j = 0; j < parts.length; j++) { + kv = parts[j].trim().split('='); + parsed[kv[0].trim()] = kv[1]; + } + return parsed; + }; + + // Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters. + SDPUtils.writeFmtp = function(codec) { + var line = ''; + var pt = codec.payloadType; + if (codec.preferredPayloadType !== undefined) { + pt = codec.preferredPayloadType; + } + if (codec.parameters && Object.keys(codec.parameters).length) { + var params = []; + Object.keys(codec.parameters).forEach(function(param) { + if (codec.parameters[param]) { + params.push(param + '=' + codec.parameters[param]); + } else { + params.push(param); + } + }); + line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n'; + } + return line; + }; + + // Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input: + // a=rtcp-fb:98 nack rpsi + SDPUtils.parseRtcpFb = function(line) { + var parts = line.substr(line.indexOf(' ') + 1).split(' '); + return { + type: parts.shift(), + parameter: parts.join(' ') + }; + }; + // Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters. + SDPUtils.writeRtcpFb = function(codec) { + var lines = ''; + var pt = codec.payloadType; + if (codec.preferredPayloadType !== undefined) { + pt = codec.preferredPayloadType; + } + if (codec.rtcpFeedback && codec.rtcpFeedback.length) { + // FIXME: special handling for trr-int? + codec.rtcpFeedback.forEach(function(fb) { + lines += 'a=rtcp-fb:' + pt + ' ' + fb.type + + (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') + + '\r\n'; + }); + } + return lines; + }; + + // Parses an RFC 5576 ssrc media attribute. Sample input: + // a=ssrc:3735928559 cname:something + SDPUtils.parseSsrcMedia = function(line) { + var sp = line.indexOf(' '); + var parts = { + ssrc: parseInt(line.substr(7, sp - 7), 10) + }; + var colon = line.indexOf(':', sp); + if (colon > -1) { + parts.attribute = line.substr(sp + 1, colon - sp - 1); + parts.value = line.substr(colon + 1); + } else { + parts.attribute = line.substr(sp + 1); + } + return parts; + }; + + SDPUtils.parseSsrcGroup = function(line) { + var parts = line.substr(13).split(' '); + return { + semantics: parts.shift(), + ssrcs: parts.map(function(ssrc) { + return parseInt(ssrc, 10); + }) + }; + }; + + // Extracts the MID (RFC 5888) from a media section. + // returns the MID or undefined if no mid line was found. + SDPUtils.getMid = function(mediaSection) { + var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0]; + if (mid) { + return mid.substr(6); + } + }; + + SDPUtils.parseFingerprint = function(line) { + var parts = line.substr(14).split(' '); + return { + algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge. + value: parts[1] + }; + }; + + // Extracts DTLS parameters from SDP media section or sessionpart. + // FIXME: for consistency with other functions this should only + // get the fingerprint line as input. See also getIceParameters. + SDPUtils.getDtlsParameters = function(mediaSection, sessionpart) { + var lines = SDPUtils.matchPrefix(mediaSection + sessionpart, + 'a=fingerprint:'); + // Note: a=setup line is ignored since we use the 'auto' role. + // Note2: 'algorithm' is not case sensitive except in Edge. + return { + role: 'auto', + fingerprints: lines.map(SDPUtils.parseFingerprint) + }; + }; + + // Serializes DTLS parameters to SDP. + SDPUtils.writeDtlsParameters = function(params, setupType) { + var sdp = 'a=setup:' + setupType + '\r\n'; + params.fingerprints.forEach(function(fp) { + sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n'; + }); + return sdp; + }; + + // Parses a=crypto lines into + // https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members + SDPUtils.parseCryptoLine = function(line) { + var parts = line.substr(9).split(' '); + return { + tag: parseInt(parts[0], 10), + cryptoSuite: parts[1], + keyParams: parts[2], + sessionParams: parts.slice(3), + }; + }; + + SDPUtils.writeCryptoLine = function(parameters) { + return 'a=crypto:' + parameters.tag + ' ' + + parameters.cryptoSuite + ' ' + + (typeof parameters.keyParams === 'object' + ? SDPUtils.writeCryptoKeyParams(parameters.keyParams) + : parameters.keyParams) + + (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') + + '\r\n'; + }; + + // Parses the crypto key parameters into + // https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam* + SDPUtils.parseCryptoKeyParams = function(keyParams) { + if (keyParams.indexOf('inline:') !== 0) { + return null; + } + var parts = keyParams.substr(7).split('|'); + return { + keyMethod: 'inline', + keySalt: parts[0], + lifeTime: parts[1], + mkiValue: parts[2] ? parts[2].split(':')[0] : undefined, + mkiLength: parts[2] ? parts[2].split(':')[1] : undefined, + }; + }; + + SDPUtils.writeCryptoKeyParams = function(keyParams) { + return keyParams.keyMethod + ':' + + keyParams.keySalt + + (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') + + (keyParams.mkiValue && keyParams.mkiLength + ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength + : ''); + }; + + // Extracts all SDES paramters. + SDPUtils.getCryptoParameters = function(mediaSection, sessionpart) { + var lines = SDPUtils.matchPrefix(mediaSection + sessionpart, + 'a=crypto:'); + return lines.map(SDPUtils.parseCryptoLine); + }; + + // Parses ICE information from SDP media section or sessionpart. + // FIXME: for consistency with other functions this should only + // get the ice-ufrag and ice-pwd lines as input. + SDPUtils.getIceParameters = function(mediaSection, sessionpart) { + var ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart, + 'a=ice-ufrag:')[0]; + var pwd = SDPUtils.matchPrefix(mediaSection + sessionpart, + 'a=ice-pwd:')[0]; + if (!(ufrag && pwd)) { + return null; + } + return { + usernameFragment: ufrag.substr(12), + password: pwd.substr(10), + }; + }; + + // Serializes ICE parameters to SDP. + SDPUtils.writeIceParameters = function(params) { + return 'a=ice-ufrag:' + params.usernameFragment + '\r\n' + + 'a=ice-pwd:' + params.password + '\r\n'; + }; + + // Parses the SDP media section and returns RTCRtpParameters. + SDPUtils.parseRtpParameters = function(mediaSection) { + var description = { + codecs: [], + headerExtensions: [], + fecMechanisms: [], + rtcp: [] + }; + var lines = SDPUtils.splitLines(mediaSection); + var mline = lines[0].split(' '); + for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..] + var pt = mline[i]; + var rtpmapline = SDPUtils.matchPrefix( + mediaSection, 'a=rtpmap:' + pt + ' ')[0]; + if (rtpmapline) { + var codec = SDPUtils.parseRtpMap(rtpmapline); + var fmtps = SDPUtils.matchPrefix( + mediaSection, 'a=fmtp:' + pt + ' '); + // Only the first a=fmtp: is considered. + codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {}; + codec.rtcpFeedback = SDPUtils.matchPrefix( + mediaSection, 'a=rtcp-fb:' + pt + ' ') + .map(SDPUtils.parseRtcpFb); + description.codecs.push(codec); + // parse FEC mechanisms from rtpmap lines. + switch (codec.name.toUpperCase()) { + case 'RED': + case 'ULPFEC': + description.fecMechanisms.push(codec.name.toUpperCase()); + break; + } + } + } + SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function(line) { + description.headerExtensions.push(SDPUtils.parseExtmap(line)); + }); + // FIXME: parse rtcp. + return description; + }; + + // Generates parts of the SDP media section describing the capabilities / + // parameters. + SDPUtils.writeRtpDescription = function(kind, caps) { + var sdp = ''; + + // Build the mline. + sdp += 'm=' + kind + ' '; + sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs. + sdp += ' UDP/TLS/RTP/SAVPF '; + sdp += caps.codecs.map(function(codec) { + if (codec.preferredPayloadType !== undefined) { + return codec.preferredPayloadType; + } + return codec.payloadType; + }).join(' ') + '\r\n'; + + sdp += 'c=IN IP4 0.0.0.0\r\n'; + sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n'; + + // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb. + caps.codecs.forEach(function(codec) { + sdp += SDPUtils.writeRtpMap(codec); + sdp += SDPUtils.writeFmtp(codec); + sdp += SDPUtils.writeRtcpFb(codec); + }); + var maxptime = 0; + caps.codecs.forEach(function(codec) { + if (codec.maxptime > maxptime) { + maxptime = codec.maxptime; + } + }); + if (maxptime > 0) { + sdp += 'a=maxptime:' + maxptime + '\r\n'; + } + sdp += 'a=rtcp-mux\r\n'; + + if (caps.headerExtensions) { + caps.headerExtensions.forEach(function(extension) { + sdp += SDPUtils.writeExtmap(extension); + }); + } + // FIXME: write fecMechanisms. + return sdp; + }; + + // Parses the SDP media section and returns an array of + // RTCRtpEncodingParameters. + SDPUtils.parseRtpEncodingParameters = function(mediaSection) { + var encodingParameters = []; + var description = SDPUtils.parseRtpParameters(mediaSection); + var hasRed = description.fecMechanisms.indexOf('RED') !== -1; + var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1; + + // filter a=ssrc:... cname:, ignore PlanB-msid + var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:') + .map(function(line) { + return SDPUtils.parseSsrcMedia(line); + }) + .filter(function(parts) { + return parts.attribute === 'cname'; + }); + var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc; + var secondarySsrc; + + var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID') + .map(function(line) { + var parts = line.substr(17).split(' '); + return parts.map(function(part) { + return parseInt(part, 10); + }); + }); + if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) { + secondarySsrc = flows[0][1]; + } + + description.codecs.forEach(function(codec) { + if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) { + var encParam = { + ssrc: primarySsrc, + codecPayloadType: parseInt(codec.parameters.apt, 10) + }; + if (primarySsrc && secondarySsrc) { + encParam.rtx = {ssrc: secondarySsrc}; + } + encodingParameters.push(encParam); + if (hasRed) { + encParam = JSON.parse(JSON.stringify(encParam)); + encParam.fec = { + ssrc: primarySsrc, + mechanism: hasUlpfec ? 'red+ulpfec' : 'red' + }; + encodingParameters.push(encParam); + } + } + }); + if (encodingParameters.length === 0 && primarySsrc) { + encodingParameters.push({ + ssrc: primarySsrc + }); + } + + // we support both b=AS and b=TIAS but interpret AS as TIAS. + var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b='); + if (bandwidth.length) { + if (bandwidth[0].indexOf('b=TIAS:') === 0) { + bandwidth = parseInt(bandwidth[0].substr(7), 10); + } else if (bandwidth[0].indexOf('b=AS:') === 0) { + // use formula from JSEP to convert b=AS to TIAS value. + bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95 + - (50 * 40 * 8); + } else { + bandwidth = undefined; + } + encodingParameters.forEach(function(params) { + params.maxBitrate = bandwidth; + }); + } + return encodingParameters; + }; + + // parses http://draft.ortc.org/#rtcrtcpparameters* + SDPUtils.parseRtcpParameters = function(mediaSection) { + var rtcpParameters = {}; + + // Gets the first SSRC. Note tha with RTX there might be multiple + // SSRCs. + var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:') + .map(function(line) { + return SDPUtils.parseSsrcMedia(line); + }) + .filter(function(obj) { + return obj.attribute === 'cname'; + })[0]; + if (remoteSsrc) { + rtcpParameters.cname = remoteSsrc.value; + rtcpParameters.ssrc = remoteSsrc.ssrc; + } + + // Edge uses the compound attribute instead of reducedSize + // compound is !reducedSize + var rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize'); + rtcpParameters.reducedSize = rsize.length > 0; + rtcpParameters.compound = rsize.length === 0; + + // parses the rtcp-mux attrіbute. + // Note that Edge does not support unmuxed RTCP. + var mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux'); + rtcpParameters.mux = mux.length > 0; + + return rtcpParameters; + }; + + // parses either a=msid: or a=ssrc:... msid lines and returns + // the id of the MediaStream and MediaStreamTrack. + SDPUtils.parseMsid = function(mediaSection) { + var parts; + var spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:'); + if (spec.length === 1) { + parts = spec[0].substr(7).split(' '); + return {stream: parts[0], track: parts[1]}; + } + var planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:') + .map(function(line) { + return SDPUtils.parseSsrcMedia(line); + }) + .filter(function(msidParts) { + return msidParts.attribute === 'msid'; + }); + if (planB.length > 0) { + parts = planB[0].value.split(' '); + return {stream: parts[0], track: parts[1]}; + } + }; + + // SCTP + // parses draft-ietf-mmusic-sctp-sdp-26 first and falls back + // to draft-ietf-mmusic-sctp-sdp-05 + SDPUtils.parseSctpDescription = function(mediaSection) { + var mline = SDPUtils.parseMLine(mediaSection); + var maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:'); + var maxMessageSize; + if (maxSizeLine.length > 0) { + maxMessageSize = parseInt(maxSizeLine[0].substr(19), 10); + } + if (isNaN(maxMessageSize)) { + maxMessageSize = 65536; + } + var sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:'); + if (sctpPort.length > 0) { + return { + port: parseInt(sctpPort[0].substr(12), 10), + protocol: mline.fmt, + maxMessageSize: maxMessageSize + }; + } + var sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:'); + if (sctpMapLines.length > 0) { + var parts = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:')[0] + .substr(10) + .split(' '); + return { + port: parseInt(parts[0], 10), + protocol: parts[1], + maxMessageSize: maxMessageSize + }; + } + }; + + // SCTP + // outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers + // support by now receiving in this format, unless we originally parsed + // as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line + // protocol of DTLS/SCTP -- without UDP/ or TCP/) + SDPUtils.writeSctpDescription = function(media, sctp) { + var output = []; + if (media.protocol !== 'DTLS/SCTP') { + output = [ + 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\r\n', + 'c=IN IP4 0.0.0.0\r\n', + 'a=sctp-port:' + sctp.port + '\r\n' + ]; + } else { + output = [ + 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\r\n', + 'c=IN IP4 0.0.0.0\r\n', + 'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\r\n' + ]; + } + if (sctp.maxMessageSize !== undefined) { + output.push('a=max-message-size:' + sctp.maxMessageSize + '\r\n'); + } + return output.join(''); + }; + + // Generate a session ID for SDP. + // https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1 + // recommends using a cryptographically random +ve 64-bit value + // but right now this should be acceptable and within the right range + SDPUtils.generateSessionId = function() { + return Math.random().toString().substr(2, 21); + }; + + // Write boilder plate for start of SDP + // sessId argument is optional - if not supplied it will + // be generated randomly + // sessVersion is optional and defaults to 2 + // sessUser is optional and defaults to 'thisisadapterortc' + SDPUtils.writeSessionBoilerplate = function(sessId, sessVer, sessUser) { + var sessionId; + var version = sessVer !== undefined ? sessVer : 2; + if (sessId) { + sessionId = sessId; + } else { + sessionId = SDPUtils.generateSessionId(); + } + var user = sessUser || 'thisisadapterortc'; + // FIXME: sess-id should be an NTP timestamp. + return 'v=0\r\n' + + 'o=' + user + ' ' + sessionId + ' ' + version + + ' IN IP4 127.0.0.1\r\n' + + 's=-\r\n' + + 't=0 0\r\n'; + }; + + SDPUtils.writeMediaSection = function(transceiver, caps, type, stream) { + var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps); + + // Map ICE parameters (ufrag, pwd) to SDP. + sdp += SDPUtils.writeIceParameters( + transceiver.iceGatherer.getLocalParameters()); + + // Map DTLS parameters to SDP. + sdp += SDPUtils.writeDtlsParameters( + transceiver.dtlsTransport.getLocalParameters(), + type === 'offer' ? 'actpass' : 'active'); + + sdp += 'a=mid:' + transceiver.mid + '\r\n'; + + if (transceiver.direction) { + sdp += 'a=' + transceiver.direction + '\r\n'; + } else if (transceiver.rtpSender && transceiver.rtpReceiver) { + sdp += 'a=sendrecv\r\n'; + } else if (transceiver.rtpSender) { + sdp += 'a=sendonly\r\n'; + } else if (transceiver.rtpReceiver) { + sdp += 'a=recvonly\r\n'; + } else { + sdp += 'a=inactive\r\n'; + } + + if (transceiver.rtpSender) { + // spec. + var msid = 'msid:' + stream.id + ' ' + + transceiver.rtpSender.track.id + '\r\n'; + sdp += 'a=' + msid; + + // for Chrome. + sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc + + ' ' + msid; + if (transceiver.sendEncodingParameters[0].rtx) { + sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc + + ' ' + msid; + sdp += 'a=ssrc-group:FID ' + + transceiver.sendEncodingParameters[0].ssrc + ' ' + + transceiver.sendEncodingParameters[0].rtx.ssrc + + '\r\n'; + } + } + // FIXME: this should be written by writeRtpDescription. + sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc + + ' cname:' + SDPUtils.localCName + '\r\n'; + if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) { + sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc + + ' cname:' + SDPUtils.localCName + '\r\n'; + } + return sdp; + }; + + // Gets the direction from the mediaSection or the sessionpart. + SDPUtils.getDirection = function(mediaSection, sessionpart) { + // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv. + var lines = SDPUtils.splitLines(mediaSection); + for (var i = 0; i < lines.length; i++) { + switch (lines[i]) { + case 'a=sendrecv': + case 'a=sendonly': + case 'a=recvonly': + case 'a=inactive': + return lines[i].substr(2); + // FIXME: What should happen here? + } + } + if (sessionpart) { + return SDPUtils.getDirection(sessionpart); + } + return 'sendrecv'; + }; + + SDPUtils.getKind = function(mediaSection) { + var lines = SDPUtils.splitLines(mediaSection); + var mline = lines[0].split(' '); + return mline[0].substr(2); + }; + + SDPUtils.isRejected = function(mediaSection) { + return mediaSection.split(' ', 2)[1] === '0'; + }; + + SDPUtils.parseMLine = function(mediaSection) { + var lines = SDPUtils.splitLines(mediaSection); + var parts = lines[0].substr(2).split(' '); + return { + kind: parts[0], + port: parseInt(parts[1], 10), + protocol: parts[2], + fmt: parts.slice(3).join(' ') + }; + }; + + SDPUtils.parseOLine = function(mediaSection) { + var line = SDPUtils.matchPrefix(mediaSection, 'o=')[0]; + var parts = line.substr(2).split(' '); + return { + username: parts[0], + sessionId: parts[1], + sessionVersion: parseInt(parts[2], 10), + netType: parts[3], + addressType: parts[4], + address: parts[5] + }; + }; + + // a very naive interpretation of a valid SDP. + SDPUtils.isValidSDP = function(blob) { + if (typeof blob !== 'string' || blob.length === 0) { + return false; + } + var lines = SDPUtils.splitLines(blob); + for (var i = 0; i < lines.length; i++) { + if (lines[i].length < 2 || lines[i].charAt(1) !== '=') { + return false; + } + // TODO: check the modifier a bit more. + } + return true; + }; + + // Expose public methods. + { + module.exports = SDPUtils; + } + }); + + /* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + + + + function fixStatsType(stat) { + return { + inboundrtp: 'inbound-rtp', + outboundrtp: 'outbound-rtp', + candidatepair: 'candidate-pair', + localcandidate: 'local-candidate', + remotecandidate: 'remote-candidate' + }[stat.type] || stat.type; + } + + function writeMediaSection(transceiver, caps, type, stream, dtlsRole) { + var sdp$1 = sdp.writeRtpDescription(transceiver.kind, caps); + + // Map ICE parameters (ufrag, pwd) to SDP. + sdp$1 += sdp.writeIceParameters( + transceiver.iceGatherer.getLocalParameters()); + + // Map DTLS parameters to SDP. + sdp$1 += sdp.writeDtlsParameters( + transceiver.dtlsTransport.getLocalParameters(), + type === 'offer' ? 'actpass' : dtlsRole || 'active'); + + sdp$1 += 'a=mid:' + transceiver.mid + '\r\n'; + + if (transceiver.rtpSender && transceiver.rtpReceiver) { + sdp$1 += 'a=sendrecv\r\n'; + } else if (transceiver.rtpSender) { + sdp$1 += 'a=sendonly\r\n'; + } else if (transceiver.rtpReceiver) { + sdp$1 += 'a=recvonly\r\n'; + } else { + sdp$1 += 'a=inactive\r\n'; + } + + if (transceiver.rtpSender) { + var trackId = transceiver.rtpSender._initialTrackId || + transceiver.rtpSender.track.id; + transceiver.rtpSender._initialTrackId = trackId; + // spec. + var msid = 'msid:' + (stream ? stream.id : '-') + ' ' + + trackId + '\r\n'; + sdp$1 += 'a=' + msid; + // for Chrome. Legacy should no longer be required. + sdp$1 += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc + + ' ' + msid; + + // RTX + if (transceiver.sendEncodingParameters[0].rtx) { + sdp$1 += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc + + ' ' + msid; + sdp$1 += 'a=ssrc-group:FID ' + + transceiver.sendEncodingParameters[0].ssrc + ' ' + + transceiver.sendEncodingParameters[0].rtx.ssrc + + '\r\n'; + } + } + // FIXME: this should be written by writeRtpDescription. + sdp$1 += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc + + ' cname:' + sdp.localCName + '\r\n'; + if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) { + sdp$1 += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc + + ' cname:' + sdp.localCName + '\r\n'; + } + return sdp$1; + } + + // Edge does not like + // 1) stun: filtered after 14393 unless ?transport=udp is present + // 2) turn: that does not have all of turn:host:port?transport=udp + // 3) turn: with ipv6 addresses + // 4) turn: occurring muliple times + function filterIceServers(iceServers, edgeVersion) { + var hasTurn = false; + iceServers = JSON.parse(JSON.stringify(iceServers)); + return iceServers.filter(function(server) { + if (server && (server.urls || server.url)) { + var urls = server.urls || server.url; + if (server.url && !server.urls) { + console.warn('RTCIceServer.url is deprecated! Use urls instead.'); + } + var isString = typeof urls === 'string'; + if (isString) { + urls = [urls]; + } + urls = urls.filter(function(url) { + var validTurn = url.indexOf('turn:') === 0 && + url.indexOf('transport=udp') !== -1 && + url.indexOf('turn:[') === -1 && + !hasTurn; + + if (validTurn) { + hasTurn = true; + return true; + } + return url.indexOf('stun:') === 0 && edgeVersion >= 14393 && + url.indexOf('?transport=udp') === -1; + }); + + delete server.url; + server.urls = isString ? urls[0] : urls; + return !!urls.length; + } + }); + } + + // Determines the intersection of local and remote capabilities. + function getCommonCapabilities(localCapabilities, remoteCapabilities) { + var commonCapabilities = { + codecs: [], + headerExtensions: [], + fecMechanisms: [] + }; + + var findCodecByPayloadType = function(pt, codecs) { + pt = parseInt(pt, 10); + for (var i = 0; i < codecs.length; i++) { + if (codecs[i].payloadType === pt || + codecs[i].preferredPayloadType === pt) { + return codecs[i]; + } + } + }; + + var rtxCapabilityMatches = function(lRtx, rRtx, lCodecs, rCodecs) { + var lCodec = findCodecByPayloadType(lRtx.parameters.apt, lCodecs); + var rCodec = findCodecByPayloadType(rRtx.parameters.apt, rCodecs); + return lCodec && rCodec && + lCodec.name.toLowerCase() === rCodec.name.toLowerCase(); + }; + + localCapabilities.codecs.forEach(function(lCodec) { + for (var i = 0; i < remoteCapabilities.codecs.length; i++) { + var rCodec = remoteCapabilities.codecs[i]; + if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() && + lCodec.clockRate === rCodec.clockRate) { + if (lCodec.name.toLowerCase() === 'rtx' && + lCodec.parameters && rCodec.parameters.apt) { + // for RTX we need to find the local rtx that has a apt + // which points to the same local codec as the remote one. + if (!rtxCapabilityMatches(lCodec, rCodec, + localCapabilities.codecs, remoteCapabilities.codecs)) { + continue; + } + } + rCodec = JSON.parse(JSON.stringify(rCodec)); // deepcopy + // number of channels is the highest common number of channels + rCodec.numChannels = Math.min(lCodec.numChannels, + rCodec.numChannels); + // push rCodec so we reply with offerer payload type + commonCapabilities.codecs.push(rCodec); + + // determine common feedback mechanisms + rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function(fb) { + for (var j = 0; j < lCodec.rtcpFeedback.length; j++) { + if (lCodec.rtcpFeedback[j].type === fb.type && + lCodec.rtcpFeedback[j].parameter === fb.parameter) { + return true; + } + } + return false; + }); + // FIXME: also need to determine .parameters + // see https://github.com/openpeer/ortc/issues/569 + break; + } + } + }); + + localCapabilities.headerExtensions.forEach(function(lHeaderExtension) { + for (var i = 0; i < remoteCapabilities.headerExtensions.length; + i++) { + var rHeaderExtension = remoteCapabilities.headerExtensions[i]; + if (lHeaderExtension.uri === rHeaderExtension.uri) { + commonCapabilities.headerExtensions.push(rHeaderExtension); + break; + } + } + }); + + // FIXME: fecMechanisms + return commonCapabilities; + } + + // is action=setLocalDescription with type allowed in signalingState + function isActionAllowedInSignalingState(action, type, signalingState) { + return { + offer: { + setLocalDescription: ['stable', 'have-local-offer'], + setRemoteDescription: ['stable', 'have-remote-offer'] + }, + answer: { + setLocalDescription: ['have-remote-offer', 'have-local-pranswer'], + setRemoteDescription: ['have-local-offer', 'have-remote-pranswer'] + } + }[type][action].indexOf(signalingState) !== -1; + } + + function maybeAddCandidate(iceTransport, candidate) { + // Edge's internal representation adds some fields therefore + // not all fieldѕ are taken into account. + var alreadyAdded = iceTransport.getRemoteCandidates() + .find(function(remoteCandidate) { + return candidate.foundation === remoteCandidate.foundation && + candidate.ip === remoteCandidate.ip && + candidate.port === remoteCandidate.port && + candidate.priority === remoteCandidate.priority && + candidate.protocol === remoteCandidate.protocol && + candidate.type === remoteCandidate.type; + }); + if (!alreadyAdded) { + iceTransport.addRemoteCandidate(candidate); + } + return !alreadyAdded; + } + + + function makeError(name, description) { + var e = new Error(description); + e.name = name; + // legacy error codes from https://heycam.github.io/webidl/#idl-DOMException-error-names + e.code = { + NotSupportedError: 9, + InvalidStateError: 11, + InvalidAccessError: 15, + TypeError: undefined, + OperationError: undefined + }[name]; + return e; + } + + var rtcpeerconnection = function(window, edgeVersion) { + // https://w3c.github.io/mediacapture-main/#mediastream + // Helper function to add the track to the stream and + // dispatch the event ourselves. + function addTrackToStreamAndFireEvent(track, stream) { + stream.addTrack(track); + stream.dispatchEvent(new window.MediaStreamTrackEvent('addtrack', + {track: track})); + } + + function removeTrackFromStreamAndFireEvent(track, stream) { + stream.removeTrack(track); + stream.dispatchEvent(new window.MediaStreamTrackEvent('removetrack', + {track: track})); + } + + function fireAddTrack(pc, track, receiver, streams) { + var trackEvent = new Event('track'); + trackEvent.track = track; + trackEvent.receiver = receiver; + trackEvent.transceiver = {receiver: receiver}; + trackEvent.streams = streams; + window.setTimeout(function() { + pc._dispatchEvent('track', trackEvent); + }); + } + + var RTCPeerConnection = function(config) { + var pc = this; + + var _eventTarget = document.createDocumentFragment(); + ['addEventListener', 'removeEventListener', 'dispatchEvent'] + .forEach(function(method) { + pc[method] = _eventTarget[method].bind(_eventTarget); + }); + + this.canTrickleIceCandidates = null; + + this.needNegotiation = false; + + this.localStreams = []; + this.remoteStreams = []; + + this._localDescription = null; + this._remoteDescription = null; + + this.signalingState = 'stable'; + this.iceConnectionState = 'new'; + this.connectionState = 'new'; + this.iceGatheringState = 'new'; + + config = JSON.parse(JSON.stringify(config || {})); + + this.usingBundle = config.bundlePolicy === 'max-bundle'; + if (config.rtcpMuxPolicy === 'negotiate') { + throw(makeError('NotSupportedError', + 'rtcpMuxPolicy \'negotiate\' is not supported')); + } else if (!config.rtcpMuxPolicy) { + config.rtcpMuxPolicy = 'require'; + } + + switch (config.iceTransportPolicy) { + case 'all': + case 'relay': + break; + default: + config.iceTransportPolicy = 'all'; + break; + } + + switch (config.bundlePolicy) { + case 'balanced': + case 'max-compat': + case 'max-bundle': + break; + default: + config.bundlePolicy = 'balanced'; + break; + } + + config.iceServers = filterIceServers(config.iceServers || [], edgeVersion); + + this._iceGatherers = []; + if (config.iceCandidatePoolSize) { + for (var i = config.iceCandidatePoolSize; i > 0; i--) { + this._iceGatherers.push(new window.RTCIceGatherer({ + iceServers: config.iceServers, + gatherPolicy: config.iceTransportPolicy + })); + } + } else { + config.iceCandidatePoolSize = 0; + } + + this._config = config; + + // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ... + // everything that is needed to describe a SDP m-line. + this.transceivers = []; + + this._sdpSessionId = sdp.generateSessionId(); + this._sdpSessionVersion = 0; + + this._dtlsRole = undefined; // role for a=setup to use in answers. + + this._isClosed = false; + }; + + Object.defineProperty(RTCPeerConnection.prototype, 'localDescription', { + configurable: true, + get: function() { + return this._localDescription; + } + }); + Object.defineProperty(RTCPeerConnection.prototype, 'remoteDescription', { + configurable: true, + get: function() { + return this._remoteDescription; + } + }); + + // set up event handlers on prototype + RTCPeerConnection.prototype.onicecandidate = null; + RTCPeerConnection.prototype.onaddstream = null; + RTCPeerConnection.prototype.ontrack = null; + RTCPeerConnection.prototype.onremovestream = null; + RTCPeerConnection.prototype.onsignalingstatechange = null; + RTCPeerConnection.prototype.oniceconnectionstatechange = null; + RTCPeerConnection.prototype.onconnectionstatechange = null; + RTCPeerConnection.prototype.onicegatheringstatechange = null; + RTCPeerConnection.prototype.onnegotiationneeded = null; + RTCPeerConnection.prototype.ondatachannel = null; + + RTCPeerConnection.prototype._dispatchEvent = function(name, event) { + if (this._isClosed) { + return; + } + this.dispatchEvent(event); + if (typeof this['on' + name] === 'function') { + this['on' + name](event); + } + }; + + RTCPeerConnection.prototype._emitGatheringStateChange = function() { + var event = new Event('icegatheringstatechange'); + this._dispatchEvent('icegatheringstatechange', event); + }; + + RTCPeerConnection.prototype.getConfiguration = function() { + return this._config; + }; + + RTCPeerConnection.prototype.getLocalStreams = function() { + return this.localStreams; + }; + + RTCPeerConnection.prototype.getRemoteStreams = function() { + return this.remoteStreams; + }; + + // internal helper to create a transceiver object. + // (which is not yet the same as the WebRTC 1.0 transceiver) + RTCPeerConnection.prototype._createTransceiver = function(kind, doNotAdd) { + var hasBundleTransport = this.transceivers.length > 0; + var transceiver = { + track: null, + iceGatherer: null, + iceTransport: null, + dtlsTransport: null, + localCapabilities: null, + remoteCapabilities: null, + rtpSender: null, + rtpReceiver: null, + kind: kind, + mid: null, + sendEncodingParameters: null, + recvEncodingParameters: null, + stream: null, + associatedRemoteMediaStreams: [], + wantReceive: true + }; + if (this.usingBundle && hasBundleTransport) { + transceiver.iceTransport = this.transceivers[0].iceTransport; + transceiver.dtlsTransport = this.transceivers[0].dtlsTransport; + } else { + var transports = this._createIceAndDtlsTransports(); + transceiver.iceTransport = transports.iceTransport; + transceiver.dtlsTransport = transports.dtlsTransport; + } + if (!doNotAdd) { + this.transceivers.push(transceiver); + } + return transceiver; + }; + + RTCPeerConnection.prototype.addTrack = function(track, stream) { + if (this._isClosed) { + throw makeError('InvalidStateError', + 'Attempted to call addTrack on a closed peerconnection.'); + } + + var alreadyExists = this.transceivers.find(function(s) { + return s.track === track; + }); + + if (alreadyExists) { + throw makeError('InvalidAccessError', 'Track already exists.'); + } + + var transceiver; + for (var i = 0; i < this.transceivers.length; i++) { + if (!this.transceivers[i].track && + this.transceivers[i].kind === track.kind) { + transceiver = this.transceivers[i]; + } + } + if (!transceiver) { + transceiver = this._createTransceiver(track.kind); + } + + this._maybeFireNegotiationNeeded(); + + if (this.localStreams.indexOf(stream) === -1) { + this.localStreams.push(stream); + } + + transceiver.track = track; + transceiver.stream = stream; + transceiver.rtpSender = new window.RTCRtpSender(track, + transceiver.dtlsTransport); + return transceiver.rtpSender; + }; + + RTCPeerConnection.prototype.addStream = function(stream) { + var pc = this; + if (edgeVersion >= 15025) { + stream.getTracks().forEach(function(track) { + pc.addTrack(track, stream); + }); + } else { + // Clone is necessary for local demos mostly, attaching directly + // to two different senders does not work (build 10547). + // Fixed in 15025 (or earlier) + var clonedStream = stream.clone(); + stream.getTracks().forEach(function(track, idx) { + var clonedTrack = clonedStream.getTracks()[idx]; + track.addEventListener('enabled', function(event) { + clonedTrack.enabled = event.enabled; + }); + }); + clonedStream.getTracks().forEach(function(track) { + pc.addTrack(track, clonedStream); + }); + } + }; + + RTCPeerConnection.prototype.removeTrack = function(sender) { + if (this._isClosed) { + throw makeError('InvalidStateError', + 'Attempted to call removeTrack on a closed peerconnection.'); + } + + if (!(sender instanceof window.RTCRtpSender)) { + throw new TypeError('Argument 1 of RTCPeerConnection.removeTrack ' + + 'does not implement interface RTCRtpSender.'); + } + + var transceiver = this.transceivers.find(function(t) { + return t.rtpSender === sender; + }); + + if (!transceiver) { + throw makeError('InvalidAccessError', + 'Sender was not created by this connection.'); + } + var stream = transceiver.stream; + + transceiver.rtpSender.stop(); + transceiver.rtpSender = null; + transceiver.track = null; + transceiver.stream = null; + + // remove the stream from the set of local streams + var localStreams = this.transceivers.map(function(t) { + return t.stream; + }); + if (localStreams.indexOf(stream) === -1 && + this.localStreams.indexOf(stream) > -1) { + this.localStreams.splice(this.localStreams.indexOf(stream), 1); + } + + this._maybeFireNegotiationNeeded(); + }; + + RTCPeerConnection.prototype.removeStream = function(stream) { + var pc = this; + stream.getTracks().forEach(function(track) { + var sender = pc.getSenders().find(function(s) { + return s.track === track; + }); + if (sender) { + pc.removeTrack(sender); + } + }); + }; + + RTCPeerConnection.prototype.getSenders = function() { + return this.transceivers.filter(function(transceiver) { + return !!transceiver.rtpSender; + }) + .map(function(transceiver) { + return transceiver.rtpSender; + }); + }; + + RTCPeerConnection.prototype.getReceivers = function() { + return this.transceivers.filter(function(transceiver) { + return !!transceiver.rtpReceiver; + }) + .map(function(transceiver) { + return transceiver.rtpReceiver; + }); + }; + + + RTCPeerConnection.prototype._createIceGatherer = function(sdpMLineIndex, + usingBundle) { + var pc = this; + if (usingBundle && sdpMLineIndex > 0) { + return this.transceivers[0].iceGatherer; + } else if (this._iceGatherers.length) { + return this._iceGatherers.shift(); + } + var iceGatherer = new window.RTCIceGatherer({ + iceServers: this._config.iceServers, + gatherPolicy: this._config.iceTransportPolicy + }); + Object.defineProperty(iceGatherer, 'state', + {value: 'new', writable: true} + ); + + this.transceivers[sdpMLineIndex].bufferedCandidateEvents = []; + this.transceivers[sdpMLineIndex].bufferCandidates = function(event) { + var end = !event.candidate || Object.keys(event.candidate).length === 0; + // polyfill since RTCIceGatherer.state is not implemented in + // Edge 10547 yet. + iceGatherer.state = end ? 'completed' : 'gathering'; + if (pc.transceivers[sdpMLineIndex].bufferedCandidateEvents !== null) { + pc.transceivers[sdpMLineIndex].bufferedCandidateEvents.push(event); + } + }; + iceGatherer.addEventListener('localcandidate', + this.transceivers[sdpMLineIndex].bufferCandidates); + return iceGatherer; + }; + + // start gathering from an RTCIceGatherer. + RTCPeerConnection.prototype._gather = function(mid, sdpMLineIndex) { + var pc = this; + var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer; + if (iceGatherer.onlocalcandidate) { + return; + } + var bufferedCandidateEvents = + this.transceivers[sdpMLineIndex].bufferedCandidateEvents; + this.transceivers[sdpMLineIndex].bufferedCandidateEvents = null; + iceGatherer.removeEventListener('localcandidate', + this.transceivers[sdpMLineIndex].bufferCandidates); + iceGatherer.onlocalcandidate = function(evt) { + if (pc.usingBundle && sdpMLineIndex > 0) { + // if we know that we use bundle we can drop candidates with + // ѕdpMLineIndex > 0. If we don't do this then our state gets + // confused since we dispose the extra ice gatherer. + return; + } + var event = new Event('icecandidate'); + event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex}; + + var cand = evt.candidate; + // Edge emits an empty object for RTCIceCandidateComplete‥ + var end = !cand || Object.keys(cand).length === 0; + if (end) { + // polyfill since RTCIceGatherer.state is not implemented in + // Edge 10547 yet. + if (iceGatherer.state === 'new' || iceGatherer.state === 'gathering') { + iceGatherer.state = 'completed'; + } + } else { + if (iceGatherer.state === 'new') { + iceGatherer.state = 'gathering'; + } + // RTCIceCandidate doesn't have a component, needs to be added + cand.component = 1; + // also the usernameFragment. TODO: update SDP to take both variants. + cand.ufrag = iceGatherer.getLocalParameters().usernameFragment; + + var serializedCandidate = sdp.writeCandidate(cand); + event.candidate = Object.assign(event.candidate, + sdp.parseCandidate(serializedCandidate)); + + event.candidate.candidate = serializedCandidate; + event.candidate.toJSON = function() { + return { + candidate: event.candidate.candidate, + sdpMid: event.candidate.sdpMid, + sdpMLineIndex: event.candidate.sdpMLineIndex, + usernameFragment: event.candidate.usernameFragment + }; + }; + } + + // update local description. + var sections = sdp.getMediaSections(pc._localDescription.sdp); + if (!end) { + sections[event.candidate.sdpMLineIndex] += + 'a=' + event.candidate.candidate + '\r\n'; + } else { + sections[event.candidate.sdpMLineIndex] += + 'a=end-of-candidates\r\n'; + } + pc._localDescription.sdp = + sdp.getDescription(pc._localDescription.sdp) + + sections.join(''); + var complete = pc.transceivers.every(function(transceiver) { + return transceiver.iceGatherer && + transceiver.iceGatherer.state === 'completed'; + }); + + if (pc.iceGatheringState !== 'gathering') { + pc.iceGatheringState = 'gathering'; + pc._emitGatheringStateChange(); + } + + // Emit candidate. Also emit null candidate when all gatherers are + // complete. + if (!end) { + pc._dispatchEvent('icecandidate', event); + } + if (complete) { + pc._dispatchEvent('icecandidate', new Event('icecandidate')); + pc.iceGatheringState = 'complete'; + pc._emitGatheringStateChange(); + } + }; + + // emit already gathered candidates. + window.setTimeout(function() { + bufferedCandidateEvents.forEach(function(e) { + iceGatherer.onlocalcandidate(e); + }); + }, 0); + }; + + // Create ICE transport and DTLS transport. + RTCPeerConnection.prototype._createIceAndDtlsTransports = function() { + var pc = this; + var iceTransport = new window.RTCIceTransport(null); + iceTransport.onicestatechange = function() { + pc._updateIceConnectionState(); + pc._updateConnectionState(); + }; + + var dtlsTransport = new window.RTCDtlsTransport(iceTransport); + dtlsTransport.ondtlsstatechange = function() { + pc._updateConnectionState(); + }; + dtlsTransport.onerror = function() { + // onerror does not set state to failed by itself. + Object.defineProperty(dtlsTransport, 'state', + {value: 'failed', writable: true}); + pc._updateConnectionState(); + }; + + return { + iceTransport: iceTransport, + dtlsTransport: dtlsTransport + }; + }; + + // Destroy ICE gatherer, ICE transport and DTLS transport. + // Without triggering the callbacks. + RTCPeerConnection.prototype._disposeIceAndDtlsTransports = function( + sdpMLineIndex) { + var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer; + if (iceGatherer) { + delete iceGatherer.onlocalcandidate; + delete this.transceivers[sdpMLineIndex].iceGatherer; + } + var iceTransport = this.transceivers[sdpMLineIndex].iceTransport; + if (iceTransport) { + delete iceTransport.onicestatechange; + delete this.transceivers[sdpMLineIndex].iceTransport; + } + var dtlsTransport = this.transceivers[sdpMLineIndex].dtlsTransport; + if (dtlsTransport) { + delete dtlsTransport.ondtlsstatechange; + delete dtlsTransport.onerror; + delete this.transceivers[sdpMLineIndex].dtlsTransport; + } + }; + + // Start the RTP Sender and Receiver for a transceiver. + RTCPeerConnection.prototype._transceive = function(transceiver, + send, recv) { + var params = getCommonCapabilities(transceiver.localCapabilities, + transceiver.remoteCapabilities); + if (send && transceiver.rtpSender) { + params.encodings = transceiver.sendEncodingParameters; + params.rtcp = { + cname: sdp.localCName, + compound: transceiver.rtcpParameters.compound + }; + if (transceiver.recvEncodingParameters.length) { + params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc; + } + transceiver.rtpSender.send(params); + } + if (recv && transceiver.rtpReceiver && params.codecs.length > 0) { + // remove RTX field in Edge 14942 + if (transceiver.kind === 'video' + && transceiver.recvEncodingParameters + && edgeVersion < 15019) { + transceiver.recvEncodingParameters.forEach(function(p) { + delete p.rtx; + }); + } + if (transceiver.recvEncodingParameters.length) { + params.encodings = transceiver.recvEncodingParameters; + } else { + params.encodings = [{}]; + } + params.rtcp = { + compound: transceiver.rtcpParameters.compound + }; + if (transceiver.rtcpParameters.cname) { + params.rtcp.cname = transceiver.rtcpParameters.cname; + } + if (transceiver.sendEncodingParameters.length) { + params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc; + } + transceiver.rtpReceiver.receive(params); + } + }; + + RTCPeerConnection.prototype.setLocalDescription = function(description) { + var pc = this; + + // Note: pranswer is not supported. + if (['offer', 'answer'].indexOf(description.type) === -1) { + return Promise.reject(makeError('TypeError', + 'Unsupported type "' + description.type + '"')); + } + + if (!isActionAllowedInSignalingState('setLocalDescription', + description.type, pc.signalingState) || pc._isClosed) { + return Promise.reject(makeError('InvalidStateError', + 'Can not set local ' + description.type + + ' in state ' + pc.signalingState)); + } + + var sections; + var sessionpart; + if (description.type === 'offer') { + // VERY limited support for SDP munging. Limited to: + // * changing the order of codecs + sections = sdp.splitSections(description.sdp); + sessionpart = sections.shift(); + sections.forEach(function(mediaSection, sdpMLineIndex) { + var caps = sdp.parseRtpParameters(mediaSection); + pc.transceivers[sdpMLineIndex].localCapabilities = caps; + }); + + pc.transceivers.forEach(function(transceiver, sdpMLineIndex) { + pc._gather(transceiver.mid, sdpMLineIndex); + }); + } else if (description.type === 'answer') { + sections = sdp.splitSections(pc._remoteDescription.sdp); + sessionpart = sections.shift(); + var isIceLite = sdp.matchPrefix(sessionpart, + 'a=ice-lite').length > 0; + sections.forEach(function(mediaSection, sdpMLineIndex) { + var transceiver = pc.transceivers[sdpMLineIndex]; + var iceGatherer = transceiver.iceGatherer; + var iceTransport = transceiver.iceTransport; + var dtlsTransport = transceiver.dtlsTransport; + var localCapabilities = transceiver.localCapabilities; + var remoteCapabilities = transceiver.remoteCapabilities; + + // treat bundle-only as not-rejected. + var rejected = sdp.isRejected(mediaSection) && + sdp.matchPrefix(mediaSection, 'a=bundle-only').length === 0; + + if (!rejected && !transceiver.rejected) { + var remoteIceParameters = sdp.getIceParameters( + mediaSection, sessionpart); + var remoteDtlsParameters = sdp.getDtlsParameters( + mediaSection, sessionpart); + if (isIceLite) { + remoteDtlsParameters.role = 'server'; + } + + if (!pc.usingBundle || sdpMLineIndex === 0) { + pc._gather(transceiver.mid, sdpMLineIndex); + if (iceTransport.state === 'new') { + iceTransport.start(iceGatherer, remoteIceParameters, + isIceLite ? 'controlling' : 'controlled'); + } + if (dtlsTransport.state === 'new') { + dtlsTransport.start(remoteDtlsParameters); + } + } + + // Calculate intersection of capabilities. + var params = getCommonCapabilities(localCapabilities, + remoteCapabilities); + + // Start the RTCRtpSender. The RTCRtpReceiver for this + // transceiver has already been started in setRemoteDescription. + pc._transceive(transceiver, + params.codecs.length > 0, + false); + } + }); + } + + pc._localDescription = { + type: description.type, + sdp: description.sdp + }; + if (description.type === 'offer') { + pc._updateSignalingState('have-local-offer'); + } else { + pc._updateSignalingState('stable'); + } + + return Promise.resolve(); + }; + + RTCPeerConnection.prototype.setRemoteDescription = function(description) { + var pc = this; + + // Note: pranswer is not supported. + if (['offer', 'answer'].indexOf(description.type) === -1) { + return Promise.reject(makeError('TypeError', + 'Unsupported type "' + description.type + '"')); + } + + if (!isActionAllowedInSignalingState('setRemoteDescription', + description.type, pc.signalingState) || pc._isClosed) { + return Promise.reject(makeError('InvalidStateError', + 'Can not set remote ' + description.type + + ' in state ' + pc.signalingState)); + } + + var streams = {}; + pc.remoteStreams.forEach(function(stream) { + streams[stream.id] = stream; + }); + var receiverList = []; + var sections = sdp.splitSections(description.sdp); + var sessionpart = sections.shift(); + var isIceLite = sdp.matchPrefix(sessionpart, + 'a=ice-lite').length > 0; + var usingBundle = sdp.matchPrefix(sessionpart, + 'a=group:BUNDLE ').length > 0; + pc.usingBundle = usingBundle; + var iceOptions = sdp.matchPrefix(sessionpart, + 'a=ice-options:')[0]; + if (iceOptions) { + pc.canTrickleIceCandidates = iceOptions.substr(14).split(' ') + .indexOf('trickle') >= 0; + } else { + pc.canTrickleIceCandidates = false; + } + + sections.forEach(function(mediaSection, sdpMLineIndex) { + var lines = sdp.splitLines(mediaSection); + var kind = sdp.getKind(mediaSection); + // treat bundle-only as not-rejected. + var rejected = sdp.isRejected(mediaSection) && + sdp.matchPrefix(mediaSection, 'a=bundle-only').length === 0; + var protocol = lines[0].substr(2).split(' ')[2]; + + var direction = sdp.getDirection(mediaSection, sessionpart); + var remoteMsid = sdp.parseMsid(mediaSection); + + var mid = sdp.getMid(mediaSection) || sdp.generateIdentifier(); + + // Reject datachannels which are not implemented yet. + if (rejected || (kind === 'application' && (protocol === 'DTLS/SCTP' || + protocol === 'UDP/DTLS/SCTP'))) { + // TODO: this is dangerous in the case where a non-rejected m-line + // becomes rejected. + pc.transceivers[sdpMLineIndex] = { + mid: mid, + kind: kind, + protocol: protocol, + rejected: true + }; + return; + } + + if (!rejected && pc.transceivers[sdpMLineIndex] && + pc.transceivers[sdpMLineIndex].rejected) { + // recycle a rejected transceiver. + pc.transceivers[sdpMLineIndex] = pc._createTransceiver(kind, true); + } + + var transceiver; + var iceGatherer; + var iceTransport; + var dtlsTransport; + var rtpReceiver; + var sendEncodingParameters; + var recvEncodingParameters; + var localCapabilities; + + var track; + // FIXME: ensure the mediaSection has rtcp-mux set. + var remoteCapabilities = sdp.parseRtpParameters(mediaSection); + var remoteIceParameters; + var remoteDtlsParameters; + if (!rejected) { + remoteIceParameters = sdp.getIceParameters(mediaSection, + sessionpart); + remoteDtlsParameters = sdp.getDtlsParameters(mediaSection, + sessionpart); + remoteDtlsParameters.role = 'client'; + } + recvEncodingParameters = + sdp.parseRtpEncodingParameters(mediaSection); + + var rtcpParameters = sdp.parseRtcpParameters(mediaSection); + + var isComplete = sdp.matchPrefix(mediaSection, + 'a=end-of-candidates', sessionpart).length > 0; + var cands = sdp.matchPrefix(mediaSection, 'a=candidate:') + .map(function(cand) { + return sdp.parseCandidate(cand); + }) + .filter(function(cand) { + return cand.component === 1; + }); + + // Check if we can use BUNDLE and dispose transports. + if ((description.type === 'offer' || description.type === 'answer') && + !rejected && usingBundle && sdpMLineIndex > 0 && + pc.transceivers[sdpMLineIndex]) { + pc._disposeIceAndDtlsTransports(sdpMLineIndex); + pc.transceivers[sdpMLineIndex].iceGatherer = + pc.transceivers[0].iceGatherer; + pc.transceivers[sdpMLineIndex].iceTransport = + pc.transceivers[0].iceTransport; + pc.transceivers[sdpMLineIndex].dtlsTransport = + pc.transceivers[0].dtlsTransport; + if (pc.transceivers[sdpMLineIndex].rtpSender) { + pc.transceivers[sdpMLineIndex].rtpSender.setTransport( + pc.transceivers[0].dtlsTransport); + } + if (pc.transceivers[sdpMLineIndex].rtpReceiver) { + pc.transceivers[sdpMLineIndex].rtpReceiver.setTransport( + pc.transceivers[0].dtlsTransport); + } + } + if (description.type === 'offer' && !rejected) { + transceiver = pc.transceivers[sdpMLineIndex] || + pc._createTransceiver(kind); + transceiver.mid = mid; + + if (!transceiver.iceGatherer) { + transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex, + usingBundle); + } + + if (cands.length && transceiver.iceTransport.state === 'new') { + if (isComplete && (!usingBundle || sdpMLineIndex === 0)) { + transceiver.iceTransport.setRemoteCandidates(cands); + } else { + cands.forEach(function(candidate) { + maybeAddCandidate(transceiver.iceTransport, candidate); + }); + } + } + + localCapabilities = window.RTCRtpReceiver.getCapabilities(kind); + + // filter RTX until additional stuff needed for RTX is implemented + // in adapter.js + if (edgeVersion < 15019) { + localCapabilities.codecs = localCapabilities.codecs.filter( + function(codec) { + return codec.name !== 'rtx'; + }); + } + + sendEncodingParameters = transceiver.sendEncodingParameters || [{ + ssrc: (2 * sdpMLineIndex + 2) * 1001 + }]; + + // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams + var isNewTrack = false; + if (direction === 'sendrecv' || direction === 'sendonly') { + isNewTrack = !transceiver.rtpReceiver; + rtpReceiver = transceiver.rtpReceiver || + new window.RTCRtpReceiver(transceiver.dtlsTransport, kind); + + if (isNewTrack) { + var stream; + track = rtpReceiver.track; + // FIXME: does not work with Plan B. + if (remoteMsid && remoteMsid.stream === '-') ; else if (remoteMsid) { + if (!streams[remoteMsid.stream]) { + streams[remoteMsid.stream] = new window.MediaStream(); + Object.defineProperty(streams[remoteMsid.stream], 'id', { + get: function() { + return remoteMsid.stream; + } + }); + } + Object.defineProperty(track, 'id', { + get: function() { + return remoteMsid.track; + } + }); + stream = streams[remoteMsid.stream]; + } else { + if (!streams.default) { + streams.default = new window.MediaStream(); + } + stream = streams.default; + } + if (stream) { + addTrackToStreamAndFireEvent(track, stream); + transceiver.associatedRemoteMediaStreams.push(stream); + } + receiverList.push([track, rtpReceiver, stream]); + } + } else if (transceiver.rtpReceiver && transceiver.rtpReceiver.track) { + transceiver.associatedRemoteMediaStreams.forEach(function(s) { + var nativeTrack = s.getTracks().find(function(t) { + return t.id === transceiver.rtpReceiver.track.id; + }); + if (nativeTrack) { + removeTrackFromStreamAndFireEvent(nativeTrack, s); + } + }); + transceiver.associatedRemoteMediaStreams = []; + } + + transceiver.localCapabilities = localCapabilities; + transceiver.remoteCapabilities = remoteCapabilities; + transceiver.rtpReceiver = rtpReceiver; + transceiver.rtcpParameters = rtcpParameters; + transceiver.sendEncodingParameters = sendEncodingParameters; + transceiver.recvEncodingParameters = recvEncodingParameters; + + // Start the RTCRtpReceiver now. The RTPSender is started in + // setLocalDescription. + pc._transceive(pc.transceivers[sdpMLineIndex], + false, + isNewTrack); + } else if (description.type === 'answer' && !rejected) { + transceiver = pc.transceivers[sdpMLineIndex]; + iceGatherer = transceiver.iceGatherer; + iceTransport = transceiver.iceTransport; + dtlsTransport = transceiver.dtlsTransport; + rtpReceiver = transceiver.rtpReceiver; + sendEncodingParameters = transceiver.sendEncodingParameters; + localCapabilities = transceiver.localCapabilities; + + pc.transceivers[sdpMLineIndex].recvEncodingParameters = + recvEncodingParameters; + pc.transceivers[sdpMLineIndex].remoteCapabilities = + remoteCapabilities; + pc.transceivers[sdpMLineIndex].rtcpParameters = rtcpParameters; + + if (cands.length && iceTransport.state === 'new') { + if ((isIceLite || isComplete) && + (!usingBundle || sdpMLineIndex === 0)) { + iceTransport.setRemoteCandidates(cands); + } else { + cands.forEach(function(candidate) { + maybeAddCandidate(transceiver.iceTransport, candidate); + }); + } + } + + if (!usingBundle || sdpMLineIndex === 0) { + if (iceTransport.state === 'new') { + iceTransport.start(iceGatherer, remoteIceParameters, + 'controlling'); + } + if (dtlsTransport.state === 'new') { + dtlsTransport.start(remoteDtlsParameters); + } + } + + // If the offer contained RTX but the answer did not, + // remove RTX from sendEncodingParameters. + var commonCapabilities = getCommonCapabilities( + transceiver.localCapabilities, + transceiver.remoteCapabilities); + + var hasRtx = commonCapabilities.codecs.filter(function(c) { + return c.name.toLowerCase() === 'rtx'; + }).length; + if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) { + delete transceiver.sendEncodingParameters[0].rtx; + } + + pc._transceive(transceiver, + direction === 'sendrecv' || direction === 'recvonly', + direction === 'sendrecv' || direction === 'sendonly'); + + // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams + if (rtpReceiver && + (direction === 'sendrecv' || direction === 'sendonly')) { + track = rtpReceiver.track; + if (remoteMsid) { + if (!streams[remoteMsid.stream]) { + streams[remoteMsid.stream] = new window.MediaStream(); + } + addTrackToStreamAndFireEvent(track, streams[remoteMsid.stream]); + receiverList.push([track, rtpReceiver, streams[remoteMsid.stream]]); + } else { + if (!streams.default) { + streams.default = new window.MediaStream(); + } + addTrackToStreamAndFireEvent(track, streams.default); + receiverList.push([track, rtpReceiver, streams.default]); + } + } else { + // FIXME: actually the receiver should be created later. + delete transceiver.rtpReceiver; + } + } + }); + + if (pc._dtlsRole === undefined) { + pc._dtlsRole = description.type === 'offer' ? 'active' : 'passive'; + } + + pc._remoteDescription = { + type: description.type, + sdp: description.sdp + }; + if (description.type === 'offer') { + pc._updateSignalingState('have-remote-offer'); + } else { + pc._updateSignalingState('stable'); + } + Object.keys(streams).forEach(function(sid) { + var stream = streams[sid]; + if (stream.getTracks().length) { + if (pc.remoteStreams.indexOf(stream) === -1) { + pc.remoteStreams.push(stream); + var event = new Event('addstream'); + event.stream = stream; + window.setTimeout(function() { + pc._dispatchEvent('addstream', event); + }); + } + + receiverList.forEach(function(item) { + var track = item[0]; + var receiver = item[1]; + if (stream.id !== item[2].id) { + return; + } + fireAddTrack(pc, track, receiver, [stream]); + }); + } + }); + receiverList.forEach(function(item) { + if (item[2]) { + return; + } + fireAddTrack(pc, item[0], item[1], []); + }); + + // check whether addIceCandidate({}) was called within four seconds after + // setRemoteDescription. + window.setTimeout(function() { + if (!(pc && pc.transceivers)) { + return; + } + pc.transceivers.forEach(function(transceiver) { + if (transceiver.iceTransport && + transceiver.iceTransport.state === 'new' && + transceiver.iceTransport.getRemoteCandidates().length > 0) { + console.warn('Timeout for addRemoteCandidate. Consider sending ' + + 'an end-of-candidates notification'); + transceiver.iceTransport.addRemoteCandidate({}); + } + }); + }, 4000); + + return Promise.resolve(); + }; + + RTCPeerConnection.prototype.close = function() { + this.transceivers.forEach(function(transceiver) { + /* not yet + if (transceiver.iceGatherer) { + transceiver.iceGatherer.close(); + } + */ + if (transceiver.iceTransport) { + transceiver.iceTransport.stop(); + } + if (transceiver.dtlsTransport) { + transceiver.dtlsTransport.stop(); + } + if (transceiver.rtpSender) { + transceiver.rtpSender.stop(); + } + if (transceiver.rtpReceiver) { + transceiver.rtpReceiver.stop(); + } + }); + // FIXME: clean up tracks, local streams, remote streams, etc + this._isClosed = true; + this._updateSignalingState('closed'); + }; + + // Update the signaling state. + RTCPeerConnection.prototype._updateSignalingState = function(newState) { + this.signalingState = newState; + var event = new Event('signalingstatechange'); + this._dispatchEvent('signalingstatechange', event); + }; + + // Determine whether to fire the negotiationneeded event. + RTCPeerConnection.prototype._maybeFireNegotiationNeeded = function() { + var pc = this; + if (this.signalingState !== 'stable' || this.needNegotiation === true) { + return; + } + this.needNegotiation = true; + window.setTimeout(function() { + if (pc.needNegotiation) { + pc.needNegotiation = false; + var event = new Event('negotiationneeded'); + pc._dispatchEvent('negotiationneeded', event); + } + }, 0); + }; + + // Update the ice connection state. + RTCPeerConnection.prototype._updateIceConnectionState = function() { + var newState; + var states = { + 'new': 0, + closed: 0, + checking: 0, + connected: 0, + completed: 0, + disconnected: 0, + failed: 0 + }; + this.transceivers.forEach(function(transceiver) { + if (transceiver.iceTransport && !transceiver.rejected) { + states[transceiver.iceTransport.state]++; + } + }); + + newState = 'new'; + if (states.failed > 0) { + newState = 'failed'; + } else if (states.checking > 0) { + newState = 'checking'; + } else if (states.disconnected > 0) { + newState = 'disconnected'; + } else if (states.new > 0) { + newState = 'new'; + } else if (states.connected > 0) { + newState = 'connected'; + } else if (states.completed > 0) { + newState = 'completed'; + } + + if (newState !== this.iceConnectionState) { + this.iceConnectionState = newState; + var event = new Event('iceconnectionstatechange'); + this._dispatchEvent('iceconnectionstatechange', event); + } + }; + + // Update the connection state. + RTCPeerConnection.prototype._updateConnectionState = function() { + var newState; + var states = { + 'new': 0, + closed: 0, + connecting: 0, + connected: 0, + completed: 0, + disconnected: 0, + failed: 0 + }; + this.transceivers.forEach(function(transceiver) { + if (transceiver.iceTransport && transceiver.dtlsTransport && + !transceiver.rejected) { + states[transceiver.iceTransport.state]++; + states[transceiver.dtlsTransport.state]++; + } + }); + // ICETransport.completed and connected are the same for this purpose. + states.connected += states.completed; + + newState = 'new'; + if (states.failed > 0) { + newState = 'failed'; + } else if (states.connecting > 0) { + newState = 'connecting'; + } else if (states.disconnected > 0) { + newState = 'disconnected'; + } else if (states.new > 0) { + newState = 'new'; + } else if (states.connected > 0) { + newState = 'connected'; + } + + if (newState !== this.connectionState) { + this.connectionState = newState; + var event = new Event('connectionstatechange'); + this._dispatchEvent('connectionstatechange', event); + } + }; + + RTCPeerConnection.prototype.createOffer = function() { + var pc = this; + + if (pc._isClosed) { + return Promise.reject(makeError('InvalidStateError', + 'Can not call createOffer after close')); + } + + var numAudioTracks = pc.transceivers.filter(function(t) { + return t.kind === 'audio'; + }).length; + var numVideoTracks = pc.transceivers.filter(function(t) { + return t.kind === 'video'; + }).length; + + // Determine number of audio and video tracks we need to send/recv. + var offerOptions = arguments[0]; + if (offerOptions) { + // Reject Chrome legacy constraints. + if (offerOptions.mandatory || offerOptions.optional) { + throw new TypeError( + 'Legacy mandatory/optional constraints not supported.'); + } + if (offerOptions.offerToReceiveAudio !== undefined) { + if (offerOptions.offerToReceiveAudio === true) { + numAudioTracks = 1; + } else if (offerOptions.offerToReceiveAudio === false) { + numAudioTracks = 0; + } else { + numAudioTracks = offerOptions.offerToReceiveAudio; + } + } + if (offerOptions.offerToReceiveVideo !== undefined) { + if (offerOptions.offerToReceiveVideo === true) { + numVideoTracks = 1; + } else if (offerOptions.offerToReceiveVideo === false) { + numVideoTracks = 0; + } else { + numVideoTracks = offerOptions.offerToReceiveVideo; + } + } + } + + pc.transceivers.forEach(function(transceiver) { + if (transceiver.kind === 'audio') { + numAudioTracks--; + if (numAudioTracks < 0) { + transceiver.wantReceive = false; + } + } else if (transceiver.kind === 'video') { + numVideoTracks--; + if (numVideoTracks < 0) { + transceiver.wantReceive = false; + } + } + }); + + // Create M-lines for recvonly streams. + while (numAudioTracks > 0 || numVideoTracks > 0) { + if (numAudioTracks > 0) { + pc._createTransceiver('audio'); + numAudioTracks--; + } + if (numVideoTracks > 0) { + pc._createTransceiver('video'); + numVideoTracks--; + } + } + + var sdp$1 = sdp.writeSessionBoilerplate(pc._sdpSessionId, + pc._sdpSessionVersion++); + pc.transceivers.forEach(function(transceiver, sdpMLineIndex) { + // For each track, create an ice gatherer, ice transport, + // dtls transport, potentially rtpsender and rtpreceiver. + var track = transceiver.track; + var kind = transceiver.kind; + var mid = transceiver.mid || sdp.generateIdentifier(); + transceiver.mid = mid; + + if (!transceiver.iceGatherer) { + transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex, + pc.usingBundle); + } + + var localCapabilities = window.RTCRtpSender.getCapabilities(kind); + // filter RTX until additional stuff needed for RTX is implemented + // in adapter.js + if (edgeVersion < 15019) { + localCapabilities.codecs = localCapabilities.codecs.filter( + function(codec) { + return codec.name !== 'rtx'; + }); + } + localCapabilities.codecs.forEach(function(codec) { + // work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552 + // by adding level-asymmetry-allowed=1 + if (codec.name === 'H264' && + codec.parameters['level-asymmetry-allowed'] === undefined) { + codec.parameters['level-asymmetry-allowed'] = '1'; + } + + // for subsequent offers, we might have to re-use the payload + // type of the last offer. + if (transceiver.remoteCapabilities && + transceiver.remoteCapabilities.codecs) { + transceiver.remoteCapabilities.codecs.forEach(function(remoteCodec) { + if (codec.name.toLowerCase() === remoteCodec.name.toLowerCase() && + codec.clockRate === remoteCodec.clockRate) { + codec.preferredPayloadType = remoteCodec.payloadType; + } + }); + } + }); + localCapabilities.headerExtensions.forEach(function(hdrExt) { + var remoteExtensions = transceiver.remoteCapabilities && + transceiver.remoteCapabilities.headerExtensions || []; + remoteExtensions.forEach(function(rHdrExt) { + if (hdrExt.uri === rHdrExt.uri) { + hdrExt.id = rHdrExt.id; + } + }); + }); + + // generate an ssrc now, to be used later in rtpSender.send + var sendEncodingParameters = transceiver.sendEncodingParameters || [{ + ssrc: (2 * sdpMLineIndex + 1) * 1001 + }]; + if (track) { + // add RTX + if (edgeVersion >= 15019 && kind === 'video' && + !sendEncodingParameters[0].rtx) { + sendEncodingParameters[0].rtx = { + ssrc: sendEncodingParameters[0].ssrc + 1 + }; + } + } + + if (transceiver.wantReceive) { + transceiver.rtpReceiver = new window.RTCRtpReceiver( + transceiver.dtlsTransport, kind); + } + + transceiver.localCapabilities = localCapabilities; + transceiver.sendEncodingParameters = sendEncodingParameters; + }); + + // always offer BUNDLE and dispose on return if not supported. + if (pc._config.bundlePolicy !== 'max-compat') { + sdp$1 += 'a=group:BUNDLE ' + pc.transceivers.map(function(t) { + return t.mid; + }).join(' ') + '\r\n'; + } + sdp$1 += 'a=ice-options:trickle\r\n'; + + pc.transceivers.forEach(function(transceiver, sdpMLineIndex) { + sdp$1 += writeMediaSection(transceiver, transceiver.localCapabilities, + 'offer', transceiver.stream, pc._dtlsRole); + sdp$1 += 'a=rtcp-rsize\r\n'; + + if (transceiver.iceGatherer && pc.iceGatheringState !== 'new' && + (sdpMLineIndex === 0 || !pc.usingBundle)) { + transceiver.iceGatherer.getLocalCandidates().forEach(function(cand) { + cand.component = 1; + sdp$1 += 'a=' + sdp.writeCandidate(cand) + '\r\n'; + }); + + if (transceiver.iceGatherer.state === 'completed') { + sdp$1 += 'a=end-of-candidates\r\n'; + } + } + }); + + var desc = new window.RTCSessionDescription({ + type: 'offer', + sdp: sdp$1 + }); + return Promise.resolve(desc); + }; + + RTCPeerConnection.prototype.createAnswer = function() { + var pc = this; + + if (pc._isClosed) { + return Promise.reject(makeError('InvalidStateError', + 'Can not call createAnswer after close')); + } + + if (!(pc.signalingState === 'have-remote-offer' || + pc.signalingState === 'have-local-pranswer')) { + return Promise.reject(makeError('InvalidStateError', + 'Can not call createAnswer in signalingState ' + pc.signalingState)); + } + + var sdp$1 = sdp.writeSessionBoilerplate(pc._sdpSessionId, + pc._sdpSessionVersion++); + if (pc.usingBundle) { + sdp$1 += 'a=group:BUNDLE ' + pc.transceivers.map(function(t) { + return t.mid; + }).join(' ') + '\r\n'; + } + sdp$1 += 'a=ice-options:trickle\r\n'; + + var mediaSectionsInOffer = sdp.getMediaSections( + pc._remoteDescription.sdp).length; + pc.transceivers.forEach(function(transceiver, sdpMLineIndex) { + if (sdpMLineIndex + 1 > mediaSectionsInOffer) { + return; + } + if (transceiver.rejected) { + if (transceiver.kind === 'application') { + if (transceiver.protocol === 'DTLS/SCTP') { // legacy fmt + sdp$1 += 'm=application 0 DTLS/SCTP 5000\r\n'; + } else { + sdp$1 += 'm=application 0 ' + transceiver.protocol + + ' webrtc-datachannel\r\n'; + } + } else if (transceiver.kind === 'audio') { + sdp$1 += 'm=audio 0 UDP/TLS/RTP/SAVPF 0\r\n' + + 'a=rtpmap:0 PCMU/8000\r\n'; + } else if (transceiver.kind === 'video') { + sdp$1 += 'm=video 0 UDP/TLS/RTP/SAVPF 120\r\n' + + 'a=rtpmap:120 VP8/90000\r\n'; + } + sdp$1 += 'c=IN IP4 0.0.0.0\r\n' + + 'a=inactive\r\n' + + 'a=mid:' + transceiver.mid + '\r\n'; + return; + } + + // FIXME: look at direction. + if (transceiver.stream) { + var localTrack; + if (transceiver.kind === 'audio') { + localTrack = transceiver.stream.getAudioTracks()[0]; + } else if (transceiver.kind === 'video') { + localTrack = transceiver.stream.getVideoTracks()[0]; + } + if (localTrack) { + // add RTX + if (edgeVersion >= 15019 && transceiver.kind === 'video' && + !transceiver.sendEncodingParameters[0].rtx) { + transceiver.sendEncodingParameters[0].rtx = { + ssrc: transceiver.sendEncodingParameters[0].ssrc + 1 + }; + } + } + } + + // Calculate intersection of capabilities. + var commonCapabilities = getCommonCapabilities( + transceiver.localCapabilities, + transceiver.remoteCapabilities); + + var hasRtx = commonCapabilities.codecs.filter(function(c) { + return c.name.toLowerCase() === 'rtx'; + }).length; + if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) { + delete transceiver.sendEncodingParameters[0].rtx; + } + + sdp$1 += writeMediaSection(transceiver, commonCapabilities, + 'answer', transceiver.stream, pc._dtlsRole); + if (transceiver.rtcpParameters && + transceiver.rtcpParameters.reducedSize) { + sdp$1 += 'a=rtcp-rsize\r\n'; + } + }); + + var desc = new window.RTCSessionDescription({ + type: 'answer', + sdp: sdp$1 + }); + return Promise.resolve(desc); + }; + + RTCPeerConnection.prototype.addIceCandidate = function(candidate) { + var pc = this; + var sections; + if (candidate && !(candidate.sdpMLineIndex !== undefined || + candidate.sdpMid)) { + return Promise.reject(new TypeError('sdpMLineIndex or sdpMid required')); + } + + // TODO: needs to go into ops queue. + return new Promise(function(resolve, reject) { + if (!pc._remoteDescription) { + return reject(makeError('InvalidStateError', + 'Can not add ICE candidate without a remote description')); + } else if (!candidate || candidate.candidate === '') { + for (var j = 0; j < pc.transceivers.length; j++) { + if (pc.transceivers[j].rejected) { + continue; + } + pc.transceivers[j].iceTransport.addRemoteCandidate({}); + sections = sdp.getMediaSections(pc._remoteDescription.sdp); + sections[j] += 'a=end-of-candidates\r\n'; + pc._remoteDescription.sdp = + sdp.getDescription(pc._remoteDescription.sdp) + + sections.join(''); + if (pc.usingBundle) { + break; + } + } + } else { + var sdpMLineIndex = candidate.sdpMLineIndex; + if (candidate.sdpMid) { + for (var i = 0; i < pc.transceivers.length; i++) { + if (pc.transceivers[i].mid === candidate.sdpMid) { + sdpMLineIndex = i; + break; + } + } + } + var transceiver = pc.transceivers[sdpMLineIndex]; + if (transceiver) { + if (transceiver.rejected) { + return resolve(); + } + var cand = Object.keys(candidate.candidate).length > 0 ? + sdp.parseCandidate(candidate.candidate) : {}; + // Ignore Chrome's invalid candidates since Edge does not like them. + if (cand.protocol === 'tcp' && (cand.port === 0 || cand.port === 9)) { + return resolve(); + } + // Ignore RTCP candidates, we assume RTCP-MUX. + if (cand.component && cand.component !== 1) { + return resolve(); + } + // when using bundle, avoid adding candidates to the wrong + // ice transport. And avoid adding candidates added in the SDP. + if (sdpMLineIndex === 0 || (sdpMLineIndex > 0 && + transceiver.iceTransport !== pc.transceivers[0].iceTransport)) { + if (!maybeAddCandidate(transceiver.iceTransport, cand)) { + return reject(makeError('OperationError', + 'Can not add ICE candidate')); + } + } + + // update the remoteDescription. + var candidateString = candidate.candidate.trim(); + if (candidateString.indexOf('a=') === 0) { + candidateString = candidateString.substr(2); + } + sections = sdp.getMediaSections(pc._remoteDescription.sdp); + sections[sdpMLineIndex] += 'a=' + + (cand.type ? candidateString : 'end-of-candidates') + + '\r\n'; + pc._remoteDescription.sdp = + sdp.getDescription(pc._remoteDescription.sdp) + + sections.join(''); + } else { + return reject(makeError('OperationError', + 'Can not add ICE candidate')); + } + } + resolve(); + }); + }; + + RTCPeerConnection.prototype.getStats = function(selector) { + if (selector && selector instanceof window.MediaStreamTrack) { + var senderOrReceiver = null; + this.transceivers.forEach(function(transceiver) { + if (transceiver.rtpSender && + transceiver.rtpSender.track === selector) { + senderOrReceiver = transceiver.rtpSender; + } else if (transceiver.rtpReceiver && + transceiver.rtpReceiver.track === selector) { + senderOrReceiver = transceiver.rtpReceiver; + } + }); + if (!senderOrReceiver) { + throw makeError('InvalidAccessError', 'Invalid selector.'); + } + return senderOrReceiver.getStats(); + } + + var promises = []; + this.transceivers.forEach(function(transceiver) { + ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport', + 'dtlsTransport'].forEach(function(method) { + if (transceiver[method]) { + promises.push(transceiver[method].getStats()); + } + }); + }); + return Promise.all(promises).then(function(allStats) { + var results = new Map(); + allStats.forEach(function(stats) { + stats.forEach(function(stat) { + results.set(stat.id, stat); + }); + }); + return results; + }); + }; + + // fix low-level stat names and return Map instead of object. + var ortcObjects = ['RTCRtpSender', 'RTCRtpReceiver', 'RTCIceGatherer', + 'RTCIceTransport', 'RTCDtlsTransport']; + ortcObjects.forEach(function(ortcObjectName) { + var obj = window[ortcObjectName]; + if (obj && obj.prototype && obj.prototype.getStats) { + var nativeGetstats = obj.prototype.getStats; + obj.prototype.getStats = function() { + return nativeGetstats.apply(this) + .then(function(nativeStats) { + var mapStats = new Map(); + Object.keys(nativeStats).forEach(function(id) { + nativeStats[id].type = fixStatsType(nativeStats[id]); + mapStats.set(id, nativeStats[id]); + }); + return mapStats; + }); + }; + } + }); + + // legacy callback shims. Should be moved to adapter.js some days. + var methods = ['createOffer', 'createAnswer']; + methods.forEach(function(method) { + var nativeMethod = RTCPeerConnection.prototype[method]; + RTCPeerConnection.prototype[method] = function() { + var args = arguments; + if (typeof args[0] === 'function' || + typeof args[1] === 'function') { // legacy + return nativeMethod.apply(this, [arguments[2]]) + .then(function(description) { + if (typeof args[0] === 'function') { + args[0].apply(null, [description]); + } + }, function(error) { + if (typeof args[1] === 'function') { + args[1].apply(null, [error]); + } + }); + } + return nativeMethod.apply(this, arguments); + }; + }); + + methods = ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']; + methods.forEach(function(method) { + var nativeMethod = RTCPeerConnection.prototype[method]; + RTCPeerConnection.prototype[method] = function() { + var args = arguments; + if (typeof args[1] === 'function' || + typeof args[2] === 'function') { // legacy + return nativeMethod.apply(this, arguments) + .then(function() { + if (typeof args[1] === 'function') { + args[1].apply(null); + } + }, function(error) { + if (typeof args[2] === 'function') { + args[2].apply(null, [error]); + } + }); + } + return nativeMethod.apply(this, arguments); + }; + }); + + // getStats is special. It doesn't have a spec legacy method yet we support + // getStats(something, cb) without error callbacks. + ['getStats'].forEach(function(method) { + var nativeMethod = RTCPeerConnection.prototype[method]; + RTCPeerConnection.prototype[method] = function() { + var args = arguments; + if (typeof args[1] === 'function') { + return nativeMethod.apply(this, arguments) + .then(function() { + if (typeof args[1] === 'function') { + args[1].apply(null); + } + }); + } + return nativeMethod.apply(this, arguments); + }; + }); + + return RTCPeerConnection; + }; + + /* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + + function shimGetUserMedia$2(window) { + const navigator = window && window.navigator; + + const shimError_ = function(e) { + return { + name: {PermissionDeniedError: 'NotAllowedError'}[e.name] || e.name, + message: e.message, + constraint: e.constraint, + toString() { + return this.name; + } + }; + }; + + // getUserMedia error shim. + const origGetUserMedia = navigator.mediaDevices.getUserMedia. + bind(navigator.mediaDevices); + navigator.mediaDevices.getUserMedia = function(c) { + return origGetUserMedia(c).catch(e => Promise.reject(shimError_(e))); + }; + } + + /* + * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + + function shimGetDisplayMedia$1(window) { + if (!('getDisplayMedia' in window.navigator)) { + return; + } + if (!(window.navigator.mediaDevices)) { + return; + } + if (window.navigator.mediaDevices && + 'getDisplayMedia' in window.navigator.mediaDevices) { + return; + } + window.navigator.mediaDevices.getDisplayMedia = + window.navigator.getDisplayMedia.bind(window.navigator); + } + + /* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + + function shimPeerConnection$1(window, browserDetails) { + if (window.RTCIceGatherer) { + if (!window.RTCIceCandidate) { + window.RTCIceCandidate = function RTCIceCandidate(args) { + return args; + }; + } + if (!window.RTCSessionDescription) { + window.RTCSessionDescription = function RTCSessionDescription(args) { + return args; + }; + } + // this adds an additional event listener to MediaStrackTrack that signals + // when a tracks enabled property was changed. Workaround for a bug in + // addStream, see below. No longer required in 15025+ + if (browserDetails.version < 15025) { + const origMSTEnabled = Object.getOwnPropertyDescriptor( + window.MediaStreamTrack.prototype, 'enabled'); + Object.defineProperty(window.MediaStreamTrack.prototype, 'enabled', { + set(value) { + origMSTEnabled.set.call(this, value); + const ev = new Event('enabled'); + ev.enabled = value; + this.dispatchEvent(ev); + } + }); + } + } + + // ORTC defines the DTMF sender a bit different. + // https://github.com/w3c/ortc/issues/714 + if (window.RTCRtpSender && !('dtmf' in window.RTCRtpSender.prototype)) { + Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', { + get() { + if (this._dtmf === undefined) { + if (this.track.kind === 'audio') { + this._dtmf = new window.RTCDtmfSender(this); + } else if (this.track.kind === 'video') { + this._dtmf = null; + } + } + return this._dtmf; + } + }); + } + // Edge currently only implements the RTCDtmfSender, not the + // RTCDTMFSender alias. See http://draft.ortc.org/#rtcdtmfsender2* + if (window.RTCDtmfSender && !window.RTCDTMFSender) { + window.RTCDTMFSender = window.RTCDtmfSender; + } + + const RTCPeerConnectionShim = rtcpeerconnection(window, + browserDetails.version); + window.RTCPeerConnection = function RTCPeerConnection(config) { + if (config && config.iceServers) { + config.iceServers = filterIceServers$1(config.iceServers, + browserDetails.version); + log$1('ICE servers after filtering:', config.iceServers); + } + return new RTCPeerConnectionShim(config); + }; + window.RTCPeerConnection.prototype = RTCPeerConnectionShim.prototype; + } + + function shimReplaceTrack(window) { + // ORTC has replaceTrack -- https://github.com/w3c/ortc/issues/614 + if (window.RTCRtpSender && + !('replaceTrack' in window.RTCRtpSender.prototype)) { + window.RTCRtpSender.prototype.replaceTrack = + window.RTCRtpSender.prototype.setTrack; + } + } + + var edgeShim = /*#__PURE__*/Object.freeze({ + __proto__: null, + shimPeerConnection: shimPeerConnection$1, + shimReplaceTrack: shimReplaceTrack, + shimGetUserMedia: shimGetUserMedia$2, + shimGetDisplayMedia: shimGetDisplayMedia$1 + }); + + /* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + + function shimGetUserMedia$1(window, browserDetails) { + const navigator = window && window.navigator; + const MediaStreamTrack = window && window.MediaStreamTrack; + + navigator.getUserMedia = function(constraints, onSuccess, onError) { + // Replace Firefox 44+'s deprecation warning with unprefixed version. + deprecated('navigator.getUserMedia', + 'navigator.mediaDevices.getUserMedia'); + navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError); + }; + + if (!(browserDetails.version > 55 && + 'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) { + const remap = function(obj, a, b) { + if (a in obj && !(b in obj)) { + obj[b] = obj[a]; + delete obj[a]; + } + }; + + const nativeGetUserMedia = navigator.mediaDevices.getUserMedia. + bind(navigator.mediaDevices); + navigator.mediaDevices.getUserMedia = function(c) { + if (typeof c === 'object' && typeof c.audio === 'object') { + c = JSON.parse(JSON.stringify(c)); + remap(c.audio, 'autoGainControl', 'mozAutoGainControl'); + remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression'); + } + return nativeGetUserMedia(c); + }; + + if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) { + const nativeGetSettings = MediaStreamTrack.prototype.getSettings; + MediaStreamTrack.prototype.getSettings = function() { + const obj = nativeGetSettings.apply(this, arguments); + remap(obj, 'mozAutoGainControl', 'autoGainControl'); + remap(obj, 'mozNoiseSuppression', 'noiseSuppression'); + return obj; + }; + } + + if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) { + const nativeApplyConstraints = + MediaStreamTrack.prototype.applyConstraints; + MediaStreamTrack.prototype.applyConstraints = function(c) { + if (this.kind === 'audio' && typeof c === 'object') { + c = JSON.parse(JSON.stringify(c)); + remap(c, 'autoGainControl', 'mozAutoGainControl'); + remap(c, 'noiseSuppression', 'mozNoiseSuppression'); + } + return nativeApplyConstraints.apply(this, [c]); + }; + } + } + } + + /* + * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + + function shimGetDisplayMedia(window, preferredMediaSource) { + if (window.navigator.mediaDevices && + 'getDisplayMedia' in window.navigator.mediaDevices) { + return; + } + if (!(window.navigator.mediaDevices)) { + return; + } + window.navigator.mediaDevices.getDisplayMedia = + function getDisplayMedia(constraints) { + if (!(constraints && constraints.video)) { + const err = new DOMException('getDisplayMedia without video ' + + 'constraints is undefined'); + err.name = 'NotFoundError'; + // from https://heycam.github.io/webidl/#idl-DOMException-error-names + err.code = 8; + return Promise.reject(err); + } + if (constraints.video === true) { + constraints.video = {mediaSource: preferredMediaSource}; + } else { + constraints.video.mediaSource = preferredMediaSource; + } + return window.navigator.mediaDevices.getUserMedia(constraints); + }; + } + + /* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + + function shimOnTrack(window) { + if (typeof window === 'object' && window.RTCTrackEvent && + ('receiver' in window.RTCTrackEvent.prototype) && + !('transceiver' in window.RTCTrackEvent.prototype)) { + Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', { + get() { + return {receiver: this.receiver}; + } + }); + } + } + + function shimPeerConnection(window, browserDetails) { + if (typeof window !== 'object' || + !(window.RTCPeerConnection || window.mozRTCPeerConnection)) { + return; // probably media.peerconnection.enabled=false in about:config + } + if (!window.RTCPeerConnection && window.mozRTCPeerConnection) { + // very basic support for old versions. + window.RTCPeerConnection = window.mozRTCPeerConnection; + } + + if (browserDetails.version < 53) { + // shim away need for obsolete RTCIceCandidate/RTCSessionDescription. + ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'] + .forEach(function(method) { + const nativeMethod = window.RTCPeerConnection.prototype[method]; + const methodObj = {[method]() { + arguments[0] = new ((method === 'addIceCandidate') ? + window.RTCIceCandidate : + window.RTCSessionDescription)(arguments[0]); + return nativeMethod.apply(this, arguments); + }}; + window.RTCPeerConnection.prototype[method] = methodObj[method]; + }); + } + + const modernStatsTypes = { + inboundrtp: 'inbound-rtp', + outboundrtp: 'outbound-rtp', + candidatepair: 'candidate-pair', + localcandidate: 'local-candidate', + remotecandidate: 'remote-candidate' + }; + + const nativeGetStats = window.RTCPeerConnection.prototype.getStats; + window.RTCPeerConnection.prototype.getStats = function getStats() { + const [selector, onSucc, onErr] = arguments; + return nativeGetStats.apply(this, [selector || null]) + .then(stats => { + if (browserDetails.version < 53 && !onSucc) { + // Shim only promise getStats with spec-hyphens in type names + // Leave callback version alone; misc old uses of forEach before Map + try { + stats.forEach(stat => { + stat.type = modernStatsTypes[stat.type] || stat.type; + }); + } catch (e) { + if (e.name !== 'TypeError') { + throw e; + } + // Avoid TypeError: "type" is read-only, in old versions. 34-43ish + stats.forEach((stat, i) => { + stats.set(i, Object.assign({}, stat, { + type: modernStatsTypes[stat.type] || stat.type + })); + }); + } + } + return stats; + }) + .then(onSucc, onErr); + }; + } + + function shimSenderGetStats(window) { + if (!(typeof window === 'object' && window.RTCPeerConnection && + window.RTCRtpSender)) { + return; + } + if (window.RTCRtpSender && 'getStats' in window.RTCRtpSender.prototype) { + return; + } + const origGetSenders = window.RTCPeerConnection.prototype.getSenders; + if (origGetSenders) { + window.RTCPeerConnection.prototype.getSenders = function getSenders() { + const senders = origGetSenders.apply(this, []); + senders.forEach(sender => sender._pc = this); + return senders; + }; + } + + const origAddTrack = window.RTCPeerConnection.prototype.addTrack; + if (origAddTrack) { + window.RTCPeerConnection.prototype.addTrack = function addTrack() { + const sender = origAddTrack.apply(this, arguments); + sender._pc = this; + return sender; + }; + } + window.RTCRtpSender.prototype.getStats = function getStats() { + return this.track ? this._pc.getStats(this.track) : + Promise.resolve(new Map()); + }; + } + + function shimReceiverGetStats(window) { + if (!(typeof window === 'object' && window.RTCPeerConnection && + window.RTCRtpSender)) { + return; + } + if (window.RTCRtpSender && 'getStats' in window.RTCRtpReceiver.prototype) { + return; + } + const origGetReceivers = window.RTCPeerConnection.prototype.getReceivers; + if (origGetReceivers) { + window.RTCPeerConnection.prototype.getReceivers = function getReceivers() { + const receivers = origGetReceivers.apply(this, []); + receivers.forEach(receiver => receiver._pc = this); + return receivers; + }; + } + wrapPeerConnectionEvent(window, 'track', e => { + e.receiver._pc = e.srcElement; + return e; + }); + window.RTCRtpReceiver.prototype.getStats = function getStats() { + return this._pc.getStats(this.track); + }; + } + + function shimRemoveStream(window) { + if (!window.RTCPeerConnection || + 'removeStream' in window.RTCPeerConnection.prototype) { + return; + } + window.RTCPeerConnection.prototype.removeStream = + function removeStream(stream) { + deprecated('removeStream', 'removeTrack'); + this.getSenders().forEach(sender => { + if (sender.track && stream.getTracks().includes(sender.track)) { + this.removeTrack(sender); + } + }); + }; + } + + function shimRTCDataChannel(window) { + // rename DataChannel to RTCDataChannel (native fix in FF60): + // https://bugzilla.mozilla.org/show_bug.cgi?id=1173851 + if (window.DataChannel && !window.RTCDataChannel) { + window.RTCDataChannel = window.DataChannel; + } + } + + function shimAddTransceiver(window) { + // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647 + // Firefox ignores the init sendEncodings options passed to addTransceiver + // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918 + if (!(typeof window === 'object' && window.RTCPeerConnection)) { + return; + } + const origAddTransceiver = window.RTCPeerConnection.prototype.addTransceiver; + if (origAddTransceiver) { + window.RTCPeerConnection.prototype.addTransceiver = + function addTransceiver() { + this.setParametersPromises = []; + const initParameters = arguments[1]; + const shouldPerformCheck = initParameters && + 'sendEncodings' in initParameters; + if (shouldPerformCheck) { + // If sendEncodings params are provided, validate grammar + initParameters.sendEncodings.forEach((encodingParam) => { + if ('rid' in encodingParam) { + const ridRegex = /^[a-z0-9]{0,16}$/i; + if (!ridRegex.test(encodingParam.rid)) { + throw new TypeError('Invalid RID value provided.'); + } + } + if ('scaleResolutionDownBy' in encodingParam) { + if (!(parseFloat(encodingParam.scaleResolutionDownBy) >= 1.0)) { + throw new RangeError('scale_resolution_down_by must be >= 1.0'); + } + } + if ('maxFramerate' in encodingParam) { + if (!(parseFloat(encodingParam.maxFramerate) >= 0)) { + throw new RangeError('max_framerate must be >= 0.0'); + } + } + }); + } + const transceiver = origAddTransceiver.apply(this, arguments); + if (shouldPerformCheck) { + // Check if the init options were applied. If not we do this in an + // asynchronous way and save the promise reference in a global object. + // This is an ugly hack, but at the same time is way more robust than + // checking the sender parameters before and after the createOffer + // Also note that after the createoffer we are not 100% sure that + // the params were asynchronously applied so we might miss the + // opportunity to recreate offer. + const {sender} = transceiver; + const params = sender.getParameters(); + if (!('encodings' in params) || + // Avoid being fooled by patched getParameters() below. + (params.encodings.length === 1 && + Object.keys(params.encodings[0]).length === 0)) { + params.encodings = initParameters.sendEncodings; + sender.sendEncodings = initParameters.sendEncodings; + this.setParametersPromises.push(sender.setParameters(params) + .then(() => { + delete sender.sendEncodings; + }).catch(() => { + delete sender.sendEncodings; + }) + ); + } + } + return transceiver; + }; + } + } + + function shimGetParameters(window) { + if (!(typeof window === 'object' && window.RTCRtpSender)) { + return; + } + const origGetParameters = window.RTCRtpSender.prototype.getParameters; + if (origGetParameters) { + window.RTCRtpSender.prototype.getParameters = + function getParameters() { + const params = origGetParameters.apply(this, arguments); + if (!('encodings' in params)) { + params.encodings = [].concat(this.sendEncodings || [{}]); + } + return params; + }; + } + } + + function shimCreateOffer(window) { + // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647 + // Firefox ignores the init sendEncodings options passed to addTransceiver + // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918 + if (!(typeof window === 'object' && window.RTCPeerConnection)) { + return; + } + const origCreateOffer = window.RTCPeerConnection.prototype.createOffer; + window.RTCPeerConnection.prototype.createOffer = function createOffer() { + if (this.setParametersPromises && this.setParametersPromises.length) { + return Promise.all(this.setParametersPromises) + .then(() => { + return origCreateOffer.apply(this, arguments); + }) + .finally(() => { + this.setParametersPromises = []; + }); + } + return origCreateOffer.apply(this, arguments); + }; + } + + function shimCreateAnswer(window) { + // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647 + // Firefox ignores the init sendEncodings options passed to addTransceiver + // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918 + if (!(typeof window === 'object' && window.RTCPeerConnection)) { + return; + } + const origCreateAnswer = window.RTCPeerConnection.prototype.createAnswer; + window.RTCPeerConnection.prototype.createAnswer = function createAnswer() { + if (this.setParametersPromises && this.setParametersPromises.length) { + return Promise.all(this.setParametersPromises) + .then(() => { + return origCreateAnswer.apply(this, arguments); + }) + .finally(() => { + this.setParametersPromises = []; + }); + } + return origCreateAnswer.apply(this, arguments); + }; + } + + var firefoxShim = /*#__PURE__*/Object.freeze({ + __proto__: null, + shimOnTrack: shimOnTrack, + shimPeerConnection: shimPeerConnection, + shimSenderGetStats: shimSenderGetStats, + shimReceiverGetStats: shimReceiverGetStats, + shimRemoveStream: shimRemoveStream, + shimRTCDataChannel: shimRTCDataChannel, + shimAddTransceiver: shimAddTransceiver, + shimGetParameters: shimGetParameters, + shimCreateOffer: shimCreateOffer, + shimCreateAnswer: shimCreateAnswer, + shimGetUserMedia: shimGetUserMedia$1, + shimGetDisplayMedia: shimGetDisplayMedia + }); + + /* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + + function shimLocalStreamsAPI(window) { + if (typeof window !== 'object' || !window.RTCPeerConnection) { + return; + } + if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) { + window.RTCPeerConnection.prototype.getLocalStreams = + function getLocalStreams() { + if (!this._localStreams) { + this._localStreams = []; + } + return this._localStreams; + }; + } + if (!('addStream' in window.RTCPeerConnection.prototype)) { + const _addTrack = window.RTCPeerConnection.prototype.addTrack; + window.RTCPeerConnection.prototype.addStream = function addStream(stream) { + if (!this._localStreams) { + this._localStreams = []; + } + if (!this._localStreams.includes(stream)) { + this._localStreams.push(stream); + } + // Try to emulate Chrome's behaviour of adding in audio-video order. + // Safari orders by track id. + stream.getAudioTracks().forEach(track => _addTrack.call(this, track, + stream)); + stream.getVideoTracks().forEach(track => _addTrack.call(this, track, + stream)); + }; + + window.RTCPeerConnection.prototype.addTrack = + function addTrack(track, ...streams) { + if (streams) { + streams.forEach((stream) => { + if (!this._localStreams) { + this._localStreams = [stream]; + } else if (!this._localStreams.includes(stream)) { + this._localStreams.push(stream); + } + }); + } + return _addTrack.apply(this, arguments); + }; + } + if (!('removeStream' in window.RTCPeerConnection.prototype)) { + window.RTCPeerConnection.prototype.removeStream = + function removeStream(stream) { + if (!this._localStreams) { + this._localStreams = []; + } + const index = this._localStreams.indexOf(stream); + if (index === -1) { + return; + } + this._localStreams.splice(index, 1); + const tracks = stream.getTracks(); + this.getSenders().forEach(sender => { + if (tracks.includes(sender.track)) { + this.removeTrack(sender); + } + }); + }; + } + } + + function shimRemoteStreamsAPI(window) { + if (typeof window !== 'object' || !window.RTCPeerConnection) { + return; + } + if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) { + window.RTCPeerConnection.prototype.getRemoteStreams = + function getRemoteStreams() { + return this._remoteStreams ? this._remoteStreams : []; + }; + } + if (!('onaddstream' in window.RTCPeerConnection.prototype)) { + Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', { + get() { + return this._onaddstream; + }, + set(f) { + if (this._onaddstream) { + this.removeEventListener('addstream', this._onaddstream); + this.removeEventListener('track', this._onaddstreampoly); + } + this.addEventListener('addstream', this._onaddstream = f); + this.addEventListener('track', this._onaddstreampoly = (e) => { + e.streams.forEach(stream => { + if (!this._remoteStreams) { + this._remoteStreams = []; + } + if (this._remoteStreams.includes(stream)) { + return; + } + this._remoteStreams.push(stream); + const event = new Event('addstream'); + event.stream = stream; + this.dispatchEvent(event); + }); + }); + } + }); + const origSetRemoteDescription = + window.RTCPeerConnection.prototype.setRemoteDescription; + window.RTCPeerConnection.prototype.setRemoteDescription = + function setRemoteDescription() { + const pc = this; + if (!this._onaddstreampoly) { + this.addEventListener('track', this._onaddstreampoly = function(e) { + e.streams.forEach(stream => { + if (!pc._remoteStreams) { + pc._remoteStreams = []; + } + if (pc._remoteStreams.indexOf(stream) >= 0) { + return; + } + pc._remoteStreams.push(stream); + const event = new Event('addstream'); + event.stream = stream; + pc.dispatchEvent(event); + }); + }); + } + return origSetRemoteDescription.apply(pc, arguments); + }; + } + } + + function shimCallbacksAPI(window) { + if (typeof window !== 'object' || !window.RTCPeerConnection) { + return; + } + const prototype = window.RTCPeerConnection.prototype; + const origCreateOffer = prototype.createOffer; + const origCreateAnswer = prototype.createAnswer; + const setLocalDescription = prototype.setLocalDescription; + const setRemoteDescription = prototype.setRemoteDescription; + const addIceCandidate = prototype.addIceCandidate; + + prototype.createOffer = + function createOffer(successCallback, failureCallback) { + const options = (arguments.length >= 2) ? arguments[2] : arguments[0]; + const promise = origCreateOffer.apply(this, [options]); + if (!failureCallback) { + return promise; + } + promise.then(successCallback, failureCallback); + return Promise.resolve(); + }; + + prototype.createAnswer = + function createAnswer(successCallback, failureCallback) { + const options = (arguments.length >= 2) ? arguments[2] : arguments[0]; + const promise = origCreateAnswer.apply(this, [options]); + if (!failureCallback) { + return promise; + } + promise.then(successCallback, failureCallback); + return Promise.resolve(); + }; + + let withCallback = function(description, successCallback, failureCallback) { + const promise = setLocalDescription.apply(this, [description]); + if (!failureCallback) { + return promise; + } + promise.then(successCallback, failureCallback); + return Promise.resolve(); + }; + prototype.setLocalDescription = withCallback; + + withCallback = function(description, successCallback, failureCallback) { + const promise = setRemoteDescription.apply(this, [description]); + if (!failureCallback) { + return promise; + } + promise.then(successCallback, failureCallback); + return Promise.resolve(); + }; + prototype.setRemoteDescription = withCallback; + + withCallback = function(candidate, successCallback, failureCallback) { + const promise = addIceCandidate.apply(this, [candidate]); + if (!failureCallback) { + return promise; + } + promise.then(successCallback, failureCallback); + return Promise.resolve(); + }; + prototype.addIceCandidate = withCallback; + } + + function shimGetUserMedia(window) { + const navigator = window && window.navigator; + + if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { + // shim not needed in Safari 12.1 + const mediaDevices = navigator.mediaDevices; + const _getUserMedia = mediaDevices.getUserMedia.bind(mediaDevices); + navigator.mediaDevices.getUserMedia = (constraints) => { + return _getUserMedia(shimConstraints(constraints)); + }; + } + + if (!navigator.getUserMedia && navigator.mediaDevices && + navigator.mediaDevices.getUserMedia) { + navigator.getUserMedia = function getUserMedia(constraints, cb, errcb) { + navigator.mediaDevices.getUserMedia(constraints) + .then(cb, errcb); + }.bind(navigator); + } + } + + function shimConstraints(constraints) { + if (constraints && constraints.video !== undefined) { + return Object.assign({}, + constraints, + {video: compactObject(constraints.video)} + ); + } + + return constraints; + } + + function shimRTCIceServerUrls(window) { + if (!window.RTCPeerConnection) { + return; + } + // migrate from non-spec RTCIceServer.url to RTCIceServer.urls + const OrigPeerConnection = window.RTCPeerConnection; + window.RTCPeerConnection = + function RTCPeerConnection(pcConfig, pcConstraints) { + if (pcConfig && pcConfig.iceServers) { + const newIceServers = []; + for (let i = 0; i < pcConfig.iceServers.length; i++) { + let server = pcConfig.iceServers[i]; + if (!server.hasOwnProperty('urls') && + server.hasOwnProperty('url')) { + deprecated('RTCIceServer.url', 'RTCIceServer.urls'); + server = JSON.parse(JSON.stringify(server)); + server.urls = server.url; + delete server.url; + newIceServers.push(server); + } else { + newIceServers.push(pcConfig.iceServers[i]); + } + } + pcConfig.iceServers = newIceServers; + } + return new OrigPeerConnection(pcConfig, pcConstraints); + }; + window.RTCPeerConnection.prototype = OrigPeerConnection.prototype; + // wrap static methods. Currently just generateCertificate. + if ('generateCertificate' in OrigPeerConnection) { + Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', { + get() { + return OrigPeerConnection.generateCertificate; + } + }); + } + } + + function shimTrackEventTransceiver(window) { + // Add event.transceiver member over deprecated event.receiver + if (typeof window === 'object' && window.RTCTrackEvent && + 'receiver' in window.RTCTrackEvent.prototype && + !('transceiver' in window.RTCTrackEvent.prototype)) { + Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', { + get() { + return {receiver: this.receiver}; + } + }); + } + } + + function shimCreateOfferLegacy(window) { + const origCreateOffer = window.RTCPeerConnection.prototype.createOffer; + window.RTCPeerConnection.prototype.createOffer = + function createOffer(offerOptions) { + if (offerOptions) { + if (typeof offerOptions.offerToReceiveAudio !== 'undefined') { + // support bit values + offerOptions.offerToReceiveAudio = + !!offerOptions.offerToReceiveAudio; + } + const audioTransceiver = this.getTransceivers().find(transceiver => + transceiver.receiver.track.kind === 'audio'); + if (offerOptions.offerToReceiveAudio === false && audioTransceiver) { + if (audioTransceiver.direction === 'sendrecv') { + if (audioTransceiver.setDirection) { + audioTransceiver.setDirection('sendonly'); + } else { + audioTransceiver.direction = 'sendonly'; + } + } else if (audioTransceiver.direction === 'recvonly') { + if (audioTransceiver.setDirection) { + audioTransceiver.setDirection('inactive'); + } else { + audioTransceiver.direction = 'inactive'; + } + } + } else if (offerOptions.offerToReceiveAudio === true && + !audioTransceiver) { + this.addTransceiver('audio'); + } + + if (typeof offerOptions.offerToReceiveVideo !== 'undefined') { + // support bit values + offerOptions.offerToReceiveVideo = + !!offerOptions.offerToReceiveVideo; + } + const videoTransceiver = this.getTransceivers().find(transceiver => + transceiver.receiver.track.kind === 'video'); + if (offerOptions.offerToReceiveVideo === false && videoTransceiver) { + if (videoTransceiver.direction === 'sendrecv') { + if (videoTransceiver.setDirection) { + videoTransceiver.setDirection('sendonly'); + } else { + videoTransceiver.direction = 'sendonly'; + } + } else if (videoTransceiver.direction === 'recvonly') { + if (videoTransceiver.setDirection) { + videoTransceiver.setDirection('inactive'); + } else { + videoTransceiver.direction = 'inactive'; + } + } + } else if (offerOptions.offerToReceiveVideo === true && + !videoTransceiver) { + this.addTransceiver('video'); + } + } + return origCreateOffer.apply(this, arguments); + }; + } + + function shimAudioContext(window) { + if (typeof window !== 'object' || window.AudioContext) { + return; + } + window.AudioContext = window.webkitAudioContext; + } + + var safariShim = /*#__PURE__*/Object.freeze({ + __proto__: null, + shimLocalStreamsAPI: shimLocalStreamsAPI, + shimRemoteStreamsAPI: shimRemoteStreamsAPI, + shimCallbacksAPI: shimCallbacksAPI, + shimGetUserMedia: shimGetUserMedia, + shimConstraints: shimConstraints, + shimRTCIceServerUrls: shimRTCIceServerUrls, + shimTrackEventTransceiver: shimTrackEventTransceiver, + shimCreateOfferLegacy: shimCreateOfferLegacy, + shimAudioContext: shimAudioContext + }); + + /* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + + function shimRTCIceCandidate(window) { + // foundation is arbitrarily chosen as an indicator for full support for + // https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface + if (!window.RTCIceCandidate || (window.RTCIceCandidate && 'foundation' in + window.RTCIceCandidate.prototype)) { + return; + } + + const NativeRTCIceCandidate = window.RTCIceCandidate; + window.RTCIceCandidate = function RTCIceCandidate(args) { + // Remove the a= which shouldn't be part of the candidate string. + if (typeof args === 'object' && args.candidate && + args.candidate.indexOf('a=') === 0) { + args = JSON.parse(JSON.stringify(args)); + args.candidate = args.candidate.substr(2); + } + + if (args.candidate && args.candidate.length) { + // Augment the native candidate with the parsed fields. + const nativeCandidate = new NativeRTCIceCandidate(args); + const parsedCandidate = sdp.parseCandidate(args.candidate); + const augmentedCandidate = Object.assign(nativeCandidate, + parsedCandidate); + + // Add a serializer that does not serialize the extra attributes. + augmentedCandidate.toJSON = function toJSON() { + return { + candidate: augmentedCandidate.candidate, + sdpMid: augmentedCandidate.sdpMid, + sdpMLineIndex: augmentedCandidate.sdpMLineIndex, + usernameFragment: augmentedCandidate.usernameFragment, + }; + }; + return augmentedCandidate; + } + return new NativeRTCIceCandidate(args); + }; + window.RTCIceCandidate.prototype = NativeRTCIceCandidate.prototype; + + // Hook up the augmented candidate in onicecandidate and + // addEventListener('icecandidate', ...) + wrapPeerConnectionEvent(window, 'icecandidate', e => { + if (e.candidate) { + Object.defineProperty(e, 'candidate', { + value: new window.RTCIceCandidate(e.candidate), + writable: 'false' + }); + } + return e; + }); + } + + function shimMaxMessageSize(window, browserDetails) { + if (!window.RTCPeerConnection) { + return; + } + + if (!('sctp' in window.RTCPeerConnection.prototype)) { + Object.defineProperty(window.RTCPeerConnection.prototype, 'sctp', { + get() { + return typeof this._sctp === 'undefined' ? null : this._sctp; + } + }); + } + + const sctpInDescription = function(description) { + if (!description || !description.sdp) { + return false; + } + const sections = sdp.splitSections(description.sdp); + sections.shift(); + return sections.some(mediaSection => { + const mLine = sdp.parseMLine(mediaSection); + return mLine && mLine.kind === 'application' + && mLine.protocol.indexOf('SCTP') !== -1; + }); + }; + + const getRemoteFirefoxVersion = function(description) { + // TODO: Is there a better solution for detecting Firefox? + const match = description.sdp.match(/mozilla...THIS_IS_SDPARTA-(\d+)/); + if (match === null || match.length < 2) { + return -1; + } + const version = parseInt(match[1], 10); + // Test for NaN (yes, this is ugly) + return version !== version ? -1 : version; + }; + + const getCanSendMaxMessageSize = function(remoteIsFirefox) { + // Every implementation we know can send at least 64 KiB. + // Note: Although Chrome is technically able to send up to 256 KiB, the + // data does not reach the other peer reliably. + // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=8419 + let canSendMaxMessageSize = 65536; + if (browserDetails.browser === 'firefox') { + if (browserDetails.version < 57) { + if (remoteIsFirefox === -1) { + // FF < 57 will send in 16 KiB chunks using the deprecated PPID + // fragmentation. + canSendMaxMessageSize = 16384; + } else { + // However, other FF (and RAWRTC) can reassemble PPID-fragmented + // messages. Thus, supporting ~2 GiB when sending. + canSendMaxMessageSize = 2147483637; + } + } else if (browserDetails.version < 60) { + // Currently, all FF >= 57 will reset the remote maximum message size + // to the default value when a data channel is created at a later + // stage. :( + // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831 + canSendMaxMessageSize = + browserDetails.version === 57 ? 65535 : 65536; + } else { + // FF >= 60 supports sending ~2 GiB + canSendMaxMessageSize = 2147483637; + } + } + return canSendMaxMessageSize; + }; + + const getMaxMessageSize = function(description, remoteIsFirefox) { + // Note: 65536 bytes is the default value from the SDP spec. Also, + // every implementation we know supports receiving 65536 bytes. + let maxMessageSize = 65536; + + // FF 57 has a slightly incorrect default remote max message size, so + // we need to adjust it here to avoid a failure when sending. + // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1425697 + if (browserDetails.browser === 'firefox' + && browserDetails.version === 57) { + maxMessageSize = 65535; + } + + const match = sdp.matchPrefix(description.sdp, + 'a=max-message-size:'); + if (match.length > 0) { + maxMessageSize = parseInt(match[0].substr(19), 10); + } else if (browserDetails.browser === 'firefox' && + remoteIsFirefox !== -1) { + // If the maximum message size is not present in the remote SDP and + // both local and remote are Firefox, the remote peer can receive + // ~2 GiB. + maxMessageSize = 2147483637; + } + return maxMessageSize; + }; + + const origSetRemoteDescription = + window.RTCPeerConnection.prototype.setRemoteDescription; + window.RTCPeerConnection.prototype.setRemoteDescription = + function setRemoteDescription() { + this._sctp = null; + // Chrome decided to not expose .sctp in plan-b mode. + // As usual, adapter.js has to do an 'ugly worakaround' + // to cover up the mess. + if (browserDetails.browser === 'chrome' && browserDetails.version >= 76) { + const {sdpSemantics} = this.getConfiguration(); + if (sdpSemantics === 'plan-b') { + Object.defineProperty(this, 'sctp', { + get() { + return typeof this._sctp === 'undefined' ? null : this._sctp; + }, + enumerable: true, + configurable: true, + }); + } + } + + if (sctpInDescription(arguments[0])) { + // Check if the remote is FF. + const isFirefox = getRemoteFirefoxVersion(arguments[0]); + + // Get the maximum message size the local peer is capable of sending + const canSendMMS = getCanSendMaxMessageSize(isFirefox); + + // Get the maximum message size of the remote peer. + const remoteMMS = getMaxMessageSize(arguments[0], isFirefox); + + // Determine final maximum message size + let maxMessageSize; + if (canSendMMS === 0 && remoteMMS === 0) { + maxMessageSize = Number.POSITIVE_INFINITY; + } else if (canSendMMS === 0 || remoteMMS === 0) { + maxMessageSize = Math.max(canSendMMS, remoteMMS); + } else { + maxMessageSize = Math.min(canSendMMS, remoteMMS); + } + + // Create a dummy RTCSctpTransport object and the 'maxMessageSize' + // attribute. + const sctp = {}; + Object.defineProperty(sctp, 'maxMessageSize', { + get() { + return maxMessageSize; + } + }); + this._sctp = sctp; + } + + return origSetRemoteDescription.apply(this, arguments); + }; + } + + function shimSendThrowTypeError(window) { + if (!(window.RTCPeerConnection && + 'createDataChannel' in window.RTCPeerConnection.prototype)) { + return; + } + + // Note: Although Firefox >= 57 has a native implementation, the maximum + // message size can be reset for all data channels at a later stage. + // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831 + + function wrapDcSend(dc, pc) { + const origDataChannelSend = dc.send; + dc.send = function send() { + const data = arguments[0]; + const length = data.length || data.size || data.byteLength; + if (dc.readyState === 'open' && + pc.sctp && length > pc.sctp.maxMessageSize) { + throw new TypeError('Message too large (can send a maximum of ' + + pc.sctp.maxMessageSize + ' bytes)'); + } + return origDataChannelSend.apply(dc, arguments); + }; + } + const origCreateDataChannel = + window.RTCPeerConnection.prototype.createDataChannel; + window.RTCPeerConnection.prototype.createDataChannel = + function createDataChannel() { + const dataChannel = origCreateDataChannel.apply(this, arguments); + wrapDcSend(dataChannel, this); + return dataChannel; + }; + wrapPeerConnectionEvent(window, 'datachannel', e => { + wrapDcSend(e.channel, e.target); + return e; + }); + } + + + /* shims RTCConnectionState by pretending it is the same as iceConnectionState. + * See https://bugs.chromium.org/p/webrtc/issues/detail?id=6145#c12 + * for why this is a valid hack in Chrome. In Firefox it is slightly incorrect + * since DTLS failures would be hidden. See + * https://bugzilla.mozilla.org/show_bug.cgi?id=1265827 + * for the Firefox tracking bug. + */ + function shimConnectionState(window) { + if (!window.RTCPeerConnection || + 'connectionState' in window.RTCPeerConnection.prototype) { + return; + } + const proto = window.RTCPeerConnection.prototype; + Object.defineProperty(proto, 'connectionState', { + get() { + return { + completed: 'connected', + checking: 'connecting' + }[this.iceConnectionState] || this.iceConnectionState; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(proto, 'onconnectionstatechange', { + get() { + return this._onconnectionstatechange || null; + }, + set(cb) { + if (this._onconnectionstatechange) { + this.removeEventListener('connectionstatechange', + this._onconnectionstatechange); + delete this._onconnectionstatechange; + } + if (cb) { + this.addEventListener('connectionstatechange', + this._onconnectionstatechange = cb); + } + }, + enumerable: true, + configurable: true + }); + + ['setLocalDescription', 'setRemoteDescription'].forEach((method) => { + const origMethod = proto[method]; + proto[method] = function() { + if (!this._connectionstatechangepoly) { + this._connectionstatechangepoly = e => { + const pc = e.target; + if (pc._lastConnectionState !== pc.connectionState) { + pc._lastConnectionState = pc.connectionState; + const newEvent = new Event('connectionstatechange', e); + pc.dispatchEvent(newEvent); + } + return e; + }; + this.addEventListener('iceconnectionstatechange', + this._connectionstatechangepoly); + } + return origMethod.apply(this, arguments); + }; + }); + } + + function removeExtmapAllowMixed(window, browserDetails) { + /* remove a=extmap-allow-mixed for webrtc.org < M71 */ + if (!window.RTCPeerConnection) { + return; + } + if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) { + return; + } + if (browserDetails.browser === 'safari' && browserDetails.version >= 605) { + return; + } + const nativeSRD = window.RTCPeerConnection.prototype.setRemoteDescription; + window.RTCPeerConnection.prototype.setRemoteDescription = + function setRemoteDescription(desc) { + if (desc && desc.sdp && desc.sdp.indexOf('\na=extmap-allow-mixed') !== -1) { + const sdp = desc.sdp.split('\n').filter((line) => { + return line.trim() !== 'a=extmap-allow-mixed'; + }).join('\n'); + // Safari enforces read-only-ness of RTCSessionDescription fields. + if (window.RTCSessionDescription && + desc instanceof window.RTCSessionDescription) { + arguments[0] = new window.RTCSessionDescription({ + type: desc.type, + sdp, + }); + } else { + desc.sdp = sdp; + } + } + return nativeSRD.apply(this, arguments); + }; + } + + function shimAddIceCandidateNullOrEmpty(window, browserDetails) { + // Support for addIceCandidate(null or undefined) + // as well as addIceCandidate({candidate: "", ...}) + // https://bugs.chromium.org/p/chromium/issues/detail?id=978582 + // Note: must be called before other polyfills which change the signature. + if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) { + return; + } + const nativeAddIceCandidate = + window.RTCPeerConnection.prototype.addIceCandidate; + if (!nativeAddIceCandidate || nativeAddIceCandidate.length === 0) { + return; + } + window.RTCPeerConnection.prototype.addIceCandidate = + function addIceCandidate() { + if (!arguments[0]) { + if (arguments[1]) { + arguments[1].apply(null); + } + return Promise.resolve(); + } + // Firefox 68+ emits and processes {candidate: "", ...}, ignore + // in older versions. + // Native support for ignoring exists for Chrome M77+. + // Safari ignores as well, exact version unknown but works in the same + // version that also ignores addIceCandidate(null). + if (((browserDetails.browser === 'chrome' && browserDetails.version < 78) + || (browserDetails.browser === 'firefox' + && browserDetails.version < 68) + || (browserDetails.browser === 'safari')) + && arguments[0] && arguments[0].candidate === '') { + return Promise.resolve(); + } + return nativeAddIceCandidate.apply(this, arguments); + }; + } + + var commonShim = /*#__PURE__*/Object.freeze({ + __proto__: null, + shimRTCIceCandidate: shimRTCIceCandidate, + shimMaxMessageSize: shimMaxMessageSize, + shimSendThrowTypeError: shimSendThrowTypeError, + shimConnectionState: shimConnectionState, + removeExtmapAllowMixed: removeExtmapAllowMixed, + shimAddIceCandidateNullOrEmpty: shimAddIceCandidateNullOrEmpty + }); + + /* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + + // Shimming starts here. + function adapterFactory({window} = {}, options = { + shimChrome: true, + shimFirefox: true, + shimEdge: true, + shimSafari: true, + }) { + // Utils. + const logging = log$1; + const browserDetails = detectBrowser(window); + + const adapter = { + browserDetails, + commonShim, + extractVersion: extractVersion, + disableLog: disableLog, + disableWarnings: disableWarnings + }; + + // Shim browser if found. + switch (browserDetails.browser) { + case 'chrome': + if (!chromeShim || !shimPeerConnection$2 || + !options.shimChrome) { + logging('Chrome shim is not included in this adapter release.'); + return adapter; + } + if (browserDetails.version === null) { + logging('Chrome shim can not determine version, not shimming.'); + return adapter; + } + logging('adapter.js shimming chrome.'); + // Export to the adapter global object visible in the browser. + adapter.browserShim = chromeShim; + + // Must be called before shimPeerConnection. + shimAddIceCandidateNullOrEmpty(window, browserDetails); + + shimGetUserMedia$3(window, browserDetails); + shimMediaStream(window); + shimPeerConnection$2(window, browserDetails); + shimOnTrack$1(window); + shimAddTrackRemoveTrack(window, browserDetails); + shimGetSendersWithDtmf(window); + shimGetStats(window); + shimSenderReceiverGetStats(window); + fixNegotiationNeeded(window, browserDetails); + + shimRTCIceCandidate(window); + shimConnectionState(window); + shimMaxMessageSize(window, browserDetails); + shimSendThrowTypeError(window); + removeExtmapAllowMixed(window, browserDetails); + break; + case 'firefox': + if (!firefoxShim || !shimPeerConnection || + !options.shimFirefox) { + logging('Firefox shim is not included in this adapter release.'); + return adapter; + } + logging('adapter.js shimming firefox.'); + // Export to the adapter global object visible in the browser. + adapter.browserShim = firefoxShim; + + // Must be called before shimPeerConnection. + shimAddIceCandidateNullOrEmpty(window, browserDetails); + + shimGetUserMedia$1(window, browserDetails); + shimPeerConnection(window, browserDetails); + shimOnTrack(window); + shimRemoveStream(window); + shimSenderGetStats(window); + shimReceiverGetStats(window); + shimRTCDataChannel(window); + shimAddTransceiver(window); + shimGetParameters(window); + shimCreateOffer(window); + shimCreateAnswer(window); + + shimRTCIceCandidate(window); + shimConnectionState(window); + shimMaxMessageSize(window, browserDetails); + shimSendThrowTypeError(window); + break; + case 'edge': + if (!edgeShim || !shimPeerConnection$1 || !options.shimEdge) { + logging('MS edge shim is not included in this adapter release.'); + return adapter; + } + logging('adapter.js shimming edge.'); + // Export to the adapter global object visible in the browser. + adapter.browserShim = edgeShim; + + shimGetUserMedia$2(window); + shimGetDisplayMedia$1(window); + shimPeerConnection$1(window, browserDetails); + shimReplaceTrack(window); + + // the edge shim implements the full RTCIceCandidate object. + + shimMaxMessageSize(window, browserDetails); + shimSendThrowTypeError(window); + break; + case 'safari': + if (!safariShim || !options.shimSafari) { + logging('Safari shim is not included in this adapter release.'); + return adapter; + } + logging('adapter.js shimming safari.'); + // Export to the adapter global object visible in the browser. + adapter.browserShim = safariShim; + + // Must be called before shimCallbackAPI. + shimAddIceCandidateNullOrEmpty(window, browserDetails); + + shimRTCIceServerUrls(window); + shimCreateOfferLegacy(window); + shimCallbacksAPI(window); + shimLocalStreamsAPI(window); + shimRemoteStreamsAPI(window); + shimTrackEventTransceiver(window); + shimGetUserMedia(window); + shimAudioContext(window); + + shimRTCIceCandidate(window); + shimMaxMessageSize(window, browserDetails); + shimSendThrowTypeError(window); + removeExtmapAllowMixed(window, browserDetails); + break; + default: + logging('Unsupported browser!'); + break; + } + + return adapter; + } + + /* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + + adapterFactory({window: typeof window === 'undefined' ? undefined : window}); + + /** + * @class AudioTrackConstraints + * @classDesc Constraints for creating an audio MediaStreamTrack. + * @memberof Owt.Base + * @constructor + * @param {Owt.Base.AudioSourceInfo} source Source info of this audio track. + */ + + class AudioTrackConstraints { + // eslint-disable-next-line require-jsdoc + constructor(source) { + if (!Object.values(AudioSourceInfo).some(v => v === source)) { + throw new TypeError('Invalid source.'); + } + /** + * @member {string} source + * @memberof Owt.Base.AudioTrackConstraints + * @desc Values could be "mic", "screen-cast", "file" or "mixed". + * @instance + */ + + + this.source = source; + /** + * @member {string} deviceId + * @memberof Owt.Base.AudioTrackConstraints + * @desc Do not provide deviceId if source is not "mic". + * @instance + * @see https://w3c.github.io/mediacapture-main/#def-constraint-deviceId + */ + + this.deviceId = undefined; + } + + } + /** + * @class VideoTrackConstraints + * @classDesc Constraints for creating a video MediaStreamTrack. + * @memberof Owt.Base + * @constructor + * @param {Owt.Base.VideoSourceInfo} source Source info of this video track. + */ + + class VideoTrackConstraints { + // eslint-disable-next-line require-jsdoc + constructor(source) { + if (!Object.values(VideoSourceInfo).some(v => v === source)) { + throw new TypeError('Invalid source.'); + } + /** + * @member {string} source + * @memberof Owt.Base.VideoTrackConstraints + * @desc Values could be "camera", "screen-cast", "file" or "mixed". + * @instance + */ + + + this.source = source; + /** + * @member {string} deviceId + * @memberof Owt.Base.VideoTrackConstraints + * @desc Do not provide deviceId if source is not "camera". + * @instance + * @see https://w3c.github.io/mediacapture-main/#def-constraint-deviceId + */ + + this.deviceId = undefined; + /** + * @member {Owt.Base.Resolution} resolution + * @memberof Owt.Base.VideoTrackConstraints + * @instance + */ + + this.resolution = undefined; + /** + * @member {number} frameRate + * @memberof Owt.Base.VideoTrackConstraints + * @instance + */ + + this.frameRate = undefined; + } + + } + /** + * @class StreamConstraints + * @classDesc Constraints for creating a MediaStream from screen mic and camera. + * @memberof Owt.Base + * @constructor + * @param {?Owt.Base.AudioTrackConstraints} audioConstraints + * @param {?Owt.Base.VideoTrackConstraints} videoConstraints + */ + + class StreamConstraints { + // eslint-disable-next-line require-jsdoc + constructor(audioConstraints = false, videoConstraints = false) { + /** + * @member {Owt.Base.MediaStreamTrackDeviceConstraintsForAudio} audio + * @memberof Owt.Base.MediaStreamDeviceConstraints + * @instance + */ + this.audio = audioConstraints; + /** + * @member {Owt.Base.MediaStreamTrackDeviceConstraintsForVideo} Video + * @memberof Owt.Base.MediaStreamDeviceConstraints + * @instance + */ + + this.video = videoConstraints; + } + + } // eslint-disable-next-line require-jsdoc + + function isVideoConstrainsForScreenCast(constraints) { + return typeof constraints.video === 'object' && constraints.video.source === VideoSourceInfo.SCREENCAST; + } + /** + * @class MediaStreamFactory + * @classDesc A factory to create MediaStream. You can also create MediaStream by yourself. + * @memberof Owt.Base + */ + + + class MediaStreamFactory { + /** + * @function createMediaStream + * @static + * @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". + * @memberof Owt.Base.MediaStreamFactory + * @return {Promise} Return a promise that is resolved when stream is successfully created, or rejected if one of the following error happened: + * - One or more parameters cannot be satisfied. + * - Specified device is busy. + * - Cannot obtain necessary permission or operation is canceled by user. + * - Video source is screen cast, while audio source is not. + * - Audio source is screen cast, while video source is disabled. + * @param {Owt.Base.StreamConstraints} constraints + */ + static createMediaStream(constraints) { + if (typeof constraints !== 'object' || !constraints.audio && !constraints.video) { + return Promise.reject(new TypeError('Invalid constrains')); + } + + if (!isVideoConstrainsForScreenCast(constraints) && typeof constraints.audio === 'object' && constraints.audio.source === AudioSourceInfo.SCREENCAST) { + return Promise.reject(new TypeError('Cannot share screen without video.')); + } + + if (isVideoConstrainsForScreenCast(constraints) && !isChrome() && !isFirefox()) { + return Promise.reject(new TypeError('Screen sharing only supports Chrome and Firefox.')); + } + + if (isVideoConstrainsForScreenCast(constraints) && typeof constraints.audio === 'object' && constraints.audio.source !== AudioSourceInfo.SCREENCAST) { + return Promise.reject(new TypeError('Cannot capture video from screen cast while capture audio from' + ' other source.')); + } // Check and convert constraints. + + + if (!constraints.audio && !constraints.video) { + return Promise.reject(new TypeError('At least one of audio and video must be requested.')); + } + + const mediaConstraints = Object.create({}); + + if (typeof constraints.audio === 'object' && constraints.audio.source === AudioSourceInfo.MIC) { + mediaConstraints.audio = Object.create({}); + + if (isEdge()) { + mediaConstraints.audio.deviceId = constraints.audio.deviceId; + } else { + mediaConstraints.audio.deviceId = { + exact: constraints.audio.deviceId + }; + } + } else { + if (constraints.audio.source === AudioSourceInfo.SCREENCAST) { + mediaConstraints.audio = true; + } else { + mediaConstraints.audio = constraints.audio; + } + } + + if (typeof constraints.video === 'object') { + mediaConstraints.video = Object.create({}); + + if (typeof constraints.video.frameRate === 'number') { + mediaConstraints.video.frameRate = constraints.video.frameRate; + } + + if (constraints.video.resolution && constraints.video.resolution.width && constraints.video.resolution.height) { + if (constraints.video.source === VideoSourceInfo.SCREENCAST) { + mediaConstraints.video.width = constraints.video.resolution.width; + mediaConstraints.video.height = constraints.video.resolution.height; + } else { + mediaConstraints.video.width = Object.create({}); + mediaConstraints.video.width.exact = constraints.video.resolution.width; + mediaConstraints.video.height = Object.create({}); + mediaConstraints.video.height.exact = constraints.video.resolution.height; + } + } + + if (typeof constraints.video.deviceId === 'string') { + mediaConstraints.video.deviceId = { + exact: constraints.video.deviceId + }; + } + + if (isFirefox() && constraints.video.source === VideoSourceInfo.SCREENCAST) { + mediaConstraints.video.mediaSource = 'screen'; + } + } else { + mediaConstraints.video = constraints.video; + } + + if (isVideoConstrainsForScreenCast(constraints)) { + return navigator.mediaDevices.getDisplayMedia(mediaConstraints); + } else { + return navigator.mediaDevices.getUserMedia(mediaConstraints); + } + } + + } + + // Copyright (C) <2018> Intel Corporation + + var media = /*#__PURE__*/Object.freeze({ + __proto__: null, + AudioTrackConstraints: AudioTrackConstraints, + VideoTrackConstraints: VideoTrackConstraints, + StreamConstraints: StreamConstraints, + MediaStreamFactory: MediaStreamFactory, + AudioSourceInfo: AudioSourceInfo, + VideoSourceInfo: VideoSourceInfo, + TrackKind: TrackKind, + Resolution: Resolution + }); + + let logger; + let errorLogger; + function setLogger() { + /*eslint-disable */ + logger = console.log; + errorLogger = console.error; + /*eslint-enable */ + } + function log(message, ...optionalParams) { + if (logger) { + logger(message, ...optionalParams); + } + } + function error(message, ...optionalParams) { + if (errorLogger) { + errorLogger(message, ...optionalParams); + } + } + + class Event$1 { + constructor(type) { + this.listener = {}; + this.type = type | ''; + } + + on(event, fn) { + if (!this.listener[event]) { + this.listener[event] = []; + } + + this.listener[event].push(fn); + return true; + } + + off(event, fn) { + if (this.listener[event]) { + var index = this.listener[event].indexOf(fn); + + if (index > -1) { + this.listener[event].splice(index, 1); + } + + return true; + } + + return false; + } + + offAll() { + this.listener = {}; + } + + dispatch(event, data) { + if (this.listener[event]) { + this.listener[event].map(each => { + each.apply(null, [data]); + }); + return true; + } + + return false; + } + + } + + var bind = function bind(fn, thisArg) { + return function wrap() { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i]; + } + return fn.apply(thisArg, args); + }; + }; + + /*global toString:true*/ + + // utils is a library of generic helper functions non-specific to axios + + var toString = Object.prototype.toString; + + /** + * Determine if a value is an Array + * + * @param {Object} val The value to test + * @returns {boolean} True if value is an Array, otherwise false + */ + function isArray(val) { + return toString.call(val) === '[object Array]'; + } + + /** + * Determine if a value is undefined + * + * @param {Object} val The value to test + * @returns {boolean} True if the value is undefined, otherwise false + */ + function isUndefined(val) { + return typeof val === 'undefined'; + } + + /** + * Determine if a value is a Buffer + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Buffer, otherwise false + */ + function isBuffer(val) { + return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor) + && typeof val.constructor.isBuffer === 'function' && val.constructor.isBuffer(val); + } + + /** + * Determine if a value is an ArrayBuffer + * + * @param {Object} val The value to test + * @returns {boolean} True if value is an ArrayBuffer, otherwise false + */ + function isArrayBuffer(val) { + return toString.call(val) === '[object ArrayBuffer]'; + } + + /** + * Determine if a value is a FormData + * + * @param {Object} val The value to test + * @returns {boolean} True if value is an FormData, otherwise false + */ + function isFormData(val) { + return (typeof FormData !== 'undefined') && (val instanceof FormData); + } + + /** + * Determine if a value is a view on an ArrayBuffer + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false + */ + function isArrayBufferView(val) { + var result; + if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) { + result = ArrayBuffer.isView(val); + } else { + result = (val) && (val.buffer) && (val.buffer instanceof ArrayBuffer); + } + return result; + } + + /** + * Determine if a value is a String + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a String, otherwise false + */ + function isString(val) { + return typeof val === 'string'; + } + + /** + * Determine if a value is a Number + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Number, otherwise false + */ + function isNumber(val) { + return typeof val === 'number'; + } + + /** + * Determine if a value is an Object + * + * @param {Object} val The value to test + * @returns {boolean} True if value is an Object, otherwise false + */ + function isObject(val) { + return val !== null && typeof val === 'object'; + } + + /** + * Determine if a value is a plain Object + * + * @param {Object} val The value to test + * @return {boolean} True if value is a plain Object, otherwise false + */ + function isPlainObject(val) { + if (toString.call(val) !== '[object Object]') { + return false; + } + + var prototype = Object.getPrototypeOf(val); + return prototype === null || prototype === Object.prototype; + } + + /** + * Determine if a value is a Date + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Date, otherwise false + */ + function isDate(val) { + return toString.call(val) === '[object Date]'; + } + + /** + * Determine if a value is a File + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a File, otherwise false + */ + function isFile(val) { + return toString.call(val) === '[object File]'; + } + + /** + * Determine if a value is a Blob + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Blob, otherwise false + */ + function isBlob(val) { + return toString.call(val) === '[object Blob]'; + } + + /** + * Determine if a value is a Function + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Function, otherwise false + */ + function isFunction(val) { + return toString.call(val) === '[object Function]'; + } + + /** + * Determine if a value is a Stream + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Stream, otherwise false + */ + function isStream(val) { + return isObject(val) && isFunction(val.pipe); + } + + /** + * Determine if a value is a URLSearchParams object + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a URLSearchParams object, otherwise false + */ + function isURLSearchParams(val) { + return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams; + } + + /** + * Trim excess whitespace off the beginning and end of a string + * + * @param {String} str The String to trim + * @returns {String} The String freed of excess whitespace + */ + function trim(str) { + return str.replace(/^\s*/, '').replace(/\s*$/, ''); + } + + /** + * Determine if we're running in a standard browser environment + * + * This allows axios to run in a web worker, and react-native. + * Both environments support XMLHttpRequest, but not fully standard globals. + * + * web workers: + * typeof window -> undefined + * typeof document -> undefined + * + * react-native: + * navigator.product -> 'ReactNative' + * nativescript + * navigator.product -> 'NativeScript' or 'NS' + */ + function isStandardBrowserEnv() { + if (typeof navigator !== 'undefined' && (navigator.product === 'ReactNative' || + navigator.product === 'NativeScript' || + navigator.product === 'NS')) { + return false; + } + return ( + typeof window !== 'undefined' && + typeof document !== 'undefined' + ); + } + + /** + * Iterate over an Array or an Object invoking a function for each item. + * + * If `obj` is an Array callback will be called passing + * the value, index, and complete array for each item. + * + * If 'obj' is an Object callback will be called passing + * the value, key, and complete object for each property. + * + * @param {Object|Array} obj The object to iterate + * @param {Function} fn The callback to invoke for each item + */ + function forEach(obj, fn) { + // Don't bother if no value provided + if (obj === null || typeof obj === 'undefined') { + return; + } + + // Force an array if not already something iterable + if (typeof obj !== 'object') { + /*eslint no-param-reassign:0*/ + obj = [obj]; + } + + if (isArray(obj)) { + // Iterate over array values + for (var i = 0, l = obj.length; i < l; i++) { + fn.call(null, obj[i], i, obj); + } + } else { + // Iterate over object keys + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + fn.call(null, obj[key], key, obj); + } + } + } + } + + /** + * Accepts varargs expecting each argument to be an object, then + * immutably merges the properties of each object and returns result. + * + * When multiple objects contain the same key the later object in + * the arguments list will take precedence. + * + * Example: + * + * ```js + * var result = merge({foo: 123}, {foo: 456}); + * console.log(result.foo); // outputs 456 + * ``` + * + * @param {Object} obj1 Object to merge + * @returns {Object} Result of all merge properties + */ + function merge(/* obj1, obj2, obj3, ... */) { + var result = {}; + function assignValue(val, key) { + if (isPlainObject(result[key]) && isPlainObject(val)) { + result[key] = merge(result[key], val); + } else if (isPlainObject(val)) { + result[key] = merge({}, val); + } else if (isArray(val)) { + result[key] = val.slice(); + } else { + result[key] = val; + } + } + + for (var i = 0, l = arguments.length; i < l; i++) { + forEach(arguments[i], assignValue); + } + return result; + } + + /** + * Extends object a by mutably adding to it the properties of object b. + * + * @param {Object} a The object to be extended + * @param {Object} b The object to copy properties from + * @param {Object} thisArg The object to bind function to + * @return {Object} The resulting value of object a + */ + function extend(a, b, thisArg) { + forEach(b, function assignValue(val, key) { + if (thisArg && typeof val === 'function') { + a[key] = bind(val, thisArg); + } else { + a[key] = val; + } + }); + return a; + } + + /** + * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM) + * + * @param {string} content with BOM + * @return {string} content value without BOM + */ + function stripBOM(content) { + if (content.charCodeAt(0) === 0xFEFF) { + content = content.slice(1); + } + return content; + } + + var utils = { + isArray: isArray, + isArrayBuffer: isArrayBuffer, + isBuffer: isBuffer, + isFormData: isFormData, + isArrayBufferView: isArrayBufferView, + isString: isString, + isNumber: isNumber, + isObject: isObject, + isPlainObject: isPlainObject, + isUndefined: isUndefined, + isDate: isDate, + isFile: isFile, + isBlob: isBlob, + isFunction: isFunction, + isStream: isStream, + isURLSearchParams: isURLSearchParams, + isStandardBrowserEnv: isStandardBrowserEnv, + forEach: forEach, + merge: merge, + extend: extend, + trim: trim, + stripBOM: stripBOM + }; + + function encode(val) { + return encodeURIComponent(val). + replace(/%3A/gi, ':'). + replace(/%24/g, '$'). + replace(/%2C/gi, ','). + replace(/%20/g, '+'). + replace(/%5B/gi, '['). + replace(/%5D/gi, ']'); + } + + /** + * Build a URL by appending params to the end + * + * @param {string} url The base of the url (e.g., http://www.google.com) + * @param {object} [params] The params to be appended + * @returns {string} The formatted url + */ + var buildURL = function buildURL(url, params, paramsSerializer) { + /*eslint no-param-reassign:0*/ + if (!params) { + return url; + } + + var serializedParams; + if (paramsSerializer) { + serializedParams = paramsSerializer(params); + } else if (utils.isURLSearchParams(params)) { + serializedParams = params.toString(); + } else { + var parts = []; + + utils.forEach(params, function serialize(val, key) { + if (val === null || typeof val === 'undefined') { + return; + } + + if (utils.isArray(val)) { + key = key + '[]'; + } else { + val = [val]; + } + + utils.forEach(val, function parseValue(v) { + if (utils.isDate(v)) { + v = v.toISOString(); + } else if (utils.isObject(v)) { + v = JSON.stringify(v); + } + parts.push(encode(key) + '=' + encode(v)); + }); + }); + + serializedParams = parts.join('&'); + } + + if (serializedParams) { + var hashmarkIndex = url.indexOf('#'); + if (hashmarkIndex !== -1) { + url = url.slice(0, hashmarkIndex); + } + + url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams; + } + + return url; + }; + + function InterceptorManager() { + this.handlers = []; + } + + /** + * Add a new interceptor to the stack + * + * @param {Function} fulfilled The function to handle `then` for a `Promise` + * @param {Function} rejected The function to handle `reject` for a `Promise` + * + * @return {Number} An ID used to remove interceptor later + */ + InterceptorManager.prototype.use = function use(fulfilled, rejected) { + this.handlers.push({ + fulfilled: fulfilled, + rejected: rejected + }); + return this.handlers.length - 1; + }; + + /** + * Remove an interceptor from the stack + * + * @param {Number} id The ID that was returned by `use` + */ + InterceptorManager.prototype.eject = function eject(id) { + if (this.handlers[id]) { + this.handlers[id] = null; + } + }; + + /** + * Iterate over all the registered interceptors + * + * This method is particularly useful for skipping over any + * interceptors that may have become `null` calling `eject`. + * + * @param {Function} fn The function to call for each interceptor + */ + InterceptorManager.prototype.forEach = function forEach(fn) { + utils.forEach(this.handlers, function forEachHandler(h) { + if (h !== null) { + fn(h); + } + }); + }; + + var InterceptorManager_1 = InterceptorManager; + + /** + * Transform the data for a request or a response + * + * @param {Object|String} data The data to be transformed + * @param {Array} headers The headers for the request or response + * @param {Array|Function} fns A single function or Array of functions + * @returns {*} The resulting transformed data + */ + var transformData = function transformData(data, headers, fns) { + /*eslint no-param-reassign:0*/ + utils.forEach(fns, function transform(fn) { + data = fn(data, headers); + }); + + return data; + }; + + var isCancel = function isCancel(value) { + return !!(value && value.__CANCEL__); + }; + + var normalizeHeaderName = function normalizeHeaderName(headers, normalizedName) { + utils.forEach(headers, function processHeader(value, name) { + if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) { + headers[normalizedName] = value; + delete headers[name]; + } + }); + }; + + /** + * Update an Error with the specified config, error code, and response. + * + * @param {Error} error The error to update. + * @param {Object} config The config. + * @param {string} [code] The error code (for example, 'ECONNABORTED'). + * @param {Object} [request] The request. + * @param {Object} [response] The response. + * @returns {Error} The error. + */ + var enhanceError = function enhanceError(error, config, code, request, response) { + error.config = config; + if (code) { + error.code = code; + } + + error.request = request; + error.response = response; + error.isAxiosError = true; + + error.toJSON = function toJSON() { + return { + // Standard + message: this.message, + name: this.name, + // Microsoft + description: this.description, + number: this.number, + // Mozilla + fileName: this.fileName, + lineNumber: this.lineNumber, + columnNumber: this.columnNumber, + stack: this.stack, + // Axios + config: this.config, + code: this.code + }; + }; + return error; + }; + + /** + * Create an Error with the specified message, config, error code, request and response. + * + * @param {string} message The error message. + * @param {Object} config The config. + * @param {string} [code] The error code (for example, 'ECONNABORTED'). + * @param {Object} [request] The request. + * @param {Object} [response] The response. + * @returns {Error} The created error. + */ + var createError = function createError(message, config, code, request, response) { + var error = new Error(message); + return enhanceError(error, config, code, request, response); + }; + + /** + * Resolve or reject a Promise based on response status. + * + * @param {Function} resolve A function that resolves the promise. + * @param {Function} reject A function that rejects the promise. + * @param {object} response The response. + */ + var settle = function settle(resolve, reject, response) { + var validateStatus = response.config.validateStatus; + if (!response.status || !validateStatus || validateStatus(response.status)) { + resolve(response); + } else { + reject(createError( + 'Request failed with status code ' + response.status, + response.config, + null, + response.request, + response + )); + } + }; + + var cookies = ( + utils.isStandardBrowserEnv() ? + + // Standard browser envs support document.cookie + (function standardBrowserEnv() { + return { + write: function write(name, value, expires, path, domain, secure) { + var cookie = []; + cookie.push(name + '=' + encodeURIComponent(value)); + + if (utils.isNumber(expires)) { + cookie.push('expires=' + new Date(expires).toGMTString()); + } + + if (utils.isString(path)) { + cookie.push('path=' + path); + } + + if (utils.isString(domain)) { + cookie.push('domain=' + domain); + } + + if (secure === true) { + cookie.push('secure'); + } + + document.cookie = cookie.join('; '); + }, + + read: function read(name) { + var match = document.cookie.match(new RegExp('(^|;\\s*)(' + name + ')=([^;]*)')); + return (match ? decodeURIComponent(match[3]) : null); + }, + + remove: function remove(name) { + this.write(name, '', Date.now() - 86400000); + } + }; + })() : + + // Non standard browser env (web workers, react-native) lack needed support. + (function nonStandardBrowserEnv() { + return { + write: function write() {}, + read: function read() { return null; }, + remove: function remove() {} + }; + })() + ); + + /** + * Determines whether the specified URL is absolute + * + * @param {string} url The URL to test + * @returns {boolean} True if the specified URL is absolute, otherwise false + */ + var isAbsoluteURL = function isAbsoluteURL(url) { + // A URL is considered absolute if it begins with "://" or "//" (protocol-relative URL). + // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed + // by any combination of letters, digits, plus, period, or hyphen. + return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url); + }; + + /** + * Creates a new URL by combining the specified URLs + * + * @param {string} baseURL The base URL + * @param {string} relativeURL The relative URL + * @returns {string} The combined URL + */ + var combineURLs = function combineURLs(baseURL, relativeURL) { + return relativeURL + ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '') + : baseURL; + }; + + /** + * Creates a new URL by combining the baseURL with the requestedURL, + * only when the requestedURL is not already an absolute URL. + * If the requestURL is absolute, this function returns the requestedURL untouched. + * + * @param {string} baseURL The base URL + * @param {string} requestedURL Absolute or relative URL to combine + * @returns {string} The combined full path + */ + var buildFullPath = function buildFullPath(baseURL, requestedURL) { + if (baseURL && !isAbsoluteURL(requestedURL)) { + return combineURLs(baseURL, requestedURL); + } + return requestedURL; + }; + + // Headers whose duplicates are ignored by node + // c.f. https://nodejs.org/api/http.html#http_message_headers + var ignoreDuplicateOf = [ + 'age', 'authorization', 'content-length', 'content-type', 'etag', + 'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since', + 'last-modified', 'location', 'max-forwards', 'proxy-authorization', + 'referer', 'retry-after', 'user-agent' + ]; + + /** + * Parse headers into an object + * + * ``` + * Date: Wed, 27 Aug 2014 08:58:49 GMT + * Content-Type: application/json + * Connection: keep-alive + * Transfer-Encoding: chunked + * ``` + * + * @param {String} headers Headers needing to be parsed + * @returns {Object} Headers parsed into an object + */ + var parseHeaders = function parseHeaders(headers) { + var parsed = {}; + var key; + var val; + var i; + + if (!headers) { return parsed; } + + utils.forEach(headers.split('\n'), function parser(line) { + i = line.indexOf(':'); + key = utils.trim(line.substr(0, i)).toLowerCase(); + val = utils.trim(line.substr(i + 1)); + + if (key) { + if (parsed[key] && ignoreDuplicateOf.indexOf(key) >= 0) { + return; + } + if (key === 'set-cookie') { + parsed[key] = (parsed[key] ? parsed[key] : []).concat([val]); + } else { + parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; + } + } + }); + + return parsed; + }; + + var isURLSameOrigin = ( + utils.isStandardBrowserEnv() ? + + // Standard browser envs have full support of the APIs needed to test + // whether the request URL is of the same origin as current location. + (function standardBrowserEnv() { + var msie = /(msie|trident)/i.test(navigator.userAgent); + var urlParsingNode = document.createElement('a'); + var originURL; + + /** + * Parse a URL to discover it's components + * + * @param {String} url The URL to be parsed + * @returns {Object} + */ + function resolveURL(url) { + var href = url; + + if (msie) { + // IE needs attribute set twice to normalize properties + urlParsingNode.setAttribute('href', href); + href = urlParsingNode.href; + } + + urlParsingNode.setAttribute('href', href); + + // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils + return { + href: urlParsingNode.href, + protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '', + host: urlParsingNode.host, + search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', + hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', + hostname: urlParsingNode.hostname, + port: urlParsingNode.port, + pathname: (urlParsingNode.pathname.charAt(0) === '/') ? + urlParsingNode.pathname : + '/' + urlParsingNode.pathname + }; + } + + originURL = resolveURL(window.location.href); + + /** + * Determine if a URL shares the same origin as the current location + * + * @param {String} requestURL The URL to test + * @returns {boolean} True if URL shares the same origin, otherwise false + */ + return function isURLSameOrigin(requestURL) { + var parsed = (utils.isString(requestURL)) ? resolveURL(requestURL) : requestURL; + return (parsed.protocol === originURL.protocol && + parsed.host === originURL.host); + }; + })() : + + // Non standard browser envs (web workers, react-native) lack needed support. + (function nonStandardBrowserEnv() { + return function isURLSameOrigin() { + return true; + }; + })() + ); + + var xhr = function xhrAdapter(config) { + return new Promise(function dispatchXhrRequest(resolve, reject) { + var requestData = config.data; + var requestHeaders = config.headers; + + if (utils.isFormData(requestData)) { + delete requestHeaders['Content-Type']; // Let the browser set it + } + + var request = new XMLHttpRequest(); + + // HTTP basic authentication + if (config.auth) { + var username = config.auth.username || ''; + var password = config.auth.password ? unescape(encodeURIComponent(config.auth.password)) : ''; + requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password); + } + + var fullPath = buildFullPath(config.baseURL, config.url); + request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true); + + // Set the request timeout in MS + request.timeout = config.timeout; + + // Listen for ready state + request.onreadystatechange = function handleLoad() { + if (!request || request.readyState !== 4) { + return; + } + + // The request errored out and we didn't get a response, this will be + // handled by onerror instead + // With one exception: request that using file: protocol, most browsers + // will return status as 0 even though it's a successful request + if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) { + return; + } + + // Prepare the response + var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null; + var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response; + var response = { + data: responseData, + status: request.status, + statusText: request.statusText, + headers: responseHeaders, + config: config, + request: request + }; + + settle(resolve, reject, response); + + // Clean up request + request = null; + }; + + // Handle browser request cancellation (as opposed to a manual cancellation) + request.onabort = function handleAbort() { + if (!request) { + return; + } + + reject(createError('Request aborted', config, 'ECONNABORTED', request)); + + // Clean up request + request = null; + }; + + // Handle low level network errors + request.onerror = function handleError() { + // Real errors are hidden from us by the browser + // onerror should only fire if it's a network error + reject(createError('Network Error', config, null, request)); + + // Clean up request + request = null; + }; + + // Handle timeout + request.ontimeout = function handleTimeout() { + var timeoutErrorMessage = 'timeout of ' + config.timeout + 'ms exceeded'; + if (config.timeoutErrorMessage) { + timeoutErrorMessage = config.timeoutErrorMessage; + } + reject(createError(timeoutErrorMessage, config, 'ECONNABORTED', + request)); + + // Clean up request + request = null; + }; + + // Add xsrf header + // This is only done if running in a standard browser environment. + // Specifically not if we're in a web worker, or react-native. + if (utils.isStandardBrowserEnv()) { + // Add xsrf header + var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ? + cookies.read(config.xsrfCookieName) : + undefined; + + if (xsrfValue) { + requestHeaders[config.xsrfHeaderName] = xsrfValue; + } + } + + // Add headers to the request + if ('setRequestHeader' in request) { + utils.forEach(requestHeaders, function setRequestHeader(val, key) { + if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') { + // Remove Content-Type if data is undefined + delete requestHeaders[key]; + } else { + // Otherwise add header to the request + request.setRequestHeader(key, val); + } + }); + } + + // Add withCredentials to request if needed + if (!utils.isUndefined(config.withCredentials)) { + request.withCredentials = !!config.withCredentials; + } + + // Add responseType to request if needed + if (config.responseType) { + try { + request.responseType = config.responseType; + } catch (e) { + // Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2. + // But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function. + if (config.responseType !== 'json') { + throw e; + } + } + } + + // Handle progress if needed + if (typeof config.onDownloadProgress === 'function') { + request.addEventListener('progress', config.onDownloadProgress); + } + + // Not all browsers support upload events + if (typeof config.onUploadProgress === 'function' && request.upload) { + request.upload.addEventListener('progress', config.onUploadProgress); + } + + if (config.cancelToken) { + // Handle cancellation + config.cancelToken.promise.then(function onCanceled(cancel) { + if (!request) { + return; + } + + request.abort(); + reject(cancel); + // Clean up request + request = null; + }); + } + + if (!requestData) { + requestData = null; + } + + // Send the request + request.send(requestData); + }); + }; + + var DEFAULT_CONTENT_TYPE = { + 'Content-Type': 'application/x-www-form-urlencoded' + }; + + function setContentTypeIfUnset(headers, value) { + if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) { + headers['Content-Type'] = value; + } + } + + function getDefaultAdapter() { + var adapter; + if (typeof XMLHttpRequest !== 'undefined') { + // For browsers use XHR adapter + adapter = xhr; + } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') { + // For node use HTTP adapter + adapter = xhr; + } + return adapter; + } + + var defaults = { + adapter: getDefaultAdapter(), + + transformRequest: [function transformRequest(data, headers) { + normalizeHeaderName(headers, 'Accept'); + normalizeHeaderName(headers, 'Content-Type'); + if (utils.isFormData(data) || + utils.isArrayBuffer(data) || + utils.isBuffer(data) || + utils.isStream(data) || + utils.isFile(data) || + utils.isBlob(data) + ) { + return data; + } + if (utils.isArrayBufferView(data)) { + return data.buffer; + } + if (utils.isURLSearchParams(data)) { + setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8'); + return data.toString(); + } + if (utils.isObject(data)) { + setContentTypeIfUnset(headers, 'application/json;charset=utf-8'); + return JSON.stringify(data); + } + return data; + }], + + transformResponse: [function transformResponse(data) { + /*eslint no-param-reassign:0*/ + if (typeof data === 'string') { + try { + data = JSON.parse(data); + } catch (e) { /* Ignore */ } + } + return data; + }], + + /** + * A timeout in milliseconds to abort a request. If set to 0 (default) a + * timeout is not created. + */ + timeout: 0, + + xsrfCookieName: 'XSRF-TOKEN', + xsrfHeaderName: 'X-XSRF-TOKEN', + + maxContentLength: -1, + maxBodyLength: -1, + + validateStatus: function validateStatus(status) { + return status >= 200 && status < 300; + } + }; + + defaults.headers = { + common: { + 'Accept': 'application/json, text/plain, */*' + } + }; + + utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) { + defaults.headers[method] = {}; + }); + + utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { + defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE); + }); + + var defaults_1 = defaults; + + /** + * Throws a `Cancel` if cancellation has been requested. + */ + function throwIfCancellationRequested(config) { + if (config.cancelToken) { + config.cancelToken.throwIfRequested(); + } + } + + /** + * Dispatch a request to the server using the configured adapter. + * + * @param {object} config The config that is to be used for the request + * @returns {Promise} The Promise to be fulfilled + */ + var dispatchRequest = function dispatchRequest(config) { + throwIfCancellationRequested(config); + + // Ensure headers exist + config.headers = config.headers || {}; + + // Transform request data + config.data = transformData( + config.data, + config.headers, + config.transformRequest + ); + + // Flatten headers + config.headers = utils.merge( + config.headers.common || {}, + config.headers[config.method] || {}, + config.headers + ); + + utils.forEach( + ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'], + function cleanHeaderConfig(method) { + delete config.headers[method]; + } + ); + + var adapter = config.adapter || defaults_1.adapter; + + return adapter(config).then(function onAdapterResolution(response) { + throwIfCancellationRequested(config); + + // Transform response data + response.data = transformData( + response.data, + response.headers, + config.transformResponse + ); + + return response; + }, function onAdapterRejection(reason) { + if (!isCancel(reason)) { + throwIfCancellationRequested(config); + + // Transform response data + if (reason && reason.response) { + reason.response.data = transformData( + reason.response.data, + reason.response.headers, + config.transformResponse + ); + } + } + + return Promise.reject(reason); + }); + }; + + /** + * Config-specific merge-function which creates a new config-object + * by merging two configuration objects together. + * + * @param {Object} config1 + * @param {Object} config2 + * @returns {Object} New object resulting from merging config2 to config1 + */ + var mergeConfig = function mergeConfig(config1, config2) { + // eslint-disable-next-line no-param-reassign + config2 = config2 || {}; + var config = {}; + + var valueFromConfig2Keys = ['url', 'method', 'data']; + var mergeDeepPropertiesKeys = ['headers', 'auth', 'proxy', 'params']; + var defaultToConfig2Keys = [ + 'baseURL', 'transformRequest', 'transformResponse', 'paramsSerializer', + 'timeout', 'timeoutMessage', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName', + 'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', 'decompress', + 'maxContentLength', 'maxBodyLength', 'maxRedirects', 'transport', 'httpAgent', + 'httpsAgent', 'cancelToken', 'socketPath', 'responseEncoding' + ]; + var directMergeKeys = ['validateStatus']; + + function getMergedValue(target, source) { + if (utils.isPlainObject(target) && utils.isPlainObject(source)) { + return utils.merge(target, source); + } else if (utils.isPlainObject(source)) { + return utils.merge({}, source); + } else if (utils.isArray(source)) { + return source.slice(); + } + return source; + } + + function mergeDeepProperties(prop) { + if (!utils.isUndefined(config2[prop])) { + config[prop] = getMergedValue(config1[prop], config2[prop]); + } else if (!utils.isUndefined(config1[prop])) { + config[prop] = getMergedValue(undefined, config1[prop]); + } + } + + utils.forEach(valueFromConfig2Keys, function valueFromConfig2(prop) { + if (!utils.isUndefined(config2[prop])) { + config[prop] = getMergedValue(undefined, config2[prop]); + } + }); + + utils.forEach(mergeDeepPropertiesKeys, mergeDeepProperties); + + utils.forEach(defaultToConfig2Keys, function defaultToConfig2(prop) { + if (!utils.isUndefined(config2[prop])) { + config[prop] = getMergedValue(undefined, config2[prop]); + } else if (!utils.isUndefined(config1[prop])) { + config[prop] = getMergedValue(undefined, config1[prop]); + } + }); + + utils.forEach(directMergeKeys, function merge(prop) { + if (prop in config2) { + config[prop] = getMergedValue(config1[prop], config2[prop]); + } else if (prop in config1) { + config[prop] = getMergedValue(undefined, config1[prop]); + } + }); + + var axiosKeys = valueFromConfig2Keys + .concat(mergeDeepPropertiesKeys) + .concat(defaultToConfig2Keys) + .concat(directMergeKeys); + + var otherKeys = Object + .keys(config1) + .concat(Object.keys(config2)) + .filter(function filterAxiosKeys(key) { + return axiosKeys.indexOf(key) === -1; + }); + + utils.forEach(otherKeys, mergeDeepProperties); + + return config; + }; + + /** + * Create a new instance of Axios + * + * @param {Object} instanceConfig The default config for the instance + */ + function Axios(instanceConfig) { + this.defaults = instanceConfig; + this.interceptors = { + request: new InterceptorManager_1(), + response: new InterceptorManager_1() + }; + } + + /** + * Dispatch a request + * + * @param {Object} config The config specific for this request (merged with this.defaults) + */ + Axios.prototype.request = function request(config) { + /*eslint no-param-reassign:0*/ + // Allow for axios('example/url'[, config]) a la fetch API + if (typeof config === 'string') { + config = arguments[1] || {}; + config.url = arguments[0]; + } else { + config = config || {}; + } + + config = mergeConfig(this.defaults, config); + + // Set config.method + if (config.method) { + config.method = config.method.toLowerCase(); + } else if (this.defaults.method) { + config.method = this.defaults.method.toLowerCase(); + } else { + config.method = 'get'; + } + + // Hook up interceptors middleware + var chain = [dispatchRequest, undefined]; + var promise = Promise.resolve(config); + + this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { + chain.unshift(interceptor.fulfilled, interceptor.rejected); + }); + + this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) { + chain.push(interceptor.fulfilled, interceptor.rejected); + }); + + while (chain.length) { + promise = promise.then(chain.shift(), chain.shift()); + } + + return promise; + }; + + Axios.prototype.getUri = function getUri(config) { + config = mergeConfig(this.defaults, config); + return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, ''); + }; + + // Provide aliases for supported request methods + utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) { + /*eslint func-names:0*/ + Axios.prototype[method] = function(url, config) { + return this.request(mergeConfig(config || {}, { + method: method, + url: url, + data: (config || {}).data + })); + }; + }); + + utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { + /*eslint func-names:0*/ + Axios.prototype[method] = function(url, data, config) { + return this.request(mergeConfig(config || {}, { + method: method, + url: url, + data: data + })); + }; + }); + + var Axios_1 = Axios; + + /** + * A `Cancel` is an object that is thrown when an operation is canceled. + * + * @class + * @param {string=} message The message. + */ + function Cancel(message) { + this.message = message; + } + + Cancel.prototype.toString = function toString() { + return 'Cancel' + (this.message ? ': ' + this.message : ''); + }; + + Cancel.prototype.__CANCEL__ = true; + + var Cancel_1 = Cancel; + + /** + * A `CancelToken` is an object that can be used to request cancellation of an operation. + * + * @class + * @param {Function} executor The executor function. + */ + function CancelToken(executor) { + if (typeof executor !== 'function') { + throw new TypeError('executor must be a function.'); + } + + var resolvePromise; + this.promise = new Promise(function promiseExecutor(resolve) { + resolvePromise = resolve; + }); + + var token = this; + executor(function cancel(message) { + if (token.reason) { + // Cancellation has already been requested + return; + } + + token.reason = new Cancel_1(message); + resolvePromise(token.reason); + }); + } + + /** + * Throws a `Cancel` if cancellation has been requested. + */ + CancelToken.prototype.throwIfRequested = function throwIfRequested() { + if (this.reason) { + throw this.reason; + } + }; + + /** + * Returns an object that contains a new `CancelToken` and a function that, when called, + * cancels the `CancelToken`. + */ + CancelToken.source = function source() { + var cancel; + var token = new CancelToken(function executor(c) { + cancel = c; + }); + return { + token: token, + cancel: cancel + }; + }; + + var CancelToken_1 = CancelToken; + + /** + * Syntactic sugar for invoking a function and expanding an array for arguments. + * + * Common use case would be to use `Function.prototype.apply`. + * + * ```js + * function f(x, y, z) {} + * var args = [1, 2, 3]; + * f.apply(null, args); + * ``` + * + * With `spread` this example can be re-written. + * + * ```js + * spread(function(x, y, z) {})([1, 2, 3]); + * ``` + * + * @param {Function} callback + * @returns {Function} + */ + var spread = function spread(callback) { + return function wrap(arr) { + return callback.apply(null, arr); + }; + }; + + /** + * Determines whether the payload is an error thrown by Axios + * + * @param {*} payload The value to test + * @returns {boolean} True if the payload is an error thrown by Axios, otherwise false + */ + var isAxiosError = function isAxiosError(payload) { + return (typeof payload === 'object') && (payload.isAxiosError === true); + }; + + /** + * Create an instance of Axios + * + * @param {Object} defaultConfig The default config for the instance + * @return {Axios} A new instance of Axios + */ + function createInstance(defaultConfig) { + var context = new Axios_1(defaultConfig); + var instance = bind(Axios_1.prototype.request, context); + + // Copy axios.prototype to instance + utils.extend(instance, Axios_1.prototype, context); + + // Copy context to instance + utils.extend(instance, context); + + return instance; + } + + // Create the default instance to be exported + var axios$1 = createInstance(defaults_1); + + // Expose Axios class to allow class inheritance + axios$1.Axios = Axios_1; + + // Factory for creating new instances + axios$1.create = function create(instanceConfig) { + return createInstance(mergeConfig(axios$1.defaults, instanceConfig)); + }; + + // Expose Cancel & CancelToken + axios$1.Cancel = Cancel_1; + axios$1.CancelToken = CancelToken_1; + axios$1.isCancel = isCancel; + + // Expose all/spread + axios$1.all = function all(promises) { + return Promise.all(promises); + }; + axios$1.spread = spread; + + // Expose isAxiosError + axios$1.isAxiosError = isAxiosError; + + var axios_1 = axios$1; + + // Allow use of default import syntax in TypeScript + var _default = axios$1; + axios_1.default = _default; + + var axios = axios_1; + + class RTCEndpoint extends Event$1 { + constructor(options) { + super('RTCPusherPlayer'); + this.TAG = '[RTCPusherPlayer]'; + let defaults = { + element: '', + // html video element + debug: false, + // if output debug log + zlmsdpUrl: '', + simulcast: false, + useCamera: true, + audioEnable: true, + videoEnable: true, + recvOnly: false, + resolution: { + w: 0, + h: 0 + }, + usedatachannel: false + }; + this.options = Object.assign({}, defaults, options); + + if (this.options.debug) { + setLogger(); + } + + this.e = { + onicecandidate: this._onIceCandidate.bind(this), + ontrack: this._onTrack.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; + this.pc = new RTCPeerConnection(null); + 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(); + } + + receive() { + + const AudioTransceiverInit = { + direction: 'recvonly', + sendEncodings: [] + }; + const VideoTransceiverInit = { + direction: 'recvonly', + sendEncodings: [] + }; + this.pc.addTransceiver('audio', AudioTransceiverInit); + this.pc.addTransceiver('video', VideoTransceiverInit); + this.pc.createOffer().then(desc => { + log(this.TAG, 'offer:', desc.sdp); + this.pc.setLocalDescription(desc).then(() => { + axios({ + method: 'post', + url: this.options.zlmsdpUrl, + responseType: 'json', + data: desc.sdp, + headers: { + 'Content-Type': 'text/plain;charset=utf-8' + } + }).then(response => { + let ret = response.data; //JSON.parse(response.data); + + if (ret.code != 0) { + // mean failed for offer/anwser exchange + this.dispatch(Events$1.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, ret); + return; + } + + let anwser = {}; + anwser.sdp = ret.sdp; + anwser.type = 'answer'; + log(this.TAG, 'answer:', ret.sdp); + this.pc.setRemoteDescription(anwser).then(() => { + log(this.TAG, 'set remote sucess'); + }).catch(e => { + error(this.TAG, e); + }); + }); + }); + }).catch(e => { + error(this.TAG, e); + }); + } + + start() { + let videoConstraints = false; + let audioConstraints = false; + + if (this.options.useCamera) { + if (this.options.videoEnable) videoConstraints = new VideoTrackConstraints(VideoSourceInfo.CAMERA); + if (this.options.audioEnable) audioConstraints = new AudioTrackConstraints(AudioSourceInfo.MIC); + } else { + if (this.options.videoEnable) { + videoConstraints = new VideoTrackConstraints(VideoSourceInfo.SCREENCAST); + if (this.options.audioEnable) audioConstraints = new AudioTrackConstraints(AudioSourceInfo.SCREENCAST); + } else { + if (this.options.audioEnable) audioConstraints = new AudioTrackConstraints(AudioSourceInfo.MIC);else { + // error shared display media not only audio + error(this.TAG, 'error paramter'); + } + } + } + + 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); + const AudioTransceiverInit = { + direction: 'sendrecv', + sendEncodings: [] + }; + const VideoTransceiverInit = { + direction: 'sendrecv', + sendEncodings: [] + }; + + if (this.options.simulcast && stream.getVideoTracks().length > 0) { + VideoTransceiverInit.sendEncodings = [{ + rid: 'h', + active: true, + maxBitrate: 1000000 + }, { + rid: 'm', + active: true, + maxBitrate: 500000, + scaleResolutionDownBy: 2 + }, { + rid: 'l', + active: true, + maxBitrate: 200000, + scaleResolutionDownBy: 4 + }]; + } + + 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 (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)=>{ + debug.log(this.TAG,track); + this.pc.addTrack(track); + }); + */ + + + this.pc.createOffer().then(desc => { + log(this.TAG, 'offer:', desc.sdp); + this.pc.setLocalDescription(desc).then(() => { + axios({ + method: 'post', + url: this.options.zlmsdpUrl, + responseType: 'json', + data: desc.sdp, + headers: { + 'Content-Type': 'text/plain;charset=utf-8' + } + }).then(response => { + let ret = response.data; //JSON.parse(response.data); + + if (ret.code != 0) { + // mean failed for offer/anwser exchange + this.dispatch(Events$1.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, ret); + return; + } + + let anwser = {}; + anwser.sdp = ret.sdp; + anwser.type = 'answer'; + log(this.TAG, 'answer:', ret.sdp); + this.pc.setRemoteDescription(anwser).then(() => { + log(this.TAG, 'set remote sucess'); + }).catch(e => { + error(this.TAG, e); + }); + }); + }); + }).catch(e => { + error(this.TAG, e); + }); + }).catch(e => { + this.dispatch(Events$1.CAPTURE_STREAM_FAILED); //debug.error(this.TAG,e); + }); //const offerOptions = {}; + + /* + if (typeof this.pc.addTransceiver === 'function') { + // |direction| seems not working on Safari. + this.pc.addTransceiver('audio', { direction: 'recvonly' }); + this.pc.addTransceiver('video', { direction: 'recvonly' }); + } else { + offerOptions.offerToReceiveAudio = true; + offerOptions.offerToReceiveVideo = true; + } + */ + } + + _onIceCandidate(event) { + if (event.candidate) { + log('Remote ICE candidate: \n ' + event.candidate.candidate); // Send the candidate to the remote peer + } + } + + _onTrack(event) { + if (this.options.element && event.streams && event.streams.length > 0) { + this.options.element.srcObject = event.streams[0]; + this._remoteStream = event.streams[0]; + this.dispatch(Events$1.WEBRTC_ON_REMOTE_STREAMS, event); + } else { + error('element pararm is failed'); + } + } + + _onIceCandidateError(event) { + 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; + } + + if (this.options) { + this.options = null; + } + + if (this._localStream) { + this._localStream.getTracks().forEach((track, idx) => { + track.stop(); + }); + } + + if (this._remoteStream) { + this._remoteStream.getTracks().forEach((track, idx) => { + track.stop(); + }); + } + } + + get remoteStream() { + return this._remoteStream; + } + + get localStream() { + return this._localStream; + } + + } + + 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 }); + + return exports; + +}({})); +//# sourceMappingURL=ZLMRTCClient.js.map diff --git a/web_src/static/js/ZLMRTCClient.js.map b/web_src/static/js/ZLMRTCClient.js.map new file mode 100644 index 000000000..34c0b5682 --- /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/config.js b/web_src/static/js/config.js new file mode 100644 index 000000000..94a105690 --- /dev/null +++ b/web_src/static/js/config.js @@ -0,0 +1,22 @@ + +window.baseUrl = "" + +// 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 +} diff --git a/web_src/static/js/jessibuca/decoder.js b/web_src/static/js/jessibuca/decoder.js new file mode 100644 index 000000000..7813b9b20 --- /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),i=n(r),a=n(t);function s(e,r){return e(r={exports:{}},r.exports),r.exports}var l=s((function(e){var r=void 0!==r?r:{},t=(r={print:function(e){console.log("Jessibuca: [worker]:",e)},printErr:function(e){console.warn("Jessibuca: [worker]:",e),postMessage({cmd:"wasmError",message:e})}},Object.assign({},r)),n="./this.program",s="object"==typeof window,l="function"==typeof importScripts,u="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,c=!s&&!u&&!l;if(r.ENVIRONMENT)throw new Error("Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -sENVIRONMENT=web or -sENVIRONMENT=node)");var d,f,p,m,h,g,v="";if(u){if("object"!=typeof process)throw new Error("not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)");v=l?o.default.dirname(v)+"/":__dirname+"/",g=()=>{h||(m=i.default,h=o.default)},d=function(e,r){return g(),e=h.normalize(e),m.readFileSync(e,r?void 0:"utf8")},p=e=>{var r=d(e,!0);return r.buffer||(r=new Uint8Array(r)),F(r.buffer),r},f=(e,r,t)=>{g(),e=h.normalize(e),m.readFile(e,(function(e,n){e?t(e):r(n.buffer)}))},process.argv.length>1&&(n=process.argv[1].replace(/\\/g,"/")),process.argv.slice(2),e.exports=r,process.on("uncaughtException",(function(e){if(!(e instanceof St))throw e})),process.on("unhandledRejection",(function(e){throw e})),r.inspect=function(){return"[Emscripten Module object]"}}else if(c){if("object"==typeof process||"object"==typeof window||"function"==typeof importScripts)throw new Error("not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)");"undefined"!=typeof read&&(d=function(e){return read(e)}),p=function(e){let r;return"function"==typeof readbuffer?new Uint8Array(readbuffer(e)):(r=read(e,"binary"),F("object"==typeof r),r)},f=function(e,r,t){setTimeout((()=>r(p(e))),0)},"undefined"!=typeof scriptArgs&&scriptArgs,"undefined"!=typeof print&&("undefined"==typeof console&&(console={}),console.log=print,console.warn=console.error="undefined"!=typeof printErr?printErr:print)}else{if(!s&&!l)throw new Error("environment detection error");if(l?v=self.location.href:"undefined"!=typeof document&&document.currentScript&&(v=document.currentScript.src),v=0!==v.indexOf("blob:")?v.substr(0,v.replace(/[?#].*/,"").lastIndexOf("/")+1):"","object"!=typeof window&&"function"!=typeof importScripts)throw new Error("not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)");d=e=>{var r=new XMLHttpRequest;return r.open("GET",e,!1),r.send(null),r.responseText},l&&(p=e=>{var r=new XMLHttpRequest;return r.open("GET",e,!1),r.responseType="arraybuffer",r.send(null),new Uint8Array(r.response)}),f=(e,r,t)=>{var n=new XMLHttpRequest;n.open("GET",e,!0),n.responseType="arraybuffer",n.onload=()=>{200==n.status||0==n.status&&n.response?r(n.response):t()},n.onerror=t,n.send(null)}}var y,E,w,b=r.print||console.log.bind(console),_=r.printErr||console.warn.bind(console);function T(e){T.shown||(T.shown={}),T.shown[e]||(T.shown[e]=1,_(e))}function k(e,t){Object.getOwnPropertyDescriptor(r,e)||Object.defineProperty(r,e,{configurable:!0,get:function(){ge("Module."+e+" has been replaced with plain "+t+" (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)")}})}function S(e,r){var t="'"+e+"' was not exported. add it to EXPORTED_RUNTIME_METHODS (see the FAQ)";return r&&(t+=". Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you"),t}function C(e,t){Object.getOwnPropertyDescriptor(r,e)||Object.defineProperty(r,e,{configurable:!0,get:function(){ge(S(e,t))}})}function P(e,t){Object.getOwnPropertyDescriptor(r,e)||(r[e]=()=>ge(S(e,t)))}Object.assign(r,t),t=null,y="fetchSettings",Object.getOwnPropertyDescriptor(r,y)&&ge("`Module."+y+"` was supplied but `"+y+"` not included in INCOMING_MODULE_JS_API"),r.arguments,k("arguments","arguments_"),r.thisProgram&&(n=r.thisProgram),k("thisProgram","thisProgram"),r.quit,k("quit","quit_"),F(void 0===r.memoryInitializerPrefixURL,"Module.memoryInitializerPrefixURL option was removed, use Module.locateFile instead"),F(void 0===r.pthreadMainPrefixURL,"Module.pthreadMainPrefixURL option was removed, use Module.locateFile instead"),F(void 0===r.cdInitializerPrefixURL,"Module.cdInitializerPrefixURL option was removed, use Module.locateFile instead"),F(void 0===r.filePackagePrefixURL,"Module.filePackagePrefixURL option was removed, use Module.locateFile instead"),F(void 0===r.read,"Module.read option was removed (modify read_ in JS)"),F(void 0===r.readAsync,"Module.readAsync option was removed (modify readAsync in JS)"),F(void 0===r.readBinary,"Module.readBinary option was removed (modify readBinary in JS)"),F(void 0===r.setWindowTitle,"Module.setWindowTitle option was removed (modify setWindowTitle in JS)"),F(void 0===r.TOTAL_MEMORY,"Module.TOTAL_MEMORY has been renamed Module.INITIAL_MEMORY"),k("read","read_"),k("readAsync","readAsync"),k("readBinary","readBinary"),k("setWindowTitle","setWindowTitle"),F(!c,"shell environment detected but not enabled at build time. Add 'shell' to `-sENVIRONMENT` to enable."),r.wasmBinary&&(E=r.wasmBinary),k("wasmBinary","wasmBinary"),r.noExitRuntime,k("noExitRuntime","noExitRuntime"),"object"!=typeof WebAssembly&&ge("no native wasm support detected");var A=!1;function F(e,r){e||ge("Assertion failed"+(r?": "+r:""))}var D="undefined"!=typeof TextDecoder?new TextDecoder("utf8"):void 0;function O(e,r,t){for(var n=r+t,o=r;e[o]&&!(o>=n);)++o;if(o-r>16&&e.buffer&&D)return D.decode(e.subarray(r,o));for(var i="";r>10,56320|1023&u)}}else i+=String.fromCharCode((31&a)<<6|s)}else i+=String.fromCharCode(a)}return i}function R(e,r){return e?O(U,e,r):""}function M(e,r,t,n){if(!(n>0))return 0;for(var o=t,i=t+n-1,a=0;a=55296&&s<=57343)s=65536+((1023&s)<<10)|1023&e.charCodeAt(++a);if(s<=127){if(t>=i)break;r[t++]=s}else if(s<=2047){if(t+1>=i)break;r[t++]=192|s>>6,r[t++]=128|63&s}else if(s<=65535){if(t+2>=i)break;r[t++]=224|s>>12,r[t++]=128|s>>6&63,r[t++]=128|63&s}else{if(t+3>=i)break;s>1114111&&T("Invalid Unicode code point 0x"+s.toString(16)+" encountered when serializing a JS string to a UTF-8 string in wasm memory! (Valid unicode code points should be in range 0-0x10FFFF)."),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 N(e,r,t){return F("number"==typeof t,"stringToUTF8(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!"),M(e,U,r,t)}function I(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 L,x,U,B,j,$,W,z,H,G="undefined"!=typeof TextDecoder?new TextDecoder("utf-16le"):void 0;function V(e,r){F(e%2==0,"Pointer passed to UTF16ToString must be aligned to two bytes!");for(var t=e,n=t>>1,o=n+r/2;!(n>=o)&&j[n];)++n;if((t=n<<1)-e>32&&G)return G.decode(U.subarray(e,t));for(var i="",a=0;!(a>=r/2);++a){var s=B[e+2*a>>1];if(0==s)break;i+=String.fromCharCode(s)}return i}function Y(e,r,t){if(F(r%2==0,"Pointer passed to stringToUTF16 must be aligned to two bytes!"),F("number"==typeof t,"stringToUTF16(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!"),void 0===t&&(t=2147483647),t<2)return 0;for(var n=r,o=(t-=2)<2*e.length?t/2:e.length,i=0;i>1]=a,r+=2}return B[r>>1]=0,r-n}function q(e){return 2*e.length}function X(e,r){F(e%4==0,"Pointer passed to UTF32ToString must be aligned to four bytes!");for(var t=0,n="";!(t>=r/4);){var o=$[e+4*t>>2];if(0==o)break;if(++t,o>=65536){var i=o-65536;n+=String.fromCharCode(55296|i>>10,56320|1023&i)}else n+=String.fromCharCode(o)}return n}function K(e,r,t){if(F(r%4==0,"Pointer passed to stringToUTF32 must be aligned to four bytes!"),F("number"==typeof t,"stringToUTF32(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!"),void 0===t&&(t=2147483647),t<4)return 0;for(var n=r,o=n+t-4,i=0;i=55296&&a<=57343)a=65536+((1023&a)<<10)|1023&e.charCodeAt(++i);if($[r>>2]=a,(r+=4)+4>o)break}return $[r>>2]=0,r-n}function J(e){for(var r=0,t=0;t=55296&&n<=57343&&++t,r+=4}return r}function Q(e){var r=I(e)+1,t=gt(r);return t&&M(e,x,t,r),t}function Z(e){L=e,r.HEAP8=x=new Int8Array(e),r.HEAP16=B=new Int16Array(e),r.HEAP32=$=new Int32Array(e),r.HEAPU8=U=new Uint8Array(e),r.HEAPU16=j=new Uint16Array(e),r.HEAPU32=W=new Uint32Array(e),r.HEAPF32=z=new Float32Array(e),r.HEAPF64=H=new Float64Array(e)}var ee=5242880;r.TOTAL_STACK&&F(ee===r.TOTAL_STACK,"the stack size can no longer be determined at runtime");var re,te=r.INITIAL_MEMORY||67108864;function ne(){var e=kt();F(0==(3&e)),$[e>>2]=34821223,$[e+4>>2]=2310721022,$[0]=1668509029}function oe(){if(!A){var e=kt(),r=W[e>>2],t=W[e+4>>2];34821223==r&&2310721022==t||ge("Stack overflow! Stack cookie has been overwritten, expected hex dwords 0x89BACDFE and 0x2135467, but received 0x"+t.toString(16)+" 0x"+r.toString(16)),1668509029!==$[0]&&ge("Runtime error: The application has corrupted its heap memory area (address zero)!")}}k("INITIAL_MEMORY","INITIAL_MEMORY"),F(te>=ee,"INITIAL_MEMORY should be larger than TOTAL_STACK, was "+te+"! (TOTAL_STACK="+"5242880)"),F("undefined"!=typeof Int32Array&&"undefined"!=typeof Float64Array&&null!=Int32Array.prototype.subarray&&null!=Int32Array.prototype.set,"JS engine does not provide full typed array support"),F(!r.wasmMemory,"Use of `wasmMemory` detected. Use -sIMPORTED_MEMORY to define wasmMemory externally"),F(67108864==te,"Detected runtime INITIAL_MEMORY setting. Use -sIMPORTED_MEMORY to define wasmMemory dynamically"),function(){var e=new Int16Array(1),r=new Int8Array(e.buffer);if(e[0]=25459,115!==r[0]||99!==r[1])throw"Runtime error: expected the system to be little-endian! (Run with -sSUPPORT_BIG_ENDIAN to bypass)"}();var ie=[],ae=[],se=[],le=!1;F(Math.imul,"This browser does not support Math.imul(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill"),F(Math.fround,"This browser does not support Math.fround(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill"),F(Math.clz32,"This browser does not support Math.clz32(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill"),F(Math.trunc,"This browser does not support Math.trunc(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill");var ue=0,ce=null,de=null,fe={};function pe(e){for(var r=e;;){if(!fe[e])return e;e=r+Math.random()}}function me(e){ue++,r.monitorRunDependencies&&r.monitorRunDependencies(ue),e?(F(!fe[e]),fe[e]=1,null===ce&&"undefined"!=typeof setInterval&&(ce=setInterval((function(){if(A)return clearInterval(ce),void(ce=null);var e=!1;for(var r in fe)e||(e=!0,_("still waiting on run dependencies:")),_("dependency: "+r);e&&_("(end of list)")}),1e4))):_("warning: run dependency added without ID")}function he(e){if(ue--,r.monitorRunDependencies&&r.monitorRunDependencies(ue),e?(F(fe[e]),delete fe[e]):_("warning: run dependency removed without ID"),0==ue&&(null!==ce&&(clearInterval(ce),ce=null),de)){var t=de;de=null,t()}}function ge(e){throw r.onAbort&&r.onAbort(e),_(e="Aborted("+e+")"),A=!0,new WebAssembly.RuntimeError(e)}var ve,ye,Ee;function we(e){return e.startsWith("data:application/octet-stream;base64,")}function be(e){return e.startsWith("file://")}function _e(e,t){return function(){var n=e,o=t;return t||(o=r.asm),F(le,"native function `"+n+"` called before runtime initialization"),o[e]||F(o[e],"exported native function `"+n+"` not found"),o[e].apply(null,arguments)}}function Te(e){try{if(e==ve&&E)return new Uint8Array(E);if(p)return p(e);throw"both async and sync fetching of the wasm failed"}catch(e){ge(e)}}function ke(e){for(;e.length>0;){var t=e.shift();if("function"!=typeof t){var n=t.func;"number"==typeof n?void 0===t.arg?Ce(n)():Ce(n)(t.arg):n(void 0===t.arg?null:t.arg)}else t(r)}}function Se(e){return e.replace(/\b_Z[\w\d_]+/g,(function(e){var r,t=(r=e,T("warning: build with -sDEMANGLE_SUPPORT to link in libcxxabi demangling"),r);return e===t?e:t+" ["+e+"]"}))}function Ce(e){return re.get(e)}function Pe(){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()}we(ve="decoder.wasm")||(ve=function(e){return r.locateFile?r.locateFile(e,v):v+e}(ve));var Ae={isAbs:e=>"/"===e.charAt(0),splitPath:e=>/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(e).slice(1),normalizeArray:(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:e=>{var r=Ae.isAbs(e),t="/"===e.substr(-1);return(e=Ae.normalizeArray(e.split("/").filter((e=>!!e)),!r).join("/"))||r||(e="."),e&&t&&(e+="/"),(r?"/":"")+e},dirname:e=>{var r=Ae.splitPath(e),t=r[0],n=r[1];return t||n?(n&&(n=n.substr(0,n.length-1)),t+n):"."},basename:e=>{if("/"===e)return"/";var r=(e=(e=Ae.normalize(e)).replace(/\/$/,"")).lastIndexOf("/");return-1===r?e:e.substr(r+1)},join:function(){var e=Array.prototype.slice.call(arguments,0);return Ae.normalize(e.join("/"))},join2:(e,r)=>Ae.normalize(e+"/"+r)};var Fe={resolve:function(){for(var e="",r=!1,t=arguments.length-1;t>=-1&&!r;t--){var n=t>=0?arguments[t]:Ie.cwd();if("string"!=typeof n)throw new TypeError("Arguments to path.resolve must be strings");if(!n)return"";e=n+"/"+e,r=Ae.isAbs(n)}return(r?"/":"")+(e=Ae.normalizeArray(e.split("/").filter((e=>!!e)),!r).join("/"))||"."},relative:(e,r)=>{function t(e){for(var r=0;r=0&&""===e[t];t--);return r>t?[]:e.slice(r,t-r+1)}e=Fe.resolve(e).substr(1),r=Fe.resolve(r).substr(1);for(var n=t(e.split("/")),o=t(r.split("/")),i=Math.min(n.length,o.length),a=i,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=pt(r,!0)}return e.input.shift()},put_char:function(e,r){null===r||10===r?(b(O(e.output,0)),e.output=[]):0!=r&&e.output.push(r)},flush:function(e){e.output&&e.output.length>0&&(b(O(e.output,0)),e.output=[])}},default_tty1_ops:{put_char:function(e,r){null===r||10===r?(_(O(e.output,0)),e.output=[]):0!=r&&e.output.push(r)},flush:function(e){e.output&&e.output.length>0&&(_(O(e.output,0)),e.output=[])}}};function Oe(e){e=function(e,r){return F(r,"alignment argument is required"),Math.ceil(e/r)*r}(e,65536);var r=bt(65536,e);return r?(function(e,r){U.fill(0,e,e+r)}(r,e),r):0}var Re={ops_table:null,mount:function(e){return Re.createNode(null,"/",16895,0)},createNode:function(e,r,t,n){if(Ie.isBlkdev(t)||Ie.isFIFO(t))throw new Ie.ErrnoError(63);Re.ops_table||(Re.ops_table={dir:{node:{getattr:Re.node_ops.getattr,setattr:Re.node_ops.setattr,lookup:Re.node_ops.lookup,mknod:Re.node_ops.mknod,rename:Re.node_ops.rename,unlink:Re.node_ops.unlink,rmdir:Re.node_ops.rmdir,readdir:Re.node_ops.readdir,symlink:Re.node_ops.symlink},stream:{llseek:Re.stream_ops.llseek}},file:{node:{getattr:Re.node_ops.getattr,setattr:Re.node_ops.setattr},stream:{llseek:Re.stream_ops.llseek,read:Re.stream_ops.read,write:Re.stream_ops.write,allocate:Re.stream_ops.allocate,mmap:Re.stream_ops.mmap,msync:Re.stream_ops.msync}},link:{node:{getattr:Re.node_ops.getattr,setattr:Re.node_ops.setattr,readlink:Re.node_ops.readlink},stream:{}},chrdev:{node:{getattr:Re.node_ops.getattr,setattr:Re.node_ops.setattr},stream:Ie.chrdev_stream_ops}});var o=Ie.createNode(e,r,t,n);return Ie.isDir(o.mode)?(o.node_ops=Re.ops_table.dir.node,o.stream_ops=Re.ops_table.dir.stream,o.contents={}):Ie.isFile(o.mode)?(o.node_ops=Re.ops_table.file.node,o.stream_ops=Re.ops_table.file.stream,o.usedBytes=0,o.contents=null):Ie.isLink(o.mode)?(o.node_ops=Re.ops_table.link.node,o.stream_ops=Re.ops_table.link.stream):Ie.isChrdev(o.mode)&&(o.node_ops=Re.ops_table.chrdev.node,o.stream_ops=Re.ops_table.chrdev.stream),o.timestamp=Date.now(),e&&(e.contents[r]=o,e.timestamp=o.timestamp),o},getFileDataAsTypedArray:function(e){return e.contents?e.contents.subarray?e.contents.subarray(0,e.usedBytes):new Uint8Array(e.contents):new Uint8Array(0)},expandFileStorage:function(e,r){var t=e.contents?e.contents.length:0;if(!(t>=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=Ie.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,Ie.isDir(e.mode)?r.size=4096:Ie.isFile(e.mode)?r.size=e.usedBytes:Ie.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&&Re.resizeFileStorage(e,r.size)},lookup:function(e,r){throw Ie.genericErrors[44]},mknod:function(e,r,t,n){return Re.createNode(e,r,t,n)},rename:function(e,r,t){if(Ie.isDir(e.mode)){var n;try{n=Ie.lookupNode(r,t)}catch(e){}if(n)for(var o in n.contents)throw new Ie.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=Ie.lookupNode(e,r);for(var n in t.contents)throw new Ie.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=Re.createNode(e,r,41471,0);return n.link=t,n},readlink:function(e){if(!Ie.isLink(e.mode))throw new Ie.ErrnoError(28);return e.link}},stream_ops:{read:function(e,r,t,n,o){var i=e.node.contents;if(o>=e.node.usedBytes)return 0;var a=Math.min(e.node.usedBytes-o,n);if(F(a>=0),a>8&&i.subarray)r.set(i.subarray(o,o+a),t);else for(var s=0;s0||n+t1&&void 0!==arguments[1]?arguments[1]:{};if(!(e=Fe.resolve(Ie.cwd(),e)))return{path:"",node:null};var t={follow_mount:!0,recurse_count:0};if(r=Object.assign(t,r),r.recurse_count>8)throw new Ie.ErrnoError(32);for(var n=Ae.normalizeArray(e.split("/").filter((e=>!!e)),!1),o=Ie.root,i="/",a=0;a40)throw new Ie.ErrnoError(32)}}return{path:i,node:o}},getPath:e=>{for(var r;;){if(Ie.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:(e,r)=>{for(var t=0,n=0;n>>0)%Ie.nameTable.length},hashAddNode:e=>{var r=Ie.hashName(e.parent.id,e.name);e.name_next=Ie.nameTable[r],Ie.nameTable[r]=e},hashRemoveNode:e=>{var r=Ie.hashName(e.parent.id,e.name);if(Ie.nameTable[r]===e)Ie.nameTable[r]=e.name_next;else for(var t=Ie.nameTable[r];t;){if(t.name_next===e){t.name_next=e.name_next;break}t=t.name_next}},lookupNode:(e,r)=>{var t=Ie.mayLookup(e);if(t)throw new Ie.ErrnoError(t,e);for(var n=Ie.hashName(e.id,r),o=Ie.nameTable[n];o;o=o.name_next){var i=o.name;if(o.parent.id===e.id&&i===r)return o}return Ie.lookup(e,r)},createNode:(e,r,t,n)=>{F("object"==typeof e);var o=new Ie.FSNode(e,r,t,n);return Ie.hashAddNode(o),o},destroyNode:e=>{Ie.hashRemoveNode(e)},isRoot:e=>e===e.parent,isMountpoint:e=>!!e.mounted,isFile:e=>32768==(61440&e),isDir:e=>16384==(61440&e),isLink:e=>40960==(61440&e),isChrdev:e=>8192==(61440&e),isBlkdev:e=>24576==(61440&e),isFIFO:e=>4096==(61440&e),isSocket:e=>49152==(49152&e),flagModes:{r:0,"r+":2,w:577,"w+":578,a:1089,"a+":1090},modeStringToFlags:e=>{var r=Ie.flagModes[e];if(void 0===r)throw new Error("Unknown file open mode: "+e);return r},flagsToPermissionString:e=>{var r=["r","w","rw"][3&e];return 512&e&&(r+="w"),r},nodePermissions:(e,r)=>Ie.ignorePermissions||(!r.includes("r")||292&e.mode)&&(!r.includes("w")||146&e.mode)&&(!r.includes("x")||73&e.mode)?0:2,mayLookup:e=>{var r=Ie.nodePermissions(e,"x");return r||(e.node_ops.lookup?0:2)},mayCreate:(e,r)=>{try{Ie.lookupNode(e,r);return 20}catch(e){}return Ie.nodePermissions(e,"wx")},mayDelete:(e,r,t)=>{var n;try{n=Ie.lookupNode(e,r)}catch(e){return e.errno}var o=Ie.nodePermissions(e,"wx");if(o)return o;if(t){if(!Ie.isDir(n.mode))return 54;if(Ie.isRoot(n)||Ie.getPath(n)===Ie.cwd())return 10}else if(Ie.isDir(n.mode))return 31;return 0},mayOpen:(e,r)=>e?Ie.isLink(e.mode)?32:Ie.isDir(e.mode)&&("r"!==Ie.flagsToPermissionString(r)||512&r)?31:Ie.nodePermissions(e,Ie.flagsToPermissionString(r)):44,MAX_OPEN_FDS:4096,nextfd:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:Ie.MAX_OPEN_FDS;for(var t=e;t<=r;t++)if(!Ie.streams[t])return t;throw new Ie.ErrnoError(33)},getStream:e=>Ie.streams[e],createStream:(e,r,t)=>{Ie.FSStream||(Ie.FSStream=function(){this.shared={}},Ie.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}},flags:{get:function(){return this.shared.flags},set:function(e){this.shared.flags=e}},position:{get function(){return this.shared.position},set:function(e){this.shared.position=e}}}),e=Object.assign(new Ie.FSStream,e);var n=Ie.nextfd(r,t);return e.fd=n,Ie.streams[n]=e,e},closeStream:e=>{Ie.streams[e]=null},chrdev_stream_ops:{open:e=>{var r=Ie.getDevice(e.node.rdev);e.stream_ops=r.stream_ops,e.stream_ops.open&&e.stream_ops.open(e)},llseek:()=>{throw new Ie.ErrnoError(70)}},major:e=>e>>8,minor:e=>255&e,makedev:(e,r)=>e<<8|r,registerDevice:(e,r)=>{Ie.devices[e]={stream_ops:r}},getDevice:e=>Ie.devices[e],getMounts:e=>{for(var r=[],t=[e];t.length;){var n=t.pop();r.push(n),t.push.apply(t,n.mounts)}return r},syncfs:(e,r)=>{"function"==typeof e&&(r=e,e=!1),Ie.syncFSRequests++,Ie.syncFSRequests>1&&_("warning: "+Ie.syncFSRequests+" FS.syncfs operations in flight at once, probably just doing extra work");var t=Ie.getMounts(Ie.root.mount),n=0;function o(e){return F(Ie.syncFSRequests>0),Ie.syncFSRequests--,r(e)}function i(e){if(e)return i.errored?void 0:(i.errored=!0,o(e));++n>=t.length&&o(null)}t.forEach((r=>{if(!r.type.syncfs)return i(null);r.type.syncfs(r,e,i)}))},mount:(e,r,t)=>{if("string"==typeof e)throw e;var n,o="/"===t,i=!t;if(o&&Ie.root)throw new Ie.ErrnoError(10);if(!o&&!i){var a=Ie.lookupPath(t,{follow_mount:!1});if(t=a.path,n=a.node,Ie.isMountpoint(n))throw new Ie.ErrnoError(10);if(!Ie.isDir(n.mode))throw new Ie.ErrnoError(54)}var s={type:e,opts:r,mountpoint:t,mounts:[]},l=e.mount(s);return l.mount=s,s.root=l,o?Ie.root=l:n&&(n.mounted=s,n.mount&&n.mount.mounts.push(s)),l},unmount:e=>{var r=Ie.lookupPath(e,{follow_mount:!1});if(!Ie.isMountpoint(r.node))throw new Ie.ErrnoError(28);var t=r.node,n=t.mounted,o=Ie.getMounts(n);Object.keys(Ie.nameTable).forEach((e=>{for(var r=Ie.nameTable[e];r;){var t=r.name_next;o.includes(r.mount)&&Ie.destroyNode(r),r=t}})),t.mounted=null;var i=t.mount.mounts.indexOf(n);F(-1!==i),t.mount.mounts.splice(i,1)},lookup:(e,r)=>e.node_ops.lookup(e,r),mknod:(e,r,t)=>{var n=Ie.lookupPath(e,{parent:!0}).node,o=Ae.basename(e);if(!o||"."===o||".."===o)throw new Ie.ErrnoError(28);var i=Ie.mayCreate(n,o);if(i)throw new Ie.ErrnoError(i);if(!n.node_ops.mknod)throw new Ie.ErrnoError(63);return n.node_ops.mknod(n,o,r,t)},create:(e,r)=>(r=void 0!==r?r:438,r&=4095,r|=32768,Ie.mknod(e,r,0)),mkdir:(e,r)=>(r=void 0!==r?r:511,r&=1023,r|=16384,Ie.mknod(e,r,0)),mkdirTree:(e,r)=>{for(var t=e.split("/"),n="",o=0;o(void 0===t&&(t=r,r=438),r|=8192,Ie.mknod(e,r,t)),symlink:(e,r)=>{if(!Fe.resolve(e))throw new Ie.ErrnoError(44);var t=Ie.lookupPath(r,{parent:!0}).node;if(!t)throw new Ie.ErrnoError(44);var n=Ae.basename(r),o=Ie.mayCreate(t,n);if(o)throw new Ie.ErrnoError(o);if(!t.node_ops.symlink)throw new Ie.ErrnoError(63);return t.node_ops.symlink(t,n,e)},rename:(e,r)=>{var t,n,o=Ae.dirname(e),i=Ae.dirname(r),a=Ae.basename(e),s=Ae.basename(r);if(t=Ie.lookupPath(e,{parent:!0}).node,n=Ie.lookupPath(r,{parent:!0}).node,!t||!n)throw new Ie.ErrnoError(44);if(t.mount!==n.mount)throw new Ie.ErrnoError(75);var l,u=Ie.lookupNode(t,a),c=Fe.relative(e,i);if("."!==c.charAt(0))throw new Ie.ErrnoError(28);if("."!==(c=Fe.relative(r,o)).charAt(0))throw new Ie.ErrnoError(55);try{l=Ie.lookupNode(n,s)}catch(e){}if(u!==l){var d=Ie.isDir(u.mode),f=Ie.mayDelete(t,a,d);if(f)throw new Ie.ErrnoError(f);if(f=l?Ie.mayDelete(n,s,d):Ie.mayCreate(n,s))throw new Ie.ErrnoError(f);if(!t.node_ops.rename)throw new Ie.ErrnoError(63);if(Ie.isMountpoint(u)||l&&Ie.isMountpoint(l))throw new Ie.ErrnoError(10);if(n!==t&&(f=Ie.nodePermissions(t,"w")))throw new Ie.ErrnoError(f);Ie.hashRemoveNode(u);try{t.node_ops.rename(u,n,s)}catch(e){throw e}finally{Ie.hashAddNode(u)}}},rmdir:e=>{var r=Ie.lookupPath(e,{parent:!0}).node,t=Ae.basename(e),n=Ie.lookupNode(r,t),o=Ie.mayDelete(r,t,!0);if(o)throw new Ie.ErrnoError(o);if(!r.node_ops.rmdir)throw new Ie.ErrnoError(63);if(Ie.isMountpoint(n))throw new Ie.ErrnoError(10);r.node_ops.rmdir(r,t),Ie.destroyNode(n)},readdir:e=>{var r=Ie.lookupPath(e,{follow:!0}).node;if(!r.node_ops.readdir)throw new Ie.ErrnoError(54);return r.node_ops.readdir(r)},unlink:e=>{var r=Ie.lookupPath(e,{parent:!0}).node;if(!r)throw new Ie.ErrnoError(44);var t=Ae.basename(e),n=Ie.lookupNode(r,t),o=Ie.mayDelete(r,t,!1);if(o)throw new Ie.ErrnoError(o);if(!r.node_ops.unlink)throw new Ie.ErrnoError(63);if(Ie.isMountpoint(n))throw new Ie.ErrnoError(10);r.node_ops.unlink(r,t),Ie.destroyNode(n)},readlink:e=>{var r=Ie.lookupPath(e).node;if(!r)throw new Ie.ErrnoError(44);if(!r.node_ops.readlink)throw new Ie.ErrnoError(28);return Fe.resolve(Ie.getPath(r.parent),r.node_ops.readlink(r))},stat:(e,r)=>{var t=Ie.lookupPath(e,{follow:!r}).node;if(!t)throw new Ie.ErrnoError(44);if(!t.node_ops.getattr)throw new Ie.ErrnoError(63);return t.node_ops.getattr(t)},lstat:e=>Ie.stat(e,!0),chmod:(e,r,t)=>{var n;"string"==typeof e?n=Ie.lookupPath(e,{follow:!t}).node:n=e;if(!n.node_ops.setattr)throw new Ie.ErrnoError(63);n.node_ops.setattr(n,{mode:4095&r|-4096&n.mode,timestamp:Date.now()})},lchmod:(e,r)=>{Ie.chmod(e,r,!0)},fchmod:(e,r)=>{var t=Ie.getStream(e);if(!t)throw new Ie.ErrnoError(8);Ie.chmod(t.node,r)},chown:(e,r,t,n)=>{var o;"string"==typeof e?o=Ie.lookupPath(e,{follow:!n}).node:o=e;if(!o.node_ops.setattr)throw new Ie.ErrnoError(63);o.node_ops.setattr(o,{timestamp:Date.now()})},lchown:(e,r,t)=>{Ie.chown(e,r,t,!0)},fchown:(e,r,t)=>{var n=Ie.getStream(e);if(!n)throw new Ie.ErrnoError(8);Ie.chown(n.node,r,t)},truncate:(e,r)=>{if(r<0)throw new Ie.ErrnoError(28);var t;"string"==typeof e?t=Ie.lookupPath(e,{follow:!0}).node:t=e;if(!t.node_ops.setattr)throw new Ie.ErrnoError(63);if(Ie.isDir(t.mode))throw new Ie.ErrnoError(31);if(!Ie.isFile(t.mode))throw new Ie.ErrnoError(28);var n=Ie.nodePermissions(t,"w");if(n)throw new Ie.ErrnoError(n);t.node_ops.setattr(t,{size:r,timestamp:Date.now()})},ftruncate:(e,r)=>{var t=Ie.getStream(e);if(!t)throw new Ie.ErrnoError(8);if(0==(2097155&t.flags))throw new Ie.ErrnoError(28);Ie.truncate(t.node,r)},utime:(e,r,t)=>{var n=Ie.lookupPath(e,{follow:!0}).node;n.node_ops.setattr(n,{timestamp:Math.max(r,t)})},open:(e,t,n,o,i)=>{if(""===e)throw new Ie.ErrnoError(44);var a;if(n=void 0===n?438:n,n=64&(t="string"==typeof t?Ie.modeStringToFlags(t):t)?4095&n|32768:0,"object"==typeof e)a=e;else{e=Ae.normalize(e);try{a=Ie.lookupPath(e,{follow:!(131072&t)}).node}catch(e){}}var s=!1;if(64&t)if(a){if(128&t)throw new Ie.ErrnoError(20)}else a=Ie.mknod(e,n,0),s=!0;if(!a)throw new Ie.ErrnoError(44);if(Ie.isChrdev(a.mode)&&(t&=-513),65536&t&&!Ie.isDir(a.mode))throw new Ie.ErrnoError(54);if(!s){var l=Ie.mayOpen(a,t);if(l)throw new Ie.ErrnoError(l)}512&t&&Ie.truncate(a,0),t&=-131713;var u=Ie.createStream({node:a,path:Ie.getPath(a),flags:t,seekable:!0,position:0,stream_ops:a.stream_ops,ungotten:[],error:!1},o,i);return u.stream_ops.open&&u.stream_ops.open(u),!r.logReadFiles||1&t||(Ie.readFiles||(Ie.readFiles={}),e in Ie.readFiles||(Ie.readFiles[e]=1)),u},close:e=>{if(Ie.isClosed(e))throw new Ie.ErrnoError(8);e.getdents&&(e.getdents=null);try{e.stream_ops.close&&e.stream_ops.close(e)}catch(e){throw e}finally{Ie.closeStream(e.fd)}e.fd=null},isClosed:e=>null===e.fd,llseek:(e,r,t)=>{if(Ie.isClosed(e))throw new Ie.ErrnoError(8);if(!e.seekable||!e.stream_ops.llseek)throw new Ie.ErrnoError(70);if(0!=t&&1!=t&&2!=t)throw new Ie.ErrnoError(28);return e.position=e.stream_ops.llseek(e,r,t),e.ungotten=[],e.position},read:(e,r,t,n,o)=>{if(n<0||o<0)throw new Ie.ErrnoError(28);if(Ie.isClosed(e))throw new Ie.ErrnoError(8);if(1==(2097155&e.flags))throw new Ie.ErrnoError(8);if(Ie.isDir(e.node.mode))throw new Ie.ErrnoError(31);if(!e.stream_ops.read)throw new Ie.ErrnoError(28);var i=void 0!==o;if(i){if(!e.seekable)throw new Ie.ErrnoError(70)}else o=e.position;var a=e.stream_ops.read(e,r,t,n,o);return i||(e.position+=a),a},write:(e,r,t,n,o,i)=>{if(n<0||o<0)throw new Ie.ErrnoError(28);if(Ie.isClosed(e))throw new Ie.ErrnoError(8);if(0==(2097155&e.flags))throw new Ie.ErrnoError(8);if(Ie.isDir(e.node.mode))throw new Ie.ErrnoError(31);if(!e.stream_ops.write)throw new Ie.ErrnoError(28);e.seekable&&1024&e.flags&&Ie.llseek(e,0,2);var a=void 0!==o;if(a){if(!e.seekable)throw new Ie.ErrnoError(70)}else o=e.position;var s=e.stream_ops.write(e,r,t,n,o,i);return a||(e.position+=s),s},allocate:(e,r,t)=>{if(Ie.isClosed(e))throw new Ie.ErrnoError(8);if(r<0||t<=0)throw new Ie.ErrnoError(28);if(0==(2097155&e.flags))throw new Ie.ErrnoError(8);if(!Ie.isFile(e.node.mode)&&!Ie.isDir(e.node.mode))throw new Ie.ErrnoError(43);if(!e.stream_ops.allocate)throw new Ie.ErrnoError(138);e.stream_ops.allocate(e,r,t)},mmap:(e,r,t,n,o,i)=>{if(0!=(2&o)&&0==(2&i)&&2!=(2097155&e.flags))throw new Ie.ErrnoError(2);if(1==(2097155&e.flags))throw new Ie.ErrnoError(2);if(!e.stream_ops.mmap)throw new Ie.ErrnoError(43);return e.stream_ops.mmap(e,r,t,n,o,i)},msync:(e,r,t,n,o)=>e&&e.stream_ops.msync?e.stream_ops.msync(e,r,t,n,o):0,munmap:e=>0,ioctl:(e,r,t)=>{if(!e.stream_ops.ioctl)throw new Ie.ErrnoError(59);return e.stream_ops.ioctl(e,r,t)},readFile:function(e){let r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(r.flags=r.flags||0,r.encoding=r.encoding||"binary","utf8"!==r.encoding&&"binary"!==r.encoding)throw new Error('Invalid encoding type "'+r.encoding+'"');var t,n=Ie.open(e,r.flags),o=Ie.stat(e),i=o.size,a=new Uint8Array(i);return Ie.read(n,a,0,i,0),"utf8"===r.encoding?t=O(a,0):"binary"===r.encoding&&(t=a),Ie.close(n),t},writeFile:function(e,r){let t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};t.flags=t.flags||577;var n=Ie.open(e,t.flags,t.mode);if("string"==typeof r){var o=new Uint8Array(I(r)+1),i=M(r,o,0,o.length);Ie.write(n,o,0,i,void 0,t.canOwn)}else{if(!ArrayBuffer.isView(r))throw new Error("Unsupported data type");Ie.write(n,r,0,r.byteLength,void 0,t.canOwn)}Ie.close(n)},cwd:()=>Ie.currentPath,chdir:e=>{var r=Ie.lookupPath(e,{follow:!0});if(null===r.node)throw new Ie.ErrnoError(44);if(!Ie.isDir(r.node.mode))throw new Ie.ErrnoError(54);var t=Ie.nodePermissions(r.node,"x");if(t)throw new Ie.ErrnoError(t);Ie.currentPath=r.path},createDefaultDirectories:()=>{Ie.mkdir("/tmp"),Ie.mkdir("/home"),Ie.mkdir("/home/web_user")},createDefaultDevices:()=>{Ie.mkdir("/dev"),Ie.registerDevice(Ie.makedev(1,3),{read:()=>0,write:(e,r,t,n,o)=>n}),Ie.mkdev("/dev/null",Ie.makedev(1,3)),De.register(Ie.makedev(5,0),De.default_tty_ops),De.register(Ie.makedev(6,0),De.default_tty1_ops),Ie.mkdev("/dev/tty",Ie.makedev(5,0)),Ie.mkdev("/dev/tty1",Ie.makedev(6,0));var e=function(){if("object"==typeof crypto&&"function"==typeof crypto.getRandomValues){var e=new Uint8Array(1);return function(){return crypto.getRandomValues(e),e[0]}}if(u)try{var r=a.default;return function(){return r.randomBytes(1)[0]}}catch(e){}return function(){ge("no cryptographic support found for randomDevice. consider polyfilling it if you want to use something insecure like Math.random(), e.g. put this in a --pre-js: var crypto = { getRandomValues: function(array) { for (var i = 0; i < array.length; i++) array[i] = (Math.random()*256)|0 } };")}}();Ie.createDevice("/dev","random",e),Ie.createDevice("/dev","urandom",e),Ie.mkdir("/dev/shm"),Ie.mkdir("/dev/shm/tmp")},createSpecialDirectories:()=>{Ie.mkdir("/proc");var e=Ie.mkdir("/proc/self");Ie.mkdir("/proc/self/fd"),Ie.mount({mount:()=>{var r=Ie.createNode(e,"fd",16895,73);return r.node_ops={lookup:(e,r)=>{var t=+r,n=Ie.getStream(t);if(!n)throw new Ie.ErrnoError(8);var o={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>n.path}};return o.parent=o,o}},r}},{},"/proc/self/fd")},createStandardStreams:()=>{r.stdin?Ie.createDevice("/dev","stdin",r.stdin):Ie.symlink("/dev/tty","/dev/stdin"),r.stdout?Ie.createDevice("/dev","stdout",null,r.stdout):Ie.symlink("/dev/tty","/dev/stdout"),r.stderr?Ie.createDevice("/dev","stderr",null,r.stderr):Ie.symlink("/dev/tty1","/dev/stderr");var e=Ie.open("/dev/stdin",0),t=Ie.open("/dev/stdout",1),n=Ie.open("/dev/stderr",1);F(0===e.fd,"invalid handle for stdin ("+e.fd+")"),F(1===t.fd,"invalid handle for stdout ("+t.fd+")"),F(2===n.fd,"invalid handle for stderr ("+n.fd+")")},ensureErrnoError:()=>{Ie.ErrnoError||(Ie.ErrnoError=function(e,r){this.node=r,this.setErrno=function(e){for(var r in this.errno=e,Ne)if(Ne[r]===e){this.code=r;break}},this.setErrno(e),this.message=Me[e],this.stack&&(Object.defineProperty(this,"stack",{value:(new Error).stack,writable:!0}),this.stack=Se(this.stack))},Ie.ErrnoError.prototype=new Error,Ie.ErrnoError.prototype.constructor=Ie.ErrnoError,[44].forEach((e=>{Ie.genericErrors[e]=new Ie.ErrnoError(e),Ie.genericErrors[e].stack=""})))},staticInit:()=>{Ie.ensureErrnoError(),Ie.nameTable=new Array(4096),Ie.mount(Re,{},"/"),Ie.createDefaultDirectories(),Ie.createDefaultDevices(),Ie.createSpecialDirectories(),Ie.filesystems={MEMFS:Re}},init:(e,t,n)=>{F(!Ie.init.initialized,"FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)"),Ie.init.initialized=!0,Ie.ensureErrnoError(),r.stdin=e||r.stdin,r.stdout=t||r.stdout,r.stderr=n||r.stderr,Ie.createStandardStreams()},quit:()=>{Ie.init.initialized=!1,wt();for(var e=0;e{var t=0;return e&&(t|=365),r&&(t|=146),t},findObject:(e,r)=>{var t=Ie.analyzePath(e,r);return t.exists?t.object:null},analyzePath:(e,r)=>{try{e=(n=Ie.lookupPath(e,{follow:!r})).path}catch(e){}var t={isRoot:!1,exists:!1,error:0,name:null,path:null,object:null,parentExists:!1,parentPath:null,parentObject:null};try{var n=Ie.lookupPath(e,{parent:!0});t.parentExists=!0,t.parentPath=n.path,t.parentObject=n.node,t.name=Ae.basename(e),n=Ie.lookupPath(e,{follow:!r}),t.exists=!0,t.path=n.path,t.object=n.node,t.name=n.node.name,t.isRoot="/"===n.path}catch(e){t.error=e.errno}return t},createPath:(e,r,t,n)=>{e="string"==typeof e?e:Ie.getPath(e);for(var o=r.split("/").reverse();o.length;){var i=o.pop();if(i){var a=Ae.join2(e,i);try{Ie.mkdir(a)}catch(e){}e=a}}return a},createFile:(e,r,t,n,o)=>{var i=Ae.join2("string"==typeof e?e:Ie.getPath(e),r),a=Ie.getMode(n,o);return Ie.create(i,a)},createDataFile:(e,r,t,n,o,i)=>{var a=r;e&&(e="string"==typeof e?e:Ie.getPath(e),a=r?Ae.join2(e,r):e);var s=Ie.getMode(n,o),l=Ie.create(a,s);if(t){if("string"==typeof t){for(var u=new Array(t.length),c=0,d=t.length;c{var o=Ae.join2("string"==typeof e?e:Ie.getPath(e),r),i=Ie.getMode(!!t,!!n);Ie.createDevice.major||(Ie.createDevice.major=64);var a=Ie.makedev(Ie.createDevice.major++,0);return Ie.registerDevice(a,{open:e=>{e.seekable=!1},close:e=>{n&&n.buffer&&n.buffer.length&&n(10)},read:(e,r,n,o,i)=>{for(var a=0,s=0;s{for(var a=0;a{if(e.isDevice||e.isFolder||e.link||e.contents)return!0;if("undefined"!=typeof XMLHttpRequest)throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.");if(!d)throw new Error("Cannot load without read() or XMLHttpRequest.");try{e.contents=pt(d(e.url),!0),e.usedBytes=e.contents.length}catch(e){throw new Ie.ErrnoError(29)}},createLazyFile:(e,r,t,n,o)=>{function i(){this.lengthKnown=!1,this.chunks=[]}if(i.prototype.get=function(e){if(!(e>this.length-1||e<0)){var r=e%this.chunkSize,t=e/this.chunkSize|0;return this.getter(t)[r]}},i.prototype.setDataGetter=function(e){this.getter=e},i.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,i=(r=e.getResponseHeader("Content-Encoding"))&&"gzip"===r,a=1048576;o||(a=n);var s=this;s.setDataGetter((e=>{var r=e*a,o=(e+1)*a-1;if(o=Math.min(o,n-1),void 0===s.chunks[e]&&(s.chunks[e]=((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!==a&&o.setRequestHeader("Range","bytes="+e+"-"+r),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||[]):pt(o.responseText||"",!0)})(r,o)),void 0===s.chunks[e])throw new Error("doXHR failed!");return s.chunks[e]})),!i&&n||(a=n=1,n=this.getter(0).length,a=n,b("LazyFiles on gzip forces download of the whole file when length is accessed")),this._length=n,this._chunkSize=a,this.lengthKnown=!0},"undefined"!=typeof XMLHttpRequest){if(!l)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var a=new i;Object.defineProperties(a,{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:a}}else s={isDevice:!1,url:t};var u=Ie.createFile(e,r,s,n,o);s.contents?u.contents=s.contents:s.url&&(u.contents=null,u.url=s.url),Object.defineProperties(u,{usedBytes:{get:function(){return this.contents.length}}});var c={};return Object.keys(u.stream_ops).forEach((e=>{var r=u.stream_ops[e];c[e]=function(){return Ie.forceLoadFile(u),r.apply(null,arguments)}})),c.read=(e,r,t,n,o)=>{Ie.forceLoadFile(u);var i=e.node.contents;if(o>=i.length)return 0;var a=Math.min(i.length-o,n);if(F(a>=0),i.slice)for(var s=0;s{var c=r?Fe.resolve(Ae.join2(e,r)):e,d=pe("cp "+c);function p(t){function f(t){u&&u(),s||Ie.createDataFile(e,r,t,n,o,l),i&&i(),he(d)}Browser.handledByPreloadPlugin(t,c,f,(()=>{a&&a(),he(d)}))||f(t)}me(d),"string"==typeof t?function(e,r,t,n){var o=n?"":pe("al "+e);f(e,(function(t){F(t,'Loading data file "'+e+'" failed (no arrayBuffer).'),r(new Uint8Array(t)),o&&he(o)}),(function(r){if(!t)throw'Loading data file "'+e+'" failed.';t()})),o&&me(o)}(t,(e=>p(e)),a):p(t)},indexedDB:()=>window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB,DB_NAME:()=>"EM_FS_"+window.location.pathname,DB_VERSION:20,DB_STORE_NAME:"FILE_DATA",saveFilesToDB:(e,r,t)=>{r=r||(()=>{}),t=t||(()=>{});var n=Ie.indexedDB();try{var o=n.open(Ie.DB_NAME(),Ie.DB_VERSION)}catch(e){return t(e)}o.onupgradeneeded=()=>{b("creating db"),o.result.createObjectStore(Ie.DB_STORE_NAME)},o.onsuccess=()=>{var n=o.result.transaction([Ie.DB_STORE_NAME],"readwrite"),i=n.objectStore(Ie.DB_STORE_NAME),a=0,s=0,l=e.length;function u(){0==s?r():t()}e.forEach((e=>{var r=i.put(Ie.analyzePath(e).object.contents,e);r.onsuccess=()=>{++a+s==l&&u()},r.onerror=()=>{s++,a+s==l&&u()}})),n.onerror=t},o.onerror=t},loadFilesFromDB:(e,r,t)=>{r=r||(()=>{}),t=t||(()=>{});var n=Ie.indexedDB();try{var o=n.open(Ie.DB_NAME(),Ie.DB_VERSION)}catch(e){return t(e)}o.onupgradeneeded=t,o.onsuccess=()=>{var n=o.result;try{var i=n.transaction([Ie.DB_STORE_NAME],"readonly")}catch(e){return void t(e)}var a=i.objectStore(Ie.DB_STORE_NAME),s=0,l=0,u=e.length;function c(){0==l?r():t()}e.forEach((e=>{var r=a.get(e);r.onsuccess=()=>{Ie.analyzePath(e).exists&&Ie.unlink(e),Ie.createDataFile(Ae.dirname(e),Ae.basename(e),r.result,!0,!0,!0),++s+l==u&&c()},r.onerror=()=>{l++,s+l==u&&c()}})),i.onerror=t},o.onerror=t},absolutePath:()=>{ge("FS.absolutePath has been removed; use PATH_FS.resolve instead")},createFolder:()=>{ge("FS.createFolder has been removed; use FS.mkdir instead")},createLink:()=>{ge("FS.createLink has been removed; use FS.symlink instead")},joinPath:()=>{ge("FS.joinPath has been removed; use PATH.join instead")},mmapAlloc:()=>{ge("FS.mmapAlloc has been replaced by the top level function mmapAlloc")},standardizePath:()=>{ge("FS.standardizePath has been removed; use PATH.normalize instead")}},Le={DEFAULT_POLLMASK:5,calculateAt:function(e,r,t){if(Ae.isAbs(r))return r;var n;if(-100===e)n=Ie.cwd();else{var o=Ie.getStream(e);if(!o)throw new Ie.ErrnoError(8);n=o.path}if(0==r.length){if(!t)throw new Ie.ErrnoError(44);return n}return Ae.join2(n,r)},doStat:function(e,r,t){try{var n=e(r)}catch(e){if(e&&e.node&&Ae.normalize(r)!==Ae.normalize(Ie.getPath(e.node)))return-54;throw e}return $[t>>2]=n.dev,$[t+4>>2]=0,$[t+8>>2]=n.ino,$[t+12>>2]=n.mode,$[t+16>>2]=n.nlink,$[t+20>>2]=n.uid,$[t+24>>2]=n.gid,$[t+28>>2]=n.rdev,$[t+32>>2]=0,Ee=[n.size>>>0,(ye=n.size,+Math.abs(ye)>=1?ye>0?(0|Math.min(+Math.floor(ye/4294967296),4294967295))>>>0:~~+Math.ceil((ye-+(~~ye>>>0))/4294967296)>>>0:0)],$[t+40>>2]=Ee[0],$[t+44>>2]=Ee[1],$[t+48>>2]=4096,$[t+52>>2]=n.blocks,$[t+56>>2]=n.atime.getTime()/1e3|0,$[t+60>>2]=0,$[t+64>>2]=n.mtime.getTime()/1e3|0,$[t+68>>2]=0,$[t+72>>2]=n.ctime.getTime()/1e3|0,$[t+76>>2]=0,Ee=[n.ino>>>0,(ye=n.ino,+Math.abs(ye)>=1?ye>0?(0|Math.min(+Math.floor(ye/4294967296),4294967295))>>>0:~~+Math.ceil((ye-+(~~ye>>>0))/4294967296)>>>0:0)],$[t+80>>2]=Ee[0],$[t+84>>2]=Ee[1],0},doMsync:function(e,r,t,n,o){var i=U.slice(e,e+t);Ie.msync(r,i,o,t,n)},doMknod:function(e,r,t){switch(61440&r){case 32768:case 8192:case 24576:case 4096:case 49152:break;default:return-28}return Ie.mknod(e,r,t),0},doReadlink:function(e,r,t){if(t<=0)return-28;var n=Ie.readlink(e),o=Math.min(t,I(n)),i=x[r+o];return N(n,r,t+1),x[r+o]=i,o},doAccess:function(e,r){if(-8&r)return-28;var t=Ie.lookupPath(e,{follow:!0}).node;if(!t)return-44;var n="";return 4&r&&(n+="r"),2&r&&(n+="w"),1&r&&(n+="x"),n&&Ie.nodePermissions(t,n)?-2:0},doReadv:function(e,r,t,n){for(var o=0,i=0;i>2],s=$[r+4>>2];r+=8;var l=Ie.read(e,x,a,s,n);if(l<0)return-1;if(o+=l,l>2],s=$[r+4>>2];r+=8;var l=Ie.write(e,x,a,s,n);if(l<0)return-1;o+=l}return o},varargs:void 0,get:function(){return F(null!=Le.varargs),Le.varargs+=4,$[Le.varargs-4>>2]},getStr:function(e){return R(e)},getStreamFromFD:function(e){var r=Ie.getStream(e);if(!r)throw new Ie.ErrnoError(8);return r}};function xe(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 Ue=void 0;function Be(e){for(var r="",t=e;U[t];)r+=Ue[U[t++]];return r}var je={},$e={},We={};function ze(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 He(e,r){return e=ze(e),new Function("body","return function "+e+'() {\n "use strict"; return body.apply(this, arguments);\n};\n')(r)}function Ge(e,r){var t=He(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 Ve=void 0;function Ye(e){throw new Ve(e)}var qe=void 0;function Xe(e){throw new qe(e)}function Ke(e,r,t){function n(r){var n=t(r);n.length!==e.length&&Xe("Mismatched type converter count");for(var o=0;o{$e.hasOwnProperty(e)?o[r]=$e[e]:(i.push(e),je.hasOwnProperty(e)||(je[e]=[]),je[e].push((()=>{o[r]=$e[e],++a===i.length&&n(o)})))})),0===i.length&&n(o)}function Je(e,r){let t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if(!("argPackAdvance"in r))throw new TypeError("registerType registeredInstance requires argPackAdvance");var n=r.name;if(e||Ye('type "'+n+'" must have a positive integer typeid pointer'),$e.hasOwnProperty(e)){if(t.ignoreDuplicateRegistrations)return;Ye("Cannot register type '"+n+"' twice")}if($e[e]=r,delete We[e],je.hasOwnProperty(e)){var o=je[e];delete je[e],o.forEach((e=>e()))}}function Qe(e){if(!(this instanceof wr))return!1;if(!(e instanceof wr))return!1;for(var r=this.$$.ptrType.registeredClass,t=this.$$.ptr,n=e.$$.ptrType.registeredClass,o=e.$$.ptr;r.baseClass;)t=r.upcast(t),r=r.baseClass;for(;n.baseClass;)o=n.upcast(o),n=n.baseClass;return r===n&&t===o}function Ze(e){Ye(e.$$.ptrType.registeredClass.name+" instance already deleted")}var er=!1;function rr(e){}function tr(e){e.count.value-=1,0===e.count.value&&function(e){e.smartPtr?e.smartPtrType.rawDestructor(e.smartPtr):e.ptrType.registeredClass.rawDestructor(e.ptr)}(e)}function nr(e,r,t){if(r===t)return e;if(void 0===t.baseClass)return null;var n=nr(e,r,t.baseClass);return null===n?null:t.downcast(n)}var or={};function ir(){return Object.keys(dr).length}function ar(){var e=[];for(var r in dr)dr.hasOwnProperty(r)&&e.push(dr[r]);return e}var sr=[];function lr(){for(;sr.length;){var e=sr.pop();e.$$.deleteScheduled=!1,e.delete()}}var ur=void 0;function cr(e){ur=e,sr.length&&ur&&ur(lr)}var dr={};function fr(e,r){return r=function(e,r){for(void 0===r&&Ye("ptr should not be undefined");e.baseClass;)r=e.upcast(r),e=e.baseClass;return r}(e,r),dr[r]}function pr(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},hr(Object.create(e,{$$:{value:r}}))}function mr(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?pr(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:r,smartPtrType:this,smartPtr:e}):pr(this.registeredClass.instancePrototype,{ptrType:this,ptr:e})}var i,a=this.registeredClass.getActualType(r),s=or[a];if(!s)return o.call(this);i=this.isConst?s.constPointerType:s.pointerType;var l=nr(r,this.registeredClass,i.registeredClass);return null===l?o.call(this):this.isSmartPointer?pr(i.registeredClass.instancePrototype,{ptrType:i,ptr:l,smartPtrType:this,smartPtr:e}):pr(i.registeredClass.instancePrototype,{ptrType:i,ptr:l})}function hr(e){return"undefined"==typeof FinalizationRegistry?(hr=e=>e,e):(er=new FinalizationRegistry((e=>{console.warn(e.leakWarning.stack.replace(/^Error: /,"")),tr(e.$$)})),hr=e=>{var r=e.$$;if(!!r.smartPtr){var t={$$:r},n=r.ptrType.registeredClass;t.leakWarning=new Error("Embind found a leaked C++ instance "+n.name+" <0x"+r.ptr.toString(16)+">.\nWe'll free it automatically in this case, but this functionality is not reliable across various environments.\nMake sure to invoke .delete() manually once you're done with the instance instead.\nOriginally allocated"),"captureStackTrace"in Error&&Error.captureStackTrace(t.leakWarning,mr),er.register(e,t,e)}return e},rr=e=>er.unregister(e),hr(e))}function gr(){if(this.$$.ptr||Ze(this),this.$$.preservePointerOnDelete)return this.$$.count.value+=1,this;var e,r=hr(Object.create(Object.getPrototypeOf(this),{$$:{value:(e=this.$$,{count:e.count,deleteScheduled:e.deleteScheduled,preservePointerOnDelete:e.preservePointerOnDelete,ptr:e.ptr,ptrType:e.ptrType,smartPtr:e.smartPtr,smartPtrType:e.smartPtrType})}}));return r.$$.count.value+=1,r.$$.deleteScheduled=!1,r}function vr(){this.$$.ptr||Ze(this),this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete&&Ye("Object already scheduled for deletion"),rr(this),tr(this.$$),this.$$.preservePointerOnDelete||(this.$$.smartPtr=void 0,this.$$.ptr=void 0)}function yr(){return!this.$$.ptr}function Er(){return this.$$.ptr||Ze(this),this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete&&Ye("Object already scheduled for deletion"),sr.push(this),1===sr.length&&ur&&ur(lr),this.$$.deleteScheduled=!0,this}function wr(){}function br(e,r,t){if(void 0===e[r].overloadTable){var n=e[r];e[r]=function(){return e[r].overloadTable.hasOwnProperty(arguments.length)||Ye("Function '"+t+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+e[r].overloadTable+")!"),e[r].overloadTable[arguments.length].apply(this,arguments)},e[r].overloadTable=[],e[r].overloadTable[n.argCount]=n}}function _r(e,r,t,n,o,i,a,s){this.name=e,this.constructor=r,this.instancePrototype=t,this.rawDestructor=n,this.baseClass=o,this.getActualType=i,this.upcast=a,this.downcast=s,this.pureVirtualFunctions=[]}function Tr(e,r,t){for(;r!==t;)r.upcast||Ye("Expected null or instance of "+t.name+", got an instance of "+r.name),e=r.upcast(e),r=r.baseClass;return e}function kr(e,r){if(null===r)return this.isReference&&Ye("null is not a valid "+this.name),0;r.$$||Ye('Cannot pass "'+qr(r)+'" as a '+this.name),r.$$.ptr||Ye("Cannot pass deleted object as a pointer of type "+this.name);var t=r.$$.ptrType.registeredClass;return Tr(r.$$.ptr,t,this.registeredClass)}function Sr(e,r){var t;if(null===r)return this.isReference&&Ye("null is not a valid "+this.name),this.isSmartPointer?(t=this.rawConstructor(),null!==e&&e.push(this.rawDestructor,t),t):0;r.$$||Ye('Cannot pass "'+qr(r)+'" as a '+this.name),r.$$.ptr||Ye("Cannot pass deleted object as a pointer of type "+this.name),!this.isConst&&r.$$.ptrType.isConst&&Ye("Cannot convert argument of type "+(r.$$.smartPtrType?r.$$.smartPtrType.name:r.$$.ptrType.name)+" to parameter type "+this.name);var n=r.$$.ptrType.registeredClass;if(t=Tr(r.$$.ptr,n,this.registeredClass),this.isSmartPointer)switch(void 0===r.$$.smartPtr&&Ye("Passing raw pointer to smart pointer is illegal"),this.sharingPolicy){case 0:r.$$.smartPtrType===this?t=r.$$.smartPtr:Ye("Cannot convert argument of type "+(r.$$.smartPtrType?r.$$.smartPtrType.name:r.$$.ptrType.name)+" to parameter type "+this.name);break;case 1:t=r.$$.smartPtr;break;case 2:if(r.$$.smartPtrType===this)t=r.$$.smartPtr;else{var o=r.clone();t=this.rawShare(t,Yr.toHandle((function(){o.delete()}))),null!==e&&e.push(this.rawDestructor,t)}break;default:Ye("Unsupporting sharing policy")}return t}function Cr(e,r){if(null===r)return this.isReference&&Ye("null is not a valid "+this.name),0;r.$$||Ye('Cannot pass "'+qr(r)+'" as a '+this.name),r.$$.ptr||Ye("Cannot pass deleted object as a pointer of type "+this.name),r.$$.ptrType.isConst&&Ye("Cannot convert argument of type "+r.$$.ptrType.name+" to parameter type "+this.name);var t=r.$$.ptrType.registeredClass;return Tr(r.$$.ptr,t,this.registeredClass)}function Pr(e){return this.fromWireType(W[e>>2])}function Ar(e){return this.rawGetPointee&&(e=this.rawGetPointee(e)),e}function Fr(e){this.rawDestructor&&this.rawDestructor(e)}function Dr(e){null!==e&&e.delete()}function Or(e,r,t,n,o,i,a,s,l,u,c){this.name=e,this.registeredClass=r,this.isReference=t,this.isConst=n,this.isSmartPointer=o,this.pointeeType=i,this.sharingPolicy=a,this.rawGetPointee=s,this.rawConstructor=l,this.rawShare=u,this.rawDestructor=c,o||void 0!==r.baseClass?this.toWireType=Sr:n?(this.toWireType=kr,this.destructorFunction=null):(this.toWireType=Cr,this.destructorFunction=null)}function Rr(e,t,n){return e.includes("j")?function(e,t,n){F("dynCall_"+e in r,"bad function pointer type - no table for sig '"+e+"'"),n&&n.length?F(n.length===e.substring(1).replace(/j/g,"--").length):F(1==e.length);var o=r["dynCall_"+e];return n&&n.length?o.apply(null,[t].concat(n)):o.call(null,t)}(e,t,n):(F(Ce(t),"missing table entry in dynCall: "+t),Ce(t).apply(null,n))}function Mr(e,r){var t=(e=Be(e)).includes("j")?function(e,r){F(e.includes("j"),"getDynCaller should only be called with i64 sigs");var t=[];return function(){return t.length=0,Object.assign(t,arguments),Rr(e,r,t)}}(e,r):Ce(r);return"function"!=typeof t&&Ye("unknown function pointer with signature "+e+": "+r),t}var Nr=void 0;function Ir(e){var r=Et(e),t=Be(r);return ht(r),t}function Lr(e,r){var t=[],n={};throw r.forEach((function e(r){n[r]||$e[r]||(We[r]?We[r].forEach(e):(t.push(r),n[r]=!0))})),new Nr(e+": "+t.map(Ir).join([", "]))}function xr(e,r){for(var t=[],n=0;n>2)+n]);return t}function Ur(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=He(e.name||"unknownFunctionName",(function(){}));t.prototype=e.prototype;var n=new t,o=e.apply(n,r);return o instanceof Object?o:n}function jr(e,r,t,n,o){var i=r.length;i<2&&Ye("argTypes array size mismatch! Must at least get return value and 'this' types!");for(var a=null!==r[1]&&null!==t,s=!1,l=1;l0?", ":"")+d),f+=(u?"var rv = ":"")+"invoker(fn"+(d.length>0?", ":"")+d+");\n",s)f+="runDestructors(destructors);\n";else for(l=a?1:2;l4&&0==--zr[e].refcount&&(zr[e]=void 0,Wr.push(e))}function Gr(){for(var e=0,r=5;r(e||Ye("Cannot use deleted val. handle = "+e),zr[e].value),toHandle:e=>{switch(e){case void 0:return 1;case null:return 2;case!0:return 3;case!1:return 4;default:var r=Wr.length?Wr.pop():zr.length;return zr[r]={refcount:1,value:e},r}}};function qr(e){if(null===e)return"null";var r=typeof e;return"object"===r||"array"===r||"function"===r?e.toString():""+e}function Xr(e,r){switch(r){case 2:return function(e){return this.fromWireType(z[e>>2])};case 3:return function(e){return this.fromWireType(H[e>>3])};default:throw new TypeError("Unknown float type: "+e)}}function Kr(e,r,t){switch(r){case 0:return t?function(e){return x[e]}:function(e){return U[e]};case 1:return t?function(e){return B[e>>1]}:function(e){return j[e>>1]};case 2:return t?function(e){return $[e>>2]}:function(e){return W[e>>2]};default:throw new TypeError("Unknown integer type: "+e)}}function Jr(e,r){var t=$e[e];return void 0===t&&Ye(r+" has unknown type "+Ir(e)),t}var Qr={};var Zr=[];var et=[];function rt(e,r){return F(r===(0|r)),(e>>>0)+4294967296*r}function tt(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 nt(e,r){return e>=0?e:r<=32?2*Math.abs(1<>3]),n+=8):"i64"==e?(r=[$[n>>2],$[n+4>>2]],n+=8):(F(0==(3&n)),e="i32",r=$[n>>2],n+=4),r}for(var i,a,s,l,u,c,d=[];;){var f=t;if(0===(i=x[t>>0]))break;if(a=x[t+1>>0],37==i){var p=!1,m=!1,h=!1,g=!1,v=!1;e:for(;;){switch(a){case 43:p=!0;break;case 45:m=!0;break;case 35:h=!0;break;case 48:if(g)break e;g=!0;break;case 32:v=!0;break;default:break e}t++,a=x[t+1>>0]}var y=0;if(42==a)y=o("i32"),t++,a=x[t+1>>0];else for(;a>=48&&a<=57;)y=10*y+(a-48),t++,a=x[t+1>>0];var E,w=!1,b=-1;if(46==a){if(b=0,w=!0,t++,42==(a=x[t+1>>0]))b=o("i32"),t++;else for(;;){var _=x[t+1>>0];if(_<48||_>57)break;b=10*b+(_-48),t++}a=x[t+1>>0]}switch(b<0&&(b=6,w=!1),String.fromCharCode(a)){case"h":104==x[t+2>>0]?(t++,E=1):E=2;break;case"l":108==x[t+2>>0]?(t++,E=8):E=4;break;case"L":case"q":case"j":E=8;break;case"z":case"t":case"I":E=4;break;default:E=null}switch(E&&t++,a=x[t+1>>0],String.fromCharCode(a)){case"d":case"i":case"u":case"o":case"x":case"X":case"p":var T=100==a||105==a;if(s=o("i"+8*(E=E||4)),8==E&&(s=117==a?(u=s[0],c=s[1],(u>>>0)+4294967296*(c>>>0)):rt(s[0],s[1])),E<=4)s=(T?tt:nt)(s&Math.pow(256,E)-1,8*E);var k=Math.abs(s),S="";if(100==a||105==a)A=tt(s,8*E).toString(10);else if(117==a)A=nt(s,8*E).toString(10),s=Math.abs(s);else if(111==a)A=(h?"0":"")+k.toString(8);else if(120==a||88==a){if(S=h&&0!=s?"0x":"",s<0){s=-s,A=(k-1).toString(16);for(var C=[],P=0;P=0&&(p?S="+"+S:v&&(S=" "+S)),"-"==A.charAt(0)&&(S="-"+S,A=A.substr(1));S.length+A.lengthR&&R>=-4?(a=(103==a?"f":"F").charCodeAt(0),b-=R+1):(a=(103==a?"e":"E").charCodeAt(0),b--),O=Math.min(b,20)}101==a||69==a?(A=s.toExponential(O),/[eE][-+]\d$/.test(A)&&(A=A.slice(0,-1)+"0"+A.slice(-1))):102!=a&&70!=a||(A=s.toFixed(O),0===s&&((l=s)<0||0===l&&1/l==-1/0)&&(A="-"+A));var M=A.split("e");if(D&&!h)for(;M[0].length>1&&M[0].includes(".")&&("0"==M[0].slice(-1)||"."==M[0].slice(-1));)M[0]=M[0].slice(0,-1);else for(h&&-1==A.indexOf(".")&&(M[0]+=".");b>O++;)M[0]+="0";A=M[0]+(M.length>1?"e"+M[1]:""),69==a&&(A=A.toUpperCase()),s>=0&&(p?A="+"+A:v&&(A=" "+A))}else A=(s<0?"-":"")+"inf",g=!1;for(;A.length>0]);else d=d.concat(pt("(null)".substr(0,I),!0));if(m)for(;I0;)d.push(32);m||d.push(o("i8"));break;case"n":var L=o("i32*");$[L>>2]=d.length;break;case"%":d.push(i);break;default:for(P=f;P>0])}t+=2}else d.push(i),t+=1}return d}function it(e){if(!e||!e.callee||!e.callee.name)return[null,"",""];e.callee.toString();var r=e.callee.name,t="(",n=!0;for(var o in e){var i=e[o];n||(t+=", "),n=!1,t+="number"==typeof i||"string"==typeof i?i:"("+typeof i+")"}t+=")";var a=e.callee.caller;return n&&(t=""),[e=a?a.arguments:[],r,t]}function at(e,r){24&e&&(r=r.replace(/\s+$/,""),r+=(r.length>0?"\n":"")+function(e){var r=Pe(),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&&T("EM_LOG_DEMANGLE is deprecated; ignoring"),8&e&&"undefined"==typeof emscripten_source_map&&(T('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 i=null;if(128&e)for(i=it(arguments);i[1].includes("_emscripten_");)i=it(i[0]);var a=r.split("\n");r="";var s=new RegExp("\\s*(.*?)@(.*?):([0-9]+):([0-9]+)"),l=new RegExp("\\s*(.*?)@(.*):(.*)(:(.*))?"),u=new RegExp("\\s*at (.*?) \\((.*):(.*):(.*)\\)");for(var c in a){var d=a[c],f="",p="",m=0,h=0,g=u.exec(d);if(g&&5==g.length)f=g[1],p=g[2],m=g[3],h=g[4];else{if((g=s.exec(d))||(g=l.exec(d)),!(g&&g.length>=4)){r+=d+"\n";continue}f=g[1],p=g[2],m=g[3],h=0|g[4]}var v=!1;if(8&e){var y=emscripten_source_map.originalPositionFor({line:m,column:h});(v=y&&y.source)&&(64&e&&(y.source=y.source.substring(y.source.replace(/\\/g,"/").lastIndexOf("/")+1)),r+=" at "+f+" ("+y.source+":"+y.line+":"+y.column+")\n")}(16&e||!v)&&(64&e&&(p=p.substring(p.replace(/\\/g,"/").lastIndexOf("/")+1)),r+=(v?" = "+f:" at "+f)+" ("+p+":"+m+":"+h+")\n"),128&e&&i[0]&&(i[1]==f&&i[2].length>0&&(r=r.replace(/\s+$/,""),r+=" with values: "+i[1]+i[2]+"\n"),i=it(i[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?_(r):b(r)}function st(e){try{return w.grow(e-L.byteLength+65535>>>16),Z(w.buffer),1}catch(r){_("emscripten_realloc_buffer: Attempted to grow heap from "+L.byteLength+" bytes to "+e+" bytes, but got error: "+r)}}var lt={};function ut(){if(!ut.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",_:n||"./this.program"};for(var r in lt)void 0===lt[r]?delete e[r]:e[r]=lt[r];var t=[];for(var r in e)t.push(r+"="+e[r]);ut.strings=t}return ut.strings}var ct=function(e,r,t,n){e||(e=this),this.parent=e,this.mount=e.mount,this.mounted=null,this.id=Ie.nextInode++,this.name=r,this.mode=t,this.node_ops={},this.stream_ops={},this.rdev=n},dt=365,ft=146;function pt(e,r,t){var n=t>0?t:I(e)+1,o=new Array(n),i=M(e,o,0,o.length);return r&&(o.length=i),o}Object.defineProperties(ct.prototype,{read:{get:function(){return(this.mode&dt)===dt},set:function(e){e?this.mode|=dt:this.mode&=-366}},write:{get:function(){return(this.mode&ft)===ft},set:function(e){e?this.mode|=ft:this.mode&=-147}},isFolder:{get:function(){return Ie.isDir(this.mode)}},isDevice:{get:function(){return Ie.isChrdev(this.mode)}}}),Ie.FSNode=ct,Ie.staticInit(),Ne={EPERM:63,ENOENT:44,ESRCH:71,EINTR:27,EIO:29,ENXIO:60,E2BIG:1,ENOEXEC:45,EBADF:8,ECHILD:12,EAGAIN:6,EWOULDBLOCK:6,ENOMEM:48,EACCES:2,EFAULT:21,ENOTBLK:105,EBUSY:10,EEXIST:20,EXDEV:75,ENODEV:43,ENOTDIR:54,EISDIR:31,EINVAL:28,ENFILE:41,EMFILE:33,ENOTTY:59,ETXTBSY:74,EFBIG:22,ENOSPC:51,ESPIPE:70,EROFS:69,EMLINK:34,EPIPE:64,EDOM:18,ERANGE:68,ENOMSG:49,EIDRM:24,ECHRNG:106,EL2NSYNC:156,EL3HLT:107,EL3RST:108,ELNRNG:109,EUNATCH:110,ENOCSI:111,EL2HLT:112,EDEADLK:16,ENOLCK:46,EBADE:113,EBADR:114,EXFULL:115,ENOANO:104,EBADRQC:103,EBADSLT:102,EDEADLOCK:16,EBFONT:101,ENOSTR:100,ENODATA:116,ETIME:117,ENOSR:118,ENONET:119,ENOPKG:120,EREMOTE:121,ENOLINK:47,EADV:122,ESRMNT:123,ECOMM:124,EPROTO:65,EMULTIHOP:36,EDOTDOT:125,EBADMSG:9,ENOTUNIQ:126,EBADFD:127,EREMCHG:128,ELIBACC:129,ELIBBAD:130,ELIBSCN:131,ELIBMAX:132,ELIBEXEC:133,ENOSYS:52,ENOTEMPTY:55,ENAMETOOLONG:37,ELOOP:32,EOPNOTSUPP:138,EPFNOSUPPORT:139,ECONNRESET:15,ENOBUFS:42,EAFNOSUPPORT:5,EPROTOTYPE:67,ENOTSOCK:57,ENOPROTOOPT:50,ESHUTDOWN:140,ECONNREFUSED:14,EADDRINUSE:3,ECONNABORTED:13,ENETUNREACH:40,ENETDOWN:38,ETIMEDOUT:73,EHOSTDOWN:142,EHOSTUNREACH:23,EINPROGRESS:26,EALREADY:7,EDESTADDRREQ:17,EMSGSIZE:35,EPROTONOSUPPORT:66,ESOCKTNOSUPPORT:137,EADDRNOTAVAIL:4,ENETRESET:39,EISCONN:30,ENOTCONN:53,ETOOMANYREFS:141,EUSERS:136,EDQUOT:19,ESTALE:72,ENOTSUP:138,ENOMEDIUM:148,EILSEQ:25,EOVERFLOW:61,ECANCELED:11,ENOTRECOVERABLE:56,EOWNERDEAD:62,ESTRPIPE:135},function(){for(var e=new Array(256),r=0;r<256;++r)e[r]=String.fromCharCode(r);Ue=e}(),Ve=r.BindingError=Ge(Error,"BindingError"),qe=r.InternalError=Ge(Error,"InternalError"),wr.prototype.isAliasOf=Qe,wr.prototype.clone=gr,wr.prototype.delete=vr,wr.prototype.isDeleted=yr,wr.prototype.deleteLater=Er,r.getInheritedInstanceCount=ir,r.getLiveInheritedInstances=ar,r.flushPendingDeletes=lr,r.setDelayFunction=cr,Or.prototype.getPointee=Ar,Or.prototype.destructor=Fr,Or.prototype.argPackAdvance=8,Or.prototype.readValueFromPointer=Pr,Or.prototype.deleteObject=Dr,Or.prototype.fromWireType=mr,Nr=r.UnboundTypeError=Ge(Error,"UnboundTypeError"),r.count_emval_handles=Gr,r.get_first_emval=Vr;var mt={__syscall_fcntl64:function(e,r,t){Le.varargs=t;try{var n=Le.getStreamFromFD(e);switch(r){case 0:return(o=Le.get())<0?-28:Ie.createStream(n,o).fd;case 1:case 2:case 6:case 7:return 0;case 3:return n.flags;case 4:var o=Le.get();return n.flags|=o,0;case 5:o=Le.get();return B[o+0>>1]=2,0;case 16:case 8:default:return-28;case 9:return i=28,$[yt()>>2]=i,-1}}catch(e){if(void 0===Ie||!(e instanceof Ie.ErrnoError))throw e;return-e.errno}var i},__syscall_openat:function(e,r,t,n){Le.varargs=n;try{r=Le.getStr(r),r=Le.calculateAt(e,r);var o=n?Le.get():0;return Ie.open(r,t,o).fd}catch(e){if(void 0===Ie||!(e instanceof Ie.ErrnoError))throw e;return-e.errno}},_embind_register_bigint:function(e,r,t,n,o){},_embind_register_bool:function(e,r,t,n,o){var i=xe(t);Je(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=x;else if(2===t)n=B;else{if(4!==t)throw new TypeError("Unknown boolean type size: "+r);n=$}return this.fromWireType(n[e>>i])},destructorFunction:null})},_embind_register_class:function(e,t,n,o,i,a,s,l,u,c,d,f,p){d=Be(d),a=Mr(i,a),l&&(l=Mr(s,l)),c&&(c=Mr(u,c)),p=Mr(f,p);var m=ze(d);!function(e,t,n){r.hasOwnProperty(e)?((void 0===n||void 0!==r[e].overloadTable&&void 0!==r[e].overloadTable[n])&&Ye("Cannot register public name '"+e+"' twice"),br(r,e,e),r.hasOwnProperty(n)&&Ye("Cannot register multiple overloads of a function with the same number of arguments ("+n+")!"),r[e].overloadTable[n]=t):(r[e]=t,void 0!==n&&(r[e].numArguments=n))}(m,(function(){Lr("Cannot construct "+d+" due to unbound types",[o])})),Ke([e,t,n],o?[o]:[],(function(t){var n,i;t=t[0],i=o?(n=t.registeredClass).instancePrototype:wr.prototype;var s=He(m,(function(){if(Object.getPrototypeOf(this)!==u)throw new Ve("Use 'new' to construct "+d);if(void 0===f.constructor_body)throw new Ve(d+" has no accessible constructor");var e=f.constructor_body[arguments.length];if(void 0===e)throw new Ve("Tried to invoke ctor of "+d+" with invalid number of parameters ("+arguments.length+") - expected ("+Object.keys(f.constructor_body).toString()+") parameters instead!");return e.apply(this,arguments)})),u=Object.create(i,{constructor:{value:s}});s.prototype=u;var f=new _r(d,s,u,p,n,a,l,c),h=new Or(d,f,!0,!1,!1),g=new Or(d+"*",f,!1,!1,!1),v=new Or(d+" const*",f,!1,!0,!1);return or[e]={pointerType:g,constPointerType:v},function(e,t,n){r.hasOwnProperty(e)||Xe("Replacing nonexistant public symbol"),void 0!==r[e].overloadTable&&void 0!==n?r[e].overloadTable[n]=t:(r[e]=t,r[e].argCount=n)}(m,s),[h,g,v]}))},_embind_register_class_constructor:function(e,r,t,n,o,i){F(r>0);var a=xr(r,t);o=Mr(n,o),Ke([],[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 Ve("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]=()=>{Lr("Cannot construct "+e.name+" due to unbound types",a)},Ke([],a,(function(n){return n.splice(1,0,null),e.registeredClass.constructor_body[r-1]=jr(t,n,null,o,i),[]})),[]}))},_embind_register_class_function:function(e,r,t,n,o,i,a,s){var l=xr(t,n);r=Be(r),i=Mr(o,i),Ke([],[e],(function(e){var n=(e=e[0]).name+"."+r;function o(){Lr("Cannot call "+n+" due to unbound types",l)}r.startsWith("@@")&&(r=Symbol[r.substring(2)]),s&&e.registeredClass.pureVirtualFunctions.push(r);var u=e.registeredClass.instancePrototype,c=u[r];return void 0===c||void 0===c.overloadTable&&c.className!==e.name&&c.argCount===t-2?(o.argCount=t-2,o.className=e.name,u[r]=o):(br(u,r,n),u[r].overloadTable[t-2]=o),Ke([],l,(function(o){var s=jr(n,o,e,i,a);return void 0===u[r].overloadTable?(s.argCount=t-2,u[r]=s):u[r].overloadTable[t-2]=s,[]})),[]}))},_embind_register_class_property:function(e,r,t,n,o,i,a,s,l,u){r=Be(r),o=Mr(n,o),Ke([],[e],(function(e){var n=(e=e[0]).name+"."+r,c={get:function(){Lr("Cannot access "+n+" due to unbound types",[t,a])},enumerable:!0,configurable:!0};return c.set=l?()=>{Lr("Cannot access "+n+" due to unbound types",[t,a])}:e=>{Ye(n+" is a read-only property")},Object.defineProperty(e.registeredClass.instancePrototype,r,c),Ke([],l?[t,a]:[t],(function(t){var a=t[0],c={get:function(){var r=$r(this,e,n+" getter");return a.fromWireType(o(i,r))},enumerable:!0};if(l){l=Mr(s,l);var d=t[1];c.set=function(r){var t=$r(this,e,n+" setter"),o=[];l(u,t,d.toWireType(o,r)),Ur(o)}}return Object.defineProperty(e.registeredClass.instancePrototype,r,c),[]})),[]}))},_embind_register_emval:function(e,r){Je(e,{name:r=Be(r),fromWireType:function(e){var r=Yr.toValue(e);return Hr(e),r},toWireType:function(e,r){return Yr.toHandle(r)},argPackAdvance:8,readValueFromPointer:Pr,destructorFunction:null})},_embind_register_float:function(e,r,t){var n=xe(t);Je(e,{name:r=Be(r),fromWireType:function(e){return e},toWireType:function(e,r){if("number"!=typeof r&&"boolean"!=typeof r)throw new TypeError('Cannot convert "'+qr(r)+'" to '+this.name);return r},argPackAdvance:8,readValueFromPointer:Xr(r,n),destructorFunction:null})},_embind_register_integer:function(e,r,t,n,o){r=Be(r),-1===o&&(o=4294967295);var i=xe(t),a=e=>e;if(0===n){var s=32-8*t;a=e=>e<>>s}var l=r.includes("unsigned"),u=(e,t)=>{if("number"!=typeof e&&"boolean"!=typeof e)throw new TypeError('Cannot convert "'+qr(e)+'" to '+t);if(eo)throw new TypeError('Passing a number "'+qr(e)+'" from JS side to C/C++ side to an argument of type "'+r+'", which is outside the valid range ['+n+", "+o+"]!")};Je(e,{name:r,fromWireType:a,toWireType:l?function(e,r){return u(r,this.name),r>>>0}:function(e,r){return u(r,this.name),r},argPackAdvance:8,readValueFromPointer:Kr(r,i,0!==n),destructorFunction:null})},_embind_register_memory_view:function(e,r,t){var n=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array][r];function o(e){var r=W,t=r[e>>=2],o=r[e+1];return new n(L,o,t)}Je(e,{name:t=Be(t),fromWireType:o,argPackAdvance:8,readValueFromPointer:o},{ignoreDuplicateRegistrations:!0})},_embind_register_std_string:function(e,r){var t="std::string"===(r=Be(r));Je(e,{name:r,fromWireType:function(e){var r,n=W[e>>2];if(t)for(var o=e+4,i=0;i<=n;++i){var a=e+4+i;if(i==n||0==U[a]){var s=R(o,a-o);void 0===r?r=s:(r+=String.fromCharCode(0),r+=s),o=a+1}}else{var l=new Array(n);for(i=0;iI(r):()=>r.length)(),i=gt(4+o+1);if(W[i>>2]=o,t&&n)N(r,i+4,o+1);else if(n)for(var a=0;a255&&(ht(i),Ye("String has UTF-16 code units that do not fit in 8 bits")),U[i+4+a]=s}else for(a=0;aj,s=1):4===r&&(n=X,o=K,a=J,i=()=>W,s=2),Je(e,{name:t,fromWireType:function(e){for(var t,o=W[e>>2],a=i(),l=e+4,u=0;u<=o;++u){var c=e+4+u*r;if(u==o||0==a[c>>s]){var d=n(l,c-l);void 0===t?t=d:(t+=String.fromCharCode(0),t+=d),l=c+r}}return ht(e),t},toWireType:function(e,n){"string"!=typeof n&&Ye("Cannot pass non-string to C++ string type "+t);var i=a(n),l=gt(4+i+r);return W[l>>2]=i>>s,o(n,l+4,i+r),null!==e&&e.push(ht,l),l},argPackAdvance:8,readValueFromPointer:Pr,destructorFunction:function(e){ht(e)}})},_embind_register_void:function(e,r){Je(e,{isVoid:!0,name:r=Be(r),argPackAdvance:0,fromWireType:function(){},toWireType:function(e,r){}})},_emscripten_date_now:function(){return Date.now()},_emval_as:function(e,r,t){e=Yr.toValue(e),r=Jr(r,"emval::as");var n=[],o=Yr.toHandle(n);return $[t>>2]=o,r.toWireType(n,e)},_emval_call_void_method:function(e,r,t,n){var o,i;(e=Zr[e])(r=Yr.toValue(r),t=void 0===(i=Qr[o=t])?Be(o):i,null,n)},_emval_decref:Hr,_emval_get_method_caller:function(e,r){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("_")+"$",i=et[o];if(void 0!==i)return i;for(var a=["retType"],s=[n],l="",u=0;u4&&(zr[e].refcount+=1)},_emval_run_destructors:function(e){Ur(Yr.toValue(e)),Hr(e)},_emval_take_value:function(e,r){var t=(e=Jr(e,"_emval_take_value")).readValueFromPointer(r);return Yr.toHandle(t)},_gmtime_js:function(e,r){var t=new Date(1e3*$[e>>2]);$[r>>2]=t.getUTCSeconds(),$[r+4>>2]=t.getUTCMinutes(),$[r+8>>2]=t.getUTCHours(),$[r+12>>2]=t.getUTCDate(),$[r+16>>2]=t.getUTCMonth(),$[r+20>>2]=t.getUTCFullYear()-1900,$[r+24>>2]=t.getUTCDay();var n=Date.UTC(t.getUTCFullYear(),0,1,0,0,0,0),o=(t.getTime()-n)/864e5|0;$[r+28>>2]=o},_localtime_js:function(e,r){var t=new Date(1e3*$[e>>2]);$[r>>2]=t.getSeconds(),$[r+4>>2]=t.getMinutes(),$[r+8>>2]=t.getHours(),$[r+12>>2]=t.getDate(),$[r+16>>2]=t.getMonth(),$[r+20>>2]=t.getFullYear()-1900,$[r+24>>2]=t.getDay();var n=new Date(t.getFullYear(),0,1),o=(t.getTime()-n.getTime())/864e5|0;$[r+28>>2]=o,$[r+36>>2]=-60*t.getTimezoneOffset();var i=new Date(t.getFullYear(),6,1).getTimezoneOffset(),a=n.getTimezoneOffset(),s=0|(i!=a&&t.getTimezoneOffset()==Math.min(a,i));$[r+32>>2]=s},_mktime_js:function(e){var r=new Date($[e+20>>2]+1900,$[e+16>>2],$[e+12>>2],$[e+8>>2],$[e+4>>2],$[e>>2],0),t=$[e+32>>2],n=r.getTimezoneOffset(),o=new Date(r.getFullYear(),0,1),i=new Date(r.getFullYear(),6,1).getTimezoneOffset(),a=o.getTimezoneOffset(),s=Math.min(a,i);if(t<0)$[e+32>>2]=Number(i!=a&&s==n);else if(t>0!=(s==n)){var l=Math.max(a,i),u=t>0?s:l;r.setTime(r.getTime()+6e4*(u-n))}$[e+24>>2]=r.getDay();var c=(r.getTime()-o.getTime())/864e5|0;return $[e+28>>2]=c,$[e>>2]=r.getSeconds(),$[e+4>>2]=r.getMinutes(),$[e+8>>2]=r.getHours(),$[e+12>>2]=r.getDate(),$[e+16>>2]=r.getMonth(),r.getTime()/1e3|0},_tzset_js:function e(r,t,n){e.called||(e.called=!0,function(e,r,t){var n=(new Date).getFullYear(),o=new Date(n,0,1),i=new Date(n,6,1),a=o.getTimezoneOffset(),s=i.getTimezoneOffset(),l=Math.max(a,s);function u(e){var r=e.toTimeString().match(/\(([A-Za-z ]+)\)$/);return r?r[1]:"GMT"}$[e>>2]=60*l,$[r>>2]=Number(a!=s);var c=u(o),d=u(i),f=Q(c),p=Q(d);s>2]=f,$[t+4>>2]=p):($[t>>2]=p,$[t+4>>2]=f)}(r,t,n))},abort:function(){ge("native code called abort()")},emscripten_log:function(e,r,t){at(e,O(ot(r,t),0))},emscripten_resize_heap:function(e){var r=U.length;F((e>>>=0)>r);var t,n,o=2147483648;if(e>o)return _("Cannot enlarge memory, asked to go up to "+e+" bytes, but the limit is "+"2147483648 bytes!"),!1;for(var i=1;i<=4;i*=2){var a=r*(1+.2/i);a=Math.min(a,e+100663296);var s=Math.min(o,(t=Math.max(e,a))+((n=65536)-t%n)%n);if(st(s))return!0}return _("Failed to grow the heap from "+r+" bytes to "+s+" bytes, not enough memory!"),!1},environ_get:function(e,r){var t=0;return ut().forEach((function(n,o){var i=r+t;$[e+4*o>>2]=i,function(e,r,t){for(var n=0;n>0]=e.charCodeAt(n);t||(x[r>>0]=0)}(n,i),t+=n.length+1})),0},environ_sizes_get:function(e,r){var t=ut();$[e>>2]=t.length;var n=0;return t.forEach((function(e){n+=e.length+1})),$[r>>2]=n,0},fd_close:function(e){try{var r=Le.getStreamFromFD(e);return Ie.close(r),0}catch(e){if(void 0===Ie||!(e instanceof Ie.ErrnoError))throw e;return e.errno}},fd_fdstat_get:function(e,r){try{var t=Le.getStreamFromFD(e),n=t.tty?2:Ie.isDir(t.mode)?3:Ie.isLink(t.mode)?7:4;return x[r>>0]=n,0}catch(e){if(void 0===Ie||!(e instanceof Ie.ErrnoError))throw e;return e.errno}},fd_read:function(e,r,t,n){try{var o=Le.getStreamFromFD(e),i=Le.doReadv(o,r,t);return $[n>>2]=i,0}catch(e){if(void 0===Ie||!(e instanceof Ie.ErrnoError))throw e;return e.errno}},fd_seek:function(e,r,t,n,o){try{var i=Le.getStreamFromFD(e),a=4294967296*t+(r>>>0),s=9007199254740992;return a<=-s||a>=s?-61:(Ie.llseek(i,a,n),Ee=[i.position>>>0,(ye=i.position,+Math.abs(ye)>=1?ye>0?(0|Math.min(+Math.floor(ye/4294967296),4294967295))>>>0:~~+Math.ceil((ye-+(~~ye>>>0))/4294967296)>>>0:0)],$[o>>2]=Ee[0],$[o+4>>2]=Ee[1],i.getdents&&0===a&&0===n&&(i.getdents=null),0)}catch(e){if(void 0===Ie||!(e instanceof Ie.ErrnoError))throw e;return e.errno}},fd_write:function(e,r,t,n){try{var o=Le.getStreamFromFD(e),i=Le.doWritev(o,r,t);return $[n>>2]=i,0}catch(e){if(void 0===Ie||!(e instanceof Ie.ErrnoError))throw e;return e.errno}},setTempRet0:function(e){}};!function(){var e={env:mt,wasi_snapshot_preview1:mt};function t(e,t){var n,o=e.exports;r.asm=o,F(w=r.asm.memory,"memory not found in wasm exports"),Z(w.buffer),F(re=r.asm.__indirect_function_table,"table not found in wasm exports"),n=r.asm.__wasm_call_ctors,ae.unshift(n),he("wasm-instantiate")}me("wasm-instantiate");var n=r;function o(e){F(r===n,"the Module object should not be replaced during async compilation - perhaps the order of HTML elements is wrong?"),n=null,t(e.instance)}function i(r){return function(){if(!E&&(s||l)){if("function"==typeof fetch&&!be(ve))return fetch(ve,{credentials:"same-origin"}).then((function(e){if(!e.ok)throw"failed to load wasm binary file at '"+ve+"'";return e.arrayBuffer()})).catch((function(){return Te(ve)}));if(f)return new Promise((function(e,r){f(ve,(function(r){e(new Uint8Array(r))}),r)}))}return Promise.resolve().then((function(){return Te(ve)}))}().then((function(r){return WebAssembly.instantiate(r,e)})).then((function(e){return e})).then(r,(function(e){_("failed to asynchronously prepare wasm: "+e),be(ve)&&_("warning: Loading from a file URI ("+ve+") is not supported in most browsers. See https://emscripten.org/docs/getting_started/FAQ.html#how-do-i-run-a-local-webserver-for-testing-why-does-my-program-stall-in-downloading-or-preparing"),ge(e)}))}if(r.instantiateWasm)try{return r.instantiateWasm(e,t)}catch(e){return _("Module.instantiateWasm callback failed with error: "+e),!1}E||"function"!=typeof WebAssembly.instantiateStreaming||we(ve)||be(ve)||"function"!=typeof fetch?i(o):fetch(ve,{credentials:"same-origin"}).then((function(r){return WebAssembly.instantiateStreaming(r,e).then(o,(function(e){return _("wasm streaming compile failed: "+e),_("falling back to ArrayBuffer instantiation"),i(o)}))}))}(),r.___wasm_call_ctors=_e("__wasm_call_ctors");var ht=r._free=_e("free"),gt=r._malloc=_e("malloc"),vt=r._strlen=_e("strlen"),yt=r.___errno_location=_e("__errno_location"),Et=r.___getTypeName=_e("__getTypeName");r.___embind_register_native_and_builtin_types=_e("__embind_register_native_and_builtin_types");var wt=r.___stdio_exit=_e("__stdio_exit"),bt=r._emscripten_builtin_memalign=_e("emscripten_builtin_memalign"),_t=r._emscripten_stack_init=function(){return(_t=r._emscripten_stack_init=r.asm.emscripten_stack_init).apply(null,arguments)};r._emscripten_stack_get_free=function(){return(r._emscripten_stack_get_free=r.asm.emscripten_stack_get_free).apply(null,arguments)},r._emscripten_stack_get_base=function(){return(r._emscripten_stack_get_base=r.asm.emscripten_stack_get_base).apply(null,arguments)};var Tt,kt=r._emscripten_stack_get_end=function(){return(kt=r._emscripten_stack_get_end=r.asm.emscripten_stack_get_end).apply(null,arguments)};function St(e){this.name="ExitStatus",this.message="Program terminated with exit("+e+")",this.status=e}function Ct(e){function t(){Tt||(Tt=!0,r.calledRun=!0,A||(oe(),F(!le),le=!0,r.noFSInit||Ie.init.initialized||Ie.init(),Ie.ignorePermissions=!1,ke(ae),r.onRuntimeInitialized&&r.onRuntimeInitialized(),F(!r._main,'compiled without a main, but one is present. if you added it from JS, use Module["onRuntimeInitialized"]'),function(){if(oe(),r.postRun)for("function"==typeof r.postRun&&(r.postRun=[r.postRun]);r.postRun.length;)e=r.postRun.shift(),se.unshift(e);var e;ke(se)}()))}ue>0||(_t(),ne(),function(){if(r.preRun)for("function"==typeof r.preRun&&(r.preRun=[r.preRun]);r.preRun.length;)e=r.preRun.shift(),ie.unshift(e);var e;ke(ie)}(),ue>0||(r.setStatus?(r.setStatus("Running..."),setTimeout((function(){setTimeout((function(){r.setStatus("")}),1),t()}),1)):t(),oe()))}if(r.stackSave=_e("stackSave"),r.stackRestore=_e("stackRestore"),r.stackAlloc=_e("stackAlloc"),r.dynCall_ijiii=_e("dynCall_ijiii"),r.dynCall_viiijj=_e("dynCall_viiijj"),r.dynCall_jij=_e("dynCall_jij"),r.dynCall_jii=_e("dynCall_jii"),r.dynCall_jiji=_e("dynCall_jiji"),r._ff_h264_cabac_tables=112940,P("intArrayFromString",!1),P("intArrayToString",!1),P("ccall",!1),P("cwrap",!1),P("setValue",!1),P("getValue",!1),P("allocate",!1),P("UTF8ArrayToString",!1),P("UTF8ToString",!1),P("stringToUTF8Array",!1),P("stringToUTF8",!1),P("lengthBytesUTF8",!1),P("stackTrace",!1),P("addOnPreRun",!1),P("addOnInit",!1),P("addOnPreMain",!1),P("addOnExit",!1),P("addOnPostRun",!1),P("writeStringToMemory",!1),P("writeArrayToMemory",!1),P("writeAsciiToMemory",!1),P("addRunDependency",!0),P("removeRunDependency",!0),P("FS_createFolder",!1),P("FS_createPath",!0),P("FS_createDataFile",!0),P("FS_createPreloadedFile",!0),P("FS_createLazyFile",!0),P("FS_createLink",!1),P("FS_createDevice",!0),P("FS_unlink",!0),P("getLEB",!1),P("getFunctionTables",!1),P("alignFunctionTables",!1),P("registerFunctions",!1),P("addFunction",!1),P("removeFunction",!1),P("prettyPrint",!1),P("dynCall",!1),P("getCompilerSetting",!1),P("print",!1),P("printErr",!1),P("getTempRet0",!1),P("setTempRet0",!1),P("callMain",!1),P("abort",!1),P("keepRuntimeAlive",!1),P("ptrToString",!1),P("zeroMemory",!1),P("stringToNewUTF8",!1),P("emscripten_realloc_buffer",!1),P("ENV",!1),P("ERRNO_CODES",!1),P("ERRNO_MESSAGES",!1),P("setErrNo",!1),P("inetPton4",!1),P("inetNtop4",!1),P("inetPton6",!1),P("inetNtop6",!1),P("readSockaddr",!1),P("writeSockaddr",!1),P("DNS",!1),P("getHostByName",!1),P("Protocols",!1),P("Sockets",!1),P("getRandomDevice",!1),P("traverseStack",!1),P("UNWIND_CACHE",!1),P("convertPCtoSourceLocation",!1),P("readAsmConstArgsArray",!1),P("readAsmConstArgs",!1),P("mainThreadEM_ASM",!1),P("jstoi_q",!1),P("jstoi_s",!1),P("getExecutableName",!1),P("listenOnce",!1),P("autoResumeAudioContext",!1),P("dynCallLegacy",!1),P("getDynCaller",!1),P("dynCall",!1),P("setWasmTableEntry",!1),P("getWasmTableEntry",!1),P("handleException",!1),P("runtimeKeepalivePush",!1),P("runtimeKeepalivePop",!1),P("callUserCallback",!1),P("maybeExit",!1),P("safeSetTimeout",!1),P("asmjsMangle",!1),P("asyncLoad",!1),P("alignMemory",!1),P("mmapAlloc",!1),P("reallyNegative",!1),P("unSign",!1),P("reSign",!1),P("formatString",!1),P("PATH",!1),P("PATH_FS",!1),P("SYSCALLS",!1),P("getSocketFromFD",!1),P("getSocketAddress",!1),P("JSEvents",!1),P("registerKeyEventCallback",!1),P("specialHTMLTargets",!1),P("maybeCStringToJsString",!1),P("findEventTarget",!1),P("findCanvasEventTarget",!1),P("getBoundingClientRect",!1),P("fillMouseEventData",!1),P("registerMouseEventCallback",!1),P("registerWheelEventCallback",!1),P("registerUiEventCallback",!1),P("registerFocusEventCallback",!1),P("fillDeviceOrientationEventData",!1),P("registerDeviceOrientationEventCallback",!1),P("fillDeviceMotionEventData",!1),P("registerDeviceMotionEventCallback",!1),P("screenOrientation",!1),P("fillOrientationChangeEventData",!1),P("registerOrientationChangeEventCallback",!1),P("fillFullscreenChangeEventData",!1),P("registerFullscreenChangeEventCallback",!1),P("registerRestoreOldStyle",!1),P("hideEverythingExceptGivenElement",!1),P("restoreHiddenElements",!1),P("setLetterbox",!1),P("currentFullscreenStrategy",!1),P("restoreOldWindowedStyle",!1),P("softFullscreenResizeWebGLRenderTarget",!1),P("doRequestFullscreen",!1),P("fillPointerlockChangeEventData",!1),P("registerPointerlockChangeEventCallback",!1),P("registerPointerlockErrorEventCallback",!1),P("requestPointerLock",!1),P("fillVisibilityChangeEventData",!1),P("registerVisibilityChangeEventCallback",!1),P("registerTouchEventCallback",!1),P("fillGamepadEventData",!1),P("registerGamepadEventCallback",!1),P("registerBeforeUnloadEventCallback",!1),P("fillBatteryEventData",!1),P("battery",!1),P("registerBatteryEventCallback",!1),P("setCanvasElementSize",!1),P("getCanvasElementSize",!1),P("demangle",!1),P("demangleAll",!1),P("jsStackTrace",!1),P("stackTrace",!1),P("getEnvStrings",!1),P("checkWasiClock",!1),P("writeI53ToI64",!1),P("writeI53ToI64Clamped",!1),P("writeI53ToI64Signaling",!1),P("writeI53ToU64Clamped",!1),P("writeI53ToU64Signaling",!1),P("readI53FromI64",!1),P("readI53FromU64",!1),P("convertI32PairToI53",!1),P("convertU32PairToI53",!1),P("dlopenMissingError",!1),P("setImmediateWrapped",!1),P("clearImmediateWrapped",!1),P("polyfillSetImmediate",!1),P("uncaughtExceptionCount",!1),P("exceptionLast",!1),P("exceptionCaught",!1),P("ExceptionInfo",!1),P("exception_addRef",!1),P("exception_decRef",!1),P("Browser",!1),P("setMainLoop",!1),P("wget",!1),P("FS",!1),P("MEMFS",!1),P("TTY",!1),P("PIPEFS",!1),P("SOCKFS",!1),P("_setNetworkCallback",!1),P("tempFixedLengthArray",!1),P("miniTempWebGLFloatBuffers",!1),P("heapObjectForWebGLType",!1),P("heapAccessShiftForWebGLHeap",!1),P("GL",!1),P("emscriptenWebGLGet",!1),P("computeUnpackAlignedImageSize",!1),P("emscriptenWebGLGetTexPixelData",!1),P("emscriptenWebGLGetUniform",!1),P("webglGetUniformLocation",!1),P("webglPrepareUniformLocationsBeforeFirstUse",!1),P("webglGetLeftBracePos",!1),P("emscriptenWebGLGetVertexAttrib",!1),P("writeGLArray",!1),P("AL",!1),P("SDL_unicode",!1),P("SDL_ttfContext",!1),P("SDL_audio",!1),P("SDL",!1),P("SDL_gfx",!1),P("GLUT",!1),P("EGL",!1),P("GLFW_Window",!1),P("GLFW",!1),P("GLEW",!1),P("IDBStore",!1),P("runAndAbortIfError",!1),P("InternalError",!1),P("BindingError",!1),P("UnboundTypeError",!1),P("PureVirtualError",!1),P("init_embind",!1),P("throwInternalError",!1),P("throwBindingError",!1),P("throwUnboundTypeError",!1),P("ensureOverloadTable",!1),P("exposePublicSymbol",!1),P("replacePublicSymbol",!1),P("extendError",!1),P("createNamedFunction",!1),P("registeredInstances",!1),P("getBasestPointer",!1),P("registerInheritedInstance",!1),P("unregisterInheritedInstance",!1),P("getInheritedInstance",!1),P("getInheritedInstanceCount",!1),P("getLiveInheritedInstances",!1),P("registeredTypes",!1),P("awaitingDependencies",!1),P("typeDependencies",!1),P("registeredPointers",!1),P("registerType",!1),P("whenDependentTypesAreResolved",!1),P("embind_charCodes",!1),P("embind_init_charCodes",!1),P("readLatin1String",!1),P("getTypeName",!1),P("heap32VectorToArray",!1),P("requireRegisteredType",!1),P("getShiftFromSize",!1),P("integerReadValueFromPointer",!1),P("enumReadValueFromPointer",!1),P("floatReadValueFromPointer",!1),P("simpleReadValueFromPointer",!1),P("runDestructors",!1),P("new_",!1),P("craftInvokerFunction",!1),P("embind__requireFunction",!1),P("tupleRegistrations",!1),P("structRegistrations",!1),P("genericPointerToWireType",!1),P("constNoSmartPtrRawPointerToWireType",!1),P("nonConstNoSmartPtrRawPointerToWireType",!1),P("init_RegisteredPointer",!1),P("RegisteredPointer",!1),P("RegisteredPointer_getPointee",!1),P("RegisteredPointer_destructor",!1),P("RegisteredPointer_deleteObject",!1),P("RegisteredPointer_fromWireType",!1),P("runDestructor",!1),P("releaseClassHandle",!1),P("finalizationRegistry",!1),P("detachFinalizer_deps",!1),P("detachFinalizer",!1),P("attachFinalizer",!1),P("makeClassHandle",!1),P("init_ClassHandle",!1),P("ClassHandle",!1),P("ClassHandle_isAliasOf",!1),P("throwInstanceAlreadyDeleted",!1),P("ClassHandle_clone",!1),P("ClassHandle_delete",!1),P("deletionQueue",!1),P("ClassHandle_isDeleted",!1),P("ClassHandle_deleteLater",!1),P("flushPendingDeletes",!1),P("delayFunction",!1),P("setDelayFunction",!1),P("RegisteredClass",!1),P("shallowCopyInternalPointer",!1),P("downcastPointer",!1),P("upcastPointer",!1),P("validateThis",!1),P("char_0",!1),P("char_9",!1),P("makeLegalFunctionName",!1),P("emval_handle_array",!1),P("emval_free_list",!1),P("emval_symbols",!1),P("init_emval",!1),P("count_emval_handles",!1),P("get_first_emval",!1),P("getStringOrSymbol",!1),P("Emval",!1),P("emval_newers",!1),P("craftEmvalAllocator",!1),P("emval_get_global",!1),P("emval_methodCallers",!1),P("emval_registeredMethods",!1),P("warnOnce",!1),P("stackSave",!1),P("stackRestore",!1),P("stackAlloc",!1),P("AsciiToString",!1),P("stringToAscii",!1),P("UTF16ToString",!1),P("stringToUTF16",!1),P("lengthBytesUTF16",!1),P("UTF32ToString",!1),P("stringToUTF32",!1),P("lengthBytesUTF32",!1),P("allocateUTF8",!1),P("allocateUTF8OnStack",!1),r.writeStackCookie=ne,r.checkStackCookie=oe,C("ALLOC_NORMAL",!1),C("ALLOC_STACK",!1),de=function e(){Tt||Ct(),Tt||(de=e)},r.run=Ct,r.preInit)for("function"==typeof r.preInit&&(r.preInit=[r.preInit]);r.preInit.length>0;)r.preInit.pop()();Ct(),e.exports=r}));const u=1e3,c=1e3,d=!1,f=!1,p=!1,m=!1,h="initVideo",g="render",v="playAudio",y="initAudio",E="audioCode",w="videoCode",b=1,_=2,T="init",k="decode",S="audioDecode",C="videoDecode",P="close",A="updateConfig",F="key",D="delta";s((function(e){!function(){var r="undefined"!=typeof window&&void 0!==window.document?window.document:{},t=e.exports,n=function(){for(var e,t=[["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"]],n=0,o=t.length,i={};n{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()}),l.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:h,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:g,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?F: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:d,useOffscreen:p,useWCS:f,videoBuffer:u,openWebglAlignment:m,videoBufferDelay:c},useOffscreen:function(){return n.opt.useOffscreen&&"undefined"!=typeof OffscreenCanvas},initAudioPlanar:function(e,t){postMessage({cmd:y,sampleRate:t,channels:e});var n=[],o=0;this.playAudioPlanar=function(t,i,a){for(var s=i,u=[],c=0,d=0;d<2;d++){var f=l.HEAPU32[(t>>2)+d]>>2;u[d]=l.HEAPF32.subarray(f,f+s)}if(o){if(!(s>=(i=1024-o)))return o+=s,r[0]=Float32Array.of(...r[0],...u[0]),void(2==e&&(r[1]=Float32Array.of(...r[1],...u[1])));n[0]=Float32Array.of(...r[0],...u[0].subarray(0,i)),2==e&&(n[1]=Float32Array.of(...r[1],...u[1].subarray(0,i))),postMessage({cmd:v,buffer:n,ts:a},n.map((e=>e.buffer))),c=i,s-=i}for(o=s;o>=1024;o-=1024)n[0]=u[0].slice(c,c+=1024),2==e&&(n[1]=u[1].slice(c-1024,c)),postMessage({cmd:v,buffer:n,ts:a},n.map((e=>e.buffer)));o&&(r[0]=u[0].slice(c),2==e&&(r[1]=u[1].slice(c)))}},setVideoCodec:function(e){postMessage({cmd:w,code:e})},setAudioCodec:function(e){postMessage({cmd:E,code:e})},setVideoSize:function(e,r){postMessage({cmd:h,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 i=e.createShader(e.FRAGMENT_SHADER);e.shaderSource(i,n),e.compileShader(i),e.getShaderParameter(i,e.COMPILE_STATUS)||console.log("Fragment shader failed to compile: "+e.getShaderInfoLog(i));var a=e.createProgram();e.attachShader(a,o),e.attachShader(a,i),e.linkProgram(a),e.getProgramParameter(a,e.LINK_STATUS)||console.log("Program failed to compile: "+e.getProgramInfoLog(a)),e.useProgram(a);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 l=e.getAttribLocation(a,"vertexPos");e.enableVertexAttribArray(l),e.vertexAttribPointer(l,2,e.FLOAT,!1,0,0);var u=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,u),e.bufferData(e.ARRAY_BUFFER,new Float32Array([1,0,0,0,1,1,0,1]),e.STATIC_DRAW);var c=e.getAttribLocation(a,"texturePos");function d(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(a,r),t),n}e.enableVertexAttribArray(c),e.vertexAttribPointer(c,2,e.FLOAT,!1,0,0);var f=d("ySampler",0),p=d("uSampler",1),m=d("vSampler",2);return{render:function(r,t,n,o,i){e.viewport(0,0,r,t),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,f),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,m),e.texImage2D(e.TEXTURE_2D,0,e.LUMINANCE,r/2,t/2,0,e.LUMINANCE,e.UNSIGNED_BYTE,i),e.drawArrays(e.TRIANGLE_STRIP,0,4)},destroy:function(){try{e.deleteProgram(a),e.deleteBuffer(s),e.deleteBuffer(u),e.deleteTexture(f),e.deleteTexture(p),e.deleteTexture(m)}catch(e){}}}})(this.offscreenCanvasGL,n.opt.openWebglAlignment),this.draw=function(n,i,a,s){const u=l.HEAPU8.subarray(i,i+t),c=l.HEAPU8.subarray(a,a+o),d=l.HEAPU8.subarray(s,s+o);this.webglObj.render(e,r,u,c,d);let f=this.offscreenCanvas.transferToImageBitmap();postMessage({cmd:g,buffer:f,delay:this.delay,ts:n},[f])}):this.draw=function(e,r,n,i){const a=[Uint8Array.from(l.HEAPU8.subarray(r,r+t)),Uint8Array.from(l.HEAPU8.subarray(n,n+o)),Uint8Array.from(l.HEAPU8.subarray(i,i+o))];postMessage({cmd:g,output:a,delay:this.delay,ts:e},a.map((e=>e.buffer)))}},getDelay:function(e){if(!e)return-1;if(this.firstTimestamp){if(e){const r=Date.now()-this.startTimestamp,t=e-this.firstTimestamp;this.delay=r>=t?r-t:t-r}}else this.firstTimestamp=e,this.startTimestamp=Date.now(),this.delay=-1;return this.delay},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===_&&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===b&&0===t.payload[1]&&r(t);!t.isIFrame&&e.length;)(t=e.shift()).type===b&&0===t.payload[1]&&r(t);t.isIFrame&&(this.dropping=!1,r(t))}else{var t=e[0];if(-1===this.getDelay(t.ts))e.shift(),r(t);else if(this.delay>n.opt.videoBuffer+n.opt.videoBufferDelay)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&&o.clear(),i.clear&&i.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===b?e.push({ts:t.ts,payload:r,decoder:o,type:b}):t.type===_&&e.push({ts:t.ts,payload:r,decoder:i,type:_,isIFrame:t.isIFrame})}},o=new l.AudioDecoder(n),i=new l.VideoDecoder(n);postMessage({cmd:T}),self.onmessage=function(e){var r=e.data;switch(r.cmd){case T:try{n.opt=Object.assign(n.opt,JSON.parse(r.opt))}catch(e){}o.sample_rate=r.sampleRate,n.init();break;case k:n.pushBuffer(r.buffer,r.options);break;case S:o.decode(r.buffer,r.ts);break;case C:i.decode(r.buffer,r.ts);break;case P:n.close();break;case A: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 000000000..89c8185a7 Binary files /dev/null and b/web_src/static/js/jessibuca/decoder.wasm differ 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 000000000..5ecdb9e77 --- /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)` + */ + isFullResize?: 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?: string; + /** + * 背景图片 + */ + 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 000000000..628cf3509 --- /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 s=0,a=1,n="flv",A="m7s",d="mp4",c="webm",l={videoBuffer:1e3,videoBufferDelay:1e3,isResize:!0,isFullResize:!1,isFlv:!1,debug:!1,hotKey:!1,loadingTimeout:10,heartTimeout:5,timeout:10,loadingTimeoutReplay:!0,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:a,demuxType:n,useWCS:!1,wcsUseVideoRender:!0,useMSE:!1,useOffscreen:!1,autoWasm:!0,wasmDecodeErrorReplay:!0,openWebglAlignment:!1,wasmDecodeAudioSyncVideo:!1,recordType:c,useWebFullScreen:!1},u="init",h="initVideo",p="render",m="playAudio",g="initAudio",f="audioCode",b="videoCode",y="wasmError",v="Invalid NAL unit size",w=1,S=2,E=8,B=9,C="init",R="decode",k="audioDecode",T="close",I="updateConfig",x={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",mseSourceBufferFull:"mseSourceBufferFull",videoWaiting:"videoWaiting",videoTimeUpdate:"videoTimeUpdate",videoSyncAudio:"videoSyncAudio",playToRenderTimes:"playToRenderTimes"},D={load:x.load,timeUpdate:x.timeUpdate,videoInfo:x.videoInfo,audioInfo:x.audioInfo,error:x.error,kBps:x.kBps,log:x.log,start:x.start,timeout:x.timeout,loadingTimeout:x.loadingTimeout,delayTimeout:x.delayTimeout,fullscreen:"fullscreen",webFullscreen:x.webFullscreen,play:x.play,pause:x.pause,mute:x.mute,stats:x.stats,performance:x.performance,recordingTimestamp:x.recordingTimestamp,recordStart:x.recordStart,recordEnd:x.recordEnd,playToRenderTimes:x.playToRenderTimes},j={playError:"playIsNotPauseOrUrlIsNull",fetchError:"fetchError",websocketError:"websocketError",webcodecsH265NotSupport:"webcodecsH265NotSupport",webcodecsDecodeError:"webcodecsDecodeError",webcodecsWidthOrHeightChange:"webcodecsWidthOrHeightChange",mediaSourceH265NotSupport:"mediaSourceH265NotSupport",mediaSourceFull:x.mseSourceBufferFull,mseSourceBufferError:x.mseSourceBufferError,mediaSourceAppendBufferError:"mediaSourceAppendBufferError",mediaSourceBufferListLarge:"mediaSourceBufferListLarge",mediaSourceAppendBufferEndTimeout:"mediaSourceAppendBufferEndTimeout",wasmDecodeError:"wasmDecodeError",webglAlignmentError:"webglAlignmentError"},L="notConnect",F="open",O="close",V="error",M={download:"download",base64:"base64",blob:"blob"},U={7:"H264(AVC)",12:"H265(HEVC)"},Q=12,W={10:"AAC",7:"ALAW",8:"MULAW"},J=38,P=0,G=1,N=2,z="webcodecs",H="webgl",Y="offscreen",X="key",q="delta",Z='video/mp4; codecs="avc1.64002A"',K="ended",_="open",$="closed",ee=1e3,te=27,ie=38,oe=40,re="A key frame is required after configure() or flush()",se="The user aborted a request",ae="AbortError",ne="AbortError";class Ae{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?t-1:0),o=1;o3&&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 ce=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,s={};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,s=new Uint8Array(r);for(;r--;)s[r]=i.charCodeAt(r);return new File([s],"file",{type:o})}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(),s=r-i;s>=1e3&&(e(t/s*1e3),i=r,t=0)}}function ye(){return/iphone|ipod|android.*mobile|windows.*phone|blackberry.*mobile/i.test(window.navigator.userAgent.toLowerCase())}function ve(e){if(null==e||""===e||0===parseInt(e)||isNaN(parseInt(e)))return"0KB/s";let t=parseFloat(e);return t=t.toFixed(2),t+"KB/s"}function we(e){return null==e}function Se(e){return!we(e)}function Ee(e){const t=e||window.event;return t.target||t.srcElement}ce.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 s=arguments.length,a=new Array(s),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=200&&t.status<=299}function Ie(e){try{e.dispatchEvent(new MouseEvent("click"))}catch(i){var t=document.createEvent("MouseEvents");t.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),e.dispatchEvent(t)}}var xe=Re.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),De="object"!=typeof window||window!==Re?function(){}:"download"in HTMLAnchorElement.prototype&&!xe?function(e,t,i){var o=Re.URL||Re.webkitURL,r=document.createElementNS("http://www.w3.org/1999/xhtml","a");t=t||e.name||"download",r.download=t,r.rel="noopener","string"==typeof e?(r.href=e,r.origin!==location.origin?Te(r.href)?ke(e,t,i):Ie(r,r.target="_blank"):Ie(r)):(r.href=o.createObjectURL(e),setTimeout((function(){o.revokeObjectURL(r.href)}),4e4),setTimeout((function(){Ie(r)}),0))}:"msSaveOrOpenBlob"in navigator?function(e,t,i){if(t=t||e.name||"download","string"==typeof e)if(Te(e))ke(e,t,i);else{var o=document.createElement("a");o.href=e,o.target="_blank",setTimeout((function(){Ie(o)}))}else navigator.msSaveOrOpenBlob(function(e,t){return void 0===t?t={autoBom:!1}:"object"!=typeof t&&(console.warn("Deprecated: Expected third argument to be a object"),t={autoBom:!t}),t.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)?new Blob([String.fromCharCode(65279),e],{type:e.type}):e}(e,i),t)}:function(e,t,i,o){if((o=o||open("","_blank"))&&(o.document.title=o.document.body.innerText="downloading..."),"string"==typeof e)return ke(e,t,i);var r="application/octet-stream"===e.type,s=/constructor/i.test(Re.HTMLElement)||Re.safari,a=/CriOS\/[\d]+/.test(navigator.userAgent);if((a||r&&s||xe)&&"undefined"!=typeof FileReader){var n=new FileReader;n.onloadend=function(){var e=n.result;e=a?e:e.replace(/^data:[^;]*;/,"data:attachment/file;"),o?o.location.href=e:location=e,o=null},n.readAsDataURL(e)}else{var A=Re.URL||Re.webkitURL,d=A.createObjectURL(e);o?o.location=d:location.href=d,o=null,setTimeout((function(){A.revokeObjectURL(d)}),4e4)}};class je extends Ce{constructor(e){super(),this.player=e;const t=document.createElement("canvas");t.style.position="absolute",t.style.top=0,t.style.left=0,this.$videoElement=t,e.$container.appendChild(this.$videoElement),this.context2D=null,this.contextGl=null,this.contextGlRender=null,this.contextGlDestroy=null,this.bitmaprenderer=null,this.renderType=null,this.videoInfo={width:"",height:"",encType:""},this._initCanvasRender(),this.player.debug.log("CanvasVideo","init")}destroy(){super.destroy(),this.contextGl&&(this.contextGl=null),this.context2D&&(this.context2D=null),this.contextGlRender&&(this.contextGlDestroy&&this.contextGlDestroy(),this.contextGlDestroy=null,this.contextGlRender=null),this.bitmaprenderer&&(this.bitmaprenderer=null),this.renderType=null,this.player.debug.log("CanvasVideoLoader","destroy")}_initContextGl(){this.contextGl=function(e){let t=null;const i=["webgl","experimental-webgl","moz-webgl","webkit-3d"];let o=0;for(;!t&&o{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 s=e.createShader(e.FRAGMENT_SHADER);e.shaderSource(s,o),e.compileShader(s),e.getShaderParameter(s,e.COMPILE_STATUS)||console.log("Fragment shader failed to compile: "+e.getShaderInfoLog(s));var a=e.createProgram();e.attachShader(a,r),e.attachShader(a,s),e.linkProgram(a),e.getProgramParameter(a,e.LINK_STATUS)||console.log("Program failed to compile: "+e.getProgramInfoLog(a)),e.useProgram(a);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(a,"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(a,"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(a,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,s){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,s),e.drawArrays(e.TRIANGLE_STRIP,0,4)},destroy:function(){try{e.deleteProgram(a),e.deleteBuffer(n),e.deleteBuffer(d),e.deleteTexture(u),e.deleteTexture(h),e.deleteTexture(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=z,this._initContext2D()):this._supportOffscreen()?(this.renderType=Y,this._bindOffscreen()):(this.renderType=H,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 Y:this.bitmaprenderer.transferFromImageBitmap(e.buffer);break;case H:this.contextGlRender(this.$videoElement.width,this.$videoElement.height,e.output[0],e.output[1],e.output[2]);break;case z:this.context2D.drawImage(e.videoFrame,0,0,this.$videoElement.width,this.$videoElement.height),(t=e.videoFrame).close?t.close():t.destroy&&t.destroy()}var t}screenshot(e,t,i,o){e=e||he(),o=o||M.download;const r={png:"image/png",jpeg:"image/jpeg",webp:"image/webp"};let s=.92;!r[t]&&M[t]&&(o=t,t="png",i=void 0),"string"==typeof i&&(o=i,i=void 0),void 0!==i&&(s=Number(i));const a=this.$videoElement.toDataURL(r[t]||r.png,s);if(o===M.base64)return a;{const t=ue(a);if(o===M.blob)return t;o===M.download&&De(t,e)}}clearView(){switch(this.renderType){case Y:(function(e,t){const i=document.createElement("canvas");return i.width=e,i.height=t,window.createImageBitmap(i,0,0,e,t)})(this.$videoElement.width,this.$videoElement.height).then((e=>{this.bitmaprenderer.transferFromImageBitmap(e)}));break;case H:this.contextGl.clear(this.contextGl.COLOR_BUFFER_BIT);break;case z: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&&e.useWebFullScreen?t-=J:i-=J);let o=this.$videoElement.width,r=this.$videoElement.height;const s=e.rotate;let a=(t-o)/2,n=(i-r)/2;270!==s&&90!==s||(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+")";s&&(l+=" rotate("+s+"deg)"),this.$videoElement.style.transform=l,this.$videoElement.style.left=a+"px",this.$videoElement.style.top=n+"px"}}class Le extends Ce{constructor(e){super(),this.player=e;const t=document.createElement("video"),i=document.createElement("canvas");t.muted=!0,t.style.position="absolute",t.style.top=0,t.style.left=0,this._delayPlay=!1,e.$container.appendChild(t),this.videoInfo={width:"",height:"",encType:""};const o=this.player._opt;o.useWCS&&o.wcsUseVideoRender&&(this.trackGenerator=new MediaStreamTrackGenerator({kind:"video"}),t.srcObject=new MediaStream([this.trackGenerator]),this.vwriter=this.trackGenerator.writable.getWriter()),this.$videoElement=t,this.$canvasElement=i,this.canvasContext=i.getContext("2d"),this.fixChromeVideoFlashBug(),this.resize();const{proxy:r}=this.player.events;r(this.$videoElement,"canplay",(()=>{this.player.debug.log("Video","canplay"),this._delayPlay&&this._play()})),r(this.$videoElement,"waiting",(()=>{this.player.emit(x.videoWaiting)})),r(this.$videoElement,"timeupdate",(e=>{const t=parseInt(e.timeStamp,10);this.player.emit(x.timeUpdate,t),this.isPlaying()||this.$videoElement.play()})),this.player.debug.log("Video","init")}destroy(){super.destroy(),this.$canvasElement=null,this.canvasContext=null,this.$videoElement&&(this.$videoElement.pause(),this.$videoElement.currentTime=0,this.$videoElement.src="",this.$videoElement.removeAttribute("src"),this.$videoElement=null),this.trackGenerator&&(this.trackGenerator.stop(),this.trackGenerator=null),this.vwriter&&(this.vwriter.close(),this.vwriter=null),this.player.debug.log("Video","destroy")}fixChromeVideoFlashBug(){const e=function(){const e=navigator.userAgent.toLowerCase(),t={},i={IE:window.ActiveXObject||"ActiveXObject"in window,Chrome:e.indexOf("chrome")>-1&&e.indexOf("safari")>-1,Firefox:e.indexOf("firefox")>-1,Opera:e.indexOf("opera")>-1,Safari:e.indexOf("safari")>-1&&-1==e.indexOf("chrome"),Edge:e.indexOf("edge")>-1,QQBrowser:/qqbrowser/.test(e),WeixinBrowser:/MicroMessenger/i.test(e)};for(let o in i)if(i[o]){let i="";if("IE"===o)i=e.match(/(msie\s|trident.*rv:)([\w.]+)/)[2];else if("Chrome"===o){for(let e in navigator.mimeTypes)"application/360softmgrplugin"===navigator.mimeTypes[e].type&&(o="360");i=e.match(/chrome\/([\d.]+)/)[1]}else"Firefox"===o?i=e.match(/firefox\/([\d.]+)/)[1]:"Opera"===o?i=e.match(/opera\/([\d.]+)/)[1]:"Safari"===o?i=e.match(/version\/([\d.]+)/)[1]:"Edge"===o?i=e.match(/edge\/([\d.]+)/)[1]:"QQBrowser"===o&&(i=e.match(/qqbrowser\/([\d.]+)/)[1]);t.type=o,t.version=parseInt(i)}return t}().type.toLowerCase();if("chrome"===e||"edge"===e){const e=this.player.$container;e.style.backdropFilter="blur(0px)",e.style.translateZ="0"}}play(){if(this.$videoElement){const e=this._getVideoReadyState();if(this.player.debug.log("Video",`play and readyState: ${e}`),0===e)return this.player.debug.warn("Video","readyState is 0 and set _delayPlay to true"),void(this._delayPlay=!0);this._play()}}_getVideoReadyState(){let e=0;return this.$videoElement&&(e=this.$videoElement.readyState),e}_play(){this.$videoElement&&this.$videoElement.play().then((()=>{this._delayPlay=!1,this.player.debug.log("Video","_play success"),setTimeout((()=>{this.isPlaying()||(this.player.debug.warn("Video","play failed and retry play"),this._play())}),100)})).catch((e=>{this.player.debug.error("Video","_play error",e)}))}pause(e){e?this.$videoElement&&this.$videoElement.pause():setTimeout((()=>{this.$videoElement&&this.$videoElement.pause()}),100)}clearView(){}screenshot(e,t,i,o){e=e||he(),o=o||M.download;const r={png:"image/png",jpeg:"image/jpeg",webp:"image/webp"};let s=.92;!r[t]&&M[t]&&(o=t,t="png",i=void 0),"string"==typeof i&&(o=i,i=void 0),void 0!==i&&(s=Number(i));const a=this.$videoElement;let n=this.$canvasElement;n.width=a.videoWidth,n.height=a.videoHeight,this.canvasContext.drawImage(a,0,0,n.width,n.height);const A=n.toDataURL(r[t]||r.png,s);if(this.canvasContext.clearRect(0,0,n.width,n.height),n.width=0,n.height=0,o===M.base64)return A;{const t=ue(A);if(o===M.blob)return t;o===M.download&&De(t,e)}}initCanvasViewSize(){this.resize()}render(e){this.vwriter&&this.vwriter.write(e.videoFrame)}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&&i.useWebFullScreen?e-=J:t-=J),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,s=(t-this.$videoElement.height)/2,a="contain";i.isResize||(a="fill"),i.isFullResize&&(a="none"),this.$videoElement.style.objectFit=a,this.$videoElement.style.transform="rotate("+o+"deg)",this.$videoElement.style.left=r+"px",this.$videoElement.style.top=s+"px"}isPlaying(){return this.$videoElement&&!this.$videoElement.paused}}class Fe{constructor(e){return new(Fe.getLoaderFactory(e._opt))(e)}static getLoaderFactory(e){return e.useMSE||e.useWCS&&!e.useOffscreen&&e.wcsUseVideoRender?Le:je}}class Oe 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(x.videoSyncAudio,(e=>{this.audioSyncVideoOption=e})),this.player.debug.log("AudioContext","init")}resetInit(){this.init=!1,this.audioInfo={encType:"",channels:"",sampleRate:""}}destroy(){this.closeAudio(),this.resetInit(),this.audioContext.close(),this.audioContext=null,this.gainNode=null,this.hasAudio=!1,this.playing=!1,this.scriptNode&&(this.scriptNode.onaudioprocess=le,this.scriptNode=null),this.audioBufferSourceNode=null,this.mediaStreamAudioDestinationNode=null,this.hasInitScriptNode=!1,this.audioSyncVideoOption={diff:null},this.off(),this.player.debug.log("AudioContext","destroy")}updateAudioInfo(e){e.encTypeCode&&(this.audioInfo.encType=W[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(x.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&&this.player._opt.wasmDecodeAudioSyncVideo){if(this.audioSyncVideoOption.diff>ee)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 Ve{constructor(e){return new(Ve.getLoaderFactory())(e)}static getLoaderFactory(){return Oe}}class Me extends Be{constructor(e){super(),this.player=e,this.playing=!1,this.abortController=new AbortController,this.streamRate=be((t=>{e.emit(x.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){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const{demux:i}=this.player;this.player._times.streamStart=he();const o=Object.assign({signal:this.abortController.signal},{headers:t.headers||{}});fetch(e,o).then((e=>{const t=e.body.getReader();this.emit(x.streamSuccess);const o=()=>{t.read().then((e=>{let{done:t,value:r}=e;t?i.close():(this.streamRate&&this.streamRate(r.byteLength),i.dispatch(r),o())})).catch((e=>{i.close();const t=e.toString();-1===t.indexOf(se)&&-1===t.indexOf(ae)&&e.name!==ne&&(this.abort(),this.emit(j.fetchError,e),this.player.emit(x.error,j.fetchError))}))};o()})).catch((e=>{"AbortError"!==e.name&&(i.close(),this.abort(),this.emit(j.fetchError,e),this.player.emit(x.error,j.fetchError))}))}abort(){this.abortController&&(this.abortController.abort(),this.abortController=null)}}class Ue extends Be{constructor(e){super(),this.player=e,this.socket=null,this.socketStatus=L,this.wsUrl=null,this.streamRate=be((t=>{e.emit(x.kBps,(t/1024).toFixed(2))})),e.debug.log("WebsocketLoader","init")}destroy(){this.socket&&(this.socket.close(),this.socket=null),this.socketStatus=L,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(x.streamSuccess),t.log("websocketLoader","socket open"),this.socketStatus=F})),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(x.streamEnd),this.socketStatus=O})),i(this.socket,"error",(e=>{t.log("websocketLoader","socket error"),this.emit(j.websocketError,e),this.player.emit(x.error,j.websocketError),this.socketStatus=V,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,t){this.player._times.streamStart=he(),this.wsUrl=e,this._createWebSocket()}}class Qe{constructor(e){return new(Qe.getLoaderFactory(e._opt.protocol))(e)}static getLoaderFactory(e){return e===a?Me:e===s?Ue:void 0}}var We=t((function(t){function i(e,t){if(!e)throw"First parameter is required.";t=new o(e,t=t||{type:"video"});var s=this;function a(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"===s.state)return s.resumeRecording(),void setTimeout((function(){n(e)}),1);"recording"===s.state||t.disableLogs||console.warn('Recording state should be: "recording", however current state is: ',s.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]&&(s[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(s,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(s,""):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 s=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);s.onmessage=function(t){e(t.data)},s.postMessage(o)}}function c(e){e=e||0,"paused"!==s.state?"stopped"!==s.state&&(e>=s.recordingDuration?n(s.onRecordingStopped):(e+=1e3,setTimeout((function(){c(e)}),1e3))):setTimeout((function(){c(e)}),1e3)}function u(e){s&&(s.state=e,"function"==typeof s.onStateChanged.call?s.onStateChanged.call(s,e):s.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: ",s.version),i&&(t=new o(e,i)),t.disableLogs||console.log("started recording "+t.type+" stream."),h?(h.clearRecordedData(),h.record(),u("recording"),s.recordingDuration&&c(),s):(a((function(){s.recordingDuration&&c()})),s)},stopRecording:n,pauseRecording:function(){h?"recording"===s.state?(u("paused"),h.pause(),t.disableLogs||console.log("Paused recording.")):t.disableLogs||console.warn("Unable to pause the recording. Recording state: ",s.state):m()},resumeRecording:function(){h?"paused"===s.state?(u("recording"),h.resume(),t.disableLogs||console.log("Resumed recording.")):t.disableLogs||console.warn("Unable to resume the recording. Recording state: ",s.state):m()},initRecorder:a,setRecordingDuration:function(e,t){if(void 0===e)throw"recordingDuration is required.";if("number"!=typeof e)throw"recordingDuration must be a number.";return s.recordingDuration=e,s.onRecordingStopped=t||function(){},{onRecordingStopped:function(e){s.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 s=document.createElement("a");s.href=l.createObjectURL(e),s.download=r,s.style="display:none;opacity:0;color:transparent;",(document.body||document.documentElement).appendChild(s),"function"==typeof s.click?s.click():(s.target="_blank",s.dispatchEvent(new MouseEvent("click",{view:window,bubbles:!0,cancelable:!0}))),l.revokeObjectURL(s.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,s=new FileReader;s.onload=function(e){o.decode(this.result).forEach((function(e){i.read(e)})),i.stop();var s=r.makeMetadataSeekable(i.metadatas,i.duration,i.cues),a=this.result.slice(i.metadataSize),n=new Blob([s,a],{type:"video/webm"});t(n)},s.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,s=[];function a(){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(){s=[],r=null,i.timestamps=[]}this.getArrayOfBlobs=function(){return s},this.record=function(){i.blob=null,i.clearRecordedData(),i.timestamps=[],d=[],s=[];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&&(s.push(e.data),a(),"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?(a(),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(s,{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,s=[],a=[],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),s=e.rightBuffers.slice(0),a=e.sampleRate,n=e.internalInterleavedLength,A=e.desiredSampRate;function d(e,t,i){var o=Math.round(e.length*(t/i)),r=[],s=Number((e.length-1)/(o-1));r[0]=e[0];for(var a=1;a96e3)&&(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(){s=[],a=[],A=0,E=!1,n=!1,y=!1,p=null,r.leftchannel=s,r.rightchannel=a,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(s.push(c),2===d){var l=e.inputBuffer.getChannelData(1),h=new Float32Array(l);a.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=s,this.rightchannel=a,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,s,a=!(!window.webkitRTCPeerConnection&&!window.webkitGetUserMedia||!window.chrome),n=50,A=navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);if(a&&A&&A[2]&&(n=parseInt(A[2],10)),a&&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(s=!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 a=new f;a.addTrack(w(e,"video")[0]),e=a}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 s=e.image.toDataURL("image/webp",1);h.frames[i].image=s})),t.disableLogs||console.log("Generating WebM"),i()}else i()},this.stop=function(e){s=!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=[],s=!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(s&&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}),s&&setTimeout(l,t.frameInterval)}})}this.pause=function(){d=!0,r instanceof C&&r.pause()},this.resume=function(){d=!1,r instanceof C?r.resume():s||this.record()},this.clearRecordedData=function(){s&&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?s?(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 s=document.createElement("canvas");s.width=c.width,s.height=c.height;var a,n,A,d=s.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=(a={length:h,functionToLoop:function(t,i){var o,r,s,a=function(){!v&&s-o<=s*y||(u&&(v=!0),l.push(e[i])),t()};if(v)a();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,s=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*s[0].width:s[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=s[0].height*n}else r.width=a.width||360,r.height=a.height||240;t&&t instanceof HTMLVideoElement&&u(t),s.forEach((function(e,t){u(e,t)})),setTimeout(l,a.frameInterval)}}function u(e,t){if(!o){var i=0,r=0,a=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&&(a=e.stream.width),void 0!==e.stream.height&&(n=e.stream.height),s.drawImage(e,i,r,a,n),"function"==typeof e.stream.onRender&&e.stream.onRender(s,i,r,a,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||a.width||360,i.height=e.height||a.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 s=a.audioContext.createMediaStreamSource(t);a.audioDestination=a.audioContext.createMediaStreamDestination(),s.connect(a.audioDestination),o.addTrack(a.audioDestination.stream.getTracks().filter((function(e){return"audio"===e.kind}))[0])}e.push(o)}))},this.releaseStreams=function(){i=[],o=!0,a.gainNode&&(a.gainNode.disconnect(),a.gainNode=null),a.audioSources.length&&(a.audioSources.forEach((function(e){e.disconnect()})),a.audioSources=[]),a.audioDestination&&(a.audioDestination.disconnect(),a.audioDestination=null),a.audioContext&&a.audioContext.close(),a.audioContext=null,s.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():a.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);a.audioContext=c.AudioContextConstructor,a.audioSources=[],!0===a.useGainNode&&(a.gainNode=a.audioContext.createGain(),a.gainNode.connect(a.audioContext.destination),a.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=a.audioContext.createMediaStreamSource(e);!0===a.useGainNode&&i.connect(a.gainNode),a.audioSources.push(i)}})),!t)return;return a.audioDestination=a.audioContext.createMediaStreamDestination(),a.audioSources.forEach((function(e){e.connect(a.audioDestination)})),a.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 F(e,t){var i,o,r;function s(){return new ReadableStream({start:function(o){var r=document.createElement("canvas"),s=document.createElement("video"),a=!0;s.srcObject=e,s.muted=!0,s.height=t.height,s.width=t.width,s.volume=0,s.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()),a&&(a=!1,t.onVideoProcessStarted&&t.onVideoProcessStarted()),e.drawImage(s,0,0),"closed"!==o._controlledReadableStream.state)try{o.enqueue(e.getImageData(0,0,t.width,t.height))}catch(e){}}),n)},s.play()}})}function a(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){a(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}),s().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,a(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=F}));class Je extends Be{constructor(e){super(),this.player=e,this.fileName="",this.fileType=e._opt.recordType||c,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,d!==t&&c!==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=We(e,t)}catch(t){e.error("Recorder","startRecord error",t),this.emit(x.recordCreateError)}this.recorder&&(this.isRecording=!0,this.player.emit(x.recording,!0),this.recorder.startRecording(),e.log("Recorder","start recording"),this.player.emit(x.recordStart),this.recordingInterval=window.setInterval((()=>{this.recordingTimestamp+=1,this.player.emit(x.recordingTimestamp,this.recordingTimestamp)}),1e3))}stopRecordAndSave(){this.recorder&&this.isRecording&&this.recorder.stopRecording((()=>{this.player.debug.log("Recorder","stop recording"),this.player.emit(x.recordEnd);const e=(this.fileName||he())+"."+(this.fileType||c);De(this.recorder.getBlob(),e),this._reset(),this.player.emit(x.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 Pe{constructor(e){return new(Pe.getLoaderFactory())(e)}static getLoaderFactory(){return Je}}class Ge{constructor(e){this.player=e,this.decoderWorker=new Worker(e._opt.decoder),this._initDecoderWorker(),e.debug.log("decoderWorker","init")}destroy(){this.decoderWorker.postMessage({cmd:T}),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 u:e.log("decoderWorker","onmessage:",u),this.player.loaded||this.player.emit(x.load),this.player.emit(x.decoderWorkerInit),this._initWork();break;case b:e.log("decoderWorker","onmessage:",b,i.code),this.player._times.decodeStart||(this.player._times.decodeStart=he()),this.player.video.updateVideoInfo({encTypeCode:i.code});break;case f:e.log("decoderWorker","onmessage:",f,i.code),this.player.audio&&this.player.audio.updateAudioInfo({encTypeCode:i.code});break;case h:if(e.log("decoderWorker","onmessage:",h,`width:${i.w},height:${i.h}`),this.player.video.updateVideoInfo({width:i.w,height:i.h}),!this.player._opt.openWebglAlignment&&i.w/2%4!=0)return void this.player.emit(j.webglAlignmentError);this.player.video.initCanvasViewSize();break;case g:e.log("decoderWorker","onmessage:",g,`channels:${i.channels},sampleRate:${i.sampleRate}`),this.player.audio&&(this.player.audio.updateAudioInfo(i),this.player.audio.initScriptNode(i));break;case p:this.player.handleRender(),this.player.video.render(i),this.player.emit(x.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 m:this.player.playing&&this.player.audio&&this.player.audio.play(i.buffer,i.ts);break;case y:i.message&&-1!==i.message.indexOf(v)&&(this.player.emit(x.error,j.wasmDecodeError),this.player.emit(j.wasmDecodeError));break;default:this.player[i.cmd]&&this.player[i.cmd](i)}}}_initWork(){const e={debug:this.player._opt.debug,useOffscreen:this.player._opt.useOffscreen,useWCS:this.player._opt.useWCS,videoBuffer:this.player._opt.videoBuffer,videoBufferDelay:this.player._opt.videoBufferDelay,openWebglAlignment:this.player._opt.openWebglAlignment};this.decoderWorker.postMessage({cmd:C,opt:JSON.stringify(e),sampleRate:this.player.audio&&this.player.audio.audioContext.sampleRate||0})}decodeVideo(e,t,i){const o={type:S,ts:Math.max(t,0),isIFrame:i};this.decoderWorker.postMessage({cmd:R,buffer:e,options:o},[e.buffer])}decodeAudio(e,t){this.player._opt.useWCS||this.player._opt.useMSE?this._decodeAudioNoDelay(e,t):this._decodeAudio(e,t)}_decodeAudio(e,t){const i={type:w,ts:Math.max(t,0)};this.decoderWorker.postMessage({cmd:R,buffer:e,options:i},[e.buffer])}_decodeAudioNoDelay(e,t){this.decoderWorker.postMessage({cmd:k,buffer:e,ts:Math.max(t,0)},[e.buffer])}updateWorkConfig(e){this.decoderWorker.postMessage({cmd:I,key:e.key,value:e.value})}}class Ne 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(),this.player.debug.log("CommonDemux","destroy")}getDelay(e){if(!e)return-1;if(this.firstTimestamp){if(e){const t=Date.now()-this.startTimestamp,i=e-this.firstTimestamp;this.delay=t>=i?t-i:i-t}}else this.firstTimestamp=e,this.startTimestamp=Date.now(),this.delay=-1;return this.delay}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,i=this.player._opt.videoBufferDelay;if(this.bufferList.length)if(this.dropping){for(e=this.bufferList.shift(),e.type===w&&0===e.payload[1]&&this._doDecoderDecode(e);!e.isIFrame&&this.bufferList.length;)e=this.bufferList.shift(),e.type===w&&0===e.payload[1]&&this._doDecoderDecode(e);e.isIFrame&&this.getDelay(e.ts)<=Math.min(t,200)&&(this.dropping=!1,this._doDecoderDecode(e))}else e=this.bufferList[0],-1===this.getDelay(e.ts)?(this.bufferList.shift(),this._doDecoderDecode(e)):this.delay>t+i?(this.resetDelay(),this.dropping=!0):(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,r){const s=this.player;let a={ts:i,cts:r,type:t,isIFrame:!1};s._opt.useWCS&&!s._opt.useOffscreen||s._opt.useMSE?(t===S&&(a.isIFrame=o),this.pushBuffer(e,a)):t===S?s.decoderWorker&&s.decoderWorker.decodeVideo(e,i,o):t===w&&s._opt.hasAudio&&s.decoderWorker&&s.decoderWorker.decodeAudio(e,i)}_doDecoderDecode(e){const t=this.player,{webcodecsDecoder:i,mseDecoder:o}=t;e.type===w?t._opt.hasAudio&&t.decoderWorker&&t.decoderWorker.decodeAudio(e.payload,e.ts):e.type===S&&(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,e.cts))}pushBuffer(e,t){t.type===w?this.bufferList.push({ts:t.ts,payload:e,type:w}):t.type===S&&this.bufferList.push({ts:t.ts,cts:t.cts,payload:e,type:S,isIFrame:t.isIFrame})}close(){}}class ze extends Ne{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 s=i[0];t[0]=e[10],t[1]=e[9],t[2]=e[8];let a=i[0];16777215===a&&(t[3]=e[11],a=i[0]);const n=yield s;switch(r){case E:o._opt.hasAudio&&(o.updateStats({abps:n.byteLength}),n.byteLength>0&&this._doDecode(n,w,a));break;case B:if(o._times.demuxStart||(o._times.demuxStart=he()),o._opt.hasVideo){o.updateStats({vbps:n.byteLength});const e=n[0]>>4==1;if(n.byteLength>0){i[0]=n[4],i[1]=n[3],i[2]=n[2],i[3]=0;let t=i[0];this._doDecode(n,S,a,e,t)}}}}}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 He extends Ne{constructor(e){super(e),e.debug.log("M7sDemux","init")}destroy(){super.destroy(),this.player.debug.log("M7sDemux","destroy"),this.player=null}dispatch(e){const t=this.player,i=new DataView(e),o=i.getUint8(0),r=i.getUint32(1,!1);switch(o){case w: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 S:if(t._opt.hasVideo)if(t._times.demuxStart||(t._times.demuxStart=he()),i.byteLength>5){const s=new Uint8Array(e,5),a=i.getUint8(5)>>4==1;t.updateStats({vbps:s.byteLength}),s.byteLength>0&&this._doDecode(s,o,r,a)}else this.player.debug.warn("M7sDemux","dispatch","dv byteLength is",i.byteLength)}}}class Ye{constructor(e){return new(Ye.getLoaderFactory(e._opt.demuxType))(e)}static getLoaderFactory(e){return e===A?He:e===n?ze:void 0}}class Xe{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 Xe(t);i.readByte();let o=i.readByte();i.readByte();let r=i.readByte();i.readUEG();let s=qe.getProfileString(o),a=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:s,level_string:a,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 s=0;s ${t.codecWidth}, height ${i.height}-> ${t.codecHeight}`),void this.player.emit(j.webcodecsWidthOrHeightChange)}if(!this.isDecodeFirstIIframe&&i&&(this.isDecodeFirstIIframe=!0),this.isDecodeFirstIIframe){const o=new EncodedVideoChunk({data:e.slice(5),timestamp:t,type:i?X:q});this.player.emit(x.timeUpdate,t);try{this.decoder.decode(o)}catch(e){this.player.debug.error("Webcodecs","VideoDecoder",e),-1!==e.toString().indexOf(re)&&this.player.emit(j.webcodecsDecodeError)}}else this.player.debug.warn("Webcodecs","VideoDecoder isDecodeFirstIIframe false")}else if(i&&0===e[1]){const t=15&e[0];if(this.player.video.updateVideoInfo({encTypeCode:t}),t===Q)return void this.emit(j.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 _e={play:"播放",pause:"暂停",audio:"",mute:"",screenshot:"截图",loading:"加载",fullscreen:"全屏",fullscreenExit:"退出全屏",record:"录制",recordStop:"停止录制"};var $e=Object.keys(_e).reduce(((e,t)=>(e[t]=`\n \n ${_e[t]?`${_e[t]}`:""}\n`,e)),{}),et=(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,s=e.height;i(o,"load",(()=>{i(o.contentDocument.defaultView,"resize",(()=>{e.width===r&&e.height===s||(r=e.width,s=e.height,e.emit(x.resize),n())}))})),e.$container.appendChild(o),e.on(x.destroy,(()=>{e.$container.removeChild(o)})),e.on(x.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(x.loading,(e=>{me(t.$loading,"display",e?"flex":"none"),me(t.$poster,"display","none"),e&&me(t.$playBig,"display","none")}));const a=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")},n=()=>{ye()&&t.$controls&&e._opt.useWebFullScreen&&setTimeout((()=>{if(e.fullscreen){let i=e.height/2-e.width+19,o=e.height/2-19;t.$controls.style.transform=`translateX(${-i}px) translateY(-${o}px) rotate(-90deg)`}else t.$controls.style.transform="translateX(0) translateY(0) rotate(0)"}),10)};try{ce.on("change",a),e.events.destroys.push((()=>{ce.off("change",a)}))}catch(e){}e.on(x.webFullscreen,(e=>{a(e),n()})),e.on(x.recording,(()=>{me(t.$record,"display",e.recording?"none":"flex"),me(t.$recordStop,"display",e.recording?"flex":"none"),me(t.$recording,"display",e.recording?"flex":"none")})),e.on(x.recordingTimestamp,(e=>{t.$recordingTime&&(t.$recordingTime.innerHTML=function(e){var t;if(e>-1){var i=Math.floor(e/3600),o=Math.floor(e/60)%60,r=e%60;t=i<10?"0"+i+":":i+":",o<10&&(t+="0"),t+=o+":",(r=Math.round(r))<10&&(t+="0"),t+=r.toFixed(0)}return t}(e))})),e.on(x.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.$qualityMenu,"display",e?"flex":"none"),me(t.$volume,"display",e?"flex":"none"),a(),e||t.$speed&&(t.$speed.innerHTML=ve(""))})),e.on(x.kBps,(e=>{const i=ve(e);t.$speed&&(t.$speed.innerHTML=i)}))};function tt(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))}}tt('@keyframes rotation{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(1turn)}}@keyframes magentaPulse{0%{background-color:#630030;-webkit-box-shadow:0 0 9px #333}50%{background-color:#a9014b;-webkit-box-shadow:0 0 18px #a9014b}to{background-color:#630030;-webkit-box-shadow:0 0 9px #333}}.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-recording{display:none;position:absolute;left:50%;top:0;padding:0 3px;transform:translateX(-50%);justify-content:space-around;align-items:center;width:95px;height:20px;background:#000;opacity:1;border-radius:0 0 8px 8px;z-index:1}.jessibuca-container .jessibuca-recording .jessibuca-recording-red-point{width:8px;height:8px;background:#ff1f1f;border-radius:50%;animation:magentaPulse 1s linear infinite}.jessibuca-container .jessibuca-recording .jessibuca-recording-time{font-size:14px;font-weight:500;color:#ddd}.jessibuca-container .jessibuca-recording .jessibuca-icon-recordStop{width:16px;height:16px;cursor:pointer}.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;width:100%;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}.jessibuca-container.jessibuca-fullscreen-web .jessibuca-play-big:after{transform:translate(-50%,-50%) rotate(270deg)}.jessibuca-container.jessibuca-fullscreen-web .jessibuca-loading{flex-direction:row}.jessibuca-container.jessibuca-fullscreen-web .jessibuca-loading-text{transform:rotate(270deg)}');class it{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 ${$e.loading}\n ${i.loadingText?`
    ${i.loadingText}
    `:""}\n
    \n ${i.hasControl&&o.play?'
    ':""}\n ${i.hasControl?`\n
    \n
    \n
    00:00:01
    \n
    ${$e.recordStop}
    \n
    \n `:""}\n ${i.hasControl?`\n
    \n
    \n
    \n ${i.showBandwidth?'
    ':""}\n
    \n
    \n ${o.audio?`\n
    \n ${$e.audio}\n ${$e.mute}\n
    \n
    \n
    \n
    \n
    \n
    \n
    \n `:""}\n ${o.play?`
    ${$e.play}
    ${$e.pause}
    `:""}\n ${o.screenshot?`
    ${$e.screenshot}
    `:""}\n ${o.record?`
    ${$e.record}
    ${$e.recordStop}
    `:""}\n ${o.fullscreen?`
    ${$e.fullscreen}
    ${$e.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,"$recording",{value:e.$container.querySelector(".jessibuca-recording")}),Object.defineProperty(t,"$recordingTime",{value:e.$container.querySelector(".jessibuca-recording-time")}),Object.defineProperty(t,"$recordingStop",{value:e.$container.querySelector(".jessibuca-recording-stop")}),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),t=this,Object.defineProperty(t,"controlsRect",{get:()=>t.$controls.getBoundingClientRect()}),et(e,this),((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)}if(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(),e.resumeAudioAfterPause()})),i(t.$playBig,"click",(t=>{e.play(),e.resumeAudioAfterPause()})),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");const o=e.volume;e.volume=0,e._lastVolume=o})),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.$recordingStop,"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"),r())})),i(e.$container,"mousemove",(()=>{e.$container&&t.$controls&&(e.fullscreen,"none"===t.$controls.style.display&&(me(t.$controls,"display","block"),r()))})),i(e.$container,"mouseout",(()=>{s(),me(t.$controls,"display","none")}));let o=null;const r=()=>{s(),o=setTimeout((()=>{me(t.$controls,"display","none")}),5e3)},s=()=>{o&&(clearTimeout(o),o=null)}}})(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(te,(()=>{e.fullscreen&&(e.fullscreen=!1)})),r(ie,(()=>{e.volume+=.05})),r(oe,(()=>{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.$recording&&this.player.$container.removeChild(this.$recording),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`}}}tt(".jessibuca-container{position:relative;display:block;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 ot{static init(){ot.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 ot.types)ot.types.hasOwnProperty(e)&&(ot.types[e]=[e.charCodeAt(0),e.charCodeAt(1),e.charCodeAt(2),e.charCodeAt(3)]);let e=ot.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 s=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 ot.box(ot.types.trak,ot.tkhd(e),ot.mdia(e))}static tkhd(e){let t=e.id,i=e.duration,o=e.presentWidth,r=e.presentHeight;return ot.box(ot.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 ot.box(ot.types.mdia,ot.mdhd(e),ot.hdlr(e),ot.minf(e))}static mdhd(e){let t=e.timescale,i=e.duration;return ot.box(ot.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?ot.constants.HDLR_AUDIO:ot.constants.HDLR_VIDEO,ot.box(ot.types.hdlr,t)}static minf(e){let t=null;return t="audio"===e.type?ot.box(ot.types.smhd,ot.constants.SMHD):ot.box(ot.types.vmhd,ot.constants.VMHD),ot.box(ot.types.minf,t,ot.dinf(),ot.stbl(e))}static dinf(){return ot.box(ot.types.dinf,ot.box(ot.types.dref,ot.constants.DREF))}static stbl(e){return ot.box(ot.types.stbl,ot.stsd(e),ot.box(ot.types.stts,ot.constants.STTS),ot.box(ot.types.stsc,ot.constants.STSC),ot.box(ot.types.stsz,ot.constants.STSZ),ot.box(ot.types.stco,ot.constants.STCO))}static stsd(e){return"audio"===e.type?ot.box(ot.types.stsd,ot.constants.STSD_PREFIX,ot.mp4a(e)):"avc"===e.videoType?ot.box(ot.types.stsd,ot.constants.STSD_PREFIX,ot.avc1(e)):ot.box(ot.types.stsd,ot.constants.STSD_PREFIX,ot.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 ot.box(ot.types.mp4a,o,ot.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 ot.box(ot.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 ot.box(ot.types.avc1,r,ot.box(ot.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 ot.box(ot.types.hvc1,r,ot.box(ot.types.hvcC,t))}static mvex(e){return ot.box(ot.types.mvex,ot.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 ot.box(ot.types.trex,i)}static moof(e,t){return ot.box(ot.types.moof,ot.mfhd(e.sequenceNumber),ot.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 ot.box(ot.types.mfhd,t)}static traf(e,t){let i=e.id,o=ot.box(ot.types.tfhd,new Uint8Array([0,0,0,0,i>>>24&255,i>>>16&255,i>>>8&255,255&i])),r=ot.box(ot.types.tfdt,new Uint8Array([0,0,0,0,t>>>24&255,t>>>16&255,t>>>8&255,255&t])),s=ot.sdtp(e),a=ot.trun(e,s.byteLength+16+16+8+16+8+8);return ot.box(ot.types.traf,o,r,a,s)}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,ot.box(ot.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,s=e.flags,a=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,s.isLeading<<2|s.dependsOn,s.isDependedOn<<6|s.hasRedundancy<<4|s.isNonSync,0,0,a>>>24&255,a>>>16&255,a>>>8&255,255&a],12),ot.box(ot.types.trun,i)}static mdat(e){return ot.box(ot.types.mdat,e)}}ot.init();class rt extends Be{constructor(e){super(),this.player=e,this.isAvc=!0,this.mediaSource=new window.MediaSource,this.sourceBuffer=null,this.hasInit=!1,this.isInitInfo=!1,this.cacheTrack={},this.timeInit=!1,this.sequenceNumber=0,this.mediaSourceOpen=!1,this.dropping=!1,this.firstRenderTime=null,this.mediaSourceAppendBufferError=!1,this.mediaSourceAppendBufferFull=!1,this.isDecodeFirstIIframe=!1,this.player.video.$videoElement.src=window.URL.createObjectURL(this.mediaSource);const{debug:t,events:{proxy:i}}=e;i(this.mediaSource,"sourceopen",(()=>{this.mediaSourceOpen=!0,this.player.emit(x.mseSourceOpen)})),i(this.mediaSource,"sourceclose",(()=>{this.player.emit(x.mseSourceClose)})),e.debug.log("MediaSource","init")}destroy(){this.stop(),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.mediaSourceAppendBufferError=!1,this.mediaSourceAppendBufferFull=!1,this.isDecodeFirstIIframe=!1,this.off(),this.player.debug.log("MediaSource","destroy")}get state(){return this.mediaSource&&this.mediaSource.readyState}get isStateOpen(){return this.state===_}get isStateClosed(){return this.state===$}get isStateEnded(){return this.state===K}get duration(){return this.mediaSource&&this.mediaSource.duration}set duration(e){this.mediaSource.duration=e}decodeVideo(e,t,i,o){const r=this.player;if(r)if(this.hasInit){if(i&&0===e[1]){let t=Ze(e.slice(5));const i=this.player.video.videoInfo;i&&i.width&&i.height&&t&&t.codecWidth&&t.codecHeight&&(t.codecWidth!==i.width||t.codecHeight!==i.height)&&(this.player.debug.warn("MediaSource",`width or height is update, width ${i.width}-> ${t.codecWidth}, height ${i.height}-> ${t.codecHeight}`),this.isInitInfo=!1,this.player.video.init=!1)}if(!this.isDecodeFirstIIframe&&i&&(this.isDecodeFirstIIframe=!0),this.isDecodeFirstIIframe){null===this.firstRenderTime&&(this.firstRenderTime=t);const r=t-this.firstRenderTime;this._decodeVideo(e,r,i,o)}else this.player.debug.warn("MediaSource","decodeVideo isDecodeFirstIIframe false")}else if(i&&0===e[1]){const o=15&e[0];if(r.video.updateVideoInfo({encTypeCode:o}),o===Q)return void this.emit(j.mediaSourceH265NotSupport);r._times.decodeStart||(r._times.decodeStart=he()),this._decodeConfigurationRecord(e,t,i,o),this.hasInit=!0}}_decodeConfigurationRecord(e,t,i,o){let r=e.slice(5),s={};s=Ze(r);const a={id:1,type:"video",timescale:1e3,duration:0,avcc:r,codecWidth:s.codecWidth,codecHeight:s.codecHeight,videoType:s.videoType},n=ot.generateInitSegment(a);this.isAvc=!0,this.appendBuffer(n.buffer),this.sequenceNumber=0,this.cacheTrack=null,this.timeInit=!1}_decodeVideo(e,t,i,o){const r=this.player;let s=e.slice(5),a=s.byteLength;const n=r.video.$videoElement,A=r._opt.videoBufferDelay;if(n.buffered.length>1&&(this.removeBuffer(n.buffered.start(0),n.buffered.end(0)),this.timeInit=!1),this.dropping&&t-this.cacheTrack.dts>A)this.dropping=!1,this.cacheTrack={};else if(this.cacheTrack&&t>=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(ot.types.mdat,4),i.set(this.cacheTrack.data,8),this.cacheTrack.duration=t-this.cacheTrack.dts;let o=ot.moof(this.cacheTrack,this.cacheTrack.dts),s=new Uint8Array(o.byteLength+i.byteLength);s.set(o,0),s.set(i,o.byteLength),this.appendBuffer(s.buffer),r.handleRender(),r.updateStats({fps:!0,ts:t,buf:r.demux&&r.demux.delay||0}),r._times.videoStart||(r._times.videoStart=he(),r.handlePlayToRenderTimes())}else r.debug.log("MediaSource","timeInit set false , cacheTrack = {}"),this.timeInit=!1,this.cacheTrack={};this.cacheTrack||(this.cacheTrack={}),this.cacheTrack.id=1,this.cacheTrack.sequenceNumber=++this.sequenceNumber,this.cacheTrack.size=a,this.cacheTrack.dts=t,this.cacheTrack.cts=o,this.cacheTrack.isKeyframe=i,this.cacheTrack.data=s,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||(r.debug.log("MediaSource","timeInit set true"),this.timeInit=!0,n.currentTime=n.buffered.end(0)),!this.isInitInfo&&n.videoWidth>0&&n.videoHeight>0&&(r.debug.log("MediaSource",`updateVideoInfo: ${n.videoWidth},${n.videoHeight}`),r.video.updateVideoInfo({width:n.videoWidth,height:n.videoHeight}),r.video.initCanvasViewSize(),this.isInitInfo=!0)}appendBuffer(e){const{debug:t,events:{proxy:i}}=this.player;if(null===this.sourceBuffer&&(this.sourceBuffer=this.mediaSource.addSourceBuffer(Z),i(this.sourceBuffer,"error",(e=>{this.player.emit(x.mseSourceBufferError,e)}))),this.mediaSourceAppendBufferError)t.error("MediaSource","this.mediaSourceAppendBufferError is true");else if(this.mediaSourceAppendBufferFull)t.error("MediaSource","this.mediaSourceAppendBufferFull is true");else if(!1===this.sourceBuffer.updating&&this.isStateOpen)try{this.sourceBuffer.appendBuffer(e)}catch(e){t.warn("MediaSource","this.sourceBuffer.appendBuffer()",e.code,e),22===e.code?(this.stop(),this.mediaSourceAppendBufferFull=!0,this.emit(j.mediaSourceFull)):11===e.code?(this.stop(),this.mediaSourceAppendBufferError=!0,this.emit(j.mediaSourceAppendBufferError)):(t.error("MediaSource","appendBuffer error",e),this.player.emit(x.mseSourceBufferError,e))}else this.isStateClosed?this.player.emit(j.mseSourceBufferError,"mediaSource is not attached to video or mediaSource is closed"):this.isStateEnded?this.player.emit(j.mseSourceBufferError,"mediaSource is closed"):!0===this.sourceBuffer.updating&&this.player.emit(x.mseSourceBufferBusy)}stop(){this.abortSourceBuffer(),this.removeSourceBuffer(),this.endOfStream()}dropSourceBuffer(e){const t=this.player.video.$videoElement;this.dropping=e,t.buffered.length>0&&t.buffered.end(0)-t.currentTime>1&&(this.player.debug.warn("MediaSource","dropSourceBuffer",`$video.buffered.end(0) is ${t.buffered.end(0)} - $video.currentTime ${t.currentTime}`),t.currentTime=t.buffered.end(0))}removeBuffer(e,t){if(this.isStateOpen&&!1===this.sourceBuffer.updating)try{this.sourceBuffer.remove(e,t)}catch(e){this.player.debug.warn("MediaSource","removeBuffer() error",e)}else this.player.debug.warn("MediaSource","removeBuffer() this.isStateOpen is",this.isStateOpen,"this.sourceBuffer.updating",this.sourceBuffer.updating)}endOfStream(){const e=this.player.video&&this.player.video.$videoElement;if(this.isStateOpen&&e&&e.readyState>=1)try{this.mediaSource.endOfStream()}catch(e){this.player.debug.warn("MediaSource","endOfStream() error",e)}}abortSourceBuffer(){this.isStateOpen&&this.sourceBuffer&&(this.sourceBuffer.abort(),this.sourceBuffer=null)}removeSourceBuffer(){if(!this.isStateClosed&&this.mediaSource&&this.sourceBuffer)try{this.mediaSource.removeSourceBuffer(this.sourceBuffer)}catch(e){this.player.debug.warn("MediaSource","removeSourceBuffer() error",e)}}}const st=()=>"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,at=()=>"wakeLock"in navigator;class nt{constructor(e){if(this.player=e,this.enabled=!1,at()){this._wakeLock=null;const e=()=>{null!==this._wakeLock&&"visible"===document.visibilityState&&this.enable()};document.addEventListener("visibilitychange",e),document.addEventListener("fullscreenchange",e)}else st()?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(at())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(st())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;at()?(this._wakeLock&&this._wakeLock.release(),this._wakeLock=null):st()?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 At extends Be{constructor(e,t){var i;super(),this.$container=e,this._opt=Object.assign({},l,t),this.debug=new Ae(this),this._opt.useWCS&&(this._opt.useWCS="VideoEncoder"in window),this._opt.useMSE&&(this._opt.useMSE=window.MediaSource&&window.MediaSource.isTypeSupported(Z)),this._opt.wcsUseVideoRender&&(this._opt.wcsUseVideoRender=window.MediaStreamTrackGenerator&&"function"==typeof window.MediaStreamTrackGenerator),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.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._checkStatsInterval=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 de(this),this.video=new Fe(this),this._opt.hasAudio&&(this.audio=new Ve(this)),this.recorder=new Pe(this),this._onlyMseOrWcsVideo()?this.loaded=!0:this.decoderWorker=new Ge(this),this.stream=null,this.demux=null,this._lastVolume=null,this._opt.useWCS&&(this.webcodecsDecoder=new Ke(this),this.loaded=!0),this._opt.useMSE&&(this.mseDecoder=new rt(this),this.loaded=!0),this.control=new it(this),ye()&&(this.keepScreenOn=new nt(this)),(e=>{try{const t=t=>{Ee(t)===e.$container&&(e.emit(D.fullscreen,e.fullscreen),e.fullscreen?e._opt.useMSE&&e.resize():e.resize())};ce.on("change",t),e.events.destroys.push((()=>{ce.off("change",t)}))}catch(e){}if(e.on(x.decoderWorkerInit,(()=>{e.debug.log("player","has loaded"),e.loaded=!0})),e.on(x.play,(()=>{e.loading=!1})),e.on(x.fullscreen,(t=>{if(t)try{ce.request(e.$container).then((()=>{})).catch((t=>{ye()&&e._opt.useWebFullScreen&&(e.webFullscreen=!0)}))}catch(t){ye()&&e._opt.useWebFullScreen&&(e.webFullscreen=!0)}else try{ce.exit().then((()=>{e.webFullscreen&&(e.webFullscreen=!1)})).catch((()=>{e.webFullscreen=!1}))}catch(t){e.webFullscreen=!1}})),ye()&&e.on(x.webFullscreen,(t=>{t?e.$container.classList.add("jessibuca-fullscreen-web"):e.$container.classList.remove("jessibuca-fullscreen-web"),e.emit(D.fullscreen,e.fullscreen)})),e.on(x.resize,(()=>{e.video&&e.video.resize()})),e._opt.debug){const t=[x.timeUpdate];Object.keys(x).forEach((i=>{e.on(x[i],(o=>{t.includes(i)||e.debug.log("player events",x[i],o)}))})),Object.keys(j).forEach((t=>{e.on(j[t],(i=>{e.debug.log("player event error",j[t],i)}))}))}})(this),(e=>{const{_opt:t,debug:i,events:{proxy:o}}=e;t.supportDblclickFullscreen&&o(e.$container,"dblclick",(t=>{const i=Ee(t).nodeName.toLowerCase();"canvas"!==i&&"video"!==i||(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._lastVolume=null,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.clearStatsInterval(),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._opt.useWebFullScreen?(this.emit(x.webFullscreen,e),setTimeout((()=>{this.updateOption({rotate:e?270:0}),this.resize()}),10)):this.emit(x.fullscreen,e)}get fullscreen(){return ce.isFullscreen||this.webFullscreen}set webFullscreen(e){this.emit(x.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(x.playing,e),this.emit(x.volumechange,this.volume),e?this.emit(x.play):this.emit(x.pause))}get playing(){return this._playing}get volume(){return this.audio&&this.audio.volume||0}set volume(e){e!==this.volume&&(this.audio&&this.audio.setVolume(e),this._lastVolume=e)}get lastVolume(){return this._lastVolume}set loading(e){this.loading!==e&&(this._loading=e,this.emit(x.loading,this._loading))}get loading(){return this._loading}set recording(e){e?this.playing&&this.recorder&&this.recorder.startRecord():this.recorder&&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(x.videoSyncAudio,{audioTimestamp:this.audioTimestamp,videoTimestamp:this.videoTimestamp,diff:this.audioTimestamp-this.videoTimestamp}))}get videoTimestamp(){return this._videoTimestamp}get isDebug(){return!0===this._opt.debug}updateOption(e){this._opt=Object.assign({},this._opt,e)}init(){return new Promise(((e,t)=>{this.stream||(this.stream=new Qe(this)),this.audio||this._opt.hasAudio&&(this.audio=new Ve(this)),this.demux||(this.demux=new Ye(this)),this._opt.useWCS&&(this.webcodecsDecoder||(this.webcodecsDecoder=new Ke(this))),this._opt.useMSE&&(this.mseDecoder||(this.mseDecoder=new rt(this))),this.decoderWorker||this._onlyMseOrWcsVideo()?e():(this.decoderWorker=new Ge(this),this.once(x.decoderWorkerInit,(()=>{e()})))}))}play(e,t){return new Promise(((i,o)=>{if(!e&&!this._opt.url)return o();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(j.webcodecsH265NotSupport,(()=>{this.emit(j.webcodecsH265NotSupport),this._opt.autoWasm||this.emit(x.error,j.webcodecsH265NotSupport)})),this.mseDecoder&&(this.mseDecoder.once(j.mediaSourceH265NotSupport,(()=>{this.emit(j.mediaSourceH265NotSupport),this._opt.autoWasm||this.emit(x.error,j.mediaSourceH265NotSupport)})),this.mseDecoder.once(j.mediaSourceFull,(()=>{this.emit(j.mediaSourceFull)})),this.mseDecoder.once(j.mediaSourceAppendBufferError,(()=>{this.emit(j.mediaSourceAppendBufferError)})),this.mseDecoder.once(j.mediaSourceBufferListLarge,(()=>{this.emit(j.mediaSourceBufferListLarge)})),this.mseDecoder.once(j.mediaSourceAppendBufferEndTimeout,(()=>{this.emit(j.mediaSourceAppendBufferEndTimeout)}))),this.enableWakeLock(),this.stream.fetchStream(e,t),this.checkLoadingTimeout(),this.stream.once(j.fetchError,(e=>{o(e)})),this.stream.once(j.websocketError,(e=>{o(e)})),this.stream.once(x.streamEnd,(()=>{o()})),this.stream.once(j.hlsError,(e=>{o(e)})),this.stream.once(x.streamSuccess,(()=>{i(),this._times.streamResponse=he(),this.video.play(),this.checkStatsInterval()}))})).catch((e=>{o(e)}))}))}close(){return new Promise(((e,t)=>{this._close().then((()=>{this.video&&this.video.clearView(),e()}))}))}resumeAudioAfterPause(){this.lastVolume&&(this.volume=this.lastVolume)}_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.audio&&(this.audio.destroy(),this.audio=null),this.clearCheckHeartTimeout(),this.clearCheckLoadingTimeout(),this.clearStatsInterval(),this.playing=!1,this.loading=!1,this.recording=!1,this.video&&(this.video.resetInit(),this.video.pause(!0)),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(){return arguments.length>0&&void 0!==arguments[0]&&arguments[0]?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((()=>{if(this.playing){if(0!==this._stats.fps)return;this.pause().then((()=>{this.emit(x.timeout,x.delayTimeout),this.emit(x.delayTimeout)}))}}),1e3*this._opt.heartTimeout)}checkStatsInterval(){this._checkStatsInterval=setInterval((()=>{this.updateStats()}),1e3)}clearCheckHeartTimeout(){this._checkHeartTimeout&&(clearTimeout(this._checkHeartTimeout),this._checkHeartTimeout=null)}checkLoadingTimeout(){this._checkLoadingTimeout=setTimeout((()=>{this.playing||this.pause().then((()=>{this.emit(x.timeout,x.loadingTimeout),this.emit(x.loadingTimeout)}))}),1e3*this._opt.loadingTimeout)}clearCheckLoadingTimeout(){this._checkLoadingTimeout&&(clearTimeout(this._checkLoadingTimeout),this._checkLoadingTimeout=null)}clearStatsInterval(){this._checkStatsInterval&&(clearInterval(this._checkStatsInterval),this._checkStatsInterval=null)}handleRender(){this.loading&&(this.emit(x.start),this.loading=!1,this.clearCheckLoadingTimeout()),this.playing||(this.playing=!0),this.checkHeart()}updateStats(e){e=e||{},this._startBpsTime||(this._startBpsTime=he()),Se(e.ts)&&(this._stats.ts=e.ts),Se(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(x.stats,this._stats),this.emit(x.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&&this.keepScreenOn.enable()}releaseWakeLock(){this._opt.keepScreenOn&&this.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(x.playToRenderTimes,e)}getOption(){return this._opt}}class dt 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");if("CANVAS"===i.nodeName||"VIDEO"===i.nodeName)throw new Error(`Jessibuca container type can not be ${i.nodeName} type`);if(t.videoBuffer>=t.heartTimeout)throw new Error(`Jessibuca videoBuffer ${t.videoBuffer}s must be less than heartTimeout ${t.heartTimeout}s`);i.classList.add("jessibuca-container"),delete t.container,t.forceNoOffscreen=!0,ye()&&(t.controlAutoHide=!1),Se(t.videoBuffer)&&(t.videoBuffer=1e3*Number(t.videoBuffer)),Se(t.timeout)&&(we(t.loadingTimeout)&&(t.loadingTimeout=t.timeout),we(t.heartTimeout)&&(t.heartTimeout=t.timeout)),this._opt=t,this.$container=i,this._loadingTimeoutReplayTimes=0,this._heartTimeoutReplayTimes=0,this.events=new de(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 At(e,t),this.player.debug.log("jessibuca","_initPlayer",this.player.getOption()),this._bindEvents()}_resetPlayer(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.player.destroy(),this.player=null,this._opt=Object.assign(this._opt,e),this._opt.url="",this._initPlayer(this.$container,this._opt)}_bindEvents(){Object.keys(D).forEach((e=>{this.player.on(D[e],(t=>{this.emit(e,t)}))}))}setDebug(e){this.player.updateOption({debug:!!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 P:t.isFullResize=!1,t.isResize=!1;break;case G:t.isFullResize=!1,t.isResize=!0;break;case N:t.isFullResize=!0,t.isResize=!0}this.player.updateOption(t),this.resize()}pause(){return this.player.pause()}close(){return this._opt.url="",this._opt.playOptions={},this.player.close()}clearView(){this.player.video.clearView()}play(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return new Promise(((i,o)=>{if(!e&&!this._opt.url)return this.emit(x.error,j.playError),void o("play url is empty");e?this._opt.url?e===this._opt.url?this.player.playing?i():(this.clearView(),this.player.play(this._opt.url,this._opt.playOptions).then((()=>{i(),this.player.resumeAudioAfterPause()})).catch((e=>{this.player.debug.warn("jessibuca","pause -> play and play error",e),this.player.pause().then((()=>{o(e)}))}))):this.player.pause().then((()=>{this.clearView(),this._play(e,t).then((()=>{i()})).catch((e=>{this.player.debug.warn("jessibuca","this._play error",e),o(e)}))})).catch((e=>{this.player.debug.warn("jessibuca","this._opt.url is null and pause error",e),o(e)})):this._play(e,t).then((()=>{i()})).catch((e=>{this.player.debug.warn("jessibuca","this._play error",e),o(e)})):this.player.play(this._opt.url,this._opt.playOptions).then((()=>{i(),this.player.resumeAudioAfterPause()})).catch((e=>{this.player.debug.warn("jessibuca","url is null and play error",e),this.player.pause().then((()=>{o(e)}))}))}))}_play(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return new Promise(((i,o)=>{this._opt.url=e,this._opt.playOptions=t;const r=0===e.indexOf("http"),d=r?a:s,c=r||-1!==e.indexOf(".flv")||this._opt.isFlv?n:A;this.player.updateOption({protocol:d,demuxType:c}),this.player.once(j.webglAlignmentError,(()=>{this.pause().then((()=>{this.player.debug.log("Jessibuca","webglAlignmentError"),this._resetPlayer({openWebglAlignment:!0}),this.play(e).then((()=>{this.player.debug.log("Jessibuca","webglAlignmentError and play success")})).catch((()=>{this.player.debug.log("Jessibuca","webglAlignmentError and play error")}))}))})),this.player.once(j.mediaSourceH265NotSupport,(()=>{this.pause().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,t).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(j.webcodecsH265NotSupport,(()=>{this.pause().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,t).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(j.mediaSourceFull,(()=>{this.pause().then((()=>{this.player.debug.log("Jessibuca","media source full"),this._resetPlayer(),this.play(e).then((()=>{this.player.debug.log("Jessibuca","media source full and reset player and play success")})).catch((()=>{this.player.debug.warn("Jessibuca","media source full and reset player and play error")}))}))})),this.player.once(j.mediaSourceAppendBufferError,(()=>{this.pause().then((()=>{this.player.debug.log("Jessibuca","media source append buffer error"),this._resetPlayer(),this.play(e).then((()=>{this.player.debug.log("Jessibuca","media source append buffer error and reset player and play success")})).catch((()=>{this.player.debug.warn("Jessibuca","media source append buffer error and reset player and play error")}))}))})),this.player.once(j.mediaSourceBufferListLarge,(()=>{this.pause().then((()=>{this.player.debug.log("Jessibuca","media source buffer list large"),this._resetPlayer(),this.play(e).then((()=>{this.player.debug.log("Jessibuca","media source buffer list large and reset player and play success")})).catch((()=>{this.player.debug.warn("Jessibuca","media source buffer list large and reset player and play error")}))}))})),this.player.once(j.mediaSourceAppendBufferEndTimeout,(()=>{this.pause().then((()=>{this.player.debug.log("Jessibuca","media source append buffer end timeout"),this._resetPlayer(),this.play(e).then((()=>{this.player.debug.log("Jessibuca","media source append buffer end timeout and reset player and play success")})).catch((()=>{this.player.debug.warn("Jessibuca","media source append buffer end timeout and reset player and play error")}))}))})),this.player.once(j.mseSourceBufferError,(()=>{this.pause().then((()=>{this.player.debug.log("Jessibuca","mseSourceBufferError close success")}))})),this.player.once(j.webcodecsH265NotSupport,(()=>{this.pause().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.warn("Jessibuca","auto wasm [wcs-> wasm] reset player and play error")})))}))})),this.player.once(j.webcodecsWidthOrHeightChange,(()=>{this.pause().then((()=>{this.player.debug.log("Jessibuca","webcodecs Width Or Height Change reset player and play"),this._resetPlayer({useWCS:!0}),this.play(e).then((()=>{this.player.debug.log("Jessibuca","webcodecs Width Or Height Change reset player and play success")})).catch((()=>{this.player.debug.warn("Jessibuca","webcodecs Width Or Height Change reset player and play error")}))}))})),this.player.once(j.webcodecsDecodeError,(()=>{this.pause().then((()=>{this.player._opt.autoWasm&&(this.player.debug.log("Jessibuca","webcodecs decode error reset player and play"),this._resetPlayer({useWCS:!1}),this.play(e).then((()=>{this.player.debug.log("Jessibuca","webcodecs decode error reset player and play success")})).catch((()=>{this.player.debug.warn("Jessibuca","webcodecs decode error reset player and play error")})))}))})),this.player.once(j.wasmDecodeError,(()=>{this.player._opt.wasmDecodeErrorReplay&&this.pause().then((()=>{this.player.debug.log("Jessibuca","wasm decode error and reset player and play"),this._resetPlayer({useWCS:!1}),this.play(e,t).then((()=>{this.player.debug.log("Jessibuca","wasm decode error and reset player and play success")})).catch((()=>{this.player.debug.warn("Jessibuca","wasm decode error and reset player and play error")}))}))})),this.player.on(x.delayTimeout,(()=>{this.player._opt.heartTimeoutReplay&&(this._heartTimeoutReplayTimes{this._heartTimeoutReplayTimes=0})).catch((()=>{})))})),this.player.on(x.loadingTimeout,(()=>{this.player._opt.loadingTimeoutReplay&&(this._loadingTimeoutReplayTimes{this._loadingTimeoutReplayTimes=0})).catch((()=>{})))})),this.hasLoaded()?this.player.play(e,t).then((()=>{i()})).catch((e=>{this.player.debug.warn("Jessibuca","hasLoaded and play error",e),this.player.pause().then((()=>{o(e)}))})):this.player.once(x.decoderWorkerInit,(()=>{this.player.play(e,t).then((()=>{i()})).catch((e=>{this.player.debug.warn("Jessibuca","decoderWorkerInit and play error",e),this.player.pause().then((()=>{o(e)}))}))}))}))}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,180,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?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&&this.player.playing}isMute(){return!this.player.audio||this.player.audio.isMute}isRecording(){return this.player.recorder.recording}}return r(dt,"ERROR",j),r(dt,"TIMEOUT",{loadingTimeout:x.loadingTimeout,delayTimeout:x.delayTimeout}),window.Jessibuca=dt,dt})); diff --git a/web_src/static/libDecoder.wasm b/web_src/static/libDecoder.wasm new file mode 100644 index 000000000..a45028dac Binary files /dev/null and b/web_src/static/libDecoder.wasm differ diff --git a/web_src/static/logo.png b/web_src/static/logo.png new file mode 100644 index 000000000..c5da2d4b9 Binary files /dev/null and b/web_src/static/logo.png differ