Commit c84399ff67650c69c7206013f024c0e6a96acd82

Authored by 王鑫
1 parent c8f9899b

fix():

1、去掉语音对讲和轮播的提示框
2、历史录像列表添加通道复选框
src/main/java/com/genersoft/iot/vmp/jtt1078/publisher/Channel.java
... ... @@ -4,7 +4,6 @@ import com.genersoft.iot.vmp.jtt1078.codec.AudioCodec;
4 4 import com.genersoft.iot.vmp.jtt1078.entity.Media;
5 5 import com.genersoft.iot.vmp.jtt1078.entity.MediaEncoding;
6 6 import com.genersoft.iot.vmp.jtt1078.flv.FlvEncoder;
7   -// [恢复] 引用 FFmpeg 进程管理类
8 7 import com.genersoft.iot.vmp.jtt1078.subscriber.RTMPPublisher;
9 8 import com.genersoft.iot.vmp.jtt1078.subscriber.Subscriber;
10 9 import com.genersoft.iot.vmp.jtt1078.subscriber.VideoSubscriber;
... ... @@ -15,8 +14,11 @@ import org.apache.commons.lang3.StringUtils;
15 14 import org.slf4j.Logger;
16 15 import org.slf4j.LoggerFactory;
17 16  
  17 +import java.util.Arrays;
18 18 import java.util.Iterator;
19 19 import java.util.concurrent.ConcurrentLinkedQueue;
  20 +import java.util.concurrent.atomic.AtomicBoolean;
  21 +import java.util.concurrent.atomic.AtomicLong;
20 22  
21 23 public class Channel
22 24 {
... ... @@ -26,7 +28,6 @@ public class Channel
26 28  
27 29 // [恢复] FFmpeg 推流进程管理器
28 30 RTMPPublisher rtmpPublisher;
29   - // [删除] ZlmRtpPublisher rtpPublisher;
30 31  
31 32 String tag;
32 33 boolean publishing;
... ... @@ -35,6 +36,39 @@ public class Channel
35 36 FlvEncoder flvEncoder;
36 37 private long firstTimestamp = -1;
37 38  
  39 + // ========== 新增:视频参数检测和FFmpeg自动重启相关 ==========
  40 +
  41 + /** 上一次接收到的SPS哈希值,用于检测视频参数变化 */
  42 + private int lastSPSHash = 0;
  43 +
  44 + /** FFmpeg是否需要重启的标志 */
  45 + private AtomicBoolean ffmpegNeedRestart = new AtomicBoolean(false);
  46 +
  47 + /** 最后一次FFmpeg重启时间(毫秒),用于防止频繁重启 */
  48 + private AtomicLong lastRestartTime = new AtomicLong(0);
  49 +
  50 + /** 重启冷却时间(毫秒),3秒内不重复重启 */
  51 + private static final long RESTART_COOLDOWN_MS = 3000;
  52 +
  53 + /** 是否正在等待新的I帧(FFmpeg重启期间) */
  54 + private AtomicBoolean waitingForIFrame = new AtomicBoolean(false);
  55 +
  56 + /** 视频参数信息 */
  57 + private volatile VideoParamInfo currentVideoParam = new VideoParamInfo();
  58 +
  59 + /** 视频参数内部类 */
  60 + private static class VideoParamInfo {
  61 + int width = 0;
  62 + int height = 0;
  63 + int fps = 0;
  64 + String resolution = "unknown";
  65 +
  66 + @Override
  67 + public String toString() {
  68 + return String.format("%dx%d@%dfps [%s]", width, height, fps, resolution);
  69 + }
  70 + }
  71 +
38 72 public Channel(String tag)
39 73 {
40 74 this.tag = tag;
... ... @@ -71,12 +105,10 @@ public class Channel
71 105 if (audioCodec == null)
72 106 {
73 107 audioCodec = AudioCodec.getCodec(pt);
74   - logger.info("audio codec: {}", MediaEncoding.getEncoding(Media.Type.Audio, pt));
  108 + logger.info("[{}] audio codec: {}", tag, MediaEncoding.getEncoding(Media.Type.Audio, pt));
75 109 }
76 110 // 写入到内部广播,FFmpeg 通过 HTTP 拉取这个数据
77 111 broadcastAudio(timestamp, audioCodec.toPCM(data));
78   -
79   - // [删除] rtpPublisher.sendAudio(...)
80 112 }
81 113  
82 114 public void writeVideo(long sequence, long timeoffset, int payloadType, byte[] h264)
... ... @@ -91,6 +123,28 @@ public class Channel
91 123 if (nalu == null) break;
92 124 if (nalu.length < 4) continue;
93 125  
  126 + // ========== 新增:检测视频参数变化 ==========
  127 + int nalType = nalu[4] & 0x1F;
  128 + boolean isIDR = (nalType == 5); // IDR帧(I帧)
  129 +
  130 + if (nalType == 7) { // SPS (Sequence Parameter Set)
  131 + checkAndHandleSPSChange(nalu);
  132 + }
  133 +
  134 + // 如果正在等待I帧,跳过所有数据直到收到新的I帧
  135 + if (waitingForIFrame.get()) {
  136 + if (isIDR) {
  137 + logger.info("[{}] 收到新的I帧,停止等待,FFmpeg应已重启完成", tag);
  138 + waitingForIFrame.set(false);
  139 + // 重置FLV编码器
  140 + this.flvEncoder = new FlvEncoder(true, true);
  141 + firstTimestamp = timeoffset;
  142 + } else {
  143 + // 跳过非I帧数据
  144 + continue;
  145 + }
  146 + }
  147 +
94 148 // 1. 封装为 FLV Tag (必须)
95 149 // FFmpeg 通过 HTTP 读取这些 FLV Tag
96 150 byte[] flvTag = this.flvEncoder.write(nalu, (int) (timeoffset - firstTimestamp));
... ... @@ -102,6 +156,281 @@ public class Channel
102 156 }
103 157 }
104 158  
  159 + /**
  160 + * 检查SPS变化并处理FFmpeg重启
  161 + */
  162 + private synchronized void checkAndHandleSPSChange(byte[] nalu) {
  163 + int currentHash = Arrays.hashCode(nalu);
  164 +
  165 + if (lastSPSHash != 0 && currentHash != lastSPSHash) {
  166 + // SPS变化了,说明视频参数发生了变化
  167 + // 注意:即使解析出来的分辨率/帧率相同,SPS的其它变化(如profile、level、VUI参数等)也会导致解码问题
  168 + // 因此:只要SPS哈希变化,就重启FFmpeg
  169 +
  170 + // 解析新的视频参数(仅用于日志显示)
  171 + VideoParamInfo newParam = parseVideoParam(nalu);
  172 + VideoParamInfo oldParam = currentVideoParam;
  173 +
  174 + logger.info("[{}] ====== 检测到SPS变化,需要重启FFmpeg ======", tag);
  175 + logger.info("[{}] 旧参数: {}", tag, oldParam);
  176 + logger.info("[{}] 新参数: {}", tag, newParam);
  177 + logger.info("[{}] SPS哈希变化: {} -> {}", tag,
  178 + Integer.toHexString(lastSPSHash),
  179 + Integer.toHexString(currentHash));
  180 +
  181 + // 检查冷却时间
  182 + long now = System.currentTimeMillis();
  183 + long timeSinceLastRestart = now - lastRestartTime.get();
  184 +
  185 + if (timeSinceLastRestart < RESTART_COOLDOWN_MS) {
  186 + logger.warn("[{}] FFmpeg重启过于频繁(距上次{}ms),跳过本次重启",
  187 + tag, timeSinceLastRestart);
  188 + // 即使跳过重启,仍需更新SPS哈希
  189 + lastSPSHash = currentHash;
  190 + currentVideoParam = newParam;
  191 + return;
  192 + }
  193 +
  194 + logger.info("[{}] ====== 准备重启FFmpeg ======", tag);
  195 + logger.info("[{}] 原因: SPS参数变化(可能包含分辨率、帧率、profile、level等)", tag);
  196 + logger.info("[{}] 预计中断时间: 1-2秒", tag);
  197 +
  198 + // 标记需要重启
  199 + ffmpegNeedRestart.set(true);
  200 +
  201 + // 立即触发重启(异步执行,避免阻塞)
  202 + final String currentTag = tag;
  203 + Thread restartThread = new Thread(() -> {
  204 + try {
  205 + logger.info("[{}] SPS变化检测到,立即触发FFmpeg重启...", currentTag);
  206 + restartRtmpPublisher();
  207 + } catch (Exception e) {
  208 + logger.error("[{}] 触发FFmpeg重启失败: {}", currentTag, e.getMessage(), e);
  209 + }
  210 + }, "FFmpeg-Restart-" + tag);
  211 + restartThread.setDaemon(true);
  212 + restartThread.start();
  213 + }
  214 +
  215 + // 更新SPS哈希和视频参数
  216 + lastSPSHash = currentHash;
  217 + currentVideoParam = parseVideoParam(nalu);
  218 + }
  219 +
  220 + /**
  221 + * 解析H.264 SPS获取视频参数
  222 + */
  223 + private VideoParamInfo parseVideoParam(byte[] sps) {
  224 + VideoParamInfo info = new VideoParamInfo();
  225 + try {
  226 + // H.264 SPS解析
  227 + // SPS格式: [NAL头(1字节) + profile_idc(1) + constraints(1) + level_idc(1) + 5字节对齐 + sps数据]
  228 +
  229 + if (sps.length < 6) {
  230 + logger.warn("[{}] SPS数据长度不足,无法解析: {}", tag, sps.length);
  231 + return info;
  232 + }
  233 +
  234 + int profile_idc = sps[4] & 0xFF;
  235 + int constraint_flags = sps[5] & 0xFF;
  236 + int level_idc = sps[6] & 0xFF;
  237 +
  238 + // 查找SPS结束位置(开始于67或68)
  239 + int spsStart = -1;
  240 + for (int i = 4; i < Math.min(sps.length - 4, 10); i++) {
  241 + if ((sps[i] & 0x1F) == 7) {
  242 + spsStart = i;
  243 + break;
  244 + }
  245 + }
  246 +
  247 + if (spsStart == -1) {
  248 + // 简化模式:直接使用SPS长度估算
  249 + int spsLen = (sps.length > 10) ? 4 : 1;
  250 + for (int i = 4; i < sps.length - 1; i++) {
  251 + if (sps[i] == 0 && sps[i+1] == 0 && sps[i+2] == 0 && sps[i+3] == 1) {
  252 + spsLen = i - 4;
  253 + break;
  254 + }
  255 + }
  256 +
  257 + // 根据SPS长度估算分辨率(非常粗略)
  258 + int lenCategory = spsLen / 10;
  259 + if (spsLen < 20) {
  260 + info.width = 352;
  261 + info.height = 288;
  262 + info.fps = 15;
  263 + } else if (spsLen < 40) {
  264 + info.width = 704;
  265 + info.height = 576;
  266 + info.fps = 25;
  267 + } else {
  268 + info.width = 1280;
  269 + info.height = 720;
  270 + info.fps = 25;
  271 + }
  272 + } else {
  273 + // 详细解析bitstream
  274 + // 这里简化处理,实际项目中可以使用完整的H.264解析库
  275 + info.width = 1280; // 默认值
  276 + info.height = 720;
  277 + info.fps = 25;
  278 + }
  279 +
  280 + // 根据SPS长度特征判断分辨率
  281 + // 这是一个简化的估算方法
  282 + int estimatedSize = sps.length;
  283 +
  284 + // 子码流特征: CIF (352x288) -> SPS约8-15字节
  285 + // 主码流特征: 720P (1280x720) -> SPS约16-30字节
  286 + // 1080P (1920x1080) -> SPS约30+字节
  287 + if (estimatedSize < 20) {
  288 + // CIF 或类似分辨率
  289 + if (estimatedSize < 12) {
  290 + info.width = 352;
  291 + info.height = 288;
  292 + info.fps = 15;
  293 + info.resolution = "CIF";
  294 + } else {
  295 + info.width = 704;
  296 + info.height = 576;
  297 + info.fps = 25;
  298 + info.resolution = "4CIF";
  299 + }
  300 + } else if (estimatedSize < 35) {
  301 + // 720P 或类似
  302 + info.width = 1280;
  303 + info.height = 720;
  304 + info.fps = 25;
  305 + info.resolution = "720P";
  306 + } else {
  307 + // 1080P 或更高
  308 + info.width = 1920;
  309 + info.height = 1080;
  310 + info.fps = 30;
  311 + info.resolution = "1080P";
  312 + }
  313 +
  314 + } catch (Exception e) {
  315 + logger.error("[{}] 解析SPS失败: {}", tag, e.getMessage());
  316 + }
  317 +
  318 + return info;
  319 + }
  320 +
  321 + /**
  322 + * 重启FFmpeg推流进程
  323 + */
  324 + public void restartRtmpPublisher() {
  325 + long now = System.currentTimeMillis();
  326 + lastRestartTime.set(now);
  327 +
  328 + logger.info("[{}] ====== 开始重启FFmpeg推流 ======", tag);
  329 + logger.info("[{}] 当前时间: {}", tag, now);
  330 +
  331 + try {
  332 + // 1. 标记等待I帧
  333 + waitingForIFrame.set(true);
  334 +
  335 + // 2. 关闭旧的FFmpeg进程
  336 + logger.info("[{}] 步骤1: 关闭旧FFmpeg进程...", tag);
  337 + if (rtmpPublisher != null) {
  338 + try {
  339 + rtmpPublisher.close();
  340 + } catch (Exception e) {
  341 + logger.warn("[{}] 关闭旧FFmpeg进程时出错: {}", tag, e.getMessage());
  342 + }
  343 + rtmpPublisher = null;
  344 + }
  345 +
  346 + // 3. 等待FFmpeg进程完全关闭
  347 + logger.info("[{}] 步骤2: 等待FFmpeg进程关闭...", tag);
  348 + Thread.sleep(500);
  349 +
  350 + // 4. 清空缓冲区,准备接收新的视频数据
  351 + logger.info("[{}] 步骤3: 清空视频缓冲区...", tag);
  352 + buffer.clear();
  353 + firstTimestamp = -1;
  354 +
  355 + // 5. 重置FLV编码器
  356 + logger.info("[{}] 步骤4: 重置FLV编码器...", tag);
  357 + flvEncoder = new FlvEncoder(true, true);
  358 +
  359 + // 6. 重新启动FFmpeg进程
  360 + logger.info("[{}] 步骤5: 启动新FFmpeg进程...", tag);
  361 + String rtmpUrl = Configs.get("rtmp.url");
  362 + if (StringUtils.isNotBlank(rtmpUrl)) {
  363 + rtmpPublisher = new RTMPPublisher(tag);
  364 + rtmpPublisher.start();
  365 + logger.info("[{}] 新FFmpeg进程已启动", tag);
  366 + } else {
  367 + logger.warn("[{}] 未配置rtmp.url,跳过启动", tag);
  368 + }
  369 +
  370 + // 7. 重置标志
  371 + ffmpegNeedRestart.set(false);
  372 +
  373 + logger.info("[{}] ====== FFmpeg重启完成 ======", tag);
  374 + logger.info("[{}] 请等待1-2秒让FFmpeg完成初始化...", tag);
  375 +
  376 + } catch (Exception e) {
  377 + logger.error("[{}] 重启FFmpeg失败: {}", tag, e.getMessage(), e);
  378 + waitingForIFrame.set(false);
  379 + ffmpegNeedRestart.set(false);
  380 + // 确保rtmpPublisher被清空
  381 + rtmpPublisher = null;
  382 + }
  383 + }
  384 +
  385 + /**
  386 + * 外部调用:主动触发码流切换(对应1078的9102指令)
  387 + * 调用此方法后,FFmpeg会在检测到视频参数变化时自动重启
  388 + */
  389 + public void notifyStreamSwitch() {
  390 + logger.info("[{}] ====== 收到码流切换通知 ======", tag);
  391 + logger.info("[{}] 即将切换码流,请等待设备响应...", tag);
  392 +
  393 + // 记录切换前的状态
  394 + VideoParamInfo beforeSwitch = currentVideoParam;
  395 + logger.info("[{}] 切换前视频参数: {}", tag, beforeSwitch);
  396 +
  397 + // 重置SPS哈希,这样一旦设备发来新的SPS就能立即检测到
  398 + lastSPSHash = 0;
  399 +
  400 + // 标记需要重启
  401 + ffmpegNeedRestart.set(true);
  402 +
  403 + // 启动一个线程来处理后续的重启逻辑
  404 + new Thread(() -> {
  405 + try {
  406 + // 等待设备切换并发送新的I帧
  407 + Thread.sleep(2000);
  408 +
  409 + // 如果还没有收到新的I帧,手动触发一次检查
  410 + if (waitingForIFrame.get()) {
  411 + logger.warn("[{}] 未在2秒内收到新I帧,检查是否需要手动重启...", tag);
  412 + // 这里不直接重启,而是等待下一个SPS自动触发
  413 + }
  414 + } catch (InterruptedException e) {
  415 + Thread.currentThread().interrupt();
  416 + }
  417 + }, "StreamSwitch-Watcher-" + tag).start();
  418 + }
  419 +
  420 + /**
  421 + * 获取当前视频参数(用于监控和调试)
  422 + */
  423 + public VideoParamInfo getCurrentVideoParam() {
  424 + return currentVideoParam;
  425 + }
  426 +
  427 + /**
  428 + * 获取FFmpeg是否需要重启
  429 + */
  430 + public boolean isFFmpegNeedRestart() {
  431 + return ffmpegNeedRestart.get();
  432 + }
  433 +
105 434 public void broadcastVideo(long timeoffset, byte[] flvTag)
106 435 {
107 436 for (Subscriber subscriber : subscribers)
... ... @@ -134,6 +463,8 @@ public class Channel
134 463  
135 464 public void close()
136 465 {
  466 + logger.info("[{}] 关闭Channel,开始清理资源...", tag);
  467 +
137 468 for (Iterator<Subscriber> itr = subscribers.iterator(); itr.hasNext(); )
138 469 {
139 470 Subscriber subscriber = itr.next();
... ... @@ -141,15 +472,17 @@ public class Channel
141 472 itr.remove();
142 473 }
143 474  
144   - // [恢复] 关闭 FFmpeg 进程
  475 + // 关闭 FFmpeg 进程
145 476 if (rtmpPublisher != null) {
  477 + logger.info("[{}] 关闭FFmpeg推流进程...", tag);
146 478 rtmpPublisher.close();
147 479 rtmpPublisher = null;
148 480 }
  481 +
  482 + logger.info("[{}] Channel已关闭", tag);
149 483 }
150 484  
151 485 // [恢复] 原版 readNalu (FFmpeg 偏好带 StartCode 的数据,或者 FlvEncoder 需要)
152   - // 之前为了 RTP 特意修改了切片逻辑,现在改回原版简单逻辑即可
153 486 private byte[] readNalu()
154 487 {
155 488 // 寻找 00 00 00 01
... ...
src/main/java/com/genersoft/iot/vmp/jtt1078/subscriber/RTMPPublisher.java
... ... @@ -10,6 +10,12 @@ import java.util.concurrent.TimeUnit;
10 10  
11 11 /**
12 12 * FFmpeg 推流器 (仅处理视频直播流)
  13 + *
  14 + * 修改记录:
  15 + * 1. 添加详细的启动和关闭日志,便于排查问题
  16 + * 2. 优化关闭逻辑,确保FFmpeg进程被正确终止
  17 + * 3. 添加FFmpeg输出日志监控
  18 + * 4. 兼容Java 8
13 19 */
14 20 public class RTMPPublisher extends Thread
15 21 {
... ... @@ -19,6 +25,18 @@ public class RTMPPublisher extends Thread
19 25 Process process = null;
20 26 private volatile boolean running = true;
21 27  
  28 + /** FFmpeg路径 */
  29 + private String ffmpegPath = null;
  30 +
  31 + /** 目标RTMP地址 */
  32 + private String rtmpUrl = null;
  33 +
  34 + /** FFmpeg命令格式标志 */
  35 + private String formatFlag = null;
  36 +
  37 + /** 进程启动时间 */
  38 + private long processStartTime = 0;
  39 +
22 40 public RTMPPublisher(String tag)
23 41 {
24 42 this.tag = tag;
... ... @@ -37,37 +55,45 @@ public class RTMPPublisher extends Thread
37 55  
38 56 try
39 57 {
  58 + // 获取FFmpeg配置
40 59 String sign = "41db35390ddad33f83944f44b8b75ded";
41   - String rtmpUrl = Configs.get("rtmp.url").replaceAll("\\{TAG\\}", tag).replaceAll("\\{sign\\}",sign);
  60 + rtmpUrl = Configs.get("rtmp.url").replaceAll("\\{TAG\\}", tag).replaceAll("\\{sign\\}",sign);
  61 + ffmpegPath = Configs.get("ffmpeg.path");
42 62  
43   - // 【修复】自动判断协议格式,避免硬编码 -f rtsp 导致 RTMP 推流失败
44   - String formatFlag = "";
  63 + // 自动判断协议格式
  64 + formatFlag = "";
45 65 if (rtmpUrl.startsWith("rtsp://")) {
46 66 formatFlag = "-f rtsp";
47 67 } else if (rtmpUrl.startsWith("rtmp://")) {
48 68 formatFlag = "-f flv";
49 69 }
50 70  
51   - // 低延迟:缩短 HTTP-FLV 探测与缓冲,尽快向 ZLM 输出(仍受设备 GOP/关键帧限制)
52   - String inputLowLatency =
53   - "-fflags +nobuffer+discardcorrupt -flags low_delay -probesize 32 -analyzeduration 0";
54   - String outputLowLatency = "-flush_packets 1";
55   -
56   - // 构造命令:只处理视频流和非对讲的音频流
57   - String cmd = String.format(
58   - "%s %s -i http://127.0.0.1:%d/video/%s -vcodec copy -acodec aac %s %s %s",
59   - Configs.get("ffmpeg.path"),
60   - inputLowLatency,
  71 + // 构造FFmpeg命令
  72 + String cmd = String.format("%s -i http://127.0.0.1:%d/video/%s -vcodec copy -acodec aac %s %s",
  73 + ffmpegPath,
61 74 Configs.getInt("server.http.port", 3333),
62 75 tag,
63   - outputLowLatency,
64 76 formatFlag,
65 77 rtmpUrl
66 78 );
67 79  
68   - logger.info("FFmpeg Push Started. Tag: {}, CMD: {}", tag, cmd);
  80 + logger.info("===========================================");
  81 + logger.info("[{}] ====== FFmpeg推流任务启动 ======", tag);
  82 + logger.info("[{}] FFmpeg路径: {}", tag, ffmpegPath);
  83 + logger.info("[{}] 目标地址: {}", tag, rtmpUrl);
  84 + logger.info("[{}] 完整命令: {}", tag, cmd);
  85 + logger.info("[{}] HTTP端口: {}", tag, Configs.getInt("server.http.port", 3333));
  86 + logger.info("[{}] 源流地址: http://127.0.0.1:{}/video/{}", tag, Configs.getInt("server.http.port", 3333), tag);
  87 + logger.info("[{}] 启动时间: {}", tag, new java.util.Date());
  88 + logger.info("===========================================");
69 89  
  90 + // 执行FFmpeg命令
70 91 process = Runtime.getRuntime().exec(cmd);
  92 + processStartTime = System.currentTimeMillis();
  93 +
  94 + // 记录进程启动信息(Java 8兼容方式)
  95 + logger.info("[{}] FFmpeg进程已启动", tag);
  96 +
71 97 stderr = process.getErrorStream();
72 98 stdout = process.getInputStream();
73 99  
... ... @@ -76,9 +102,15 @@ public class RTMPPublisher extends Thread
76 102 Thread stdoutConsumer = new Thread(() -> {
77 103 try {
78 104 byte[] buffer = new byte[512];
  105 + int count = 0;
79 106 while (running && finalStdout.read(buffer) > -1) {
80   - // 只消费,不输出
  107 + count++;
  108 + // 每1000次读取才打印一次(避免刷屏)
  109 + if (debugMode && count % 1000 == 0) {
  110 + logger.debug("[{}] FFmpeg stdout消费中... count: {}", tag, count);
  111 + }
81 112 }
  113 + logger.info("[{}] FFmpeg stdout消费结束, 共消费{}次", tag, count);
82 114 } catch (Exception e) {
83 115 // 忽略异常
84 116 }
... ... @@ -86,26 +118,60 @@ public class RTMPPublisher extends Thread
86 118 stdoutConsumer.setDaemon(true);
87 119 stdoutConsumer.start();
88 120  
89   - // 消费 stderr 日志流,防止阻塞
  121 + // 消费 stderr 日志流
  122 + StringBuilder errorLog = new StringBuilder();
  123 + int errorCount = 0;
90 124 while (running && (len = stderr.read(buff)) > -1)
91 125 {
92 126 if (debugMode) {
93 127 System.out.print(new String(buff, 0, len));
94 128 }
  129 +
  130 + // 收集错误日志(便于排查问题)
  131 + errorLog.append(new String(buff, 0, len));
  132 + errorCount++;
  133 +
  134 + // 每100条错误日志打印一次摘要
  135 + if (errorCount % 100 == 0) {
  136 + String lastError = errorLog.length() > 500
  137 + ? errorLog.substring(errorLog.length() - 500)
  138 + : errorLog.toString();
  139 + logger.debug("[{}] FFmpeg错误日志摘要: {}", tag, lastError);
  140 + }
95 141 }
96 142  
97 143 // 进程退出处理
98 144 int exitCode = process.waitFor();
99   - logger.warn("FFmpeg process exited. Code: {}, Tag: {}", exitCode, tag);
  145 + long runDuration = System.currentTimeMillis() - processStartTime;
  146 + logger.warn("===========================================");
  147 + logger.warn("[{}] ====== FFmpeg推流任务结束 ======", tag);
  148 + logger.warn("[{}] 退出代码: {}", tag, exitCode);
  149 + logger.warn("[{}] 运行时间: {} ms", tag, runDuration);
  150 + logger.warn("[{}] 错误日志条数: {}", tag, errorCount);
  151 +
  152 + // 分析退出原因
  153 + if (exitCode == 0) {
  154 + logger.info("[{}] FFmpeg正常退出", tag);
  155 + } else if (exitCode == -1 || exitCode == 255) {
  156 + logger.warn("[{}] FFmpeg被信号终止 (exitCode={})", tag, exitCode);
  157 + } else {
  158 + // 保存最后一段错误日志
  159 + String lastError = errorLog.length() > 1000
  160 + ? errorLog.substring(errorLog.length() - 1000)
  161 + : errorLog.toString();
  162 + logger.error("[{}] FFmpeg异常退出, 最后错误日志:\n{}", tag, lastError);
  163 + }
  164 + logger.warn("===========================================");
  165 +
100 166 }
101 167 catch(InterruptedException ex)
102 168 {
103   - logger.info("RTMPPublisher interrupted: {}", tag);
  169 + logger.info("[{}] RTMPPublisher被中断: {}", tag, ex.getMessage());
104 170 Thread.currentThread().interrupt();
105 171 }
106 172 catch(Exception ex)
107 173 {
108   - logger.error("RTMPPublisher Error: " + tag, ex);
  174 + logger.error("[{}] RTMPPublisher异常: {}", tag, ex);
109 175 }
110 176 finally
111 177 {
... ... @@ -117,40 +183,184 @@ public class RTMPPublisher extends Thread
117 183 closeQuietly(process.getOutputStream());
118 184 closeQuietly(process.getErrorStream());
119 185 }
  186 + logger.info("[{}] RTMPPublisher资源已释放", tag);
120 187 }
121 188 }
122 189  
  190 + /**
  191 + * 关闭FFmpeg推流
  192 + * 优化关闭逻辑,确保进程被正确终止(Java 8兼容)
  193 + */
123 194 public void close()
124 195 {
  196 + logger.info("[{}] ====== 开始关闭FFmpeg推流 ======", tag);
  197 + logger.info("[{}] 关闭请求时间: {}", tag, new java.util.Date());
  198 +
125 199 try {
126 200 // 设置停止标志
127 201 running = false;
128 202  
129 203 if (process != null) {
  204 + long runDuration = processStartTime > 0 ? System.currentTimeMillis() - processStartTime : 0;
  205 + logger.info("[{}] 正在终止FFmpeg进程... (已运行{}ms)", tag, runDuration);
  206 +
130 207 // 先尝试正常终止
131 208 process.destroy();
132 209  
133   - // 等待最多 3
  210 + // 等待最多3
134 211 boolean exited = process.waitFor(3, TimeUnit.SECONDS);
135 212  
136 213 if (!exited) {
137   - // 如果还没退出,强制终止
138   - logger.warn("FFmpeg process did not exit gracefully, forcing termination: {}", tag);
  214 + logger.warn("[{}] FFmpeg进程未能在3秒内正常退出,开始强制终止...", tag);
  215 +
  216 + // 强制终止(Java 8的方式)
139 217 process.destroyForcibly();
140   - process.waitFor(2, TimeUnit.SECONDS);
  218 +
  219 + // 再等待2秒
  220 + try {
  221 + exited = process.waitFor(2, TimeUnit.SECONDS);
  222 + } catch (InterruptedException e) {
  223 + Thread.currentThread().interrupt();
  224 + }
  225 + if (!exited) {
  226 + logger.error("[{}] FFmpeg进程强制终止失败,可能存在资源泄漏", tag);
  227 + } else {
  228 + logger.info("[{}] FFmpeg进程已强制终止", tag);
  229 + }
  230 + } else {
  231 + int exitCode = process.exitValue();
  232 + logger.info("[{}] FFmpeg进程已正常终止, 退出代码: {}", tag, exitCode);
141 233 }
142 234  
143   - logger.info("FFmpeg process terminated: {}", tag);
  235 + // 检查是否需要杀掉残留进程
  236 + checkAndKillOrphanedProcesses();
  237 +
  238 + } else {
  239 + logger.info("[{}] FFmpeg进程为空,无需关闭", tag);
144 240 }
145 241  
  242 + logger.info("[{}] ====== FFmpeg推流已关闭 ======", tag);
  243 +
146 244 // 中断线程(如果还在阻塞读取)
147 245 this.interrupt();
148 246  
149 247 // 等待线程结束
150 248 this.join(2000);
  249 + if (this.isAlive()) {
  250 + logger.warn("[{}] RTMPPublisher线程未能正常结束", tag);
  251 + }
151 252  
152 253 } catch(Exception e) {
153   - logger.error("Error closing RTMPPublisher: " + tag, e);
  254 + logger.error("[{}] 关闭RTMPPublisher时出错: {}", tag, e);
  255 + }
  256 + }
  257 +
  258 + /**
  259 + * 检查并杀掉可能残留的FFmpeg进程
  260 + * Java 8兼容实现,使用系统命令查找和终止进程
  261 + */
  262 + private void checkAndKillOrphanedProcesses() {
  263 + try {
  264 + logger.debug("[{}] 检查是否有FFmpeg残留进程...", tag);
  265 + // 判断操作系统类型
  266 + String os = System.getProperty("os.name").toLowerCase();
  267 + boolean isWindows = os.contains("win");
  268 +
  269 + if (isWindows) {
  270 + // Windows: 使用tasklist和taskkill
  271 + killOrphanedProcessesWindows();
  272 + } else {
  273 + // Linux/Unix: 使用ps和kill
  274 + killOrphanedProcessesUnix();
  275 + }
  276 + } catch (Exception e) {
  277 + logger.error("[{}] 检查残留进程时出错: {}", tag, e.getMessage());
  278 + }
  279 + }
  280 +
  281 + /**
  282 + * Windows环境下查找并终止残留的FFmpeg进程
  283 + */
  284 + private void killOrphanedProcessesWindows() {
  285 + try {
  286 + // 查找包含ffmpeg的进程
  287 + ProcessBuilder pb = new ProcessBuilder("tasklist", "/FI", "IMAGENAME eq ffmpeg.exe", "/FO", "CSV", "/NH");
  288 + Process process = pb.start();
  289 + java.io.BufferedReader reader = new java.io.BufferedReader(
  290 + new java.io.InputStreamReader(process.getInputStream()));
  291 +
  292 + String line;
  293 + while ((line = reader.readLine()) != null) {
  294 + // CSV格式: "Image Name","PID","Session Name","Session#","Mem Usage"
  295 + String[] parts = line.split(",");
  296 + if (parts.length > 1) {
  297 + String pidStr = parts[1].replaceAll("\"", "").trim();
  298 + try {
  299 + long pid = Long.parseLong(pidStr);
  300 + // 检查命令行是否包含当前tag或wvp-jt1078
  301 + ProcessBuilder cmdPb = new ProcessBuilder("wmic", "process", "where", "ProcessId=" + pid, "get", "CommandLine");
  302 + Process cmdProcess = cmdPb.start();
  303 + java.io.BufferedReader cmdReader = new java.io.BufferedReader(
  304 + new java.io.InputStreamReader(cmdProcess.getInputStream()));
  305 +
  306 + String cmdLine;
  307 + boolean shouldKill = false;
  308 + while ((cmdLine = cmdReader.readLine()) != null) {
  309 + if (cmdLine.toLowerCase().contains("ffmpeg")
  310 + && (cmdLine.contains(tag) || cmdLine.contains("wvp-jt1078"))) {
  311 + shouldKill = true;
  312 + break;
  313 + }
  314 + }
  315 +
  316 + if (shouldKill) {
  317 + logger.warn("[{}] 发现残留FFmpeg进程[PID:{}],正在终止...", tag, pid);
  318 + Process killProcess = Runtime.getRuntime().exec("taskkill /PID " + pid + " /F");
  319 + killProcess.waitFor();
  320 + logger.info("[{}] 残留FFmpeg进程已终止[PID:{}]", tag, pid);
  321 + }
  322 + } catch (NumberFormatException e) {
  323 + // 忽略非数字PID
  324 + }
  325 + }
  326 + }
  327 + } catch (Exception e) {
  328 + logger.error("[{}] Windows检查残留进程时出错: {}", tag, e.getMessage());
  329 + }
  330 + }
  331 +
  332 + /**
  333 + * Linux/Unix环境下查找并终止残留的FFmpeg进程
  334 + */
  335 + private void killOrphanedProcessesUnix() {
  336 + try {
  337 + // 查找包含ffmpeg的进程
  338 + ProcessBuilder pb = new ProcessBuilder("ps", "-ef");
  339 + Process process = pb.start();
  340 + java.io.BufferedReader reader = new java.io.BufferedReader(
  341 + new java.io.InputStreamReader(process.getInputStream()));
  342 +
  343 + String line;
  344 + while ((line = reader.readLine()) != null) {
  345 + // ps -ef 输出格式: UID PID PPID C STIME TTY TIME CMD
  346 + if (line.toLowerCase().contains("ffmpeg") && line.contains(tag)) {
  347 + String[] parts = line.split("\\s+");
  348 + if (parts.length > 1) {
  349 + String pidStr = parts[1];
  350 + try {
  351 + long pid = Long.parseLong(pidStr);
  352 + logger.warn("[{}] 发现残留FFmpeg进程[PID:{}],正在终止...", tag, pid);
  353 + Process killProcess = Runtime.getRuntime().exec("kill -9 " + pid);
  354 + killProcess.waitFor();
  355 + logger.info("[{}] 残留FFmpeg进程已终止[PID:{}]", tag, pid);
  356 + } catch (NumberFormatException e) {
  357 + // 忽略非数字PID
  358 + }
  359 + }
  360 + }
  361 + }
  362 + } catch (Exception e) {
  363 + logger.error("[{}] Unix检查残留进程时出错: {}", tag, e.getMessage());
154 364 }
155 365 }
156 366  
... ... @@ -166,4 +376,27 @@ public class RTMPPublisher extends Thread
166 376 }
167 377 }
168 378 }
  379 +
  380 + /**
  381 + * 获取推流状态
  382 + */
  383 + public boolean isRunning() {
  384 + return running && this.isAlive() && process != null;
  385 + }
  386 +
  387 + /**
  388 + * 获取FFmpeg进程信息(用于调试)
  389 + */
  390 + public String getProcessInfo() {
  391 + if (process == null) {
  392 + return "process is null";
  393 + }
  394 + try {
  395 + long runDuration = processStartTime > 0 ? System.currentTimeMillis() - processStartTime : 0;
  396 + return String.format("FFmpeg进程, 已运行%dms, alive=%s",
  397 + runDuration, process.isAlive());
  398 + } catch (Exception e) {
  399 + return "获取进程信息失败: " + e.getMessage();
  400 + }
  401 + }
169 402 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
1   -package com.genersoft.iot.vmp.media.zlm;
2   -
3   -import com.alibaba.fastjson2.JSON;
4   -import com.alibaba.fastjson2.JSONObject;
5   -import com.genersoft.iot.vmp.common.InviteInfo;
6   -import com.genersoft.iot.vmp.common.InviteSessionType;
7   -import com.genersoft.iot.vmp.common.StreamInfo;
8   -import com.genersoft.iot.vmp.common.VideoManagerConstants;
9   -import com.genersoft.iot.vmp.conf.UserSetting;
10   -import com.genersoft.iot.vmp.conf.exception.ServiceException;
11   -import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
12   -import com.genersoft.iot.vmp.gb28181.bean.*;
13   -import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
14   -import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
15   -import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
16   -import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
17   -import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
18   -import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
19   -import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
20   -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
21   -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
22   -import com.genersoft.iot.vmp.media.zlm.dto.HookType;
23   -import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
24   -import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
25   -import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
26   -import com.genersoft.iot.vmp.media.zlm.dto.hook.*;
27   -import com.genersoft.iot.vmp.service.*;
28   -import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
29   -import com.genersoft.iot.vmp.service.bean.SSRCInfo;
30   -import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
31   -import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
32   -import com.genersoft.iot.vmp.utils.DateUtil;
33   -import com.genersoft.iot.vmp.vmanager.bean.*;
34   -import com.genersoft.iot.vmp.vmanager.jt1078.platform.Jt1078OfCarController;
35   -import com.genersoft.iot.vmp.vmanager.jt1078.platform.service.IntercomService;
36   -import org.apache.commons.lang3.StringUtils;
37   -import org.slf4j.Logger;
38   -import org.slf4j.LoggerFactory;
39   -import org.springframework.beans.factory.annotation.Autowired;
40   -import org.springframework.beans.factory.annotation.Qualifier;
41   -import org.springframework.data.redis.core.RedisTemplate;
42   -import org.springframework.scheduling.annotation.Scheduled;
43   -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
44   -import org.springframework.util.ObjectUtils;
45   -import org.springframework.web.bind.annotation.*;
46   -import org.springframework.web.context.request.async.DeferredResult;
47   -
48   -import javax.annotation.Resource;
49   -import javax.servlet.http.HttpServletRequest;
50   -import javax.sip.InvalidArgumentException;
51   -import javax.sip.SipException;
52   -import java.text.ParseException;
53   -import java.util.*;
54   -import java.util.concurrent.TimeUnit;
55   -
56   -/**
57   - * @description:针对 ZLMediaServer的hook事件监听
58   - * @author: swwheihei
59   - * @date: 2020年5月8日 上午10:46:48
60   - */
61   -@RestController
62   -@RequestMapping("/index/hook")
63   -public class ZLMHttpHookListener {
64   -
65   - private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class);
66   -
67   - @Autowired
68   - private SIPCommander cmder;
69   -
70   - @Autowired
71   - private ISIPCommanderForPlatform commanderFroPlatform;
72   -
73   - @Autowired
74   - private AudioBroadcastManager audioBroadcastManager;
75   -
76   - @Autowired
77   - private IPlayService playService;
78   -
79   - @Autowired
80   - private IVideoManagerStorage storager;
81   -
82   - @Autowired
83   - private IRedisCatchStorage redisCatchStorage;
84   -
85   - @Autowired
86   - private IInviteStreamService inviteStreamService;
87   -
88   - @Autowired
89   - private IDeviceService deviceService;
90   -
91   - @Autowired
92   - private IMediaServerService mediaServerService;
93   -
94   - @Autowired
95   - private IStreamProxyService streamProxyService;
96   -
97   - @Autowired
98   - private DeferredResultHolder resultHolder;
99   -
100   - @Autowired
101   - private IMediaService mediaService;
102   -
103   - @Autowired
104   - private EventPublisher eventPublisher;
105   -
106   - @Autowired
107   - private ZLMMediaListManager zlmMediaListManager;
108   -
109   - @Autowired
110   - private ZlmHttpHookSubscribe subscribe;
111   -
112   - @Autowired
113   - private UserSetting userSetting;
114   -
115   - @Autowired
116   - private IUserService userService;
117   -
118   - @Autowired
119   - private ICloudRecordService cloudRecordService;
120   -
121   - @Autowired
122   - private VideoStreamSessionManager sessionManager;
123   -
124   - @Autowired
125   - private SSRCFactory ssrcFactory;
126   -
127   - @Qualifier("taskExecutor")
128   - @Autowired
129   - private ThreadPoolTaskExecutor taskExecutor;
130   -
131   - @Autowired
132   - private RedisTemplate<Object, Object> redisTemplate;
133   -
134   - @Autowired
135   - private Jt1078OfCarController jt1078OfCarController;
136   -
137   - @Autowired
138   - private StremProxyService1078 stremProxyService1078;
139   - @Resource
140   - private IntercomService intercomService;
141   -
142   -
143   - /**
144   - * 服务器定时上报时间,上报间隔可配置,默认10s上报一次
145   - */
146   - @ResponseBody
147   -
148   - @PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8")
149   - public HookResult onServerKeepalive(@RequestBody OnServerKeepaliveHookParam param) {
150   -
151   -
152   - taskExecutor.execute(() -> {
153   - List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive);
154   - if (subscribes != null && !subscribes.isEmpty()) {
155   - for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
156   - subscribe.response(null, param);
157   - }
158   - }
159   - });
160   - mediaServerService.updateMediaServerKeepalive(param.getMediaServerId(), param.getData());
161   -
162   - return HookResult.SUCCESS();
163   - }
164   -
165   - /**
166   - * 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。
167   - */
168   - @ResponseBody
169   -
170   - @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
171   - public HookResult onPlay(@RequestBody OnPlayHookParam param) {
172   - if (logger.isDebugEnabled()) {
173   - logger.debug("[ZLM HOOK] 播放鉴权:{}->{}", param.getMediaServerId(), param);
174   - }
175   - String mediaServerId = param.getMediaServerId();
176   -
177   - taskExecutor.execute(() -> {
178   - JSONObject json = (JSONObject) JSON.toJSON(param);
179   - ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_play, json);
180   - if (subscribe != null) {
181   - MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
182   - if (mediaInfo != null) {
183   - subscribe.response(mediaInfo, param);
184   - }
185   - }
186   - });
187   - if (!"rtp".equals(param.getApp())) {
188   - Map<String, String> paramMap = urlParamToMap(param.getParams());
189   - StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
190   - if (streamAuthorityInfo != null && streamAuthorityInfo.getCallId() != null && !streamAuthorityInfo.getCallId().equals(paramMap.get("callId"))) {
191   - return new HookResult(401, "Unauthorized");
192   - }
193   - }
194   - return HookResult.SUCCESS();
195   - }
196   -
197   - /**
198   - * rtsp/rtmp/rtp推流鉴权事件。
199   - */
200   - @ResponseBody
201   - @PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8")
202   - public HookResultForOnPublish onPublish(@RequestBody OnPublishHookParam param) {
203   -
204   - JSONObject json = (JSONObject) JSON.toJSON(param);
205   -
206   - logger.info("[ZLM HOOK]推流鉴权:{}->{}", param.getMediaServerId(), param);
207   -
208   - String mediaServerId = json.getString("mediaServerId");
209   - MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
210   - if (mediaInfo == null) {
211   - return new HookResultForOnPublish(200, "success");
212   - }
213   - // 推流鉴权的处理
214   - if (!"rtp".equals(param.getApp()) && !"schedule".equals(param.getApp())) {
215   - StreamProxyItem stream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
216   - if (stream != null) {
217   - HookResultForOnPublish result = HookResultForOnPublish.SUCCESS();
218   - result.setEnable_audio(stream.isEnableAudio());
219   - result.setEnable_mp4(stream.isEnableMp4());
220   - return result;
221   - }
222   - if (userSetting.getPushAuthority()) {
223   - // 推流鉴权
224   - if (param.getParams() == null) {
225   - logger.info("推流鉴权失败: 缺少必要参数:sign=md5(user表的pushKey)");
226   - return new HookResultForOnPublish(401, "Unauthorized");
227   - }
228   - Map<String, String> paramMap = urlParamToMap(param.getParams());
229   - String sign = paramMap.get("sign");
230   - if (sign == null) {
231   - logger.info("推流鉴权失败: 缺少必要参数:sign=md5(user表的pushKey)");
232   - return new HookResultForOnPublish(401, "Unauthorized");
233   - }
234   - // 鉴权配置
235   - // 推流自定义播放鉴权码
236   - // 鉴权配置
237   - boolean hasAuthority = userService.checkPushAuthority(null, sign);
238   - if (!hasAuthority) {
239   - logger.info("推流鉴权失败: sign 无权限: sign={}", sign);
240   - return new HookResultForOnPublish(401, "Unauthorized");
241   - }
242   - StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
243   - streamAuthorityInfo.setCallId(sign);
244   - streamAuthorityInfo.setSign(sign);
245   - // 鉴权通过
246   - redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
247   - }
248   - } else {
249   - zlmMediaListManager.sendStreamEvent(param.getApp(), param.getStream(), param.getMediaServerId());
250   - }
251   -
252   -
253   - HookResultForOnPublish result = HookResultForOnPublish.SUCCESS();
254   - result.setEnable_audio(true);
255   - taskExecutor.execute(() -> {
256   - ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json);
257   - if (subscribe != null) {
258   - subscribe.response(mediaInfo, param);
259   - }
260   - });
261   -
262   - // 是否录像
263   - if ("rtp".equals(param.getApp())) {
264   - result.setEnable_mp4(userSetting.getRecordSip());
265   - } else {
266   - result.setEnable_mp4(userSetting.isRecordPushLive());
267   - }
268   - // 国标流
269   - if ("rtp".equals(param.getApp())) {
270   -
271   - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
272   -
273   - // 单端口模式下修改流 ID
274   - if (!mediaInfo.isRtpEnable() && inviteInfo == null) {
275   - String ssrc = String.format("%010d", Long.parseLong(param.getStream(), 16));
276   - inviteInfo = inviteStreamService.getInviteInfoBySSRC(ssrc);
277   - if (inviteInfo != null) {
278   - result.setStream_replace(inviteInfo.getStream());
279   - logger.info("[ZLM HOOK]推流鉴权 stream: {} 替换为 {}", param.getStream(), inviteInfo.getStream());
280   - }
281   - }
282   -
283   - // 设置音频信息及录制信息
284   - List<SsrcTransaction> ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream());
285   - if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) {
286   -
287   - // 为录制国标模拟一个鉴权信息, 方便后续写入录像文件时使用
288   - StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
289   - streamAuthorityInfo.setApp(param.getApp());
290   - streamAuthorityInfo.setStream(ssrcTransactionForAll.get(0).getStream());
291   - streamAuthorityInfo.setCallId(ssrcTransactionForAll.get(0).getSipTransactionInfo().getCallId());
292   -
293   - redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), ssrcTransactionForAll.get(0).getStream(), streamAuthorityInfo);
294   -
295   - String deviceId = ssrcTransactionForAll.get(0).getDeviceId();
296   - String channelId = ssrcTransactionForAll.get(0).getChannelId();
297   - DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
298   - if (deviceChannel != null) {
299   - result.setEnable_audio(deviceChannel.isHasAudio());
300   - }
301   - // 如果是录像下载就设置视频间隔十秒
302   - if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.DOWNLOAD) {
303   - // 获取录像的总时长,然后设置为这个视频的时长
304   - InviteInfo inviteInfoForDownload = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, param.getStream());
305   - if (inviteInfoForDownload != null && inviteInfoForDownload.getStreamInfo() != null) {
306   - String startTime = inviteInfoForDownload.getStreamInfo().getStartTime();
307   - String endTime = inviteInfoForDownload.getStreamInfo().getEndTime();
308   - long difference = DateUtil.getDifference(startTime, endTime) / 1000;
309   - result.setMp4_max_second((int) difference);
310   - result.setEnable_mp4(true);
311   - // 设置为2保证得到的mp4的时长是正常的
312   - result.setModify_stamp(2);
313   - }
314   - }
315   - // 如果是talk对讲,则默认获取声音
316   - if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.TALK) {
317   - result.setEnable_audio(true);
318   - }
319   - }
320   - } else if (param.getApp().equals("broadcast")) {
321   - result.setEnable_audio(true);
322   - } else if (param.getApp().equals("talk")) {
323   - result.setEnable_audio(true);
324   - }
325   - if (param.getApp().equalsIgnoreCase("rtp")) {
326   - String receiveKey = VideoManagerConstants.WVP_OTHER_RECEIVE_RTP_INFO + userSetting.getServerId() + "_" + param.getStream();
327   - OtherRtpSendInfo otherRtpSendInfo = (OtherRtpSendInfo) redisTemplate.opsForValue().get(receiveKey);
328   -
329   - String receiveKeyForPS = VideoManagerConstants.WVP_OTHER_RECEIVE_PS_INFO + userSetting.getServerId() + "_" + param.getStream();
330   - OtherPsSendInfo otherPsSendInfo = (OtherPsSendInfo) redisTemplate.opsForValue().get(receiveKeyForPS);
331   - if (otherRtpSendInfo != null || otherPsSendInfo != null) {
332   - result.setEnable_mp4(true);
333   - }
334   - }
335   - logger.info("[ZLM HOOK]推流鉴权 响应:{}->{}->>>>{}", param.getMediaServerId(), param, result);
336   - return result;
337   - }
338   -
339   -
340   - /**
341   - * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。
342   - */
343   - @ResponseBody
344   - @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
345   - public HookResult onStreamChanged(@RequestBody OnStreamChangedHookParam param) {
346   -
347   - if (param.isRegist()) {
348   - logger.info("[ZLM HOOK] 流注册, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
349   - } else {
350   - logger.info("[ZLM HOOK] 流注销, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
351   - }
352   -
353   - JSONObject json = (JSONObject) JSON.toJSON(param);
354   - taskExecutor.execute(() -> {
355   - ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json);
356   - MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
357   - if (mediaInfo == null) {
358   - logger.info("[ZLM HOOK] 流变化未找到ZLM, {}", param.getMediaServerId());
359   - return;
360   - }
361   - if (subscribe != null) {
362   - subscribe.response(mediaInfo, param);
363   - }
364   -
365   - List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
366   - // TODO 重构此处逻辑
367   - if (param.isRegist()) {
368   - // 处理流注册的鉴权信息, 流注销这里不再删除鉴权信息,下次来了新的鉴权信息会对就的进行覆盖
369   - if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
370   - || param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
371   - || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
372   - StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
373   - if (streamAuthorityInfo == null) {
374   - streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
375   - } else {
376   - streamAuthorityInfo.setOriginType(param.getOriginType());
377   - streamAuthorityInfo.setOriginTypeStr(param.getOriginTypeStr());
378   - }
379   - redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
380   - }
381   - }
382   - if ("rtsp".equals(param.getSchema())) {
383   - logger.info("流变化:注册->{}, app->{}, stream->{}", param.isRegist(), param.getApp(), param.getStream());
384   - if (param.isRegist()) {
385   - mediaServerService.addCount(param.getMediaServerId());
386   - } else {
387   - mediaServerService.removeCount(param.getMediaServerId());
388   - }
389   -
390   - int updateStatusResult = streamProxyService.updateStatus(param.isRegist(), param.getApp(), param.getStream());
391   - if (updateStatusResult > 0) {
392   -
393   - }
394   -
395   - if ("rtp".equals(param.getApp()) && !param.isRegist()) {
396   - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
397   - if (inviteInfo != null && (inviteInfo.getType() == InviteSessionType.PLAY || inviteInfo.getType() == InviteSessionType.PLAYBACK)) {
398   - inviteStreamService.removeInviteInfo(inviteInfo);
399   - storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
400   - }
401   - } else if ("broadcast".equals(param.getApp())) {
402   - // 语音对讲推流 stream需要满足格式deviceId_channelId
403   - if (param.getStream().indexOf("_") > 0) {
404   - String[] streamArray = param.getStream().split("_");
405   - if (streamArray.length == 2) {
406   - String deviceId = streamArray[0];
407   - String channelId = streamArray[1];
408   - Device device = deviceService.getDevice(deviceId);
409   - if (device != null) {
410   - if (param.isRegist()) {
411   - if (audioBroadcastManager.exit(deviceId, channelId)) {
412   - playService.stopAudioBroadcast(deviceId, channelId);
413   - }
414   - // 开启语音对讲通道
415   - try {
416   - playService.audioBroadcastCmd(device, channelId, mediaInfo, param.getApp(), param.getStream(), 60, false, (msg) -> {
417   - logger.info("[语音对讲] 通道建立成功, device: {}, channel: {}", deviceId, channelId);
418   - });
419   - } catch (InvalidArgumentException | ParseException | SipException e) {
420   - logger.error("[命令发送失败] 语音对讲: {}", e.getMessage());
421   - }
422   - } else {
423   - // 流注销
424   - playService.stopAudioBroadcast(deviceId, channelId);
425   - }
426   - } else {
427   - logger.info("[语音对讲] 未找到设备:{}", deviceId);
428   - }
429   - }
430   - }
431   - } else if ("talk".equals(param.getApp())) {
432   - // 语音对讲推流 stream需要满足格式deviceId_channelId
433   - if (param.getStream().indexOf("_") > 0) {
434   - String[] streamArray = param.getStream().split("_");
435   - if (streamArray.length == 2) {
436   - String deviceId = streamArray[0];
437   - String channelId = streamArray[1];
438   - Device device = deviceService.getDevice(deviceId);
439   - if (device != null) {
440   - if (param.isRegist()) {
441   - if (audioBroadcastManager.exit(deviceId, channelId)) {
442   - playService.stopAudioBroadcast(deviceId, channelId);
443   - }
444   - // 开启语音对讲通道
445   - playService.talkCmd(device, channelId, mediaInfo, param.getStream(), (msg) -> {
446   - logger.info("[语音对讲] 通道建立成功, device: {}, channel: {}", deviceId, channelId);
447   - });
448   - } else {
449   - // 流注销
450   - playService.stopTalk(device, channelId, param.isRegist());
451   - }
452   - } else {
453   - logger.info("[语音对讲] 未找到设备:{}", deviceId);
454   - }
455   - }
456   - }
457   -
458   - } else {
459   - if (!"rtp".equals(param.getApp())) {
460   - String type = OriginType.values()[param.getOriginType()].getType();
461   - if (param.isRegist()) {
462   - StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(
463   - param.getApp(), param.getStream());
464   - String callId = null;
465   - if (streamAuthorityInfo != null) {
466   - callId = streamAuthorityInfo.getCallId();
467   - }
468   - StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaInfo,
469   - param.getApp(), param.getStream(), tracks, callId);
470   - param.setStreamInfo(new StreamContent(streamInfoByAppAndStream));
471   - redisCatchStorage.addStream(mediaInfo, type, param.getApp(), param.getStream(), param);
472   - if (param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
473   - || param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
474   - || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
475   - param.setSeverId(userSetting.getServerId());
476   - zlmMediaListManager.addPush(param);
477   -
478   - // 冗余数据,自己系统中自用
479   - redisCatchStorage.addPushListItem(param.getApp(), param.getStream(), param);
480   - }
481   - } else {
482   - // 兼容流注销时类型从redis记录获取
483   - OnStreamChangedHookParam onStreamChangedHookParam = redisCatchStorage.getStreamInfo(
484   - param.getApp(), param.getStream(), param.getMediaServerId());
485   - if (onStreamChangedHookParam != null) {
486   - type = OriginType.values()[onStreamChangedHookParam.getOriginType()].getType();
487   - redisCatchStorage.removeStream(mediaInfo.getId(), type, param.getApp(), param.getStream());
488   - if ("PUSH".equalsIgnoreCase(type)) {
489   - // 冗余数据,自己系统中自用
490   - redisCatchStorage.removePushListItem(param.getApp(), param.getStream(), param.getMediaServerId());
491   - }
492   - }
493   - GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
494   - if (gbStream != null) {
495   -// eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF);
496   - }
497   - zlmMediaListManager.removeMedia(param.getApp(), param.getStream());
498   - }
499   - GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
500   - if (gbStream != null) {
501   - if (userSetting.isUsePushingAsStatus()) {
502   - eventPublisher.catalogEventPublishForStream(null, gbStream, param.isRegist() ? CatalogEvent.ON : CatalogEvent.OFF);
503   - }
504   - }
505   - if (type != null) {
506   - // 发送流变化redis消息
507   - JSONObject jsonObject = new JSONObject();
508   - jsonObject.put("serverId", userSetting.getServerId());
509   - jsonObject.put("app", param.getApp());
510   - jsonObject.put("stream", param.getStream());
511   - jsonObject.put("register", param.isRegist());
512   - jsonObject.put("mediaServerId", param.getMediaServerId());
513   - redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
514   - }
515   - }
516   - }
517   - if (!param.isRegist()) {
518   - List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
519   - if (!sendRtpItems.isEmpty()) {
520   - for (SendRtpItem sendRtpItem : sendRtpItems) {
521   - if (sendRtpItem != null && sendRtpItem.getApp().equals(param.getApp())) {
522   - String platformId = sendRtpItem.getPlatformId();
523   - ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
524   - Device device = deviceService.getDevice(platformId);
525   -
526   - try {
527   - if (platform != null) {
528   - commanderFroPlatform.streamByeCmd(platform, sendRtpItem);
529   - redisCatchStorage.deleteSendRTPServer(platformId, sendRtpItem.getChannelId(),
530   - sendRtpItem.getCallId(), sendRtpItem.getStream());
531   - } else {
532   - cmder.streamByeCmd(device, sendRtpItem.getChannelId(), param.getStream(), sendRtpItem.getCallId());
533   - if (sendRtpItem.getPlayType().equals(InviteStreamType.BROADCAST)
534   - || sendRtpItem.getPlayType().equals(InviteStreamType.TALK)) {
535   - AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
536   - if (audioBroadcastCatch != null) {
537   - // 来自上级平台的停止对讲
538   - logger.info("[停止对讲] 来自上级,平台:{}, 通道:{}", sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
539   - audioBroadcastManager.del(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
540   - }
541   - }
542   - }
543   - } catch (SipException | InvalidArgumentException | ParseException |
544   - SsrcTransactionNotFoundException e) {
545   - logger.error("[命令发送失败] 发送BYE: {}", e.getMessage());
546   - }
547   - }
548   - }
549   - }
550   - }
551   - }
552   - });
553   - return HookResult.SUCCESS();
554   - }
555   -
556   - /**
557   - * 流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流。
558   - */
559   - @ResponseBody
560   - @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8")
561   - public JSONObject onStreamNoneReader(@RequestBody OnStreamNoneReaderHookParam param) {
562   -
563   - logger.info("[ZLM HOOK]流无人观看:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(),
564   - param.getApp(), param.getStream());
565   - JSONObject ret = new JSONObject();
566   - ret.put("code", 0);
567   - stremProxyService1078.sendIORequestStop(param.getStream());
568   - // 国标类型的流
569   - if ("rtp".equals(param.getApp())) {
570   - ret.put("close", userSetting.getStreamOnDemand());
571   - String stream = param.getStream();
572   - Object port = redisTemplate.opsForValue().get("tag:history:port:" + stream);
573   - Object httpPort = redisTemplate.opsForValue().get("tag:history:httpPort:" + stream);
574   - Object time = redisTemplate.opsForValue().get("tag:history:httpPort:time:" + stream);
575   -
576   - if (ObjectUtils.isEmpty(port)) {
577   - port = -1;
578   - }
579   -
580   - if (ObjectUtils.isEmpty(httpPort)) {
581   - httpPort = -1;
582   - }
583   -
584   - if (Objects.isNull(time) || Objects.equals(0L, time)) {
585   - Map<String, Object> resl = jt1078OfCarController.sendIORequestStop(StringUtils.substringBefore(stream, "-"), StringUtils.substringAfter(stream, "-"), stream, (Integer) port, (Integer) httpPort);
586   - if (!StringUtils.equals(String.valueOf(resl.get("code")), "1")) {
587   - logger.info("停流失败,稍后再试:{},{},{}", stream, port, httpPort);
588   - return ret;
589   - }
590   - }
591   -
592   - // 国标流, 点播/录像回放/录像下载
593   - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
594   - // 点播
595   - if (inviteInfo != null) {
596   - // 录像下载
597   - if (inviteInfo.getType() == InviteSessionType.DOWNLOAD) {
598   - ret.put("close", false);
599   - return ret;
600   - }
601   - // 收到无人观看说明流也没有在往上级推送
602   - if (redisCatchStorage.isChannelSendingRTP(inviteInfo.getChannelId())) {
603   - List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByChannelId(
604   - inviteInfo.getChannelId());
605   - if (!sendRtpItems.isEmpty()) {
606   - for (SendRtpItem sendRtpItem : sendRtpItems) {
607   - ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
608   - try {
609   - commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
610   - } catch (SipException | InvalidArgumentException | ParseException e) {
611   - logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
612   - }
613   - redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
614   - sendRtpItem.getCallId(), sendRtpItem.getStream());
615   - if (InviteStreamType.PUSH == sendRtpItem.getPlayType()) {
616   - MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
617   - sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(),
618   - sendRtpItem.getPlatformId(), parentPlatform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId());
619   - messageForPushChannel.setPlatFormIndex(parentPlatform.getId());
620   - redisCatchStorage.sendPlatformStopPlayMsg(messageForPushChannel);
621   - }
622   - }
623   - }
624   - }
625   - Device device = deviceService.getDevice(inviteInfo.getDeviceId());
626   - if (device != null) {
627   - try {
628   - // 多查询一次防止已经被处理了
629   - InviteInfo info = inviteStreamService.getInviteInfo(inviteInfo.getType(),
630   - inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream());
631   - if (info != null) {
632   - cmder.streamByeCmd(device, inviteInfo.getChannelId(),
633   - inviteInfo.getStream(), null);
634   - } else {
635   - logger.info("[无人观看] 未找到设备的点播信息: {}, 流:{}", inviteInfo.getDeviceId(), param.getStream());
636   - }
637   - } catch (InvalidArgumentException | ParseException | SipException |
638   - SsrcTransactionNotFoundException e) {
639   - logger.error("[无人观看]点播, 发送BYE失败 {}", e.getMessage());
640   - }
641   - } else {
642   - logger.info("[无人观看] 未找到设备: {},流:{}", inviteInfo.getDeviceId(), param.getStream());
643   - }
644   -
645   - inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(),
646   - inviteInfo.getChannelId(), inviteInfo.getStream());
647   - storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
648   - return ret;
649   - }
650   - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, param.getStream(), null);
651   - if (sendRtpItem != null && "talk".equals(sendRtpItem.getApp())) {
652   - ret.put("close", false);
653   - return ret;
654   - }
655   - } else if ("talk".equals(param.getApp()) || "broadcast".equals(param.getApp())) {
656   - ret.put("close", false);
657   - } else {
658   - // 非国标流 推流/拉流代理
659   - // 拉流代理
660   - StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
661   - if (streamProxyItem != null) {
662   - if (streamProxyItem.isEnableRemoveNoneReader()) {
663   - // 无人观看自动移除
664   - ret.put("close", true);
665   - streamProxyService.del(param.getApp(), param.getStream());
666   - String url = streamProxyItem.getUrl() != null ? streamProxyItem.getUrl() : streamProxyItem.getSrcUrl();
667   - logger.info("[{}/{}]<-[{}] 拉流代理无人观看已经移除", param.getApp(), param.getStream(), url);
668   - } else if (streamProxyItem.isEnableDisableNoneReader()) {
669   - // 无人观看停用
670   - ret.put("close", true);
671   - // 修改数据
672   - streamProxyService.stop(param.getApp(), param.getStream());
673   - } else {
674   - // 无人观看不做处理
675   - ret.put("close", false);
676   - }
677   - return ret;
678   - }
679   -
680   -
681   - // TODO 推流具有主动性,暂时不做处理
682   -// StreamPushItem streamPushItem = streamPushService.getPush(app, streamId);
683   -// if (streamPushItem != null) {
684   -// // TODO 发送停止
685   -// }
686   - //推流需要更新缓存数据以防止端口占用
687   - logger.info("无人观看流 ---> {}", JSONObject.toJSONString(param));
688   - Object object = redisTemplate.opsForValue().get("patrol:stream:" + param.getStream());
689   - if (object == null) {
690   - String[] split = param.getStream().split("_");
691   - if (split != null && split.length == 2) {
692   - if (split[1].equals("voice")){
693   - intercomService.stopIntercom(split[0]);
694   - logger.info("{} 历史语音断流成功", split[0]);
695   - }else {
696   - try {
697   - jt1078OfCarController.clearMap(split[1],split[0]);
698   - } catch (ServiceException e) {
699   - throw new RuntimeException(e);
700   - }
701   - }
702   - }
703   - }
704   - }
705   - return ret;
706   - }
707   -
708   - /**
709   - * 流未找到事件,用户可以在此事件触发时,立即去拉流,这样可以实现按需拉流;此事件对回复不敏感。
710   - */
711   - @ResponseBody
712   - @PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8")
713   - public DeferredResult<HookResult> onStreamNotFound(@RequestBody OnStreamNotFoundHookParam param) {
714   - logger.info("[ZLM HOOK] 流未找到:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
715   -
716   - DeferredResult<HookResult> defaultResult = new DeferredResult<>();
717   -
718   - MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
719   - if (!userSetting.isAutoApplyPlay() || mediaInfo == null) {
720   - defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
721   - return defaultResult;
722   - }
723   - if ("rtp".equals(param.getApp())) {
724   - String[] s = param.getStream().split("_");
725   - if ((s.length != 2 && s.length != 4)) {
726   - defaultResult.setResult(HookResult.SUCCESS());
727   - return defaultResult;
728   - }
729   - String deviceId = s[0];
730   - String channelId = s[1];
731   - Device device = redisCatchStorage.getDevice(deviceId);
732   - if (device == null || !device.isOnLine()) {
733   - defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
734   - return defaultResult;
735   - }
736   - DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
737   - if (deviceChannel == null) {
738   - defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
739   - return defaultResult;
740   - }
741   - if (s.length == 2) {
742   - logger.info("[ZLM HOOK] 预览流未找到, 发起自动点播:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
743   -
744   - RequestMessage msg = new RequestMessage();
745   - String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
746   - boolean exist = resultHolder.exist(key, null);
747   - msg.setKey(key);
748   - String uuid = UUID.randomUUID().toString();
749   - msg.setId(uuid);
750   - DeferredResult<HookResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
751   -
752   - result.onTimeout(() -> {
753   - logger.info("[ZLM HOOK] 预览流自动点播, 等待超时");
754   - msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "点播超时"));
755   - resultHolder.invokeAllResult(msg);
756   - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
757   - storager.stopPlay(deviceId, channelId);
758   - });
759   -
760   - resultHolder.put(key, uuid, result);
761   -
762   - if (!exist) {
763   - playService.play(mediaInfo, deviceId, channelId, null, (code, message, data) -> {
764   - msg.setData(new HookResult(code, message));
765   - resultHolder.invokeResult(msg);
766   - });
767   - }
768   - return result;
769   - } else if (s.length == 4) {
770   - // 此时为录像回放, 录像回放格式为> 设备ID_通道ID_开始时间_结束时间
771   - String startTimeStr = s[2];
772   - String endTimeStr = s[3];
773   - if (startTimeStr == null || endTimeStr == null || startTimeStr.length() != 14 || endTimeStr.length() != 14) {
774   - defaultResult.setResult(HookResult.SUCCESS());
775   - return defaultResult;
776   - }
777   - String startTime = DateUtil.urlToyyyy_MM_dd_HH_mm_ss(startTimeStr);
778   - String endTime = DateUtil.urlToyyyy_MM_dd_HH_mm_ss(endTimeStr);
779   - logger.info("[ZLM HOOK] 回放流未找到, 发起自动点播:{}->{}->{}/{}-{}-{}",
780   - param.getMediaServerId(), param.getSchema(),
781   - param.getApp(), param.getStream(),
782   - startTime, endTime
783   - );
784   - RequestMessage msg = new RequestMessage();
785   - String key = DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId;
786   - boolean exist = resultHolder.exist(key, null);
787   - msg.setKey(key);
788   - String uuid = UUID.randomUUID().toString();
789   - msg.setId(uuid);
790   - DeferredResult<HookResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
791   -
792   - result.onTimeout(() -> {
793   - logger.info("[ZLM HOOK] 回放流自动点播, 等待超时");
794   - // 释放rtpserver
795   - msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "点播超时"));
796   - resultHolder.invokeResult(msg);
797   - });
798   -
799   - resultHolder.put(key, uuid, result);
800   -
801   - if (!exist) {
802   - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaInfo, param.getStream(), null,
803   - device.isSsrcCheck(), true, 0, false, false, device.getStreamModeForParam());
804   - playService.playBack(mediaInfo, ssrcInfo, deviceId, channelId, startTime, endTime, (code, message, data) -> {
805   - msg.setData(new HookResult(code, message));
806   - resultHolder.invokeResult(msg);
807   - });
808   - }
809   - return result;
810   - } else {
811   - defaultResult.setResult(HookResult.SUCCESS());
812   - return defaultResult;
813   - }
814   -
815   - } else {
816   - // 拉流代理
817   - StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
818   - if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnableDisableNoneReader()) {
819   - streamProxyService.start(param.getApp(), param.getStream());
820   - }
821   - DeferredResult<HookResult> result = new DeferredResult<>();
822   - result.setResult(HookResult.SUCCESS());
823   - return result;
824   - }
825   - }
826   -
827   - /**
828   - * 服务器启动事件,可以用于监听服务器崩溃重启;此事件对回复不敏感。
829   - */
830   - @ResponseBody
831   - @PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8")
832   - public HookResult onServerStarted(HttpServletRequest request, @RequestBody JSONObject jsonObject) {
833   -
834   - jsonObject.put("ip", request.getRemoteAddr());
835   - ZLMServerConfig zlmServerConfig = JSON.to(ZLMServerConfig.class, jsonObject);
836   - zlmServerConfig.setIp(request.getRemoteAddr());
837   - logger.info("[ZLM HOOK] zlm 启动 " + zlmServerConfig.getGeneralMediaServerId());
838   - taskExecutor.execute(() -> {
839   - List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_started);
840   - if (subscribes != null && !subscribes.isEmpty()) {
841   - for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
842   - subscribe.response(null, zlmServerConfig);
843   - }
844   - }
845   - mediaServerService.zlmServerOnline(zlmServerConfig);
846   - });
847   -
848   - return HookResult.SUCCESS();
849   - }
850   -
851   - /**
852   - * 发送rtp(startSendRtp)被动关闭时回调
853   - */
854   - @ResponseBody
855   - @PostMapping(value = "/on_send_rtp_stopped", produces = "application/json;charset=UTF-8")
856   - public HookResult onSendRtpStopped(HttpServletRequest request, @RequestBody OnSendRtpStoppedHookParam param) {
857   -
858   - logger.info("[ZLM HOOK] rtp发送关闭:{}->{}/{}", param.getMediaServerId(), param.getApp(), param.getStream());
859   -
860   - // 查找对应的上级推流,发送停止
861   - if (!"rtp".equals(param.getApp())) {
862   - return HookResult.SUCCESS();
863   - }
864   - taskExecutor.execute(() -> {
865   - List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
866   - if (sendRtpItems.size() > 0) {
867   - for (SendRtpItem sendRtpItem : sendRtpItems) {
868   - ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
869   - ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
870   - try {
871   - commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
872   - } catch (SipException | InvalidArgumentException | ParseException e) {
873   - logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
874   - }
875   - redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
876   - sendRtpItem.getCallId(), sendRtpItem.getStream());
877   - }
878   - }
879   - });
880   -
881   - return HookResult.SUCCESS();
882   - }
883   -
884   - /**
885   - * rtpServer收流超时
886   - */
887   - @ResponseBody
888   - @PostMapping(value = "/on_rtp_server_timeout", produces = "application/json;charset=UTF-8")
889   - public HookResult onRtpServerTimeout(@RequestBody OnRtpServerTimeoutHookParam
890   - param) {
891   - logger.info("[ZLM HOOK] rtpServer收流超时:{}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc());
892   -
893   - taskExecutor.execute(() -> {
894   - List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
895   - if (subscribes != null && !subscribes.isEmpty()) {
896   - for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
897   - subscribe.response(null, param);
898   - }
899   - }
900   - });
901   -
902   - return HookResult.SUCCESS();
903   - }
904   -
905   - /**
906   - * 录像完成事件
907   - */
908   - @ResponseBody
909   - @PostMapping(value = "/on_record_mp4", produces = "application/json;charset=UTF-8")
910   - public HookResult onRecordMp4(HttpServletRequest request, @RequestBody OnRecordMp4HookParam param) {
911   - logger.info("[ZLM HOOK] 录像完成事件:{}->{}", param.getMediaServerId(), param.getFile_path());
912   -
913   - taskExecutor.execute(() -> {
914   - List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_record_mp4);
915   - if (subscribes != null && !subscribes.isEmpty()) {
916   - for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
917   - subscribe.response(null, param);
918   - }
919   - }
920   - cloudRecordService.addRecord(param);
921   -
922   - });
923   -
924   - return HookResult.SUCCESS();
925   - }
926   -
927   - private Map<String, String> urlParamToMap(String params) {
928   - HashMap<String, String> map = new HashMap<>();
929   - if (ObjectUtils.isEmpty(params)) {
930   - return map;
931   - }
932   - String[] paramsArray = params.split("&");
933   - if (paramsArray.length == 0) {
934   - return map;
935   - }
936   - for (String param : paramsArray) {
937   - String[] paramArray = param.split("=");
938   - if (paramArray.length == 2) {
939   - map.put(paramArray[0], paramArray[1]);
940   - }
941   - }
942   - return map;
943   - }
944   -}
  1 +package com.genersoft.iot.vmp.media.zlm;
  2 +
  3 +import com.alibaba.fastjson2.JSON;
  4 +import com.alibaba.fastjson2.JSONObject;
  5 +import com.genersoft.iot.vmp.common.InviteInfo;
  6 +import com.genersoft.iot.vmp.common.InviteSessionType;
  7 +import com.genersoft.iot.vmp.common.StreamInfo;
  8 +import com.genersoft.iot.vmp.common.VideoManagerConstants;
  9 +import com.genersoft.iot.vmp.conf.UserSetting;
  10 +import com.genersoft.iot.vmp.conf.exception.ServiceException;
  11 +import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
  12 +import com.genersoft.iot.vmp.gb28181.bean.*;
  13 +import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
  14 +import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
  15 +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
  16 +import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
  17 +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
  18 +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
  19 +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
  20 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
  21 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
  22 +import com.genersoft.iot.vmp.media.zlm.dto.HookType;
  23 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  24 +import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
  25 +import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
  26 +import com.genersoft.iot.vmp.media.zlm.dto.hook.*;
  27 +import com.genersoft.iot.vmp.service.*;
  28 +import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
  29 +import com.genersoft.iot.vmp.service.bean.SSRCInfo;
  30 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  31 +import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
  32 +import com.genersoft.iot.vmp.utils.DateUtil;
  33 +import com.genersoft.iot.vmp.vmanager.bean.*;
  34 +import com.genersoft.iot.vmp.vmanager.jt1078.platform.Jt1078OfCarController;
  35 +import com.genersoft.iot.vmp.vmanager.jt1078.platform.service.IntercomService;
  36 +import org.apache.commons.lang3.StringUtils;
  37 +import org.slf4j.Logger;
  38 +import org.slf4j.LoggerFactory;
  39 +import org.springframework.beans.factory.annotation.Autowired;
  40 +import org.springframework.beans.factory.annotation.Qualifier;
  41 +import org.springframework.data.redis.core.RedisTemplate;
  42 +import org.springframework.scheduling.annotation.Scheduled;
  43 +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
  44 +import org.springframework.util.ObjectUtils;
  45 +import org.springframework.web.bind.annotation.*;
  46 +import org.springframework.web.context.request.async.DeferredResult;
  47 +
  48 +import javax.annotation.Resource;
  49 +import javax.servlet.http.HttpServletRequest;
  50 +import javax.sip.InvalidArgumentException;
  51 +import javax.sip.SipException;
  52 +import java.text.ParseException;
  53 +import java.util.*;
  54 +import java.util.concurrent.TimeUnit;
  55 +
  56 +/**
  57 + * @description:针对 ZLMediaServer的hook事件监听
  58 + * @author: swwheihei
  59 + * @date: 2020年5月8日 上午10:46:48
  60 + */
  61 +@RestController
  62 +@RequestMapping("/index/hook")
  63 +public class ZLMHttpHookListener {
  64 +
  65 + private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class);
  66 +
  67 + @Autowired
  68 + private SIPCommander cmder;
  69 +
  70 + @Autowired
  71 + private ISIPCommanderForPlatform commanderFroPlatform;
  72 +
  73 + @Autowired
  74 + private AudioBroadcastManager audioBroadcastManager;
  75 +
  76 + @Autowired
  77 + private IPlayService playService;
  78 +
  79 + @Autowired
  80 + private IVideoManagerStorage storager;
  81 +
  82 + @Autowired
  83 + private IRedisCatchStorage redisCatchStorage;
  84 +
  85 + @Autowired
  86 + private IInviteStreamService inviteStreamService;
  87 +
  88 + @Autowired
  89 + private IDeviceService deviceService;
  90 +
  91 + @Autowired
  92 + private IMediaServerService mediaServerService;
  93 +
  94 + @Autowired
  95 + private IStreamProxyService streamProxyService;
  96 +
  97 + @Autowired
  98 + private DeferredResultHolder resultHolder;
  99 +
  100 + @Autowired
  101 + private IMediaService mediaService;
  102 +
  103 + @Autowired
  104 + private EventPublisher eventPublisher;
  105 +
  106 + @Autowired
  107 + private ZLMMediaListManager zlmMediaListManager;
  108 +
  109 + @Autowired
  110 + private ZlmHttpHookSubscribe subscribe;
  111 +
  112 + @Autowired
  113 + private UserSetting userSetting;
  114 +
  115 + @Autowired
  116 + private IUserService userService;
  117 +
  118 + @Autowired
  119 + private ICloudRecordService cloudRecordService;
  120 +
  121 + @Autowired
  122 + private VideoStreamSessionManager sessionManager;
  123 +
  124 + @Autowired
  125 + private SSRCFactory ssrcFactory;
  126 +
  127 + @Qualifier("taskExecutor")
  128 + @Autowired
  129 + private ThreadPoolTaskExecutor taskExecutor;
  130 +
  131 + @Autowired
  132 + private RedisTemplate<Object, Object> redisTemplate;
  133 +
  134 + @Autowired
  135 + private Jt1078OfCarController jt1078OfCarController;
  136 +
  137 + @Autowired
  138 + private StremProxyService1078 stremProxyService1078;
  139 + @Resource
  140 + private IntercomService intercomService;
  141 +
  142 +
  143 + /**
  144 + * 服务器定时上报时间,上报间隔可配置,默认10s上报一次
  145 + */
  146 + @ResponseBody
  147 +
  148 + @PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8")
  149 + public HookResult onServerKeepalive(@RequestBody OnServerKeepaliveHookParam param) {
  150 +
  151 +
  152 + taskExecutor.execute(() -> {
  153 + List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive);
  154 + if (subscribes != null && !subscribes.isEmpty()) {
  155 + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
  156 + subscribe.response(null, param);
  157 + }
  158 + }
  159 + });
  160 + mediaServerService.updateMediaServerKeepalive(param.getMediaServerId(), param.getData());
  161 +
  162 + return HookResult.SUCCESS();
  163 + }
  164 +
  165 + /**
  166 + * 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。
  167 + */
  168 + @ResponseBody
  169 +
  170 + @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
  171 + public HookResult onPlay(@RequestBody OnPlayHookParam param) {
  172 + if (logger.isDebugEnabled()) {
  173 + logger.debug("[ZLM HOOK] 播放鉴权:{}->{}", param.getMediaServerId(), param);
  174 + }
  175 + String mediaServerId = param.getMediaServerId();
  176 +
  177 + taskExecutor.execute(() -> {
  178 + JSONObject json = (JSONObject) JSON.toJSON(param);
  179 + ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_play, json);
  180 + if (subscribe != null) {
  181 + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
  182 + if (mediaInfo != null) {
  183 + subscribe.response(mediaInfo, param);
  184 + }
  185 + }
  186 + });
  187 + if (!"rtp".equals(param.getApp())) {
  188 + Map<String, String> paramMap = urlParamToMap(param.getParams());
  189 + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
  190 + if (streamAuthorityInfo != null && streamAuthorityInfo.getCallId() != null && !streamAuthorityInfo.getCallId().equals(paramMap.get("callId"))) {
  191 + return new HookResult(401, "Unauthorized");
  192 + }
  193 + }
  194 + return HookResult.SUCCESS();
  195 + }
  196 +
  197 + /**
  198 + * rtsp/rtmp/rtp推流鉴权事件。
  199 + */
  200 + @ResponseBody
  201 + @PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8")
  202 + public HookResultForOnPublish onPublish(@RequestBody OnPublishHookParam param) {
  203 +
  204 + JSONObject json = (JSONObject) JSON.toJSON(param);
  205 +
  206 + logger.info("[ZLM HOOK]推流鉴权:{}->{}", param.getMediaServerId(), param);
  207 +
  208 + String mediaServerId = json.getString("mediaServerId");
  209 + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
  210 + if (mediaInfo == null) {
  211 + return new HookResultForOnPublish(200, "success");
  212 + }
  213 + // 推流鉴权的处理
  214 + if (!"rtp".equals(param.getApp()) && !"schedule".equals(param.getApp())) {
  215 + StreamProxyItem stream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
  216 + if (stream != null) {
  217 + HookResultForOnPublish result = HookResultForOnPublish.SUCCESS();
  218 + result.setEnable_audio(stream.isEnableAudio());
  219 + result.setEnable_mp4(stream.isEnableMp4());
  220 + return result;
  221 + }
  222 + if (userSetting.getPushAuthority()) {
  223 + // 推流鉴权
  224 + if (param.getParams() == null) {
  225 + logger.info("推流鉴权失败: 缺少必要参数:sign=md5(user表的pushKey)");
  226 + return new HookResultForOnPublish(401, "Unauthorized");
  227 + }
  228 + Map<String, String> paramMap = urlParamToMap(param.getParams());
  229 + String sign = paramMap.get("sign");
  230 + if (sign == null) {
  231 + logger.info("推流鉴权失败: 缺少必要参数:sign=md5(user表的pushKey)");
  232 + return new HookResultForOnPublish(401, "Unauthorized");
  233 + }
  234 + // 鉴权配置
  235 + // 推流自定义播放鉴权码
  236 + // 鉴权配置
  237 + boolean hasAuthority = userService.checkPushAuthority(null, sign);
  238 + if (!hasAuthority) {
  239 + logger.info("推流鉴权失败: sign 无权限: sign={}", sign);
  240 + return new HookResultForOnPublish(401, "Unauthorized");
  241 + }
  242 + StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
  243 + streamAuthorityInfo.setCallId(sign);
  244 + streamAuthorityInfo.setSign(sign);
  245 + // 鉴权通过
  246 + redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
  247 + }
  248 + } else {
  249 + zlmMediaListManager.sendStreamEvent(param.getApp(), param.getStream(), param.getMediaServerId());
  250 + }
  251 +
  252 +
  253 + HookResultForOnPublish result = HookResultForOnPublish.SUCCESS();
  254 + result.setEnable_audio(true);
  255 + taskExecutor.execute(() -> {
  256 + ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json);
  257 + if (subscribe != null) {
  258 + subscribe.response(mediaInfo, param);
  259 + }
  260 + });
  261 +
  262 + // 是否录像
  263 + if ("rtp".equals(param.getApp())) {
  264 + result.setEnable_mp4(userSetting.getRecordSip());
  265 + } else {
  266 + result.setEnable_mp4(userSetting.isRecordPushLive());
  267 + }
  268 + // 国标流
  269 + if ("rtp".equals(param.getApp())) {
  270 +
  271 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
  272 +
  273 + // 单端口模式下修改流 ID
  274 + if (!mediaInfo.isRtpEnable() && inviteInfo == null) {
  275 + String ssrc = String.format("%010d", Long.parseLong(param.getStream(), 16));
  276 + inviteInfo = inviteStreamService.getInviteInfoBySSRC(ssrc);
  277 + if (inviteInfo != null) {
  278 + result.setStream_replace(inviteInfo.getStream());
  279 + logger.info("[ZLM HOOK]推流鉴权 stream: {} 替换为 {}", param.getStream(), inviteInfo.getStream());
  280 + }
  281 + }
  282 +
  283 + // 设置音频信息及录制信息
  284 + List<SsrcTransaction> ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream());
  285 + if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) {
  286 +
  287 + // 为录制国标模拟一个鉴权信息, 方便后续写入录像文件时使用
  288 + StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
  289 + streamAuthorityInfo.setApp(param.getApp());
  290 + streamAuthorityInfo.setStream(ssrcTransactionForAll.get(0).getStream());
  291 + streamAuthorityInfo.setCallId(ssrcTransactionForAll.get(0).getSipTransactionInfo().getCallId());
  292 +
  293 + redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), ssrcTransactionForAll.get(0).getStream(), streamAuthorityInfo);
  294 +
  295 + String deviceId = ssrcTransactionForAll.get(0).getDeviceId();
  296 + String channelId = ssrcTransactionForAll.get(0).getChannelId();
  297 + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
  298 + if (deviceChannel != null) {
  299 + result.setEnable_audio(deviceChannel.isHasAudio());
  300 + }
  301 + // 如果是录像下载就设置视频间隔十秒
  302 + if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.DOWNLOAD) {
  303 + // 获取录像的总时长,然后设置为这个视频的时长
  304 + InviteInfo inviteInfoForDownload = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, param.getStream());
  305 + if (inviteInfoForDownload != null && inviteInfoForDownload.getStreamInfo() != null) {
  306 + String startTime = inviteInfoForDownload.getStreamInfo().getStartTime();
  307 + String endTime = inviteInfoForDownload.getStreamInfo().getEndTime();
  308 + long difference = DateUtil.getDifference(startTime, endTime) / 1000;
  309 + result.setMp4_max_second((int) difference);
  310 + result.setEnable_mp4(true);
  311 + // 设置为2保证得到的mp4的时长是正常的
  312 + result.setModify_stamp(2);
  313 + }
  314 + }
  315 + // 如果是talk对讲,则默认获取声音
  316 + if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.TALK) {
  317 + result.setEnable_audio(true);
  318 + }
  319 + }
  320 + } else if (param.getApp().equals("broadcast")) {
  321 + result.setEnable_audio(true);
  322 + } else if (param.getApp().equals("talk")) {
  323 + result.setEnable_audio(true);
  324 + }
  325 + if (param.getApp().equalsIgnoreCase("rtp")) {
  326 + String receiveKey = VideoManagerConstants.WVP_OTHER_RECEIVE_RTP_INFO + userSetting.getServerId() + "_" + param.getStream();
  327 + OtherRtpSendInfo otherRtpSendInfo = (OtherRtpSendInfo) redisTemplate.opsForValue().get(receiveKey);
  328 +
  329 + String receiveKeyForPS = VideoManagerConstants.WVP_OTHER_RECEIVE_PS_INFO + userSetting.getServerId() + "_" + param.getStream();
  330 + OtherPsSendInfo otherPsSendInfo = (OtherPsSendInfo) redisTemplate.opsForValue().get(receiveKeyForPS);
  331 + if (otherRtpSendInfo != null || otherPsSendInfo != null) {
  332 + result.setEnable_mp4(true);
  333 + }
  334 + }
  335 + logger.info("[ZLM HOOK]推流鉴权 响应:{}->{}->>>>{}", param.getMediaServerId(), param, result);
  336 + return result;
  337 + }
  338 +
  339 +
  340 + /**
  341 + * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。
  342 + */
  343 + @ResponseBody
  344 + @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
  345 + public HookResult onStreamChanged(@RequestBody OnStreamChangedHookParam param) {
  346 +
  347 + if (param.isRegist()) {
  348 + logger.info("[ZLM HOOK] 流注册, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
  349 + } else {
  350 + logger.info("[ZLM HOOK] 流注销, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
  351 + }
  352 +
  353 + JSONObject json = (JSONObject) JSON.toJSON(param);
  354 + taskExecutor.execute(() -> {
  355 + ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json);
  356 + MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
  357 + if (mediaInfo == null) {
  358 + logger.info("[ZLM HOOK] 流变化未找到ZLM, {}", param.getMediaServerId());
  359 + return;
  360 + }
  361 + if (subscribe != null) {
  362 + subscribe.response(mediaInfo, param);
  363 + }
  364 +
  365 + List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
  366 + // TODO 重构此处逻辑
  367 + if (param.isRegist()) {
  368 + // 处理流注册的鉴权信息, 流注销这里不再删除鉴权信息,下次来了新的鉴权信息会对就的进行覆盖
  369 + if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
  370 + || param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
  371 + || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
  372 + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
  373 + if (streamAuthorityInfo == null) {
  374 + streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
  375 + } else {
  376 + streamAuthorityInfo.setOriginType(param.getOriginType());
  377 + streamAuthorityInfo.setOriginTypeStr(param.getOriginTypeStr());
  378 + }
  379 + redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
  380 + }
  381 + }
  382 + if ("rtsp".equals(param.getSchema())) {
  383 + logger.info("流变化:注册->{}, app->{}, stream->{}", param.isRegist(), param.getApp(), param.getStream());
  384 + if (param.isRegist()) {
  385 + mediaServerService.addCount(param.getMediaServerId());
  386 + } else {
  387 + mediaServerService.removeCount(param.getMediaServerId());
  388 + }
  389 +
  390 + int updateStatusResult = streamProxyService.updateStatus(param.isRegist(), param.getApp(), param.getStream());
  391 + if (updateStatusResult > 0) {
  392 +
  393 + }
  394 +
  395 + if ("rtp".equals(param.getApp()) && !param.isRegist()) {
  396 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
  397 + if (inviteInfo != null && (inviteInfo.getType() == InviteSessionType.PLAY || inviteInfo.getType() == InviteSessionType.PLAYBACK)) {
  398 + inviteStreamService.removeInviteInfo(inviteInfo);
  399 + storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
  400 + }
  401 + } else if ("broadcast".equals(param.getApp())) {
  402 + // 语音对讲推流 stream需要满足格式deviceId_channelId
  403 + if (param.getStream().indexOf("_") > 0) {
  404 + String[] streamArray = param.getStream().split("_");
  405 + if (streamArray.length == 2) {
  406 + String deviceId = streamArray[0];
  407 + String channelId = streamArray[1];
  408 + Device device = deviceService.getDevice(deviceId);
  409 + if (device != null) {
  410 + if (param.isRegist()) {
  411 + if (audioBroadcastManager.exit(deviceId, channelId)) {
  412 + playService.stopAudioBroadcast(deviceId, channelId);
  413 + }
  414 + // 开启语音对讲通道
  415 + try {
  416 + playService.audioBroadcastCmd(device, channelId, mediaInfo, param.getApp(), param.getStream(), 60, false, (msg) -> {
  417 + logger.info("[语音对讲] 通道建立成功, device: {}, channel: {}", deviceId, channelId);
  418 + });
  419 + } catch (InvalidArgumentException | ParseException | SipException e) {
  420 + logger.error("[命令发送失败] 语音对讲: {}", e.getMessage());
  421 + }
  422 + } else {
  423 + // 流注销
  424 + playService.stopAudioBroadcast(deviceId, channelId);
  425 + }
  426 + } else {
  427 + logger.info("[语音对讲] 未找到设备:{}", deviceId);
  428 + }
  429 + }
  430 + }
  431 + } else if ("talk".equals(param.getApp())) {
  432 + // 语音对讲推流 stream需要满足格式deviceId_channelId
  433 + if (param.getStream().indexOf("_") > 0) {
  434 + String[] streamArray = param.getStream().split("_");
  435 + if (streamArray.length == 2) {
  436 + String deviceId = streamArray[0];
  437 + String channelId = streamArray[1];
  438 + Device device = deviceService.getDevice(deviceId);
  439 + if (device != null) {
  440 + if (param.isRegist()) {
  441 + if (audioBroadcastManager.exit(deviceId, channelId)) {
  442 + playService.stopAudioBroadcast(deviceId, channelId);
  443 + }
  444 + // 开启语音对讲通道
  445 + playService.talkCmd(device, channelId, mediaInfo, param.getStream(), (msg) -> {
  446 + logger.info("[语音对讲] 通道建立成功, device: {}, channel: {}", deviceId, channelId);
  447 + });
  448 + } else {
  449 + // 流注销
  450 + playService.stopTalk(device, channelId, param.isRegist());
  451 + }
  452 + } else {
  453 + logger.info("[语音对讲] 未找到设备:{}", deviceId);
  454 + }
  455 + }
  456 + }
  457 +
  458 + } else {
  459 + if (!"rtp".equals(param.getApp())) {
  460 + String type = OriginType.values()[param.getOriginType()].getType();
  461 + if (param.isRegist()) {
  462 + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(
  463 + param.getApp(), param.getStream());
  464 + String callId = null;
  465 + if (streamAuthorityInfo != null) {
  466 + callId = streamAuthorityInfo.getCallId();
  467 + }
  468 + StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaInfo,
  469 + param.getApp(), param.getStream(), tracks, callId);
  470 + param.setStreamInfo(new StreamContent(streamInfoByAppAndStream));
  471 + redisCatchStorage.addStream(mediaInfo, type, param.getApp(), param.getStream(), param);
  472 + if (param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
  473 + || param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
  474 + || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
  475 + param.setSeverId(userSetting.getServerId());
  476 + zlmMediaListManager.addPush(param);
  477 +
  478 + // 冗余数据,自己系统中自用
  479 + redisCatchStorage.addPushListItem(param.getApp(), param.getStream(), param);
  480 + }
  481 + } else {
  482 + // 兼容流注销时类型从redis记录获取
  483 + OnStreamChangedHookParam onStreamChangedHookParam = redisCatchStorage.getStreamInfo(
  484 + param.getApp(), param.getStream(), param.getMediaServerId());
  485 + if (onStreamChangedHookParam != null) {
  486 + type = OriginType.values()[onStreamChangedHookParam.getOriginType()].getType();
  487 + redisCatchStorage.removeStream(mediaInfo.getId(), type, param.getApp(), param.getStream());
  488 + if ("PUSH".equalsIgnoreCase(type)) {
  489 + // 冗余数据,自己系统中自用
  490 + redisCatchStorage.removePushListItem(param.getApp(), param.getStream(), param.getMediaServerId());
  491 + }
  492 + }
  493 + GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
  494 + if (gbStream != null) {
  495 +// eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF);
  496 + }
  497 + zlmMediaListManager.removeMedia(param.getApp(), param.getStream());
  498 + }
  499 + GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
  500 + if (gbStream != null) {
  501 + if (userSetting.isUsePushingAsStatus()) {
  502 + eventPublisher.catalogEventPublishForStream(null, gbStream, param.isRegist() ? CatalogEvent.ON : CatalogEvent.OFF);
  503 + }
  504 + }
  505 + if (type != null) {
  506 + // 发送流变化redis消息
  507 + JSONObject jsonObject = new JSONObject();
  508 + jsonObject.put("serverId", userSetting.getServerId());
  509 + jsonObject.put("app", param.getApp());
  510 + jsonObject.put("stream", param.getStream());
  511 + jsonObject.put("register", param.isRegist());
  512 + jsonObject.put("mediaServerId", param.getMediaServerId());
  513 + redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
  514 + }
  515 + }
  516 + }
  517 + if (!param.isRegist()) {
  518 + List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
  519 + if (!sendRtpItems.isEmpty()) {
  520 + for (SendRtpItem sendRtpItem : sendRtpItems) {
  521 + if (sendRtpItem != null && sendRtpItem.getApp().equals(param.getApp())) {
  522 + String platformId = sendRtpItem.getPlatformId();
  523 + ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
  524 + Device device = deviceService.getDevice(platformId);
  525 +
  526 + try {
  527 + if (platform != null) {
  528 + commanderFroPlatform.streamByeCmd(platform, sendRtpItem);
  529 + redisCatchStorage.deleteSendRTPServer(platformId, sendRtpItem.getChannelId(),
  530 + sendRtpItem.getCallId(), sendRtpItem.getStream());
  531 + } else {
  532 + cmder.streamByeCmd(device, sendRtpItem.getChannelId(), param.getStream(), sendRtpItem.getCallId());
  533 + if (sendRtpItem.getPlayType().equals(InviteStreamType.BROADCAST)
  534 + || sendRtpItem.getPlayType().equals(InviteStreamType.TALK)) {
  535 + AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
  536 + if (audioBroadcastCatch != null) {
  537 + // 来自上级平台的停止对讲
  538 + logger.info("[停止对讲] 来自上级,平台:{}, 通道:{}", sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
  539 + audioBroadcastManager.del(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
  540 + }
  541 + }
  542 + }
  543 + } catch (SipException | InvalidArgumentException | ParseException |
  544 + SsrcTransactionNotFoundException e) {
  545 + logger.error("[命令发送失败] 发送BYE: {}", e.getMessage());
  546 + }
  547 + }
  548 + }
  549 + }
  550 + }
  551 + }
  552 + });
  553 + return HookResult.SUCCESS();
  554 + }
  555 +
  556 + /**
  557 + * 流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流。
  558 + */
  559 + @ResponseBody
  560 + @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8")
  561 + public JSONObject onStreamNoneReader(@RequestBody OnStreamNoneReaderHookParam param) {
  562 +
  563 + logger.info("[ZLM HOOK]流无人观看:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(),
  564 + param.getApp(), param.getStream());
  565 + JSONObject ret = new JSONObject();
  566 + ret.put("code", 0);
  567 + stremProxyService1078.sendIORequestStop(param.getStream());
  568 + // 国标类型的流
  569 + if ("rtp".equals(param.getApp())) {
  570 + ret.put("close", userSetting.getStreamOnDemand());
  571 + String stream = param.getStream();
  572 + Object port = redisTemplate.opsForValue().get("tag:history:port:" + stream);
  573 + Object httpPort = redisTemplate.opsForValue().get("tag:history:httpPort:" + stream);
  574 + Object time = redisTemplate.opsForValue().get("tag:history:httpPort:time:" + stream);
  575 +
  576 + if (ObjectUtils.isEmpty(port)) {
  577 + port = -1;
  578 + }
  579 +
  580 + if (ObjectUtils.isEmpty(httpPort)) {
  581 + httpPort = -1;
  582 + }
  583 +
  584 + if (Objects.isNull(time) || Objects.equals(0L, time)) {
  585 + Map<String, Object> resl = jt1078OfCarController.sendIORequestStop(StringUtils.substringBefore(stream, "-"), StringUtils.substringAfter(stream, "-"), stream, (Integer) port, (Integer) httpPort);
  586 + if (!StringUtils.equals(String.valueOf(resl.get("code")), "1")) {
  587 + logger.info("停流失败,稍后再试:{},{},{}", stream, port, httpPort);
  588 + return ret;
  589 + }
  590 + }
  591 +
  592 + // 国标流, 点播/录像回放/录像下载
  593 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
  594 + // 点播
  595 + if (inviteInfo != null) {
  596 + // 录像下载
  597 + if (inviteInfo.getType() == InviteSessionType.DOWNLOAD) {
  598 + ret.put("close", false);
  599 + return ret;
  600 + }
  601 + // 收到无人观看说明流也没有在往上级推送
  602 + if (redisCatchStorage.isChannelSendingRTP(inviteInfo.getChannelId())) {
  603 + List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByChannelId(
  604 + inviteInfo.getChannelId());
  605 + if (!sendRtpItems.isEmpty()) {
  606 + for (SendRtpItem sendRtpItem : sendRtpItems) {
  607 + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
  608 + try {
  609 + commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
  610 + } catch (SipException | InvalidArgumentException | ParseException e) {
  611 + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
  612 + }
  613 + redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
  614 + sendRtpItem.getCallId(), sendRtpItem.getStream());
  615 + if (InviteStreamType.PUSH == sendRtpItem.getPlayType()) {
  616 + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
  617 + sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(),
  618 + sendRtpItem.getPlatformId(), parentPlatform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId());
  619 + messageForPushChannel.setPlatFormIndex(parentPlatform.getId());
  620 + redisCatchStorage.sendPlatformStopPlayMsg(messageForPushChannel);
  621 + }
  622 + }
  623 + }
  624 + }
  625 + Device device = deviceService.getDevice(inviteInfo.getDeviceId());
  626 + if (device != null) {
  627 + try {
  628 + // 多查询一次防止已经被处理了
  629 + InviteInfo info = inviteStreamService.getInviteInfo(inviteInfo.getType(),
  630 + inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream());
  631 + if (info != null) {
  632 + cmder.streamByeCmd(device, inviteInfo.getChannelId(),
  633 + inviteInfo.getStream(), null);
  634 + } else {
  635 + logger.info("[无人观看] 未找到设备的点播信息: {}, 流:{}", inviteInfo.getDeviceId(), param.getStream());
  636 + }
  637 + } catch (InvalidArgumentException | ParseException | SipException |
  638 + SsrcTransactionNotFoundException e) {
  639 + logger.error("[无人观看]点播, 发送BYE失败 {}", e.getMessage());
  640 + }
  641 + } else {
  642 + logger.info("[无人观看] 未找到设备: {},流:{}", inviteInfo.getDeviceId(), param.getStream());
  643 + }
  644 +
  645 + inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(),
  646 + inviteInfo.getChannelId(), inviteInfo.getStream());
  647 + storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
  648 + return ret;
  649 + }
  650 + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, param.getStream(), null);
  651 + if (sendRtpItem != null && "talk".equals(sendRtpItem.getApp())) {
  652 + ret.put("close", false);
  653 + return ret;
  654 + }
  655 + } else if ("talk".equals(param.getApp()) || "broadcast".equals(param.getApp())) {
  656 + ret.put("close", false);
  657 + } else {
  658 + // 非国标流 推流/拉流代理
  659 + // 拉流代理
  660 + StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
  661 + if (streamProxyItem != null) {
  662 + if (streamProxyItem.isEnableRemoveNoneReader()) {
  663 + // 无人观看自动移除
  664 + ret.put("close", true);
  665 + streamProxyService.del(param.getApp(), param.getStream());
  666 + String url = streamProxyItem.getUrl() != null ? streamProxyItem.getUrl() : streamProxyItem.getSrcUrl();
  667 + logger.info("[{}/{}]<-[{}] 拉流代理无人观看已经移除", param.getApp(), param.getStream(), url);
  668 + } else if (streamProxyItem.isEnableDisableNoneReader()) {
  669 + // 无人观看停用
  670 + ret.put("close", true);
  671 + // 修改数据
  672 + streamProxyService.stop(param.getApp(), param.getStream());
  673 + } else {
  674 + // 无人观看不做处理
  675 + ret.put("close", false);
  676 + }
  677 + return ret;
  678 + }
  679 +
  680 +
  681 + // TODO 推流具有主动性,暂时不做处理
  682 +// StreamPushItem streamPushItem = streamPushService.getPush(app, streamId);
  683 +// if (streamPushItem != null) {
  684 +// // TODO 发送停止
  685 +// }
  686 + //推流需要更新缓存数据以防止端口占用
  687 + logger.info("无人观看流 ---> {}", JSONObject.toJSONString(param));
  688 + Object object = redisTemplate.opsForValue().get("patrol:stream:" + param.getStream());
  689 + if (object == null) {
  690 + String[] split = param.getStream().split("_");
  691 + if (split != null && split.length == 2) {
  692 + if (split[1].equals("voice")){
  693 + intercomService.stopIntercom(split[0]);
  694 + logger.info("{} 历史语音断流成功", split[0]);
  695 + }else {
  696 + try {
  697 + jt1078OfCarController.clearMap(split[1],split[0]);
  698 + } catch (ServiceException e) {
  699 + throw new RuntimeException(e);
  700 + }
  701 + }
  702 + }
  703 + }
  704 + }
  705 + return ret;
  706 + }
  707 +
  708 + /**
  709 + * 流未找到事件,用户可以在此事件触发时,立即去拉流,这样可以实现按需拉流;此事件对回复不敏感。
  710 + */
  711 + @ResponseBody
  712 + @PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8")
  713 + public DeferredResult<HookResult> onStreamNotFound(@RequestBody OnStreamNotFoundHookParam param) {
  714 + logger.info("[ZLM HOOK] 流未找到:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
  715 +
  716 + DeferredResult<HookResult> defaultResult = new DeferredResult<>();
  717 +
  718 + MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
  719 + if (!userSetting.isAutoApplyPlay() || mediaInfo == null) {
  720 + defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
  721 + return defaultResult;
  722 + }
  723 + if ("rtp".equals(param.getApp())) {
  724 + String[] s = param.getStream().split("_");
  725 + if ((s.length != 2 && s.length != 4)) {
  726 + defaultResult.setResult(HookResult.SUCCESS());
  727 + return defaultResult;
  728 + }
  729 + String deviceId = s[0];
  730 + String channelId = s[1];
  731 + Device device = redisCatchStorage.getDevice(deviceId);
  732 + if (device == null || !device.isOnLine()) {
  733 + defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
  734 + return defaultResult;
  735 + }
  736 + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
  737 + if (deviceChannel == null) {
  738 + defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
  739 + return defaultResult;
  740 + }
  741 + if (s.length == 2) {
  742 + logger.info("[ZLM HOOK] 预览流未找到, 发起自动点播:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
  743 +
  744 + RequestMessage msg = new RequestMessage();
  745 + String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
  746 + boolean exist = resultHolder.exist(key, null);
  747 + msg.setKey(key);
  748 + String uuid = UUID.randomUUID().toString();
  749 + msg.setId(uuid);
  750 + DeferredResult<HookResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
  751 +
  752 + result.onTimeout(() -> {
  753 + logger.info("[ZLM HOOK] 预览流自动点播, 等待超时");
  754 + msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "点播超时"));
  755 + resultHolder.invokeAllResult(msg);
  756 + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
  757 + storager.stopPlay(deviceId, channelId);
  758 + });
  759 +
  760 + resultHolder.put(key, uuid, result);
  761 +
  762 + if (!exist) {
  763 + playService.play(mediaInfo, deviceId, channelId, null, (code, message, data) -> {
  764 + msg.setData(new HookResult(code, message));
  765 + resultHolder.invokeResult(msg);
  766 + });
  767 + }
  768 + return result;
  769 + } else if (s.length == 4) {
  770 + // 此时为录像回放, 录像回放格式为> 设备ID_通道ID_开始时间_结束时间
  771 + String startTimeStr = s[2];
  772 + String endTimeStr = s[3];
  773 + if (startTimeStr == null || endTimeStr == null || startTimeStr.length() != 14 || endTimeStr.length() != 14) {
  774 + defaultResult.setResult(HookResult.SUCCESS());
  775 + return defaultResult;
  776 + }
  777 + String startTime = DateUtil.urlToyyyy_MM_dd_HH_mm_ss(startTimeStr);
  778 + String endTime = DateUtil.urlToyyyy_MM_dd_HH_mm_ss(endTimeStr);
  779 + logger.info("[ZLM HOOK] 回放流未找到, 发起自动点播:{}->{}->{}/{}-{}-{}",
  780 + param.getMediaServerId(), param.getSchema(),
  781 + param.getApp(), param.getStream(),
  782 + startTime, endTime
  783 + );
  784 + RequestMessage msg = new RequestMessage();
  785 + String key = DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId;
  786 + boolean exist = resultHolder.exist(key, null);
  787 + msg.setKey(key);
  788 + String uuid = UUID.randomUUID().toString();
  789 + msg.setId(uuid);
  790 + DeferredResult<HookResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
  791 +
  792 + result.onTimeout(() -> {
  793 + logger.info("[ZLM HOOK] 回放流自动点播, 等待超时");
  794 + // 释放rtpserver
  795 + msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "点播超时"));
  796 + resultHolder.invokeResult(msg);
  797 + });
  798 +
  799 + resultHolder.put(key, uuid, result);
  800 +
  801 + if (!exist) {
  802 + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaInfo, param.getStream(), null,
  803 + device.isSsrcCheck(), true, 0, false, false, device.getStreamModeForParam());
  804 + playService.playBack(mediaInfo, ssrcInfo, deviceId, channelId, startTime, endTime, (code, message, data) -> {
  805 + msg.setData(new HookResult(code, message));
  806 + resultHolder.invokeResult(msg);
  807 + });
  808 + }
  809 + return result;
  810 + } else {
  811 + defaultResult.setResult(HookResult.SUCCESS());
  812 + return defaultResult;
  813 + }
  814 +
  815 + } else {
  816 + // 拉流代理
  817 + StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
  818 + if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnableDisableNoneReader()) {
  819 + streamProxyService.start(param.getApp(), param.getStream());
  820 + }
  821 + DeferredResult<HookResult> result = new DeferredResult<>();
  822 + result.setResult(HookResult.SUCCESS());
  823 + return result;
  824 + }
  825 + }
  826 +
  827 + /**
  828 + * 服务器启动事件,可以用于监听服务器崩溃重启;此事件对回复不敏感。
  829 + */
  830 + @ResponseBody
  831 + @PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8")
  832 + public HookResult onServerStarted(HttpServletRequest request, @RequestBody JSONObject jsonObject) {
  833 +
  834 + jsonObject.put("ip", request.getRemoteAddr());
  835 + ZLMServerConfig zlmServerConfig = JSON.to(ZLMServerConfig.class, jsonObject);
  836 + zlmServerConfig.setIp(request.getRemoteAddr());
  837 + logger.info("[ZLM HOOK] zlm 启动 " + zlmServerConfig.getGeneralMediaServerId());
  838 + taskExecutor.execute(() -> {
  839 + List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_started);
  840 + if (subscribes != null && !subscribes.isEmpty()) {
  841 + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
  842 + subscribe.response(null, zlmServerConfig);
  843 + }
  844 + }
  845 + mediaServerService.zlmServerOnline(zlmServerConfig);
  846 + });
  847 +
  848 + return HookResult.SUCCESS();
  849 + }
  850 +
  851 + /**
  852 + * 发送rtp(startSendRtp)被动关闭时回调
  853 + */
  854 + @ResponseBody
  855 + @PostMapping(value = "/on_send_rtp_stopped", produces = "application/json;charset=UTF-8")
  856 + public HookResult onSendRtpStopped(HttpServletRequest request, @RequestBody OnSendRtpStoppedHookParam param) {
  857 +
  858 + logger.info("[ZLM HOOK] rtp发送关闭:{}->{}/{}", param.getMediaServerId(), param.getApp(), param.getStream());
  859 +
  860 + // 查找对应的上级推流,发送停止
  861 + if (!"rtp".equals(param.getApp())) {
  862 + return HookResult.SUCCESS();
  863 + }
  864 + taskExecutor.execute(() -> {
  865 + List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
  866 + if (sendRtpItems.size() > 0) {
  867 + for (SendRtpItem sendRtpItem : sendRtpItems) {
  868 + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
  869 + ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
  870 + try {
  871 + commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
  872 + } catch (SipException | InvalidArgumentException | ParseException e) {
  873 + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
  874 + }
  875 + redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
  876 + sendRtpItem.getCallId(), sendRtpItem.getStream());
  877 + }
  878 + }
  879 + });
  880 +
  881 + return HookResult.SUCCESS();
  882 + }
  883 +
  884 + /**
  885 + * rtpServer收流超时
  886 + */
  887 + @ResponseBody
  888 + @PostMapping(value = "/on_rtp_server_timeout", produces = "application/json;charset=UTF-8")
  889 + public HookResult onRtpServerTimeout(@RequestBody OnRtpServerTimeoutHookParam
  890 + param) {
  891 + logger.info("[ZLM HOOK] rtpServer收流超时:{}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc());
  892 +
  893 + taskExecutor.execute(() -> {
  894 + List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
  895 + if (subscribes != null && !subscribes.isEmpty()) {
  896 + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
  897 + subscribe.response(null, param);
  898 + }
  899 + }
  900 + });
  901 +
  902 + return HookResult.SUCCESS();
  903 + }
  904 +
  905 + /**
  906 + * 录像完成事件
  907 + */
  908 + @ResponseBody
  909 + @PostMapping(value = "/on_record_mp4", produces = "application/json;charset=UTF-8")
  910 + public HookResult onRecordMp4(HttpServletRequest request, @RequestBody OnRecordMp4HookParam param) {
  911 + logger.info("[ZLM HOOK] 录像完成事件:{}->{}", param.getMediaServerId(), param.getFile_path());
  912 +
  913 + taskExecutor.execute(() -> {
  914 + List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_record_mp4);
  915 + if (subscribes != null && !subscribes.isEmpty()) {
  916 + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
  917 + subscribe.response(null, param);
  918 + }
  919 + }
  920 + cloudRecordService.addRecord(param);
  921 +
  922 + });
  923 +
  924 + return HookResult.SUCCESS();
  925 + }
  926 +
  927 + private Map<String, String> urlParamToMap(String params) {
  928 + HashMap<String, String> map = new HashMap<>();
  929 + if (ObjectUtils.isEmpty(params)) {
  930 + return map;
  931 + }
  932 + String[] paramsArray = params.split("&");
  933 + if (paramsArray.length == 0) {
  934 + return map;
  935 + }
  936 + for (String param : paramsArray) {
  937 + String[] paramArray = param.split("=");
  938 + if (paramArray.length == 2) {
  939 + map.put(paramArray[0], paramArray[1]);
  940 + }
  941 + }
  942 + return map;
  943 + }
  944 +}
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
... ... @@ -107,7 +107,6 @@ public class ZLMRESTfulUtils {
107 107 responseJSON = JSON.parseObject(responseStr);
108 108 }
109 109 }else {
110   - System.out.println( 2222);
111 110 System.out.println( response.code());
112 111 response.close();
113 112 Objects.requireNonNull(response.body()).close();
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
... ... @@ -10,6 +10,7 @@ import com.genersoft.iot.vmp.conf.UserSetting;
10 10 import com.genersoft.iot.vmp.gb28181.bean.*;
11 11 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
12 12 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
  13 +import com.genersoft.iot.vmp.jtt1078.util.Configs;
13 14 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
14 15 import com.genersoft.iot.vmp.media.zlm.dto.*;
15 16 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
... ... @@ -23,8 +24,10 @@ import com.genersoft.iot.vmp.storager.mapper.*;
23 24 import com.genersoft.iot.vmp.utils.DateUtil;
24 25 import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo;
25 26 import com.genersoft.iot.vmp.vmanager.jt1078.platform.config.Jt1078ConfigBean;
  27 +import com.genersoft.iot.vmp.vmanager.jt1078.platform.config.RtspConfigBean;
26 28 import com.genersoft.iot.vmp.vmanager.jt1078.platform.config.ThirdPartyHttpService;
27 29 import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.StreamSwitch;
  30 +import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.T9101;
28 31 import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.T9102;
29 32 import com.genersoft.iot.vmp.vmanager.util.RedisCache;
30 33 import com.github.pagehelper.PageHelper;
... ... @@ -55,6 +58,9 @@ public class StreamPushServiceImpl implements IStreamPushService {
55 58 private GbStreamMapper gbStreamMapper;
56 59  
57 60 @Autowired
  61 + private RtspConfigBean rtspConfigBean;
  62 +
  63 + @Autowired
58 64 private StreamPushMapper streamPushMapper;
59 65  
60 66 @Autowired
... ...
src/main/resources/app.properties
1 1 server.port = 40000
2   -server.http.port = 3333
  2 +server.http.port = 3335
3 3 server.history.port = 40001
4 4 server.backlog = 1024
5 5  
... ... @@ -14,7 +14,7 @@ rtmp.url = rtsp://127.0.0.1:554/schedule/{TAG}?sign={sign}
14 14  
15 15 #rtmp.url = rtsp://192.168.169.100:19555/schedule/{TAG}?sign={sign}
16 16 # 设置为on时,控制台将输出ffmpeg的输出
17   -debug.mode = off
  17 +debug.mode = on
18 18  
19 19 zlm.host = 127.0.0.1
20 20 zlm.http.port = 80
... ...
web_src/config/index.js
... ... @@ -29,7 +29,7 @@ module.exports = {
29 29 // Various Dev Server settings
30 30 host: "127.0.0.1",
31 31 useLocalIp: false, // can be overwritten by process.env.HOST
32   - port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
  32 + port: 8085, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
33 33 autoOpenBrowser: false,
34 34 errorOverlay: true,
35 35 notifyOnErrors: true,
... ...
web_src/src/components/DeviceList1078.vue
... ... @@ -6,6 +6,7 @@
6 6 ref="vehicleList"
7 7 @tree-loaded="handleTreeLoaded"
8 8 @node-click="nodeClick"
  9 + @node-dblclick="onVehicleNodeDblclick"
9 10 @node-contextmenu="nodeContextmenu"
10 11 />
11 12 </div>
... ... @@ -85,6 +86,7 @@
85 86 @playerClick="handleClick"
86 87 :video-url="videoUrl"
87 88 :videoDataList="videoDataList"
  89 + :has-audio="true"
88 90 v-model="windowNum"
89 91 style="width: 100%; height: 100%;"
90 92 ></player-list-component>
... ... @@ -712,7 +714,7 @@ export default {
712 714 this.intercomTarget = null;
713 715 this.intercomTargetName = '';
714 716 this.currentIntercomSim = null;
715   - this.$message.info("对讲已挂断");
  717 + // this.$message.info("对讲已挂断");
716 718 },
717 719  
718 720 // 工具:降采样 (Float32 -> Int16)
... ... @@ -795,6 +797,12 @@ export default {
795 797 }
796 798 },
797 799  
  800 + /** 双击车辆(含子通道的节点):与右键「一键播放该设备」相同 */
  801 + onVehicleNodeDblclick(data) {
  802 + if (!data.children || data.children.length === 0) return;
  803 + this.batchPlayback(data);
  804 + },
  805 +
798 806 playSingleChannel(data) {
799 807 let stream = data.code.replace('-', '_');
800 808 let arr = stream.split("_");
... ... @@ -905,7 +913,7 @@ export default {
905 913 clearTimeout(this.carouselTimer);
906 914 this.carouselTimer = null;
907 915 }
908   - this.$message.info("轮播已停止");
  916 + // this.$message.info("轮播已停止");
909 917 },
910 918  
911 919 async startCarousel(config) {
... ...
web_src/src/components/HistoricalRecord.vue
... ... @@ -29,22 +29,44 @@
29 29 :query-params="queryParams"
30 30 @handleQuery="handleQuery"
31 31 />
32   - <el-row v-if="deviceData || channelData" :gutter="10" class="mb8" style="margin-bottom: 10px;">
  32 + <el-row v-if="showSelectionTag" :gutter="10" class="mb8" style="margin-bottom: 10px;">
33 33 <el-col :span="24">
34 34 <el-tag effect="dark" type="success">
35 35 <i class="el-icon-video-camera"></i> {{ deviceTitle }}
36 36 </el-tag>
37 37 </el-col>
38 38 </el-row>
  39 +
  40 + <div v-if="allVehicleChannels.length" class="channel-pick">
  41 + <div class="channel-pick-main">
  42 + <span class="channel-pick-label">查询通道(可多选):</span>
  43 + <el-checkbox-group v-model="checkedChannelKeys" class="channel-pick-group">
  44 + <el-checkbox
  45 + v-for="c in allVehicleChannels"
  46 + :key="c.key"
  47 + :label="c.key"
  48 + >
  49 + {{ c.channelName }}
  50 + </el-checkbox>
  51 + </el-checkbox-group>
  52 + </div>
  53 + <div class="channel-pick-actions">
  54 + <el-button type="text" size="mini" @click="selectAllChannels">全选</el-button>
  55 + <el-button type="text" size="mini" @click="clearChannelSelection">清空</el-button>
  56 + </div>
  57 + </div>
39 58 </div>
40 59  
41 60 <div class="table-section">
42 61 <history-search-table
43 62 ref="historySearchTable"
44   - style="width: 100%; height: 100%;"
  63 + class="history-search-table-host"
45 64 :table-data="historyData"
  65 + :table-height="historyTableHeight"
  66 + :busy-channel-keys="busyChannelKeys"
46 67 @playHistoryVideo="clickHistoricalPlay"
47   - @uploadHistoryVideo="uploadHistoryVideo"
  68 + @uploadHistoryVideo="onUploadHistoryVideo"
  69 + @channel-operation="onChannelOperation"
48 70 />
49 71 </div>
50 72  
... ... @@ -57,19 +79,18 @@
57 79 </template>
58 80  
59 81 <script>
60   -// 1. 引入 VehicleList (替换 Device1078Tree)
61 82 import VehicleList from "./JT1078Components/deviceList/VehicleList.vue";
62 83 import HistoryPlayDialog from "./JT1078Components/HistoryPlayDialog.vue";
63 84 import HistoricalRecordForm from "./JT1078Components/HistoryRecordFrom.vue";
64 85 import HistorySearchTable from "./JT1078Components/HistorySearchTable.vue";
65 86 import { Splitpanes, Pane } from 'splitpanes'
66 87 import 'splitpanes/dist/splitpanes.css'
67   -import {parseTime} from "../../utils/ruoyi";
  88 +import { parseTime } from "../../utils/ruoyi";
68 89  
69 90 export default {
70 91 name: "HistoricalRecord",
71 92 components: {
72   - VehicleList, // 注册组件
  93 + VehicleList,
73 94 HistoryPlayDialog,
74 95 HistorySearchTable,
75 96 HistoricalRecordForm,
... ... @@ -78,18 +99,18 @@ export default {
78 99 },
79 100 data() {
80 101 return {
81   - //列表定时器
82 102 timer: null,
83   - //历史视频列表定时器
84 103 historyTimer: null,
85 104 historyData: [],
86   - //源列表数据
87 105 sourceValue: [],
88   - //遮罩层
89 106 loading: false,
90   - //sim号和通道号
91 107 sim_channel: null,
92   - channelData: null,
  108 + selectedChannels: [],
  109 + /** 当前车辆下可选通道(用于复选框) */
  110 + allVehicleChannels: [],
  111 + /** 选中的通道 key:sim_channel */
  112 + checkedChannelKeys: [],
  113 + channelData: null,
93 114 nodeChannelData: null,
94 115 deviceData: null,
95 116 deviceTitle: '',
... ... @@ -101,8 +122,28 @@ export default {
101 122 },
102 123 videoUrl: [],
103 124 deviceNode: null,
  125 + busyChannelKeys: [],
  126 + /** 用于计算表格固定高度,避免 el-table height=100% 无像素高度导致无法滚动 */
  127 + viewportHeight: typeof window !== 'undefined' ? window.innerHeight : 600
104 128 };
105 129 },
  130 + computed: {
  131 + showSelectionTag() {
  132 + return this.allVehicleChannels.length > 0 || this.deviceData || this.channelData
  133 + },
  134 + historyTableHeight() {
  135 + const v = this.viewportHeight || 600
  136 + /* 预留:顶栏/标签/表单/通道区/内边距(略多留一点以免裁切) */
  137 + const reserved = 340
  138 + return Math.max(260, Math.floor(v - reserved))
  139 + }
  140 + },
  141 + mounted() {
  142 + this._onViewportResize = () => {
  143 + this.viewportHeight = window.innerHeight
  144 + }
  145 + window.addEventListener('resize', this._onViewportResize)
  146 + },
106 147 watch: {
107 148 deviceNode(val) {
108 149 this.deviceNode = val
... ... @@ -111,12 +152,12 @@ export default {
111 152 }
112 153 }
113 154 },
114   - created() {
115   - // 移除 getCarInfoBuffer() 调用,VehicleList 会自动加载
116   - },
117 155 destroyed() {
118 156 clearInterval(this.timer)
119 157 clearInterval(this.historyTimer)
  158 + if (this._onViewportResize) {
  159 + window.removeEventListener('resize', this._onViewportResize)
  160 + }
120 161 },
121 162 methods: {
122 163 getTodayRange() {
... ... @@ -127,148 +168,304 @@ export default {
127 168 return [startOfToday, endOfToday]
128 169 },
129 170  
130   - /**
131   - * 接收 VehicleList 加载完成的数据
132   - */
133 171 handleTreeLoaded(data) {
134 172 this.sourceValue = data;
135 173 },
136 174  
  175 + channelKey(sim, channel) {
  176 + return String(sim) + '_' + String(channel)
  177 + },
  178 +
  179 + parseChannelFromId(nodeData) {
  180 + const parts = String(nodeData.id).split('_')
  181 + if (parts.length !== 3) return null
  182 + const sim = parts[1]
  183 + const channel = parts[2]
  184 + return {
  185 + sim,
  186 + channel,
  187 + channelName: nodeData.name,
  188 + vehicleName: nodeData.pid,
  189 + key: this.channelKey(sim, channel)
  190 + }
  191 + },
  192 +
  193 + buildNodeChannelMap(children) {
  194 + const map = {}
  195 + if (!children) return map
  196 + for (let i = 0; i < children.length; i++) {
  197 + const c = children[i]
  198 + const ids = c.id.split('_')
  199 + if (ids.length === 3) {
  200 + c.deviceId = ids[0]
  201 + c.channelId = ids[2]
  202 + map[ids[2]] = c
  203 + }
  204 + }
  205 + return map
  206 + },
  207 +
137 208 /**
138   - * 树点击事件 (逻辑保持不变,适配 VehicleList 的数据结构)
  209 + * 点击通道:单通道并默认勾选;点击车辆:列出通道,需手动勾选再搜索
139 210 */
140 211 nodeClick(data, node) {
141   - if (data) {
142   - // VehicleList 生成的 ID 格式通常为 deviceId_sim_channel
143   - let split = data.id.split("_");
144   - this.deviceNode = node
145   - this.nodeChannelData = {};
146   - let nodeChannelDataList = [];
147   -
148   - // 判断是否为通道节点 (根据你的逻辑,长度为3代表是通道)
149   - if (split.length === 3) {
150   - this.sim_channel = split[1] + '_' + split[2]
151   - this.channelData = data
152   - this.deviceTitle = `车辆:${data.pid} 通道:${data.name}` // data.pid 是 VehicleList 处理好的父级ID
153   -
154   - // 获取同级所有通道用于显示
155   - let children = node.parent.data.children;
156   - if (children) {
157   - for (let i in children) {
158   - const nodeChannelData = children[i];
159   - let ids = nodeChannelData.id.split("_");
160   - if (ids.length === 3) {
161   - nodeChannelData.deviceId = ids[0];
162   - nodeChannelData.channelId = ids[2];
163   - nodeChannelDataList.push(nodeChannelData)
164   - }
  212 + if (!data) return
  213 + const split = data.id.split('_')
  214 + this.deviceNode = node
  215 +
  216 + if (split.length === 3) {
  217 + const ch = this.parseChannelFromId(data)
  218 + this.channelData = data
  219 + this.deviceData = node.parent && node.parent.data ? node.parent.data : null
  220 + this.deviceTitle = `车辆:${data.pid} 通道:${data.name}`
  221 +
  222 + const children = node.parent && node.parent.data && node.parent.data.children
  223 + this.nodeChannelData = this.buildNodeChannelMap(children)
  224 +
  225 + this.allVehicleChannels = []
  226 + if (children && children.length) {
  227 + for (let i = 0; i < children.length; i++) {
  228 + const p = this.parseChannelFromId(children[i])
  229 + if (p) {
  230 + p.vehicleName = data.pid
  231 + this.allVehicleChannels.push(p)
165 232 }
166   - this.nodeChannelData = nodeChannelDataList.reduce((map, item) => {
167   - map[item.channelId] = item;
168   - return map;
169   - }, {});
170 233 }
171   - } else {
172   - // 点击了父设备节点
173   - this.deviceData = data;
174   - this.deviceTitle = `车辆:${data.name}`;
175   - // 清空通道选择,或者默认选择第一个通道,视业务需求而定
176   - this.sim_channel = null;
  234 + } else if (ch) {
  235 + this.allVehicleChannels = [ch]
  236 + }
  237 + this.checkedChannelKeys = ch ? [ch.key] : []
  238 + this.selectedChannels = ch ? [ch] : []
  239 + this.sim_channel = ch ? ch.sim + '_' + ch.channel : null
  240 + return
  241 + }
  242 +
  243 + this.deviceData = data
  244 + this.channelData = null
  245 + const kids = data.children
  246 + if (kids && kids.length > 0) {
  247 + const fp = kids[0] && kids[0].id ? String(kids[0].id).split('_') : []
  248 + if (fp.length === 3) {
  249 + this.allVehicleChannels = []
  250 + for (let i = 0; i < kids.length; i++) {
  251 + const p = this.parseChannelFromId(kids[i])
  252 + if (p) {
  253 + p.vehicleName = data.name
  254 + this.allVehicleChannels.push(p)
  255 + }
  256 + }
  257 + this.nodeChannelData = this.buildNodeChannelMap(kids)
  258 + this.checkedChannelKeys = []
  259 + this.selectedChannels = []
  260 + this.sim_channel = null
  261 + this.deviceTitle = `车辆:${data.name} · 请勾选要查询的通道后点击搜索`
  262 + return
177 263 }
178 264 }
  265 + this.allVehicleChannels = []
  266 + this.checkedChannelKeys = []
  267 + this.selectedChannels = []
  268 + this.sim_channel = null
  269 + this.nodeChannelData = {}
  270 + this.deviceTitle = `车辆/分组:${data.name}(请展开后选择车辆或通道)`
  271 + },
  272 +
  273 + selectAllChannels() {
  274 + this.checkedChannelKeys = this.allVehicleChannels.map(c => c.key)
  275 + },
  276 +
  277 + clearChannelSelection() {
  278 + this.checkedChannelKeys = []
179 279 },
180 280  
181 281 handleQuery(queryParams) {
182   - this.queryParams = {...this.queryParams, ...queryParams};
  282 + this.queryParams = { ...this.queryParams, ...queryParams };
183 283 this.searchHistoryList()
184 284 },
185 285  
186   - // --- 【删除】所有手动获取和处理树数据的方法 ---
187   - // 删除 getCarInfoBuffer, getCarInfo, statisticsOnline, processingTreeData
188   - // 删除 addChannels, disableItemsByName, processingSimList
189   - // 理由:VehicleList 组件内部已经完成了这些工作
190   -
191 286 clickHistoricalPlay(data) {
192 287 this.playHistoryItem(data)
193 288 },
194 289  
195   - uploadHistoryVideo(data) {
  290 + channelBusyKey(row) {
  291 + return String(row.sim) + '_' + String(row.channel)
  292 + },
  293 +
  294 + onChannelOperation({ key, busy }) {
  295 + if (busy) {
  296 + if (this.busyChannelKeys.indexOf(key) < 0) {
  297 + this.busyChannelKeys.push(key)
  298 + }
  299 + } else {
  300 + this.busyChannelKeys = this.busyChannelKeys.filter(k => k !== key)
  301 + }
  302 + },
  303 +
  304 + onUploadHistoryVideo(row) {
  305 + const key = this.channelBusyKey(row)
  306 + if (this.busyChannelKeys.indexOf(key) >= 0) {
  307 + this.$message.warning('该通道正在上传或下载,请稍候')
  308 + return
  309 + }
  310 + this.busyChannelKeys.push(key)
196 311 this.loading = true
197 312 this.$axios({
198 313 method: 'get',
199   - url: '/api/jt1078/query/history/uploading/' + data.name
200   - }).then(res => {
  314 + url: '/api/jt1078/query/history/uploading/' + row.name
  315 + }).then(() => {
201 316 this.$message.success("视频开始上传,请等待")
202 317 this.searchHistoryList()
203   - this.loading = false
204   - }).catch(err => {
  318 + }).catch(() => {
205 319 this.$message.error("视频上传失败")
  320 + }).finally(() => {
  321 + this.busyChannelKeys = this.busyChannelKeys.filter(k => k !== key)
206 322 this.loading = false
207 323 })
208 324 },
209 325  
210   - searchHistoryList() {
211   - let simChannel = this.sim_channel;
212   - if (this.isEmpty(simChannel)) {
213   - return this.$message.error('请先点击左侧选择车辆通道');
  326 + extractHistoryItems(res) {
  327 + try {
  328 + const items = res.data && res.data.data && res.data.data.obj && res.data.data.obj.data && res.data.data.obj.data.items
  329 + return Array.isArray(items) ? items : null
  330 + } catch (e) {
  331 + return null
  332 + }
  333 + },
  334 +
  335 + listResponseErrorMsg(res) {
  336 + const top = res && res.data
  337 + const body = top && top.data
  338 + if (body && (body.msg || body.message)) return body.msg || body.message
  339 + if (top && (top.msg || top.message)) return top.msg || top.message
  340 + return ''
  341 + },
  342 +
  343 + channelDisplayName(sim, channelId) {
  344 + const k = String(channelId)
  345 + if (this.nodeChannelData && this.nodeChannelData[k]) {
  346 + return this.nodeChannelData[k].name
214 347 }
  348 + const found = this.allVehicleChannels.find(
  349 + c => String(c.sim) === String(sim) && String(c.channel) === k
  350 + )
  351 + if (found && found.channelName) return found.channelName
  352 + return `通道${Number(channelId)}`
  353 + },
215 354  
216   - let split = simChannel.split('_');
217   - let sim = split[0];
218   - let channel = split[1];
  355 + defaultDeviceIdForRow(sim) {
  356 + if (this.deviceData && this.deviceData.name) return this.deviceData.name
  357 + if (this.channelData && this.channelData.pid) return this.channelData.pid
  358 + const hit = this.allVehicleChannels.find(c => String(c.sim) === String(sim))
  359 + return (hit && hit.vehicleName) ? hit.vehicleName : sim
  360 + },
219 361  
  362 + /** 串行请求历史列表,避免多路并发压垮 JT1078 网关 9205 */
  363 + async searchHistoryList() {
  364 + if (!this.allVehicleChannels.length) {
  365 + return this.$message.error('请先点击左侧选择车辆或通道')
  366 + }
  367 + if (!this.checkedChannelKeys.length) {
  368 + return this.$message.warning('请至少勾选一个要查询的通道')
  369 + }
220 370 if (!this.queryParams.time) {
221 371 return this.$message.error('请选择开始和结束时间');
222 372 }
223 373  
224   - this.loading = true;
225   - this.$axios({
226   - method: 'get',
227   - url: '/api/jt1078/query/history/list/' + sim + '/' + channel + "/" + parseTime(this.queryParams.time[0], '{y}-{m}-{d} {h}:{i}:{s}') + "/" + parseTime(this.queryParams.time[1], '{y}-{m}-{d} {h}:{i}:{s}')
228   - }).then(res => {
229   - let items = res.data.data.obj.data.items;
230   - if (items) {
231   - for (let i in items) {
232   - items[i].disabled = false;
233   - items[i].countdown = 10;
234   - items[i].channelName = this.nodeChannelData[items[i].channel] ? this.nodeChannelData[items[i].channel].name : `通道${Number(items[i].channel)}`
235   - items[i].deviceId = this.deviceData ? this.deviceData.name : this.channelData.pid
  374 + const toQuery = this.allVehicleChannels.filter(c => this.checkedChannelKeys.indexOf(c.key) >= 0)
  375 + if (!toQuery.length) {
  376 + return this.$message.warning('所选通道无效,请重新勾选')
  377 + }
  378 +
  379 + const t0 = parseTime(this.queryParams.time[0], '{y}-{m}-{d} {h}:{i}:{s}')
  380 + const t1 = parseTime(this.queryParams.time[1], '{y}-{m}-{d} {h}:{i}:{s}')
  381 + this.loading = true
  382 + const merged = []
  383 + let failCount = 0
  384 +
  385 + try {
  386 + for (let i = 0; i < toQuery.length; i++) {
  387 + const ch = toQuery[i]
  388 + try {
  389 + const res = await this.$axios({
  390 + method: 'get',
  391 + url: '/api/jt1078/query/history/list/' + ch.sim + '/' + ch.channel + '/' + t0 + '/' + t1
  392 + })
  393 + const inner = res.data && res.data.data
  394 + const code = inner && inner.code
  395 + if (code === '-1' || code === -1) {
  396 + const msg = this.listResponseErrorMsg(res) || '该通道查询失败'
  397 + this.$message.warning(`通道 ${ch.channelName}: ${msg}`)
  398 + failCount++
  399 + continue
  400 + }
  401 + const items = this.extractHistoryItems(res)
  402 + if (!items || !items.length) continue
  403 + for (let j = 0; j < items.length; j++) {
  404 + const it = { ...items[j] }
  405 + it.disabled = false
  406 + it.countdown = 10
  407 + it.channelName = this.channelDisplayName(it.sim || ch.sim, it.channel)
  408 + it.deviceId = this.defaultDeviceIdForRow(it.sim || ch.sim)
  409 + merged.push(it)
  410 + }
  411 + } catch (e) {
  412 + failCount++
  413 + console.warn(e)
  414 + this.$message.warning(`通道 ${ch.channelName} 请求异常`)
236 415 }
237   - this.historyData = items;
238   - } else {
239   - this.historyData = [];
240   - this.$message.warning("搜索历史列表为空");
241 416 }
  417 + merged.sort((a, b) => {
  418 + const ta = new Date(a.startTime || 0).getTime()
  419 + const tb = new Date(b.startTime || 0).getTime()
  420 + return ta - tb
  421 + })
  422 + this.historyData = merged
  423 + this.selectedChannels = toQuery
  424 + if (merged.length === 0) {
  425 + this.$message.warning(failCount > 0 ? '全部通道未返回列表,请查看网关或超时配置' : '搜索历史列表为空')
  426 + }
  427 + } finally {
242 428 this.loading = false
243   - }).catch(error => {
244   - this.loading = false
245   - this.$message.error("发送历史视频列表指令异常");
246   - })
  429 + }
247 430 },
248 431  
249 432 playHistoryItem(e) {
250   - this.videoUrl = [];
251 433 this.loading = true
252 434 this.$axios({
253 435 method: 'get',
254 436 url: '/api/jt1078/query/send/request/io/history/' + e.sim + '/' + e.channel + "/" + e.startTime + "/" + e.endTime + "/" + e.channelMapping
255 437 }).then(res => {
256 438 if (res.data && res.data.data && res.data.data.data) {
257   - let videoUrl1 = (location.protocol === "https:") ? res.data.data.data.wss_flv : res.data.data.data.ws_flv;
258   -
259   - this.$refs.historyPlayDialog.updateOpen(true)
260   - this.$refs.historyPlayDialog.data = {
261   - videoUrl: videoUrl1,
262   - startTime: e.startTime,
263   - endTime: e.endTime,
264   - deviceId: e.deviceId,
265   - channelName: e.channelName,
266   - channel: e.channel,
267   - sim: e.sim
  439 + const d = res.data.data.data
  440 + const videoUrl1 = location.protocol === 'https:'
  441 + ? (d.wss_flv || d.ws_flv || d.flv)
  442 + : (d.ws_flv || d.wss_flv || d.flv)
  443 + if (!videoUrl1) {
  444 + this.$message.error('未获取到播放地址')
  445 + this.loading = false
  446 + return
268 447 }
  448 + const dlg = this.$refs.historyPlayDialog
  449 + dlg.updateOpen(true)
  450 + this.$nextTick(() => {
  451 + dlg.data = {
  452 + videoUrl: videoUrl1,
  453 + startTime: e.startTime,
  454 + endTime: e.endTime,
  455 + deviceId: e.deviceId,
  456 + channelName: e.channelName,
  457 + channel: e.channel,
  458 + sim: e.sim,
  459 + channelMapping: e.channelMapping
  460 + }
  461 + this.loading = false
  462 + })
269 463 } else {
270 464 this.$message.error(res.data.msg || "获取播放地址失败");
  465 + this.loading = false
271 466 }
  467 + }).catch(() => {
  468 + this.$message.error("请求播放地址异常")
272 469 this.loading = false
273 470 })
274 471 },
... ... @@ -285,7 +482,6 @@ export default {
285 482 </script>
286 483  
287 484 <style scoped>
288   -/* 1. 全局容器 */
289 485 .history-container {
290 486 height: 100%;
291 487 width: 100%;
... ... @@ -295,16 +491,21 @@ export default {
295 491 .layout-main {
296 492 padding: 0;
297 493 height: 100%;
  494 + min-height: 0;
298 495 overflow: hidden;
299 496 }
300 497  
301   -/* 2. SplitPanes 容器背景统一 */
302 498 .splitpanes-container {
303 499 height: 100%;
304   - background-color: #ffffff; /* 【修改】统一为白色 */
  500 + min-height: 0;
  501 + background-color: #ffffff;
  502 +}
  503 +
  504 +.splitpanes-container ::v-deep .splitpanes__pane {
  505 + min-height: 0;
  506 + overflow: hidden;
305 507 }
306 508  
307   -/* 3. 左侧侧边栏 (保持之前的优化) */
308 509 .aside-pane {
309 510 background-color: #ffffff;
310 511 border-right: 1px solid #dcdfe6;
... ... @@ -324,58 +525,103 @@ export default {
324 525 box-sizing: border-box;
325 526 }
326 527  
327   -/* 样式穿透修复 DeviceList (保持之前的优化) */
328 528 .tree-wrapper ::v-deep .head-container { background-color: transparent !important; padding: 0 !important; margin: 0 !important; }
329 529 .tree-wrapper ::v-deep .head-container .el-row { margin: 0 !important; display: block; }
330 530 .tree-wrapper ::v-deep .head-container .el-col { width: 100% !important; padding: 0 !important; float: none !important; }
331 531 .tree-wrapper ::v-deep .el-input__inner { border-radius: 4px; }
332 532 .tree-wrapper ::v-deep .vue-easy-tree, .tree-wrapper ::v-deep .filter-tree { margin-top: 10px !important; padding-left: 5px !important; background-color: #ffffff !important; }
333 533  
334   -/* ============================================================
335   - 【核心修改区】右侧样式统一
336   - ============================================================ */
337   -
338   -/* 4. 右侧主面板 */
339 534 .main-pane {
340   - background-color: #ffffff; /* 【修改】背景纯白 */
  535 + background-color: #ffffff;
341 536 padding: 0;
342 537 overflow: hidden;
  538 + height: 100%;
  539 + display: flex;
  540 + flex-direction: column;
  541 + min-height: 0;
343 542 }
344 543  
345   -/* 5. 内容包裹层 */
346 544 .content-main {
347   - height: 100%;
  545 + flex: 1;
  546 + min-height: 0;
348 547 width: 100%;
349 548 display: flex;
350 549 flex-direction: column;
351   - background-color: #ffffff; /* 【修改】背景纯白 */
352   -
353   - /* 【关键修改】移除外边距和圆角,填满整个区域 */
  550 + background-color: #ffffff;
354 551 margin: 0;
355 552 border-radius: 0;
356   -
357   - /* 内部保留间距 */
358 553 padding: 20px;
359 554 box-sizing: border-box;
360 555 }
361 556  
362   -/* 6. 顶部搜索区 */
363 557 .header-section {
364 558 flex-shrink: 0;
365 559 margin-bottom: 15px;
366   - border-bottom: 1px solid #EBEEF5; /* 保留底部分割线 */
  560 + border-bottom: 1px solid #EBEEF5;
367 561 padding-bottom: 10px;
368 562 }
369 563  
370   -/* 7. 表格区域 */
  564 +.channel-pick {
  565 + margin-top: 10px;
  566 + padding: 10px 12px;
  567 + background: #f5f7fa;
  568 + border-radius: 4px;
  569 + border: 1px solid #ebeef5;
  570 + display: flex;
  571 + flex-direction: row;
  572 + align-items: flex-start;
  573 + justify-content: space-between;
  574 + gap: 16px;
  575 +}
  576 +
  577 +.channel-pick-main {
  578 + flex: 1;
  579 + min-width: 0;
  580 + display: flex;
  581 + flex-wrap: wrap;
  582 + align-items: flex-start;
  583 + gap: 8px 12px;
  584 +}
  585 +
  586 +.channel-pick-label {
  587 + flex-shrink: 0;
  588 + font-size: 13px;
  589 + color: #606266;
  590 + line-height: 28px;
  591 +}
  592 +
  593 +.channel-pick-group {
  594 + display: flex;
  595 + flex-wrap: wrap;
  596 + align-items: center;
  597 + gap: 4px 12px;
  598 + flex: 1;
  599 + min-width: 0;
  600 +}
  601 +
  602 +.channel-pick-actions {
  603 + flex-shrink: 0;
  604 + margin-left: auto;
  605 + padding-left: 12px;
  606 + white-space: nowrap;
  607 + line-height: 28px;
  608 +}
  609 +
371 610 .table-section {
372   - flex: 1; /* 撑满剩余高度 */
  611 + flex: 1;
  612 + min-height: 0;
373 613 overflow: hidden;
374 614 background: #ffffff;
375 615 position: relative;
376 616 }
377 617  
378   -/* 修复搜索表单 Label 颜色 */
  618 +.history-search-table-host {
  619 + width: 100%;
  620 + height: 100%;
  621 + min-height: 0;
  622 + display: block;
  623 +}
  624 +
379 625 ::v-deep .el-form-item__label {
380 626 color: #606266;
381 627 font-weight: 500;
... ...
web_src/src/components/JT1078Components/HistoryPlayDialog.vue
... ... @@ -5,60 +5,59 @@
5 5 :visible.sync="open"
6 6 width="90%"
7 7 center
8   - :before-close="handleClose"
9   - class="history-dialog-center">
  8 + append-to-body
  9 + @opened="onDialogOpened"
  10 + >
10 11 <el-container>
11   - <el-main>
12   - <el-card class="box-card" shadow="always" :body-style="{ height: '95%' }">
13   - <div class='main-play'>
14   - <video-player :class="`video`" ref="player"
15   - :initial-play-url="videoUrl" style="width: 100%;height: 100%;"
16   - @getTime="getTime"
17   - ></video-player>
  12 + <el-main style="display: flex;justify-content: center;align-items: center;background-color: #e9eef3;">
  13 + <el-card class="box-card" shadow="always" :body-style="{ height: '100%', padding: '10px' }">
  14 + <div class="main-play">
  15 + <jess-video-player
  16 + v-if="videoUrl"
  17 + ref="histPlayer"
  18 + :key="'hist-' + playSessionKey"
  19 + class="hist-jess-player"
  20 + :initial-play-url="videoUrl"
  21 + :vod="true"
  22 + :jessibuca-timeout="60"
  23 + :ui-play-timeout-sec="45"
  24 + :player-auto-resize="true"
  25 + />
18 26 </div>
19 27 </el-card>
20 28 </el-main>
21 29 </el-container>
22   - <el-footer>
23   - <div>
24   - <TimeLine
25   - ref="time_line"
26   - @change="changeDate"
27   - :width="width"
28   - :mark-time="markTime"
29   - :time-range="time_range"
30   - :isAutoPlay="isAutoPlay"
31   - :startMeddleTime="startMeddleTime"
32   - @click="clickCanvas"
33   - />
34   - </div>
  30 + <el-footer style="padding: 0;background-color: #2b2f33;">
  31 + <TimeLine
  32 + ref="time_line"
  33 + @change="changeDate"
  34 + :width="width"
  35 + :mark-time="markTime"
  36 + :time-range="time_range"
  37 + :isAutoPlay="isAutoPlay"
  38 + :startMeddleTime="startMeddleTime"
  39 + @click="clickCanvas"
  40 + />
35 41 </el-footer>
36 42 <span slot="footer" class="dialog-footer">
37   - <el-button @click="handleClose">取 消</el-button>
38   - </span>
  43 + <el-button @click="handleClose">取 消</el-button>
  44 + </span>
39 45 </el-dialog>
40 46 </template>
41 47  
42 48 <script>
43   -import videoPlayer from "../common/JessVideoPlayer.vue";
  49 +import JessVideoPlayer from '../common/JessVideoPlayer.vue'
44 50 import TimeLine from './TimeLineCanvas.vue'
45   -import dayjs from 'dayjs'
46   -import {formattedTime} from "../../../utils/dateFormate";
47   -//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等),
48   -//例如:import 《组件名称》 from '《组件路径》,
  51 +
49 52 export default {
50   - name: "HistoryPlayDialog",
51   - //import引入的组件需要注入到对象中才能使用"
52   - components: {TimeLine, videoPlayer},
53   - props: {
54   - },
  53 + name: 'HistoryPlayDialog',
  54 + components: { TimeLine, JessVideoPlayer },
55 55 data() {
56   - //这里存放数据"
57 56 return {
58   - data:{},
  57 + data: {},
59 58 videoUrl: null,
60 59 isAutoPlay: false,
61   - width: "100%",
  60 + width: '100%',
62 61 startMeddleTime: null,
63 62 startTime: null,
64 63 endTime: null,
... ... @@ -67,19 +66,16 @@ export default {
67 66 form: {
68 67 code: '',
69 68 startTime: '',
70   - endTime: '',
  69 + endTime: ''
71 70 },
72   - channelList: [],
73   - pickerOptions: {},
74 71 open: false,
75   - };
  72 + playSessionKey: 0
  73 + }
76 74 },
77   - //计算属性 类似于data概念",
78   - computed: {},
79   - //监控data中的数据变化",
80 75 watch: {
81 76 data(val) {
82   - console.log('播放数据', val)
  77 + if (!val || !val.videoUrl) return
  78 + this.playSessionKey += 1
83 79 this.videoUrl = val.videoUrl
84 80 this.startMeddleTime = val.startTime
85 81 this.startTime = val.startTime
... ... @@ -89,149 +85,80 @@ export default {
89 85 {
90 86 beginTime: val.startTime,
91 87 endTime: val.endTime,
92   - bgColor: "green",
93   - text: "有视频",
94   - },
  88 + bgColor: 'green',
  89 + text: '有视频'
  90 + }
95 91 ]
96 92 this.form.startTime = this.startTime
97 93 this.form.endTime = this.endTime
  94 + this.$nextTick(() => {
  95 + const p = this.$refs.histPlayer
  96 + if (p && typeof p.tryResize === 'function') p.tryResize()
  97 + })
98 98 },
  99 + videoUrl() {
  100 + this.$nextTick(() => {
  101 + const p = this.$refs.histPlayer
  102 + if (p && typeof p.tryResize === 'function') p.tryResize()
  103 + })
  104 + }
99 105 },
100   - //方法集合",
101 106 methods: {
102   - getTime(time) {
103   - // console.log('当前视频帧',time)
  107 + onDialogOpened() {
  108 + this.$nextTick(() => {
  109 + const p = this.$refs.histPlayer
  110 + if (p && typeof p.tryResize === 'function') p.tryResize()
  111 + })
104 112 },
105   - updateOpen(flag){
  113 + updateOpen(flag) {
106 114 this.open = flag
107 115 },
108 116 clickCanvas(date) {
  117 + const map = this.data.channelMapping != null ? String(this.data.channelMapping) : ''
109 118 this.$axios({
110 119 method: 'get',
111   - url: '/api/jt1078/query/send/request/io/history/' + this.data.sim + '/' + this.data.channel + "/" + this.data.startTime + "/" + date + "/" + undefined
  120 + url: '/api/jt1078/query/send/request/io/history/' + this.data.sim + '/' + this.data.channel + '/' + this.data.startTime + '/' + date + '/' + encodeURIComponent(map)
112 121 }).then(res => {
113 122 if (res.data && res.data.data && res.data.data.data) {
114   - let videoUrl1;
115   - if (location.protocol === "https:") {
116   - videoUrl1 = res.data.data.data.wss_flv;
117   - } else {
118   - videoUrl1 = res.data.data.data.ws_flv;
  123 + const d = res.data.data.data
  124 + const videoUrl1 = location.protocol === 'https:'
  125 + ? (d.wss_flv || d.ws_flv || d.flv)
  126 + : (d.ws_flv || d.wss_flv || d.flv)
  127 + if (videoUrl1) {
  128 + this.playSessionKey += 1
  129 + this.videoUrl = videoUrl1
119 130 }
120   - this.videoUrl = videoUrl1;
121 131 } else if (res.data.data && res.data.data.msg) {
122   - this.$message.error(res.data.data.msg);
  132 + this.$message.error(res.data.data.msg)
123 133 } else if (res.data.msg) {
124   - this.$message.error(res.data.msg);
125   - } else if (res.msg) {
126   - this.$message.error(res.msg);
  134 + this.$message.error(res.data.msg)
127 135 }
128 136 })
129 137 },
130 138 changeDate(date, status) {
131   - console.log("选择时间:" + date + " 播放状态:" + status);
  139 + console.log('选择时间:' + date + ' 播放状态:' + status)
132 140 },
133   - handleClose(){
  141 + handleClose() {
134 142 this.open = false
135 143 }
136   - },
137   - //生命周期 - 创建完成(可以访问当前this实例)",
138   - created() {
139   - },
140   - //生命周期 - 挂载完成(可以访问DOM元素)",
141   - mounted() {
142   - },
143   - beforeCreate() {
144   - }, //生命周期 - 创建之前",
145   - beforeMount() {
146   - }, //生命周期 - 挂载之前",
147   - beforeUpdate() {
148   - }, //生命周期 - 更新之前",
149   - updated() {
150   - }, //生命周期 - 更新之后",
151   - beforeDestroy() {
152   - }, //生命周期 - 销毁之前",
153   - destroyed() {
154   - }, //生命周期 - 销毁完成",
155   - activated() {
156   - } //如果页面有keep-alive缓存功能,这个函数会触发",
157   -};
158   -</script>
159   -<style scoped>
160   -.el-header {
161   - background-color: #B3C0D1;
162   - color: #333;
163   - text-align: center;
164   - line-height: 60px;
165   -}
166   -
167   -.el-footer {
168   - background-color: #B3C0D1;
169   - color: #333;
170   - text-align: center;
171   - line-height: 60px;
172   -}
173   -
174   -.el-aside {
175   - background-color: #D3DCE6;
176   - color: #333;
177   - text-align: center;
178   - line-height: 200px;
179   - height: 75vh;
180   -}
181   -
182   -.el-main {
183   - display: flex;
184   - justify-content: center; /* 水平居中 */
185   - align-items: center; /* 垂直居中 */
186   - background-color: #E9EEF3;
187   - color: #333;
188   - text-align: center;
189   - line-height: 160px;
190   - height: 80vh;
191   -}
192   -
193   -body > .el-container {
194   - margin-bottom: 40px;
195   -}
196   -
197   -.el-container:nth-child(5) .el-aside,
198   -.el-container:nth-child(6) .el-aside {
199   - line-height: 260px;
  144 + }
200 145 }
  146 +</script>
201 147  
  148 +<style scoped>
202 149 .main-play {
203 150 width: 100%;
204   - height: 100%;
205   - background-color: black;
  151 + height: 500px;
  152 + background-color: #000000;
206 153 }
207 154  
208   -.box-card {
209   - width: 80%;
210   - height: 100%;
211   -}
212   -/* 在现有样式基础上添加 */
213   -.history-dialog-center {
214   - display: flex;
215   - justify-content: center;
216   - align-items: center;
217   - position: fixed;
218   - top: 0;
219   - left: 0;
  155 +.hist-jess-player {
220 156 width: 100%;
221 157 height: 100%;
222   - margin: 0 !important;
223   -}
224   -
225   -.history-dialog-center .el-dialog {
226   - margin: 0 auto !important;
227   - max-height: 90vh;
228   - display: flex;
229   - flex-direction: column;
230 158 }
231 159  
232   -.history-dialog-center .el-dialog__body {
233   - flex: 1;
234   - overflow-y: auto;
  160 +.box-card {
  161 + width: 100%;
  162 + max-width: 1280px;
235 163 }
236   -
237 164 </style>
... ...
web_src/src/components/JT1078Components/HistorySearchTable.vue
1 1 <template>
2   - <div style="width: 100%;height: 100%;">
  2 + <div class="history-table-wrap">
3 3 <el-table
4 4 v-if="tableData.length > 0"
5 5 ref="singleTable"
6 6 :data="tableData"
7 7 :header-cell-style="{ textAlign: 'center' ,height:'100%',lineHeight:'100%' }"
8 8 border
9   - height="calc(100% - 100px)"
  9 + :height="tableHeightNum"
10 10 highlight-current-row
11 11 @current-change="handleCurrentChange"
12 12 style="width: 100%;text-align: center">
... ... @@ -55,14 +55,31 @@
55 55 label="操作">
56 56 <template slot-scope="scope">
57 57 <el-button icon="el-icon-video-play" size="small" style="border: none" @click="playHistoryVideo(scope.row)"></el-button>
58   - <el-button v-if="scope.row.status === '0'" icon="el-icon-upload" size="small" style="border: none" @click="uploadHistoryVideo(scope.row)"></el-button>
59   - <el-button v-if="scope.row.status === '3'"
60   - icon="el-icon-download"
61   - :disabled="scope.row.disabled"
62   - size="small" style="border: none"
63   - @click="downloadHistoryVideo(scope.row)">
64   - {{ scope.row.disabled ? `${scope.row.countdown}s` : '' }}
65   - </el-button>
  58 + <el-tooltip :disabled="!isChannelBusy(scope.row)" content="该通道正在上传或下载" placement="top">
  59 + <span>
  60 + <el-button
  61 + v-if="scope.row.status === '0'"
  62 + icon="el-icon-upload"
  63 + size="small"
  64 + style="border: none"
  65 + :disabled="isChannelBusy(scope.row)"
  66 + @click="uploadHistoryVideo(scope.row)"
  67 + />
  68 + </span>
  69 + </el-tooltip>
  70 + <el-tooltip :disabled="!isChannelBusy(scope.row)" content="该通道正在上传或下载" placement="top">
  71 + <span>
  72 + <el-button
  73 + v-if="scope.row.status === '3'"
  74 + icon="el-icon-download"
  75 + :disabled="scope.row.disabled || isChannelBusy(scope.row)"
  76 + size="small"
  77 + style="border: none"
  78 + @click="downloadHistoryVideo(scope.row)">
  79 + {{ scope.row.disabled ? `${scope.row.countdown}s` : '' }}
  80 + </el-button>
  81 + </span>
  82 + </el-tooltip>
66 83 </template>
67 84 </el-table-column>
68 85 </el-table>
... ... @@ -71,69 +88,78 @@
71 88 </template>
72 89  
73 90 <script>
74   -//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等),
75   -//例如:import 《组件名称》 from '《组件路径》,
76 91 export default {
77   - //import引入的组件需要注入到对象中才能使用"
78 92 name: "historySearchTable",
79 93 components: {},
80 94 props: {
81 95 tableData: {
82 96 type: Array,
83   - default: []
  97 + default: () => []
  98 + },
  99 + /** 像素高度;el-table 需要数值高度才能出现纵向滚动条 */
  100 + tableHeight: {
  101 + type: Number,
  102 + default: 400
  103 + },
  104 + busyChannelKeys: {
  105 + type: Array,
  106 + default: () => []
  107 + }
  108 + },
  109 + computed: {
  110 + tableHeightNum() {
  111 + const h = Number(this.tableHeight)
  112 + return h > 120 ? h : 400
84 113 }
85 114 },
86 115 data() {
87   - //这里存放数据"
88 116 return {
89 117 currentRow: null,
90 118 historyTimer: null,
91   - countdown: 10, // 假设倒计时为10秒
  119 + countdown: 10,
92 120 disabled: false
93 121 };
94 122 },
95   - //计算属性 类似于data概念",
96   - computed: {},
97   - //监控data中的数据变化",
98   - watch: {},
99   - //方法集合",
100 123 methods: {
101   - /**
102   - * 选中行
103   - * @param val
104   - */
  124 + channelKey(row) {
  125 + if (!row || row.sim == null || row.channel == null) return ''
  126 + return String(row.sim) + '_' + String(row.channel)
  127 + },
  128 + isChannelBusy(row) {
  129 + const k = this.channelKey(row)
  130 + if (!k) return false
  131 + return this.busyChannelKeys.indexOf(k) >= 0
  132 + },
105 133 handleCurrentChange(val) {
106 134 this.currentRow = val;
107 135 },
108 136 setCurrent(row) {
109 137 this.$refs.singleTable.setCurrentRow(row);
110 138 },
111   - /**
112   - * 播放历史视频
113   - * @param row
114   - */
115 139 playHistoryVideo(row){
116 140 this.$emit("playHistoryVideo", row);
117 141 },
118   - /**
119   - * 上传历史视频
120   - * @param row
121   - */
122 142 uploadHistoryVideo(row){
  143 + if (this.isChannelBusy(row)) {
  144 + this.$message.warning('该通道正在上传或下载,请稍候')
  145 + return
  146 + }
123 147 this.$emit('uploadHistoryVideo', row);
124 148 },
125   - /**
126   - * 下载历史视频
127   - * @param row
128   - */
129 149 downloadHistoryVideo(data){
130 150 if (data.disabled) return;
  151 + if (this.isChannelBusy(data)) {
  152 + this.$message.warning('该通道正在上传或下载,请稍候')
  153 + return
  154 + }
131 155 if (data.historyTimer){
132 156 this.$message.warning("点击太频繁,请稍后重试")
133 157 return;
134 158 }
  159 + const opKey = this.channelKey(data)
  160 + this.$emit('channel-operation', { key: opKey, busy: true })
135 161 data.disabled = true;
136   - data.countdown = 10; // 重置倒计时
  162 + data.countdown = 10;
137 163 data.isCountingDown = true;
138 164 const timer = setInterval(() => {
139 165 if (data.countdown > 0) {
... ... @@ -141,66 +167,51 @@
141 167 } else {
142 168 clearInterval(timer);
143 169 data.disabled = false;
144   - data.countdown = 10; // 重置倒计时
  170 + data.countdown = 10;
145 171 data.isCountingDown = false;
146 172 }
147 173 }, 1000);
148   - data.historyTimer = timer; // 保存计时器引用
149   - /**
150   - * 下载历史视频
151   - */
  174 + data.historyTimer = timer;
152 175 this.$axios({
153   - url: `/api/jt1078/query/history/download/${data.name}`, // 请求URL
154   - method: 'GET', // 将FTP文件路径作为参数传递
155   - responseType: 'blob', // 告知axios以二进制数据流的形式处理响应
  176 + url: `/api/jt1078/query/history/download/${data.name}`,
  177 + method: 'GET',
  178 + responseType: 'blob',
156 179 }).then((response) => {
157 180 const blob = new Blob([response.data], { type: 'application/octet-stream' });
158 181 const link = document.createElement('a');
159 182 link.href = window.URL.createObjectURL(blob);
160   - link.download = data.name + ".mp4"; // 设置下载文件名
  183 + link.download = data.name + ".mp4";
161 184 document.body.appendChild(link);
162 185 link.click();
163 186 document.body.removeChild(link);
164 187 clearInterval(timer);
165 188 data.disabled = false;
166   - data.countdown = 10; // 重置倒计时
  189 + data.countdown = 10;
167 190 data.isCountingDown = false;
168 191 data.historyTimer = null;
169 192 this.$message.success(`${link.download} 下载成功`);
170 193 }).catch(error => {
171 194 clearInterval(timer);
172 195 data.disabled = false;
173   - data.countdown = 10; // 重置倒计时
  196 + data.countdown = 10;
174 197 data.isCountingDown = false;
175 198 data.historyTimer = null;
176 199 this.$message.error(`${data.name} 下载失败`);
177 200 console.error("下载失败", error);
  201 + }).finally(() => {
  202 + this.$emit('channel-operation', { key: opKey, busy: false })
178 203 });
179 204 }
180 205 },
181   - //生命周期 - 创建完成(可以访问当前this实例)",
182   - created() {
183   - },
184   - //生命周期 - 挂载完成(可以访问DOM元素)",
185   - mounted() {
186   - },
187   - beforeCreate() {
188   - }, //生命周期 - 创建之前",
189   - beforeMount() {
190   - }, //生命周期 - 挂载之前",
191   - beforeUpdate() {
192   - }, //生命周期 - 更新之前",
193   - updated() {
194   - }, //生命周期 - 更新之后",
195 206 beforeDestroy() {
196 207 clearInterval(this.historyTimer)
197   - }, //生命周期 - 销毁之前",
198   - destroyed() {
199   - }, //生命周期 - 销毁完成",
200   - activated() {
201   - } //如果页面有keep-alive缓存功能,这个函数会触发",
  208 + },
202 209 };
203 210 </script>
204 211 <style scoped>
205   -
  212 +.history-table-wrap {
  213 + width: 100%;
  214 + height: 100%;
  215 + min-height: 0;
  216 +}
206 217 </style>
... ...
web_src/src/components/JT1078Components/deviceList/VehicleList.vue
... ... @@ -35,7 +35,7 @@
35 35 @node-collapse="handleNodeCollapse"
36 36 style="margin-top: 10px"
37 37 >
38   - <span class="custom-tree-node" slot-scope="{ node, data }">
  38 + <span class="custom-tree-node" slot-scope="{ node, data }" @dblclick.stop="onTreeNodeDblclick(data, node)">
39 39 <el-tooltip :disabled="data.abnormalStatus === undefined"
40 40 :visible="tooltipVisible && hoveredNode === data" placement="right">
41 41 <div slot="content" style="line-height: 23px">
... ... @@ -145,6 +145,10 @@ export default {
145 145 this.$emit('node-click', data, node)
146 146 },
147 147  
  148 + onTreeNodeDblclick(data, node) {
  149 + this.$emit('node-dblclick', data, node)
  150 + },
  151 +
148 152 nodeContextmenu(event, data, node, fun) {
149 153 this.$emit('node-contextmenu', event, data, node);
150 154 },
... ... @@ -168,7 +172,7 @@ export default {
168 172 // 只有在启用测试SIM模式时才应用测试逻辑
169 173 if (this.enableTestSim) {
170 174 //, '39045172840',39045172800
171   - const fixedSims = ['40028816490'];
  175 + const fixedSims = ['40028816490','103071510'];
172 176 const toggleSim = '';
173 177  
174 178 // 计算当前时间处于哪个10分钟区间,用于实现 toggleSim 的状态切换
... ...
web_src/src/components/common/EasyPlayer.vue
... ... @@ -5,6 +5,7 @@
5 5 @click="onPlayerClick"
6 6 @mousemove="onMouseMove"
7 7 @mouseleave="onMouseLeave"
  8 + @contextmenu="onContextMenuWrapper"
8 9 >
9 10 <div class="custom-top-bar" :class="{ 'hide-bar': !showControls }">
10 11 <div class="top-bar-left">
... ... @@ -18,6 +19,21 @@
18 19 </div>
19 20  
20 21 <div :id="uniqueId" ref="container" class="player-box"></div>
  22 +
  23 + <!-- 自定义右键菜单 - 全屏时附加到 body -->
  24 + <div
  25 + v-if="showContextMenu"
  26 + ref="contextMenu"
  27 + class="custom-contextmenu"
  28 + :class="{ 'menu-fixed': isFullscreen }"
  29 + :style="{ top: contextMenuY + 'px', left: contextMenuX + 'px' }"
  30 + @click.stop
  31 + >
  32 + <div class="contextmenu-item" @click.stop="onShowVideoInfo">视频信息</div>
  33 + <div class="contextmenu-divider"></div>
  34 + <div class="contextmenu-item" @click.stop="onSwitchMainStream">主码流</div>
  35 + <div class="contextmenu-item" @click.stop="onSwitchSubStream">子码流</div>
  36 + </div>
21 37 </div>
22 38 </template>
23 39  
... ... @@ -28,7 +44,10 @@ export default {
28 44 initialPlayUrl: { type: String, default: '' },
29 45 isResize: { type: Boolean, default: true },
30 46 videoTitle: { type: String, default: '' },
31   - hasAudio: { type: Boolean, default: false }
  47 + hasAudio: { type: Boolean, default: false },
  48 + // 码流切换所需参数
  49 + sim: { type: String, default: '' },
  50 + channel: { type: [String, Number], default: '' }
32 51 },
33 52 data() {
34 53 return {
... ... @@ -38,6 +57,15 @@ export default {
38 57 hasStarted: false,
39 58 showControls: false,
40 59 controlTimer: null,
  60 + isFullscreen: false, // 全屏状态
  61 +
  62 + // 自定义右键菜单
  63 + showContextMenu: false,
  64 + contextMenuX: 0,
  65 + contextMenuY: 0,
  66 + contextMenuHandler: null, // 保存右键菜单监听器函数
  67 + docContextMenuHandler: null, // 保存文档级右键菜单监听器函数
  68 + containerContextMenuHandler: null, // 保存容器级右键菜单监听器函数
41 69  
42 70 // 【配置控制表】
43 71 controlsConfig: {
... ... @@ -94,10 +122,77 @@ export default {
94 122 }
95 123 }, 500);
96 124 });
  125 + // 添加全局点击事件监听,用于隐藏右键菜单
  126 + document.addEventListener('click', this.hideContextMenu);
  127 + // 添加全屏变化监听
  128 + document.addEventListener('fullscreenchange', this.onFullscreenChange);
  129 + document.addEventListener('webkitfullscreenchange', this.onFullscreenChange);
  130 + // 在 document 级别使用 capture 模式拦截 contextmenu 事件
  131 + this.docContextMenuHandler = (e) => {
  132 + // 检查播放器是否在播放
  133 + if (!this.hasStarted) {
  134 + return;
  135 + }
  136 +
  137 + // 优先使用 this.isFullscreen(通过 onFullscreenChange 更新的),因为 EasyPlayerPro 可能不会更新 document.fullscreenElement
  138 + const isFullscreen = !!(document.fullscreenElement || document.webkitFullscreenElement || this.isFullscreen);
  139 + console.log('Contextmenu event - isFullscreen:', isFullscreen, 'docFullscreen:', !!(document.fullscreenElement || document.webkitFullscreenElement), 'this.isFullscreen:', this.isFullscreen, 'target:', e.target);
  140 +
  141 + // 全屏模式下,使用 DOM 遍历方式检测
  142 + if (isFullscreen) {
  143 + console.log('Entering fullscreen branch');
  144 + let target = e.target;
  145 +
  146 + // 简化检测:直接检查点击目标是否在当前播放器元素内
  147 + const isInPlayer = this.$el.contains(target);
  148 + console.log('isInPlayer (this.$el.contains):', isInPlayer, 'target:', target, 'this.$el:', this.$el);
  149 +
  150 + if (isInPlayer) {
  151 + e.preventDefault();
  152 + setTimeout(() => {
  153 + this.hideNativeContextMenu();
  154 + this.onContextMenu(e);
  155 + }, 10);
  156 + }
  157 + return;
  158 + }
  159 +
  160 + // 非全屏模式下,使用坐标检测
  161 + const rect = this.$el.getBoundingClientRect();
  162 + const isClickInPlayer = (
  163 + e.clientX >= rect.left &&
  164 + e.clientX <= rect.right &&
  165 + e.clientY >= rect.top &&
  166 + e.clientY <= rect.bottom
  167 + );
  168 +
  169 + // 如果点击不在当前播放器内,不处理
  170 + if (!isClickInPlayer) {
  171 + return;
  172 + }
  173 +
  174 + // 点击在当前播放器内,阻止原生菜单并显示自定义菜单
  175 + e.preventDefault();
  176 + // 延迟一点执行,确保我们的菜单能显示
  177 + setTimeout(() => {
  178 + this.hideNativeContextMenu();
  179 + this.onContextMenu(e);
  180 + }, 10);
  181 + };
  182 + document.addEventListener('contextmenu', this.docContextMenuHandler, true);
97 183 },
98 184 beforeDestroy() {
99 185 this.destroy();
100 186 if (this.controlTimer) clearTimeout(this.controlTimer);
  187 + // 移除全局点击事件监听
  188 + document.removeEventListener('click', this.hideContextMenu);
  189 + // 移除全屏变化监听
  190 + document.removeEventListener('fullscreenchange', this.onFullscreenChange);
  191 + document.removeEventListener('webkitfullscreenchange', this.onFullscreenChange);
  192 + // 移除 document 级别的 contextmenu 监听
  193 + if (this.docContextMenuHandler) {
  194 + document.removeEventListener('contextmenu', this.docContextMenuHandler, true);
  195 + }
101 196 },
102 197 methods: {
103 198 onMouseMove() {
... ... @@ -158,6 +253,32 @@ export default {
158 253 console.warn('Player Error:', err);
159 254 });
160 255  
  256 + // 监听播放器的 contextmenu 事件,防止显示原生菜单
  257 + this.playerInstance.on('contextmenu', (e) => {
  258 + e.preventDefault();
  259 + });
  260 +
  261 + // 添加原生 contextmenu 监听器到容器元素和 wrapper 元素,作为 document 级监听器的备份
  262 + // 在 fullscreen 模式下,事件可能不会冒泡到 document
  263 + this.containerContextMenuHandler = (e) => {
  264 + console.log('Container contextmenu fired (native listener), hasStarted:', this.hasStarted);
  265 + if (!this.hasStarted) {
  266 + console.log('hasStarted check failed');
  267 + return;
  268 + }
  269 + e.preventDefault();
  270 + console.log('About to call onContextMenu');
  271 + this.hideNativeContextMenu();
  272 + this.onContextMenu(e);
  273 + console.log('onContextMenu called, showContextMenu should be:', this.showContextMenu);
  274 + };
  275 + container.addEventListener('contextmenu', this.containerContextMenuHandler, true);
  276 + // 也在 wrapper (this.$el) 上添加监听,fullscreen 时 this.$el 可能是全屏元素
  277 + this.$el.addEventListener('contextmenu', this.containerContextMenuHandler, true);
  278 +
  279 + // 注意:document 级别的 contextmenu 监听器已在 mounted 中添加,这里不需要重复添加
  280 +
  281 +
161 282 } catch (e) {
162 283 console.error("Create Error:", e);
163 284 }
... ... @@ -179,6 +300,17 @@ export default {
179 300 destroy() {
180 301 this.hasStarted = false;
181 302 this.showControls = false;
  303 + // 移除 document 级别的 contextmenu 监听
  304 + if (this.docContextMenuHandler) {
  305 + document.removeEventListener('contextmenu', this.docContextMenuHandler, true);
  306 + this.docContextMenuHandler = null;
  307 + }
  308 + // 移除容器级别的 contextmenu 监听
  309 + if (this.containerContextMenuHandler && this.$refs.container) {
  310 + this.$refs.container.removeEventListener('contextmenu', this.containerContextMenuHandler, true);
  311 + this.$el.removeEventListener('contextmenu', this.containerContextMenuHandler, true);
  312 + this.containerContextMenuHandler = null;
  313 + }
182 314 if (this.playerInstance) {
183 315 this.playerInstance.destroy();
184 316 this.playerInstance = null;
... ... @@ -197,8 +329,328 @@ export default {
197 329 this.$emit('click');
198 330 },
199 331  
  332 + // 右键菜单处理 - 直接在 wrapper 上拦截
  333 + onContextMenuWrapper(e) {
  334 + // 阻止事件冒泡到 EasyPlayerPro
  335 + e.stopPropagation();
  336 + // document 级别的监听器会处理阻止默认行为
  337 + },
  338 +
  339 + // 右键菜单 - 用于 capture 模式拦截
  340 + onContextMenuCapture(e) {
  341 + // 阻止原生右键菜单
  342 + e.preventDefault();
  343 + e.stopPropagation();
  344 +
  345 + // 调用正常的右键菜单处理
  346 + this.onContextMenu(e);
  347 + },
  348 +
  349 + // 右键菜单
  350 + onContextMenu(e) {
  351 + console.log('onContextMenu called, hasStarted:', this.hasStarted);
  352 + // 只有在播放器已启动后才显示菜单
  353 + if (!this.hasStarted) return;
  354 +
  355 + // 计算菜单位置,确保不超出播放器边界
  356 + const menuWidth = 150;
  357 + const menuHeight = 140;
  358 +
  359 + // 检查是否是全屏模式
  360 + const isFs = !!(document.fullscreenElement || document.webkitFullscreenElement || this.isFullscreen);
  361 +
  362 + // 全屏模式:使用 viewport 坐标
  363 + // 非全屏模式:使用播放器容器坐标
  364 + let x, y;
  365 + if (isFs) {
  366 + // 全屏时使用绝对坐标
  367 + x = e.clientX;
  368 + y = e.clientY;
  369 + console.log('Fullscreen mode - using viewport coords:', x, y);
  370 + } else {
  371 + // 非全屏时使用相对于 player-wrapper 的坐标
  372 + const containerRect = this.$el.getBoundingClientRect();
  373 + x = e.clientX - containerRect.left;
  374 + y = e.clientY - containerRect.top;
  375 + console.log('Normal mode - using relative coords:', x, y, 'containerRect:', containerRect);
  376 + }
  377 +
  378 + // 边界检测
  379 + const maxX = isFs ? window.innerWidth : (isFs ? window.innerWidth : this.$el.getBoundingClientRect().width);
  380 + const maxY = isFs ? window.innerHeight : (isFs ? window.innerHeight : this.$el.getBoundingClientRect().height);
  381 +
  382 + if (x + menuWidth > maxX) {
  383 + x = maxX - menuWidth;
  384 + }
  385 + if (y + menuHeight > maxY) {
  386 + y = maxY - menuHeight;
  387 + }
  388 +
  389 + // 确保最小值
  390 + x = Math.max(0, x);
  391 + y = Math.max(0, y);
  392 +
  393 + this.contextMenuX = x;
  394 + this.contextMenuY = y;
  395 + console.log('Setting showContextMenu=true, x:', x, 'y:', y);
  396 +
  397 + // 阻止事件冒泡
  398 + e.stopPropagation();
  399 +
  400 + // 先隐藏原生菜单(多次调用确保隐藏)
  401 + this.hideNativeContextMenu();
  402 +
  403 + this.showContextMenu = true;
  404 + this.$forceUpdate();
  405 + console.log('showContextMenu is now:', this.showContextMenu);
  406 +
  407 + // 全屏时将菜单附加到 body
  408 + if (isFs && this.$refs.contextMenu) {
  409 + console.log('Appending menu to body for fullscreen');
  410 + document.body.appendChild(this.$refs.contextMenu);
  411 + // 强制设置 position: fixed 和 top/left
  412 + this.$refs.contextMenu.style.position = 'fixed';
  413 + this.$refs.contextMenu.style.top = y + 'px';
  414 + this.$refs.contextMenu.style.left = x + 'px';
  415 + }
  416 +
  417 + // 检查 DOM
  418 + this.$nextTick(() => {
  419 + const menu = this.$refs.contextMenu || this.$el.querySelector('.custom-contextmenu');
  420 + console.log('DOM check - menu exists:', !!menu, 'parent:', menu ? menu.parentElement : 'N/A');
  421 + console.log('isFullscreen data property:', this.isFullscreen);
  422 + console.log('menu-fixed class applied:', menu ? menu.classList.contains('menu-fixed') : 'N/A');
  423 + if (menu) {
  424 + console.log('Menu computed position:', getComputedStyle(menu).position);
  425 + console.log('Menu rect:', menu.getBoundingClientRect(), 'viewport:', window.innerWidth, window.innerHeight);
  426 + // 检查原生菜单状态
  427 + const nativeMenus = document.querySelectorAll('[class*="easyplayer-contextmenu"]');
  428 + console.log('Native menus count:', nativeMenus.length);
  429 +
  430 + // 检查是否有其他元素覆盖在菜单上面
  431 + const elemBelow = document.elementFromPoint(x, y);
  432 + console.log('Element at menu position (x,y):', x, y, 'is:', elemBelow);
  433 +
  434 + // 使用 requestAnimationFrame 确保菜单在原生菜单之后渲染
  435 + requestAnimationFrame(() => {
  436 + requestAnimationFrame(() => {
  437 + this.hideNativeContextMenu();
  438 + // 强制确保菜单可见
  439 + menu.style.setProperty('display', 'block', 'important');
  440 + menu.style.setProperty('visibility', 'visible', 'important');
  441 + menu.style.setProperty('opacity', '1', 'important');
  442 + console.log('After rAF forcing styles - display:', getComputedStyle(menu).display, 'visibility:', getComputedStyle(menu).visibility, 'opacity:', getComputedStyle(menu).opacity, 'position:', getComputedStyle(menu).position);
  443 + // 再次检查覆盖元素
  444 + const elemBelowAfter = document.elementFromPoint(x, y);
  445 + console.log('Element at menu position AFTER forcing:', x, y, 'is:', elemBelowAfter);
  446 + });
  447 + });
  448 + }
  449 + });
  450 + },
  451 +
  452 + // 隐藏右键菜单
  453 + hideContextMenu() {
  454 + // 如果菜单被附加到 body,将其移回 player-wrapper
  455 + if (this.$refs.contextMenu && this.$refs.contextMenu.parentElement !== this.$el) {
  456 + this.$el.appendChild(this.$refs.contextMenu);
  457 + }
  458 + this.showContextMenu = false;
  459 + // 同时隐藏原生菜单
  460 + this.hideNativeContextMenu();
  461 + },
  462 +
  463 + // 隐藏原生右键菜单
  464 + hideNativeContextMenu() {
  465 + const menus = document.querySelectorAll('[class*="easyplayer-contextmenu"]');
  466 + menus.forEach(menu => {
  467 + if (!menu.classList.contains('custom-contextmenu')) {
  468 + menu.style.display = 'none';
  469 + }
  470 + });
  471 + },
  472 +
  473 + // 全屏状态变化处理
  474 + onFullscreenChange() {
  475 + this.isFullscreen = !!(document.fullscreenElement || document.webkitFullscreenElement);
  476 + // 全屏时隐藏右键菜单
  477 + if (!this.isFullscreen) {
  478 + this.hideContextMenu();
  479 + }
  480 + },
  481 +
  482 + // 视频信息
  483 + onShowVideoInfo() {
  484 + const instance = this.playerInstance;
  485 +
  486 + // 方法1: 直接调用
  487 + if (instance && typeof instance.showVideoInfo === 'function') {
  488 + instance.showVideoInfo();
  489 + this.hideContextMenu();
  490 + return;
  491 + }
  492 +
  493 + // 方法2: 通过 player 调用
  494 + if (instance && instance.player && typeof instance.player.showVideoInfo === 'function') {
  495 + instance.player.showVideoInfo();
  496 + this.hideContextMenu();
  497 + return;
  498 + }
  499 +
  500 + // 方法3: 调用 _videoinfo 方法
  501 + if (instance && typeof instance._videoinfo === 'function') {
  502 + instance._videoinfo();
  503 + this.hideContextMenu();
  504 + return;
  505 + }
  506 +
  507 + // 方法4: 尝试通过 events 触发
  508 + if (instance && instance.events && instance.events.emit) {
  509 + instance.events.emit('showVideoInfo');
  510 + this.hideContextMenu();
  511 + return;
  512 + }
  513 +
  514 + // 方法5: 触发原生菜单中的视频信息按钮
  515 + // 先隐藏我们的自定义菜单,然后让原生菜单显示,再点击其视频信息按钮
  516 + const customMenu = this.$el.querySelector('.custom-contextmenu');
  517 + if (customMenu) {
  518 + customMenu.style.display = 'none';
  519 + }
  520 +
  521 + // 强制显示原生菜单(移除我们的隐藏样式)
  522 + const allMenus = document.querySelectorAll('.easyplayer-contextmenu-btn');
  523 + allMenus.forEach(menu => {
  524 + menu.style.display = '';
  525 + menu.style.opacity = '';
  526 + menu.style.visibility = '';
  527 + });
  528 +
  529 + // 延迟一点让原生菜单显示
  530 + setTimeout(() => {
  531 + // 在当前播放器容器内查找原生菜单
  532 + const container = this.$refs.container;
  533 + let infoBtn = null;
  534 +
  535 + if (container) {
  536 + const nativeMenu = container.querySelector('.easyplayer-contextmenu-btn');
  537 + if (nativeMenu) {
  538 + infoBtn = nativeMenu.querySelector('.easyplayer-contextmenu-info');
  539 + }
  540 + }
  541 +
  542 + // 如果没找到,尝试基于位置匹配
  543 + if (!infoBtn) {
  544 + const playerRect = this.$el.getBoundingClientRect();
  545 + for (let i = 0; i < allMenus.length; i++) {
  546 + const menu = allMenus[i];
  547 + const menuRect = menu.getBoundingClientRect();
  548 + const isOverlapping =
  549 + Math.abs(menuRect.left - playerRect.left) < 100 &&
  550 + Math.abs(menuRect.top - playerRect.top) < 100;
  551 + if (isOverlapping) {
  552 + infoBtn = menu.querySelector('.easyplayer-contextmenu-info');
  553 + break;
  554 + }
  555 + }
  556 + }
  557 +
  558 + if (infoBtn) {
  559 + infoBtn.click();
  560 + }
  561 +
  562 + // 隐藏原生菜单
  563 + allMenus.forEach(menu => {
  564 + menu.style.display = 'none';
  565 + });
  566 + }, 50);
  567 + },
  568 +
  569 + // 切换主码流
  570 + onSwitchMainStream() {
  571 + this.hideContextMenu();
  572 + this.switchStream(0);
  573 + },
  574 +
  575 + // 切换子码流
  576 + onSwitchSubStream() {
  577 + this.hideContextMenu();
  578 + this.switchStream(1);
  579 + },
  580 +
200 581 setControls(config) {
201 582 this.controlsConfig = { ...this.controlsConfig, ...config };
  583 + },
  584 +
  585 + // 切换码流
  586 + switchStream(type) {
  587 + // 如果没有sim和channel参数,则不发送请求
  588 + if (!this.sim || !this.channel) {
  589 + console.log('码流切换跳过: 缺少sim或channel参数');
  590 + return;
  591 + }
  592 +
  593 + const params = {
  594 + sim: this.sim,
  595 + channel: parseInt(this.channel),
  596 + type: type
  597 + };
  598 +
  599 + console.log(`码流切换: sim=${params.sim}, channel=${params.channel}, type=${type} (0=主码流, 1=子码流)`);
  600 +
  601 + // 使用箭头函数保存this引用
  602 + const doSwitch = (axiosInstance) => {
  603 + axiosInstance.post('/api/jt1078/query/switch/stream', params)
  604 + .then(res => {
  605 + if (res.data.code === 200 || res.data.code === 0) {
  606 + console.log('码流切换成功,等待设备切换...');
  607 + } else {
  608 + console.warn('码流切换失败:', res.data.msg || res.data);
  609 + }
  610 + })
  611 + .catch(err => {
  612 + console.error('码流切换请求失败:', err);
  613 + });
  614 + };
  615 +
  616 + if (this.$axios) {
  617 + doSwitch(this.$axios);
  618 + } else if (window.axios) {
  619 + doSwitch(window.axios);
  620 + }
  621 + },
  622 +
  623 + // 刷新播放地址并重新播放
  624 + refreshPlayUrl() {
  625 + if (!this.sim || !this.channel) return;
  626 +
  627 + const axiosInstance = this.$axios || window.axios;
  628 + if (!axiosInstance) return;
  629 +
  630 + axiosInstance.get(`/api/jt1078/query/send/request/io/${this.sim}/${this.channel}`)
  631 + .then(res => {
  632 + if (res.data.code === 200 || res.data.code === 0) {
  633 + const newUrl = res.data.data.ws_flv || res.data.data.wss_flv;
  634 + console.log('获取新播放地址:', newUrl);
  635 +
  636 + if (newUrl) {
  637 + // 重新创建播放器并播放新地址
  638 + // 分步执行:先销毁,等待,再重建,确保播放器完全重置
  639 + this.destroy();
  640 +
  641 + // 等待一段时间后重新创建播放器
  642 + setTimeout(() => {
  643 + this.create();
  644 + this.play(newUrl);
  645 + }, 500); // 等待500ms确保完全销毁后再重建
  646 + }
  647 + } else {
  648 + console.warn('获取播放地址失败:', res.data.msg || res.data);
  649 + }
  650 + })
  651 + .catch(err => {
  652 + console.error('获取播放地址失败:', err);
  653 + });
202 654 }
203 655 },
204 656 };
... ... @@ -272,7 +724,7 @@ export default {
272 724 }
273 725  
274 726 /* 音量按钮 */
275   -.player-wrapper.hide-btn-audio .easyplayer-audio-box {
  727 +.player-wrapper.hide-btn-audio .easyplayer-volume {
276 728 display: none !important;
277 729 }
278 730  
... ... @@ -451,4 +903,52 @@ export default {
451 903 .player-wrapper .easyplayer-loading-text {
452 904 display: none !important;
453 905 }
  906 +
  907 +/* =========================================
  908 + 3. 自定义右键菜单
  909 + ========================================= */
  910 +
  911 +/* 隐藏原生右键菜单 - 使用更高优先级 */
  912 +[class*="easyplayer-contextmenu"] {
  913 + display: none !important;
  914 + visibility: hidden !important;
  915 +}
  916 +
  917 +/* 自定义右键菜单样式 */
  918 +.custom-contextmenu {
  919 + position: absolute;
  920 + z-index: 2147483647 !important;
  921 + background: rgba(30, 30, 30, 0.95);
  922 + border: 1px solid rgba(80, 80, 80, 0.8);
  923 + border-radius: 6px;
  924 + padding: 6px 0;
  925 + min-width: 140px;
  926 + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
  927 +}
  928 +
  929 +/* 全屏模式下使用 fixed 定位,并强制使用视口作为定位上下文 */
  930 +.custom-contextmenu.menu-fixed {
  931 + position: fixed !important;
  932 + top: var(--menu-top, 0) !important;
  933 + left: var(--menu-left, 0) !important;
  934 + transform: none !important;
  935 +}
  936 +
  937 +.custom-contextmenu .contextmenu-divider {
  938 + height: 1px;
  939 + background: rgba(80, 80, 80, 0.6);
  940 + margin: 4px 0;
  941 +}
  942 +
  943 +.custom-contextmenu .contextmenu-item {
  944 + padding: 8px 16px;
  945 + color: #fff;
  946 + font-size: 13px;
  947 + cursor: pointer;
  948 + transition: background 0.2s;
  949 +}
  950 +
  951 +.custom-contextmenu .contextmenu-item:hover {
  952 + background: rgba(30, 144, 255, 0.6);
  953 +}
454 954 </style>
... ...
web_src/src/components/common/PlayerListComponent.vue
... ... @@ -15,6 +15,8 @@
15 15 :initial-buffer-time="0.1"
16 16 :show-custom-mask="false"
17 17 :has-audio="true"
  18 + :sim="playerDataList[i] ? playerDataList[i].sim : ''"
  19 + :channel="playerDataList[i] ? playerDataList[i].channel : ''"
18 20 style="width: 100%;height: 100%;"
19 21 @click="playerClick(item, i, items.length)"
20 22 ></easyPlayer>
... ... @@ -38,6 +40,10 @@ export default {
38 40 type: String,
39 41 default: '9'
40 42 },
  43 + hasAudio: {
  44 + type: Boolean,
  45 + default: true
  46 + },
41 47 videoUrl: {
42 48 type: Array,
43 49 default: []
... ... @@ -57,6 +63,16 @@ export default {
57 63 },
58 64 //计算属性 类似于data概念",
59 65 computed: {
  66 + // 生成播放器数据列表,包含 sim 和 channel
  67 + playerDataList() {
  68 + return this.items.map((item, i) => {
  69 + const data = this.videoDataList[i] || {};
  70 + return {
  71 + sim: this.getSim(data),
  72 + channel: this.getChannel(data)
  73 + };
  74 + });
  75 + },
60 76 },
61 77 //监控data中的数据变化",
62 78 watch: {
... ... @@ -272,6 +288,22 @@ export default {
272 288 }
273 289 });
274 290 },
  291 +
  292 + // 获取SIM卡号 (从parent对象中获取)
  293 + getSim(data) {
  294 + if (!data) return '';
  295 + // 优先从自身获取,否则从parent对象获取
  296 + return data.sim || (data.parent && data.parent.sim) || '';
  297 + },
  298 +
  299 + // 获取通道号 (从code中提取,格式: id_sim_channelNumber)
  300 + getChannel(data) {
  301 + console.log(data)
  302 + if (!data || !data.code) return '';
  303 + const parts = data.code.split('_');
  304 + // code格式: id_sim_channelNumber, channel是最后一部分
  305 + return parts.length >= 3 ? parts[parts.length - 1] : '';
  306 + }
275 307 },
276 308 //生命周期 - 创建完成(可以访问当前this实例)",
277 309 created() {
... ...
web_src/static/js/jessibuca/decoder.js 0 → 100644
  1 +!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(require("path"),require("fs"),require("crypto")):"function"==typeof define&&define.amd?define(["path","fs","crypto"],r):r((e="undefined"!=typeof globalThis?globalThis:e||self).path,e.fs,e.crypto$1)}(this,(function(e,r,t){"use strict";function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var o=n(e),a=n(r),i=n(t);var s=function(e,r){return e(r={exports:{}},r.exports),r.exports}((function(e){var r=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=a.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)),D(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"),D("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_"),D(void 0===r.memoryInitializerPrefixURL,"Module.memoryInitializerPrefixURL option was removed, use Module.locateFile instead"),D(void 0===r.pthreadMainPrefixURL,"Module.pthreadMainPrefixURL option was removed, use Module.locateFile instead"),D(void 0===r.cdInitializerPrefixURL,"Module.cdInitializerPrefixURL option was removed, use Module.locateFile instead"),D(void 0===r.filePackagePrefixURL,"Module.filePackagePrefixURL option was removed, use Module.locateFile instead"),D(void 0===r.read,"Module.read option was removed (modify read_ in JS)"),D(void 0===r.readAsync,"Module.readAsync option was removed (modify readAsync in JS)"),D(void 0===r.readBinary,"Module.readBinary option was removed (modify readBinary in JS)"),D(void 0===r.setWindowTitle,"Module.setWindowTitle option was removed (modify setWindowTitle in JS)"),D(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"),D(!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 D(e,r){e||ge("Assertion failed"+(r?": "+r:""))}var O="undefined"!=typeof TextDecoder?new TextDecoder("utf8"):void 0;function F(e,r,t){for(var n=r+t,o=r;e[o]&&!(o>=n);)++o;if(o-r>16&&e.buffer&&O)return O.decode(e.subarray(r,o));for(var a="";r<o;){var i=e[r++];if(128&i){var s=63&e[r++];if(192!=(224&i)){var l=63&e[r++];if(224==(240&i)?i=(15&i)<<12|s<<6|l:(240!=(248&i)&&T("Invalid UTF-8 leading byte 0x"+i.toString(16)+" encountered when deserializing a UTF-8 string in wasm memory to a JS string!"),i=(7&i)<<18|s<<12|l<<6|63&e[r++]),i<65536)a+=String.fromCharCode(i);else{var u=i-65536;a+=String.fromCharCode(55296|u>>10,56320|1023&u)}}else a+=String.fromCharCode((31&i)<<6|s)}else a+=String.fromCharCode(i)}return a}function M(e,r){return e?F(U,e,r):""}function R(e,r,t,n){if(!(n>0))return 0;for(var o=t,a=t+n-1,i=0;i<e.length;++i){var s=e.charCodeAt(i);if(s>=55296&&s<=57343)s=65536+((1023&s)<<10)|1023&e.charCodeAt(++i);if(s<=127){if(t>=a)break;r[t++]=s}else if(s<=2047){if(t+1>=a)break;r[t++]=192|s>>6,r[t++]=128|63&s}else if(s<=65535){if(t+2>=a)break;r[t++]=224|s>>12,r[t++]=128|s>>6&63,r[t++]=128|63&s}else{if(t+3>=a)break;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 D("number"==typeof t,"stringToUTF8(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!"),R(e,U,r,t)}function I(e){for(var r=0,t=0;t<e.length;++t){var n=e.charCodeAt(t);n>=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){D(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 a="",i=0;!(i>=r/2);++i){var s=B[e+2*i>>1];if(0==s)break;a+=String.fromCharCode(s)}return a}function Y(e,r,t){if(D(r%2==0,"Pointer passed to stringToUTF16 must be aligned to two bytes!"),D("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,a=0;a<o;++a){var i=e.charCodeAt(a);B[r>>1]=i,r+=2}return B[r>>1]=0,r-n}function X(e){return 2*e.length}function q(e,r){D(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 a=o-65536;n+=String.fromCharCode(55296|a>>10,56320|1023&a)}else n+=String.fromCharCode(o)}return n}function K(e,r,t){if(D(r%4==0,"Pointer passed to stringToUTF32 must be aligned to four bytes!"),D("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,a=0;a<e.length;++a){var i=e.charCodeAt(a);if(i>=55296&&i<=57343)i=65536+((1023&i)<<10)|1023&e.charCodeAt(++a);if($[r>>2]=i,(r+=4)+4>o)break}return $[r>>2]=0,r-n}function J(e){for(var r=0,t=0;t<e.length;++t){var n=e.charCodeAt(t);n>=55296&&n<=57343&&++t,r+=4}return r}function Q(e){var r=I(e)+1,t=gt(r);return t&&R(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&&D(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();D(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"),D(te>=ee,"INITIAL_MEMORY should be larger than TOTAL_STACK, was "+te+"! (TOTAL_STACK="+"5242880)"),D("undefined"!=typeof Int32Array&&"undefined"!=typeof Float64Array&&null!=Int32Array.prototype.subarray&&null!=Int32Array.prototype.set,"JS engine does not provide full typed array support"),D(!r.wasmMemory,"Use of `wasmMemory` detected. Use -sIMPORTED_MEMORY to define wasmMemory externally"),D(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 ae=[],ie=[],se=[],le=!1;D(Math.imul,"This browser does not support Math.imul(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill"),D(Math.fround,"This browser does not support Math.fround(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill"),D(Math.clz32,"This browser does not support Math.clz32(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill"),D(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?(D(!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?(D(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),D(le,"native function `"+n+"` called before runtime initialization"),o[e]||D(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 De={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<e.length&&""===e[r];r++);for(var t=e.length-1;t>=0&&""===e[t];t--);return r>t?[]:e.slice(r,t-r+1)}e=De.resolve(e).substr(1),r=De.resolve(r).substr(1);for(var n=t(e.split("/")),o=t(r.split("/")),a=Math.min(n.length,o.length),i=a,s=0;s<a;s++)if(n[s]!==o[s]){i=s;break}var l=[];for(s=i;s<n.length;s++)l.push("..");return(l=l.concat(o.slice(i))).join("/")}},Oe={ttys:[],init:function(){},shutdown:function(){},register:function(e,r){Oe.ttys[e]={input:[],output:[],ops:r},Ie.registerDevice(e,Oe.stream_ops)},stream_ops:{open:function(e){var r=Oe.ttys[e.node.rdev];if(!r)throw new Ie.ErrnoError(43);e.tty=r,e.seekable=!1},close:function(e){e.tty.ops.flush(e.tty)},flush:function(e){e.tty.ops.flush(e.tty)},read:function(e,r,t,n,o){if(!e.tty||!e.tty.ops.get_char)throw new Ie.ErrnoError(60);for(var a=0,i=0;i<n;i++){var s;try{s=e.tty.ops.get_char(e.tty)}catch(e){throw new Ie.ErrnoError(29)}if(void 0===s&&0===a)throw new Ie.ErrnoError(6);if(null==s)break;a++,r[t+i]=s}return a&&(e.node.timestamp=Date.now()),a},write:function(e,r,t,n,o){if(!e.tty||!e.tty.ops.put_char)throw new Ie.ErrnoError(60);try{for(var a=0;a<n;a++)e.tty.ops.put_char(e.tty,r[t+a])}catch(e){throw new Ie.ErrnoError(29)}return n&&(e.node.timestamp=Date.now()),a}},default_tty_ops:{get_char:function(e){if(!e.input.length){var r=null;if(u){var t=Buffer.alloc(256),n=0;try{n=m.readSync(process.stdin.fd,t,0,256,-1)}catch(e){if(!e.toString().includes("EOF"))throw e;n=0}r=n>0?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(F(e.output,0)),e.output=[]):0!=r&&e.output.push(r)},flush:function(e){e.output&&e.output.length>0&&(b(F(e.output,0)),e.output=[])}},default_tty1_ops:{put_char:function(e,r){null===r||10===r?(_(F(e.output,0)),e.output=[]):0!=r&&e.output.push(r)},flush:function(e){e.output&&e.output.length>0&&(_(F(e.output,0)),e.output=[])}}};function Fe(e){e=function(e,r){return D(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 Me={ops_table:null,mount:function(e){return Me.createNode(null,"/",16895,0)},createNode:function(e,r,t,n){if(Ie.isBlkdev(t)||Ie.isFIFO(t))throw new Ie.ErrnoError(63);Me.ops_table||(Me.ops_table={dir:{node:{getattr:Me.node_ops.getattr,setattr:Me.node_ops.setattr,lookup:Me.node_ops.lookup,mknod:Me.node_ops.mknod,rename:Me.node_ops.rename,unlink:Me.node_ops.unlink,rmdir:Me.node_ops.rmdir,readdir:Me.node_ops.readdir,symlink:Me.node_ops.symlink},stream:{llseek:Me.stream_ops.llseek}},file:{node:{getattr:Me.node_ops.getattr,setattr:Me.node_ops.setattr},stream:{llseek:Me.stream_ops.llseek,read:Me.stream_ops.read,write:Me.stream_ops.write,allocate:Me.stream_ops.allocate,mmap:Me.stream_ops.mmap,msync:Me.stream_ops.msync}},link:{node:{getattr:Me.node_ops.getattr,setattr:Me.node_ops.setattr,readlink:Me.node_ops.readlink},stream:{}},chrdev:{node:{getattr:Me.node_ops.getattr,setattr:Me.node_ops.setattr},stream:Ie.chrdev_stream_ops}});var o=Ie.createNode(e,r,t,n);return Ie.isDir(o.mode)?(o.node_ops=Me.ops_table.dir.node,o.stream_ops=Me.ops_table.dir.stream,o.contents={}):Ie.isFile(o.mode)?(o.node_ops=Me.ops_table.file.node,o.stream_ops=Me.ops_table.file.stream,o.usedBytes=0,o.contents=null):Ie.isLink(o.mode)?(o.node_ops=Me.ops_table.link.node,o.stream_ops=Me.ops_table.link.stream):Ie.isChrdev(o.mode)&&(o.node_ops=Me.ops_table.chrdev.node,o.stream_ops=Me.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&&Me.resizeFileStorage(e,r.size)},lookup:function(e,r){throw Ie.genericErrors[44]},mknod:function(e,r,t,n){return Me.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=Me.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 a=e.node.contents;if(o>=e.node.usedBytes)return 0;var i=Math.min(e.node.usedBytes-o,n);if(D(i>=0),i>8&&a.subarray)r.set(a.subarray(o,o+i),t);else for(var s=0;s<i;s++)r[t+s]=a[o+s];return i},write:function(e,r,t,n,o,a){if(D(!(r instanceof ArrayBuffer)),r.buffer===x.buffer&&(a=!1),!n)return 0;var i=e.node;if(i.timestamp=Date.now(),r.subarray&&(!i.contents||i.contents.subarray)){if(a)return D(0===o,"canOwn must imply no weird position inside the file"),i.contents=r.subarray(t,t+n),i.usedBytes=n,n;if(0===i.usedBytes&&0===o)return i.contents=r.slice(t,t+n),i.usedBytes=n,n;if(o+n<=i.usedBytes)return i.contents.set(r.subarray(t,t+n),o),n}if(Me.expandFileStorage(i,o+n),i.contents.subarray&&r.subarray)i.contents.set(r.subarray(t,t+n),o);else for(var s=0;s<n;s++)i.contents[o+s]=r[t+s];return i.usedBytes=Math.max(i.usedBytes,o+n),n},llseek:function(e,r,t){var n=r;if(1===t?n+=e.position:2===t&&Ie.isFile(e.node.mode)&&(n+=e.node.usedBytes),n<0)throw new Ie.ErrnoError(28);return n},allocate:function(e,r,t){Me.expandFileStorage(e.node,r+t),e.node.usedBytes=Math.max(e.node.usedBytes,r+t)},mmap:function(e,r,t,n,o,a){if(0!==r)throw new Ie.ErrnoError(28);if(!Ie.isFile(e.node.mode))throw new Ie.ErrnoError(43);var i,s,l=e.node.contents;if(2&a||l.buffer!==L){if((n>0||n+t<l.length)&&(l=l.subarray?l.subarray(n,n+t):Array.prototype.slice.call(l,n,n+t)),s=!0,!(i=Fe(t)))throw new Ie.ErrnoError(48);x.set(l,i)}else s=!1,i=l.byteOffset;return{ptr:i,allocated:s}},msync:function(e,r,t,n,o){if(!Ie.isFile(e.node.mode))throw new Ie.ErrnoError(43);return 2&o||Me.stream_ops.write(e,r,0,n,t,!1),0}}};var Re={0:"Success",1:"Arg list too long",2:"Permission denied",3:"Address already in use",4:"Address not available",5:"Address family not supported by protocol family",6:"No more processes",7:"Socket already connected",8:"Bad file number",9:"Trying to read unreadable message",10:"Mount device busy",11:"Operation canceled",12:"No children",13:"Connection aborted",14:"Connection refused",15:"Connection reset by peer",16:"File locking deadlock error",17:"Destination address required",18:"Math arg out of domain of func",19:"Quota exceeded",20:"File exists",21:"Bad address",22:"File too large",23:"Host is unreachable",24:"Identifier removed",25:"Illegal byte sequence",26:"Connection already in progress",27:"Interrupted system call",28:"Invalid argument",29:"I/O error",30:"Socket is already connected",31:"Is a directory",32:"Too many symbolic links",33:"Too many open files",34:"Too many links",35:"Message too long",36:"Multihop attempted",37:"File or path name too long",38:"Network interface is not configured",39:"Connection reset by network",40:"Network is unreachable",41:"Too many open files in system",42:"No buffer space available",43:"No such device",44:"No such file or directory",45:"Exec format error",46:"No record locks available",47:"The link has been severed",48:"Not enough core",49:"No message of desired type",50:"Protocol not available",51:"No space left on device",52:"Function not implemented",53:"Socket is not connected",54:"Not a directory",55:"Directory not empty",56:"State not recoverable",57:"Socket operation on non-socket",59:"Not a typewriter",60:"No such device or address",61:"Value too large for defined data type",62:"Previous owner died",63:"Not super-user",64:"Broken pipe",65:"Protocol error",66:"Unknown protocol",67:"Protocol wrong type for socket",68:"Math result not representable",69:"Read only file system",70:"Illegal seek",71:"No such process",72:"Stale file handle",73:"Connection timed out",74:"Text file busy",75:"Cross-device link",100:"Device not a stream",101:"Bad font file fmt",102:"Invalid slot",103:"Invalid request code",104:"No anode",105:"Block device required",106:"Channel number out of range",107:"Level 3 halted",108:"Level 3 reset",109:"Link number out of range",110:"Protocol driver not attached",111:"No CSI structure available",112:"Level 2 halted",113:"Invalid exchange",114:"Invalid request descriptor",115:"Exchange full",116:"No data (for no delay io)",117:"Timer expired",118:"Out of streams resources",119:"Machine is not on the network",120:"Package not installed",121:"The object is remote",122:"Advertise error",123:"Srmount error",124:"Communication error on send",125:"Cross mount point (not really error)",126:"Given log. name not unique",127:"f.d. invalid for this operation",128:"Remote address changed",129:"Can access a needed shared lib",130:"Accessing a corrupted shared lib",131:".lib section in a.out corrupted",132:"Attempting to link in too many libs",133:"Attempting to exec a shared library",135:"Streams pipe error",136:"Too many users",137:"Socket type not supported",138:"Not supported",139:"Protocol family not supported",140:"Can't send after socket shutdown",141:"Too many references",142:"Host is down",148:"No medium (in tape drive)",156:"Level 2 not synchronized"},Ne={},Ie={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:!1,ignorePermissions:!0,ErrnoError:null,genericErrors:{},filesystems:null,syncFSRequests:0,lookupPath:function(e){let r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!(e=De.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,a="/",i=0;i<n.length;i++){var s=i===n.length-1;if(s&&r.parent)break;if(o=Ie.lookupNode(o,n[i]),a=Ae.join2(a,n[i]),Ie.isMountpoint(o)&&(!s||s&&r.follow_mount)&&(o=o.mounted.root),!s||r.follow)for(var l=0;Ie.isLink(o.mode);){var u=Ie.readlink(a);a=De.resolve(Ae.dirname(a),u);var c=Ie.lookupPath(a,{recurse_count:r.recurse_count+1});if(o=c.node,l++>40)throw new Ie.ErrnoError(32)}}return{path:a,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<r.length;n++)t=(t<<5)-t+r.charCodeAt(n)|0;return(e+t>>>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 a=o.name;if(o.parent.id===e.id&&a===r)return o}return Ie.lookup(e,r)},createNode:(e,r,t,n)=>{D("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 D(Ie.syncFSRequests>0),Ie.syncFSRequests--,r(e)}function a(e){if(e)return a.errored?void 0:(a.errored=!0,o(e));++n>=t.length&&o(null)}t.forEach((r=>{if(!r.type.syncfs)return a(null);r.type.syncfs(r,e,a)}))},mount:(e,r,t)=>{if("string"==typeof e)throw e;var n,o="/"===t,a=!t;if(o&&Ie.root)throw new Ie.ErrnoError(10);if(!o&&!a){var i=Ie.lookupPath(t,{follow_mount:!1});if(t=i.path,n=i.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 a=t.mount.mounts.indexOf(n);D(-1!==a),t.mount.mounts.splice(a,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 a=Ie.mayCreate(n,o);if(a)throw new Ie.ErrnoError(a);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<t.length;++o)if(t[o]){n+="/"+t[o];try{Ie.mkdir(n,r)}catch(e){if(20!=e.errno)throw e}}},mkdev:(e,r,t)=>(void 0===t&&(t=r,r=438),r|=8192,Ie.mknod(e,r,t)),symlink:(e,r)=>{if(!De.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),a=Ae.dirname(r),i=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,i),c=De.relative(e,a);if("."!==c.charAt(0))throw new Ie.ErrnoError(28);if("."!==(c=De.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,i,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 De.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,a)=>{if(""===e)throw new Ie.ErrnoError(44);var i;if(n=void 0===n?438:n,n=64&(t="string"==typeof t?Ie.modeStringToFlags(t):t)?4095&n|32768:0,"object"==typeof e)i=e;else{e=Ae.normalize(e);try{i=Ie.lookupPath(e,{follow:!(131072&t)}).node}catch(e){}}var s=!1;if(64&t)if(i){if(128&t)throw new Ie.ErrnoError(20)}else i=Ie.mknod(e,n,0),s=!0;if(!i)throw new Ie.ErrnoError(44);if(Ie.isChrdev(i.mode)&&(t&=-513),65536&t&&!Ie.isDir(i.mode))throw new Ie.ErrnoError(54);if(!s){var l=Ie.mayOpen(i,t);if(l)throw new Ie.ErrnoError(l)}512&t&&Ie.truncate(i,0),t&=-131713;var u=Ie.createStream({node:i,path:Ie.getPath(i),flags:t,seekable:!0,position:0,stream_ops:i.stream_ops,ungotten:[],error:!1},o,a);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 a=void 0!==o;if(a){if(!e.seekable)throw new Ie.ErrnoError(70)}else o=e.position;var i=e.stream_ops.read(e,r,t,n,o);return a||(e.position+=i),i},write:(e,r,t,n,o,a)=>{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 i=void 0!==o;if(i){if(!e.seekable)throw new Ie.ErrnoError(70)}else o=e.position;var s=e.stream_ops.write(e,r,t,n,o,a);return i||(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,a)=>{if(0!=(2&o)&&0==(2&a)&&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,a)},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),a=o.size,i=new Uint8Array(a);return Ie.read(n,i,0,a,0),"utf8"===r.encoding?t=F(i,0):"binary"===r.encoding&&(t=i),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),a=R(r,o,0,o.length);Ie.write(n,o,0,a,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)),Oe.register(Ie.makedev(5,0),Oe.default_tty_ops),Oe.register(Ie.makedev(6,0),Oe.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=i.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);D(0===e.fd,"invalid handle for stdin ("+e.fd+")"),D(1===t.fd,"invalid handle for stdout ("+t.fd+")"),D(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=Re[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="<generic error, no stack>"})))},staticInit:()=>{Ie.ensureErrnoError(),Ie.nameTable=new Array(4096),Ie.mount(Me,{},"/"),Ie.createDefaultDirectories(),Ie.createDefaultDevices(),Ie.createSpecialDirectories(),Ie.filesystems={MEMFS:Me}},init:(e,t,n)=>{D(!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<Ie.streams.length;e++){var r=Ie.streams[e];r&&Ie.close(r)}},getMode:(e,r)=>{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 a=o.pop();if(a){var i=Ae.join2(e,a);try{Ie.mkdir(i)}catch(e){}e=i}}return i},createFile:(e,r,t,n,o)=>{var a=Ae.join2("string"==typeof e?e:Ie.getPath(e),r),i=Ie.getMode(n,o);return Ie.create(a,i)},createDataFile:(e,r,t,n,o,a)=>{var i=r;e&&(e="string"==typeof e?e:Ie.getPath(e),i=r?Ae.join2(e,r):e);var s=Ie.getMode(n,o),l=Ie.create(i,s);if(t){if("string"==typeof t){for(var u=new Array(t.length),c=0,d=t.length;c<d;++c)u[c]=t.charCodeAt(c);t=u}Ie.chmod(l,146|s);var f=Ie.open(l,577);Ie.write(f,t,0,t.length,0,a),Ie.close(f),Ie.chmod(l,s)}return l},createDevice:(e,r,t,n)=>{var o=Ae.join2("string"==typeof e?e:Ie.getPath(e),r),a=Ie.getMode(!!t,!!n);Ie.createDevice.major||(Ie.createDevice.major=64);var i=Ie.makedev(Ie.createDevice.major++,0);return Ie.registerDevice(i,{open:e=>{e.seekable=!1},close:e=>{n&&n.buffer&&n.buffer.length&&n(10)},read:(e,r,n,o,a)=>{for(var i=0,s=0;s<o;s++){var l;try{l=t()}catch(e){throw new Ie.ErrnoError(29)}if(void 0===l&&0===i)throw new Ie.ErrnoError(6);if(null==l)break;i++,r[n+s]=l}return i&&(e.node.timestamp=Date.now()),i},write:(e,r,t,o,a)=>{for(var i=0;i<o;i++)try{n(r[t+i])}catch(e){throw new Ie.ErrnoError(29)}return o&&(e.node.timestamp=Date.now()),i}}),Ie.mkdev(o,a,i)},forceLoadFile:e=>{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 a(){this.lengthKnown=!1,this.chunks=[]}if(a.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]}},a.prototype.setDataGetter=function(e){this.getter=e},a.prototype.cacheLength=function(){var e=new XMLHttpRequest;if(e.open("HEAD",t,!1),e.send(null),!(e.status>=200&&e.status<300||304===e.status))throw new Error("Couldn't load "+t+". Status: "+e.status);var r,n=Number(e.getResponseHeader("Content-length")),o=(r=e.getResponseHeader("Accept-Ranges"))&&"bytes"===r,a=(r=e.getResponseHeader("Content-Encoding"))&&"gzip"===r,i=1048576;o||(i=n);var s=this;s.setDataGetter((e=>{var r=e*i,o=(e+1)*i-1;if(o=Math.min(o,n-1),void 0===s.chunks[e]&&(s.chunks[e]=((e,r)=>{if(e>r)throw new Error("invalid range ("+e+", "+r+") or no bytes requested!");if(r>n-1)throw new Error("only "+n+" bytes available! programmer error!");var o=new XMLHttpRequest;if(o.open("GET",t,!1),n!==i&&o.setRequestHeader("Range","bytes="+e+"-"+r),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]})),!a&&n||(i=n=1,n=this.getter(0).length,i=n,b("LazyFiles on gzip forces download of the whole file when length is accessed")),this._length=n,this._chunkSize=i,this.lengthKnown=!0},"undefined"!=typeof XMLHttpRequest){if(!l)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var i=new a;Object.defineProperties(i,{length:{get:function(){return this.lengthKnown||this.cacheLength(),this._length}},chunkSize:{get:function(){return this.lengthKnown||this.cacheLength(),this._chunkSize}}});var s={isDevice:!1,contents:i}}else s={isDevice:!1,url:t};var 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 a=e.node.contents;if(o>=a.length)return 0;var i=Math.min(a.length-o,n);if(D(i>=0),a.slice)for(var s=0;s<i;s++)r[t+s]=a[o+s];else for(s=0;s<i;s++)r[t+s]=a.get(o+s);return i},u.stream_ops=c,u},createPreloadedFile:(e,r,t,n,o,a,i,s,l,u)=>{var c=r?De.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),a&&a(),he(d)}Browser.handledByPreloadPlugin(t,c,f,(()=>{i&&i(),he(d)}))||f(t)}me(d),"string"==typeof t?function(e,r,t,n){var o=n?"":pe("al "+e);f(e,(function(t){D(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)),i):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"),a=n.objectStore(Ie.DB_STORE_NAME),i=0,s=0,l=e.length;function u(){0==s?r():t()}e.forEach((e=>{var r=a.put(Ie.analyzePath(e).object.contents,e);r.onsuccess=()=>{++i+s==l&&u()},r.onerror=()=>{s++,i+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 a=n.transaction([Ie.DB_STORE_NAME],"readonly")}catch(e){return void t(e)}var i=a.objectStore(Ie.DB_STORE_NAME),s=0,l=0,u=e.length;function c(){0==l?r():t()}e.forEach((e=>{var r=i.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()}})),a.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 a=U.slice(e,e+t);Ie.msync(r,a,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)),a=x[r+o];return N(n,r,t+1),x[r+o]=a,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,a=0;a<t;a++){var i=$[r>>2],s=$[r+4>>2];r+=8;var l=Ie.read(e,x,i,s,n);if(l<0)return-1;if(o+=l,l<s)break}return o},doWritev:function(e,r,t,n){for(var o=0,a=0;a<t;a++){var i=$[r>>2],s=$[r+4>>2];r+=8;var l=Ie.write(e,x,i,s,n);if(l<0)return-1;o+=l}return o},varargs:void 0,get:function(){return D(null!=Le.varargs),Le.varargs+=4,$[Le.varargs-4>>2]},getStr:function(e){return M(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 Xe=void 0;function qe(e){throw new Xe(e)}function Ke(e,r,t){function n(r){var n=t(r);n.length!==e.length&&qe("Mismatched type converter count");for(var o=0;o<e.length;++o)Je(e[o],n[o])}e.forEach((function(e){We[e]=r}));var o=new Array(r.length),a=[],i=0;r.forEach(((e,r)=>{$e.hasOwnProperty(e)?o[r]=$e[e]:(a.push(e),je.hasOwnProperty(e)||(je[e]=[]),je[e].push((()=>{o[r]=$e[e],++i===a.length&&n(o)})))})),0===a.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 ar(){return Object.keys(dr).length}function ir(){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||qe("makeClassHandle requires ptr and ptrType"),!!r.smartPtrType!==!!r.smartPtr&&qe("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 a,i=this.registeredClass.getActualType(r),s=or[i];if(!s)return o.call(this);a=this.isConst?s.constPointerType:s.pointerType;var l=nr(r,this.registeredClass,a.registeredClass);return null===l?o.call(this):this.isSmartPointer?pr(a.registeredClass.instancePrototype,{ptrType:a,ptr:l,smartPtrType:this,smartPtr:e}):pr(a.registeredClass.instancePrototype,{ptrType:a,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,a,i,s){this.name=e,this.constructor=r,this.instancePrototype=t,this.rawDestructor=n,this.baseClass=o,this.getActualType=a,this.upcast=i,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 "'+Xr(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 "'+Xr(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 "'+Xr(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 Dr(e){this.rawDestructor&&this.rawDestructor(e)}function Or(e){null!==e&&e.delete()}function Fr(e,r,t,n,o,a,i,s,l,u,c){this.name=e,this.registeredClass=r,this.isReference=t,this.isConst=n,this.isSmartPointer=o,this.pointeeType=a,this.sharingPolicy=i,this.rawGetPointee=s,this.rawConstructor=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 Mr(e,t,n){return e.includes("j")?function(e,t,n){D("dynCall_"+e in r,"bad function pointer type - no table for sig '"+e+"'"),n&&n.length?D(n.length===e.substring(1).replace(/j/g,"--").length):D(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):(D(Ce(t),"missing table entry in dynCall: "+t),Ce(t).apply(null,n))}function Rr(e,r){var t=(e=Be(e)).includes("j")?function(e,r){D(e.includes("j"),"getDynCaller should only be called with i64 sigs");var t=[];return function(){return t.length=0,Object.assign(t,arguments),Mr(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<e;n++)t.push($[(r>>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 a=r.length;a<2&&Ye("argTypes array size mismatch! Must at least get return value and 'this' types!");for(var i=null!==r[1]&&null!==t,s=!1,l=1;l<r.length;++l)if(null!==r[l]&&void 0===r[l].destructorFunction){s=!0;break}var u="void"!==r[0].name,c="",d="";for(l=0;l<a-2;++l)c+=(0!==l?", ":"")+"arg"+l,d+=(0!==l?", ":"")+"arg"+l+"Wired";var f="return function "+ze(e)+"("+c+") {\nif (arguments.length !== "+(a-2)+") {\nthrowBindingError('function "+e+" called with ' + arguments.length + ' arguments, expected "+(a-2)+" args!');\n}\n";s&&(f+="var destructors = [];\n");var p=s?"destructors":"null",m=["throwBindingError","invoker","fn","runDestructors","retType","classParam"],h=[Ye,n,o,Ur,r[0],r[1]];i&&(f+="var thisWired = classParam.toWireType("+p+", this);\n");for(l=0;l<a-2;++l)f+="var arg"+l+"Wired = argType"+l+".toWireType("+p+", arg"+l+"); // "+r[l+2].name+"\n",m.push("argType"+l),h.push(r[l+2]);if(i&&(d="thisWired"+(d.length>0?", ":"")+d),f+=(u?"var rv = ":"")+"invoker(fn"+(d.length>0?", ":"")+d+");\n",s)f+="runDestructors(destructors);\n";else for(l=i?1:2;l<r.length;++l){var g=1===l?"thisWired":"arg"+(l-2)+"Wired";null!==r[l].destructorFunction&&(f+=g+"_dtor("+g+"); // "+r[l].name+"\n",m.push(g+"_dtor"),h.push(r[l].destructorFunction))}return u&&(f+="var ret = retType.fromWireType(rv);\nreturn ret;\n"),f+="}\n",m.push(f),Br(Function,m).apply(null,h)}function $r(e,r,t){return e instanceof Object||Ye(t+' with invalid "this": '+e),e instanceof r.registeredClass.constructor||Ye(t+' incompatible with "this" of type '+e.constructor.name),e.$$.ptr||Ye("cannot call emscripten binding method "+t+" on deleted object"),Tr(e.$$.ptr,e.$$.ptrType.registeredClass,r.registeredClass)}var Wr=[],zr=[{},{value:void 0},{value:null},{value:!0},{value:!1}];function Hr(e){e>4&&0==--zr[e].refcount&&(zr[e]=void 0,Wr.push(e))}function Gr(){for(var e=0,r=5;r<zr.length;++r)void 0!==zr[r]&&++e;return e}function Vr(){for(var e=5;e<zr.length;++e)if(void 0!==zr[e])return zr[e];return null}var Yr={toValue:e=>(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 Xr(e){if(null===e)return"null";var r=typeof e;return"object"===r||"array"===r||"function"===r?e.toString():""+e}function qr(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 D(r===(0|r)),(e>>>0)+4294967296*r}function tt(e,r){if(e<=0)return e;var t=r<=32?Math.abs(1<<r-1):Math.pow(2,r-1);return e>=t&&(r<=32||e>t)&&(e=-2*t+e),e}function nt(e,r){return e>=0?e:r<=32?2*Math.abs(1<<r-1)+e:Math.pow(2,r)+e}function ot(e,r){D(0==(3&r));var t=e,n=r;function o(e){var r;return n=function(e,r){return"double"===r||"i64"===r?7&e&&(D(4==(7&e)),e+=4):D(0==(3&e)),e}(n,e),"double"===e?(r=Number(H[n>>3]),n+=8):"i64"==e?(r=[$[n>>2],$[n+4>>2]],n+=8):(D(0==(3&n)),e="i32",r=$[n>>2],n+=4),r}for(var a,i,s,l,u,c,d=[];;){var f=t;if(0===(a=x[t>>0]))break;if(i=x[t+1>>0],37==a){var p=!1,m=!1,h=!1,g=!1,v=!1;e:for(;;){switch(i){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++,i=x[t+1>>0]}var y=0;if(42==i)y=o("i32"),t++,i=x[t+1>>0];else for(;i>=48&&i<=57;)y=10*y+(i-48),t++,i=x[t+1>>0];var E,w=!1,b=-1;if(46==i){if(b=0,w=!0,t++,42==(i=x[t+1>>0]))b=o("i32"),t++;else for(;;){var _=x[t+1>>0];if(_<48||_>57)break;b=10*b+(_-48),t++}i=x[t+1>>0]}switch(b<0&&(b=6,w=!1),String.fromCharCode(i)){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++,i=x[t+1>>0],String.fromCharCode(i)){case"d":case"i":case"u":case"o":case"x":case"X":case"p":var T=100==i||105==i;if(s=o("i"+8*(E=E||4)),8==E&&(s=117==i?(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==i||105==i)A=tt(s,8*E).toString(10);else if(117==i)A=nt(s,8*E).toString(10),s=Math.abs(s);else if(111==i)A=(h?"0":"")+k.toString(8);else if(120==i||88==i){if(S=h&&0!=s?"0x":"",s<0){s=-s,A=(k-1).toString(16);for(var C=[],P=0;P<A.length;P++)C.push((15-parseInt(A[P],16)).toString(16));for(A=C.join("");A.length<2*E;)A="f"+A}else A=k.toString(16);88==i&&(S=S.toUpperCase(),A=A.toUpperCase())}else 112==i&&(0===k?A="(nil)":(S="0x",A=k.toString(16)));if(w)for(;A.length<b;)A="0"+A;for(s>=0&&(p?S="+"+S:v&&(S=" "+S)),"-"==A.charAt(0)&&(S="-"+S,A=A.substr(1));S.length+A.length<y;)m?A+=" ":g?A="0"+A:S=" "+S;(A=S+A).split("").forEach((function(e){d.push(e.charCodeAt(0))}));break;case"f":case"F":case"e":case"E":case"g":case"G":var A;if(s=o("double"),isNaN(s))A="nan",g=!1;else if(isFinite(s)){var O=!1,F=Math.min(b,20);if(103==i||71==i){O=!0,b=b||1;var M=parseInt(s.toExponential(F).split("e")[1],10);b>M&&M>=-4?(i=(103==i?"f":"F").charCodeAt(0),b-=M+1):(i=(103==i?"e":"E").charCodeAt(0),b--),F=Math.min(b,20)}101==i||69==i?(A=s.toExponential(F),/[eE][-+]\d$/.test(A)&&(A=A.slice(0,-1)+"0"+A.slice(-1))):102!=i&&70!=i||(A=s.toFixed(F),0===s&&((l=s)<0||0===l&&1/l==-1/0)&&(A="-"+A));var R=A.split("e");if(O&&!h)for(;R[0].length>1&&R[0].includes(".")&&("0"==R[0].slice(-1)||"."==R[0].slice(-1));)R[0]=R[0].slice(0,-1);else for(h&&-1==A.indexOf(".")&&(R[0]+=".");b>F++;)R[0]+="0";A=R[0]+(R.length>1?"e"+R[1]:""),69==i&&(A=A.toUpperCase()),s>=0&&(p?A="+"+A:v&&(A=" "+A))}else A=(s<0?"-":"")+"inf",g=!1;for(;A.length<y;)m?A+=" ":A=!g||"-"!=A[0]&&"+"!=A[0]?(g?"0":" ")+A:A[0]+"0"+A.slice(1);i<97&&(A=A.toUpperCase()),A.split("").forEach((function(e){d.push(e.charCodeAt(0))}));break;case"s":var N=o("i8*"),I=N?vt(N):"(null)".length;if(w&&(I=Math.min(I,b)),!m)for(;I<y--;)d.push(32);if(N)for(P=0;P<I;P++)d.push(U[N++>>0]);else d=d.concat(pt("(null)".substr(0,I),!0));if(m)for(;I<y--;)d.push(32);break;case"c":for(m&&d.push(o("i8"));--y>0;)d.push(32);m||d.push(o("i8"));break;case"n":var L=o("i32*");$[L>>2]=d.length;break;case"%":d.push(a);break;default:for(P=f;P<t+2;P++)d.push(x[P>>0])}t+=2}else d.push(a),t+=1}return d}function at(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 a=e[o];n||(t+=", "),n=!1,t+="number"==typeof a||"string"==typeof a?a:"("+typeof a+")"}t+=")";var i=e.callee.caller;return n&&(t=""),[e=i?i.arguments:[],r,t]}function it(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 a=null;if(128&e)for(a=at(arguments);a[1].includes("_emscripten_");)a=at(a[0]);var i=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 i){var d=i[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&&a[0]&&(a[1]==f&&a[2].length>0&&(r=r.replace(/\s+$/,""),r+=" with values: "+a[1]+a[2]+"\n"),a=at(a[0]))}return r.replace(/\s+$/,"")}(e)),1&e?4&e?console.error(r):2&e?console.warn(r):512&e?console.info(r):256&e?console.debug(r):console.log(r):6&e?_(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),a=R(e,o,0,o.length);return r&&(o.length=a),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"),Xe=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=ar,r.getLiveInheritedInstances=ir,r.flushPendingDeletes=lr,r.setDelayFunction=cr,Fr.prototype.getPointee=Ar,Fr.prototype.destructor=Dr,Fr.prototype.argPackAdvance=8,Fr.prototype.readValueFromPointer=Pr,Fr.prototype.deleteObject=Or,Fr.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 a=28,$[yt()>>2]=a,-1}}catch(e){if(void 0===Ie||!(e instanceof Ie.ErrnoError))throw e;return-e.errno}var a},__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 a=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>>a])},destructorFunction:null})},_embind_register_class:function(e,t,n,o,a,i,s,l,u,c,d,f,p){d=Be(d),i=Rr(a,i),l&&(l=Rr(s,l)),c&&(c=Rr(u,c)),p=Rr(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,a;t=t[0],a=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(a,{constructor:{value:s}});s.prototype=u;var f=new _r(d,s,u,p,n,i,l,c),h=new Fr(d,f,!0,!1,!1),g=new Fr(d+"*",f,!1,!1,!1),v=new Fr(d+" const*",f,!1,!0,!1);return or[e]={pointerType:g,constPointerType:v},function(e,t,n){r.hasOwnProperty(e)||qe("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,a){D(r>0);var i=xr(r,t);o=Rr(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",i)},Ke([],i,(function(n){return n.splice(1,0,null),e.registeredClass.constructor_body[r-1]=jr(t,n,null,o,a),[]})),[]}))},_embind_register_class_function:function(e,r,t,n,o,a,i,s){var l=xr(t,n);r=Be(r),a=Rr(o,a),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,a,i);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,a,i,s,l,u){r=Be(r),o=Rr(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,i])},enumerable:!0,configurable:!0};return c.set=l?()=>{Lr("Cannot access "+n+" due to unbound types",[t,i])}:e=>{Ye(n+" is a read-only property")},Object.defineProperty(e.registeredClass.instancePrototype,r,c),Ke([],l?[t,i]:[t],(function(t){var i=t[0],c={get:function(){var r=$r(this,e,n+" getter");return i.fromWireType(o(a,r))},enumerable:!0};if(l){l=Rr(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 "'+Xr(r)+'" to '+this.name);return r},argPackAdvance:8,readValueFromPointer:qr(r,n),destructorFunction:null})},_embind_register_integer:function(e,r,t,n,o){r=Be(r),-1===o&&(o=4294967295);var a=xe(t),i=e=>e;if(0===n){var s=32-8*t;i=e=>e<<s>>>s}var l=r.includes("unsigned"),u=(e,t)=>{if("number"!=typeof e&&"boolean"!=typeof e)throw new TypeError('Cannot convert "'+Xr(e)+'" to '+t);if(e<n||e>o)throw new TypeError('Passing a number "'+Xr(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:i,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,a,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,a=0;a<=n;++a){var i=e+4+a;if(a==n||0==U[i]){var s=M(o,i-o);void 0===r?r=s:(r+=String.fromCharCode(0),r+=s),o=i+1}}else{var l=new Array(n);for(a=0;a<n;++a)l[a]=String.fromCharCode(U[e+4+a]);r=l.join("")}return ht(e),r},toWireType:function(e,r){r instanceof ArrayBuffer&&(r=new Uint8Array(r));var n="string"==typeof r;n||r instanceof Uint8Array||r instanceof Uint8ClampedArray||r instanceof Int8Array||Ye("Cannot pass non-string to std::string");var o=(t&&n?()=>I(r):()=>r.length)(),a=gt(4+o+1);if(W[a>>2]=o,t&&n)N(r,a+4,o+1);else if(n)for(var i=0;i<o;++i){var s=r.charCodeAt(i);s>255&&(ht(a),Ye("String has UTF-16 code units that do not fit in 8 bits")),U[a+4+i]=s}else for(i=0;i<o;++i)U[a+4+i]=r[i];return null!==e&&e.push(ht,a),a},argPackAdvance:8,readValueFromPointer:Pr,destructorFunction:function(e){ht(e)}})},_embind_register_std_wstring:function(e,r,t){var n,o,a,i,s;t=Be(t),2===r?(n=V,o=Y,i=X,a=()=>j,s=1):4===r&&(n=q,o=K,i=J,a=()=>W,s=2),Je(e,{name:t,fromWireType:function(e){for(var t,o=W[e>>2],i=a(),l=e+4,u=0;u<=o;++u){var c=e+4+u*r;if(u==o||0==i[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 a=i(n),l=gt(4+a+r);return W[l>>2]=a>>s,o(n,l+4,a+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,a;(e=Zr[e])(r=Yr.toValue(r),t=void 0===(a=Qr[o=t])?Be(o):a,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<e;++n)t[n]=Jr($[(r>>2)+n],"parameter "+n);return t}(e,r),n=t[0],o=n.name+"_$"+t.slice(1).map((function(e){return e.name})).join("_")+"$",a=et[o];if(void 0!==a)return a;for(var i=["retType"],s=[n],l="",u=0;u<e-1;++u)l+=(0!==u?", ":"")+"arg"+u,i.push("argType"+u),s.push(t[1+u]);var c="return function "+ze("methodCaller_"+o)+"(handle, name, destructors, args) {\n",d=0;for(u=0;u<e-1;++u)c+=" var arg"+u+" = argType"+u+".readValueFromPointer(args"+(d?"+"+d:"")+");\n",d+=t[u+1].argPackAdvance;for(c+=" var rv = handle[name]("+l+");\n",u=0;u<e-1;++u)t[u+1].deleteObject&&(c+=" argType"+u+".deleteObject(arg"+u+");\n");n.isVoid||(c+=" return retType.toWireType(destructors, rv);\n"),c+="};\n",i.push(c);var f,p,m=Br(Function,i).apply(null,s);return f=m,p=Zr.length,Zr.push(f),a=p,et[o]=a,a},_emval_incref:function(e){e>4&&(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 a=new Date(t.getFullYear(),6,1).getTimezoneOffset(),i=n.getTimezoneOffset(),s=0|(a!=i&&t.getTimezoneOffset()==Math.min(i,a));$[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),a=new Date(r.getFullYear(),6,1).getTimezoneOffset(),i=o.getTimezoneOffset(),s=Math.min(i,a);if(t<0)$[e+32>>2]=Number(a!=i&&s==n);else if(t>0!=(s==n)){var l=Math.max(i,a),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),a=new Date(n,6,1),i=o.getTimezoneOffset(),s=a.getTimezoneOffset(),l=Math.max(i,s);function u(e){var r=e.toTimeString().match(/\(([A-Za-z ]+)\)$/);return r?r[1]:"GMT"}$[e>>2]=60*l,$[r>>2]=Number(i!=s);var c=u(o),d=u(a),f=Q(c),p=Q(d);s<i?($[t>>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){it(e,F(ot(r,t),0))},emscripten_resize_heap:function(e){var r=U.length;D((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 a=1;a<=4;a*=2){var i=r*(1+.2/a);i=Math.min(i,e+100663296);var s=Math.min(o,(t=Math.max(e,i))+((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 a=r+t;$[e+4*o>>2]=a,function(e,r,t){for(var n=0;n<e.length;++n)D(e.charCodeAt(n)===(255&e.charCodeAt(n))),x[r++>>0]=e.charCodeAt(n);t||(x[r>>0]=0)}(n,a),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),a=Le.doReadv(o,r,t);return $[n>>2]=a,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 a=Le.getStreamFromFD(e),i=4294967296*t+(r>>>0),s=9007199254740992;return i<=-s||i>=s?-61:(Ie.llseek(a,i,n),Ee=[a.position>>>0,(ye=a.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],a.getdents&&0===i&&0===n&&(a.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),a=Le.doWritev(o,r,t);return $[n>>2]=a,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,D(w=r.asm.memory,"memory not found in wasm exports"),Z(w.buffer),D(re=r.asm.__indirect_function_table,"table not found in wasm exports"),n=r.asm.__wasm_call_ctors,ie.unshift(n),he("wasm-instantiate")}me("wasm-instantiate");var n=r;function o(e){D(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 a(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?a(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"),a(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(),D(!le),le=!0,r.noFSInit||Ie.init.initialized||Ie.init(),Ie.ignorePermissions=!1,ke(ie),r.onRuntimeInitialized&&r.onRuntimeInitialized(),D(!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(),ae.unshift(e);var e;ke(ae)}(),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 l=1e3,u=1e3,c=!1,d=!1,f=!1,p=!1,m="initVideo",h="render",g="playAudio",v="initAudio",y="audioCode",E="videoCode",w=1,b=2,_="init",T="decode",k="audioDecode",S="videoDecode",C="close",P="updateConfig",A="key",D="delta";function O(e){let r=0;for(let t=0;t<10;t++){0===e[t]&&(r+=1)}return 10===r}(()=>{try{if("object"==typeof WebAssembly&&"function"==typeof WebAssembly.instantiate){const e=new WebAssembly.Module(Uint8Array.of(0,97,115,109,1,0,0,0));if(e instanceof WebAssembly.Module)return new WebAssembly.Instance(e)instanceof WebAssembly.Instance}}catch(e){}})(),Date.now||(Date.now=function(){return(new Date).getTime()}),s.postRun=function(){var e=[],r=[],t={};"VideoEncoder"in self&&(t={hasInit:!1,isEmitInfo:!1,offscreenCanvas:null,offscreenCanvasCtx:null,decoder:new VideoDecoder({output:function(e){t.isEmitInfo||(n.opt.debug&&console.log("Jessibuca: [worker] Webcodecs Video Decoder initSize"),postMessage({cmd:m,w:e.codedWidth,h:e.codedHeight}),t.isEmitInfo=!0,t.offscreenCanvas=new OffscreenCanvas(e.codedWidth,e.codedHeight),t.offscreenCanvasCtx=t.offscreenCanvas.getContext("2d")),t.offscreenCanvasCtx.drawImage(e,0,0,e.codedWidth,e.codedHeight);let r=t.offscreenCanvas.transferToImageBitmap();postMessage({cmd:h,buffer:r,delay:n.delay,ts:0},[r]),setTimeout((function(){e.close?e.close():e.destroy()}),100)},error:function(e){console.error(e)}}),decode:function(e,r){const o=e[0]>>4==1;if(t.hasInit){const n=new EncodedVideoChunk({data:e.slice(5),timestamp:r,type:o?A:D});t.decoder.decode(n)}else if(o&&0===e[1]){const r=15&e[0];n.setVideoCodec(r);const o=function(e){let r=e.subarray(1,4),t="avc1.";for(let e=0;e<3;e++){let n=r[e].toString(16);n.length<2&&(n="0"+n),t+=n}return{codec:t,description:e}}(e.slice(5));t.decoder.configure(o),t.hasInit=!0}},reset(){t.hasInit=!1,t.isEmitInfo=!1,t.offscreenCanvas=null,t.offscreenCanvasCtx=null}});var n={opt:{debug:c,useOffscreen:f,useWCS:d,videoBuffer:l,openWebglAlignment:p,videoBufferDelay:u},useOffscreen:function(){return n.opt.useOffscreen&&"undefined"!=typeof OffscreenCanvas},initAudioPlanar:function(e,t){postMessage({cmd:v,sampleRate:t,channels:e});var n=[],o=0;this.playAudioPlanar=function(t,a,i){for(var l=a,u=[],c=0,d=0;d<2;d++){var f=s.HEAPU32[(t>>2)+d]>>2;u[d]=s.HEAPF32.subarray(f,f+l)}if(o){if(!(l>=(a=1024-o)))return o+=l,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,a)),2==e&&(n[1]=Float32Array.of(...r[1],...u[1].subarray(0,a))),postMessage({cmd:g,buffer:n,ts:i},n.map((e=>e.buffer))),c=a,l-=a}for(o=l;o>=1024;o-=1024)n[0]=u[0].slice(c,c+=1024),2==e&&(n[1]=u[1].slice(c-1024,c)),postMessage({cmd:g,buffer:n,ts:i},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:E,code:e})},setAudioCodec:function(e){postMessage({cmd:y,code:e})},setVideoSize:function(e,r){postMessage({cmd:m,w:e,h:r});var t=e*r,o=t>>2;n.useOffscreen()?(this.offscreenCanvas=new OffscreenCanvas(e,r),this.offscreenCanvasGL=this.offscreenCanvas.getContext("webgl"),this.webglObj=((e,r)=>{var t=["attribute vec4 vertexPos;","attribute vec4 texturePos;","varying vec2 textureCoord;","void main()","{","gl_Position = vertexPos;","textureCoord = texturePos.xy;","}"].join("\n"),n=["precision highp float;","varying highp vec2 textureCoord;","uniform sampler2D ySampler;","uniform sampler2D uSampler;","uniform sampler2D vSampler;","const mat4 YUV2RGB = mat4","(","1.1643828125, 0, 1.59602734375, -.87078515625,","1.1643828125, -.39176171875, -.81296875, .52959375,","1.1643828125, 2.017234375, 0, -1.081390625,","0, 0, 0, 1",");","void main(void) {","highp float y = texture2D(ySampler, textureCoord).r;","highp float u = texture2D(uSampler, textureCoord).r;","highp float v = texture2D(vSampler, textureCoord).r;","gl_FragColor = vec4(y, u, v, 1) * YUV2RGB;","}"].join("\n");r&&e.pixelStorei(e.UNPACK_ALIGNMENT,1);var o=e.createShader(e.VERTEX_SHADER);e.shaderSource(o,t),e.compileShader(o),e.getShaderParameter(o,e.COMPILE_STATUS)||console.log("Vertex shader failed to compile: "+e.getShaderInfoLog(o));var a=e.createShader(e.FRAGMENT_SHADER);e.shaderSource(a,n),e.compileShader(a),e.getShaderParameter(a,e.COMPILE_STATUS)||console.log("Fragment shader failed to compile: "+e.getShaderInfoLog(a));var i=e.createProgram();e.attachShader(i,o),e.attachShader(i,a),e.linkProgram(i),e.getProgramParameter(i,e.LINK_STATUS)||console.log("Program failed to compile: "+e.getProgramInfoLog(i)),e.useProgram(i);var s=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,s),e.bufferData(e.ARRAY_BUFFER,new Float32Array([1,1,-1,1,1,-1,-1,-1]),e.STATIC_DRAW);var l=e.getAttribLocation(i,"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(i,"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(i,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,a){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,a),e.drawArrays(e.TRIANGLE_STRIP,0,4)},destroy:function(){try{e.deleteProgram(i),e.deleteBuffer(s),e.deleteBuffer(u),e.deleteTexture(f),e.deleteTexture(p),e.deleteBuffer(m)}catch(e){}}}})(this.offscreenCanvasGL,n.opt.openWebglAlignment),this.draw=function(a,i,l,u){const c=s.HEAPU8.subarray(i,i+t),d=s.HEAPU8.subarray(l,l+o),f=s.HEAPU8.subarray(u,u+o);if(O(Uint8Array.from(c)))return void(n.opt.debug&&console.log("Jessibuca: [worker]: draw offscreenCanvas is green yuv"));this.webglObj.render(e,r,c,d,f);let p=this.offscreenCanvas.transferToImageBitmap();postMessage({cmd:h,buffer:p,delay:this.delay,ts:a},[p])}):this.draw=function(e,r,a,i){const l=Uint8Array.from(s.HEAPU8.subarray(r,r+t)),u=Uint8Array.from(s.HEAPU8.subarray(a,a+o)),c=Uint8Array.from(s.HEAPU8.subarray(i,i+o));if(O(l))return void(n.opt.debug&&console.log("Jessibuca: [worker]: draw is green yuv"));const d=[l,u,c];postMessage({cmd:h,output:d,delay:this.delay,ts:e},d.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===b&&t.decode?t.decode(e.payload,e.ts):e.decoder.decode(e.payload,e.ts)};this.stopId=setInterval((()=>{if(e.length)if(this.dropping){for((t=e.shift()).type===w&&0===t.payload[1]&&r(t);!t.isIFrame&&e.length;)(t=e.shift()).type===w&&0===t.payload[1]&&r(t);t.isIFrame&&(this.dropping=!1,r(t))}else{var t=e[0];if(-1===this.getDelay(t.ts))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(),a.clear&&a.clear(),t.reset&&t.reset(),this.firstTimestamp=null,this.startTimestamp=null,this.delay=-1,this.dropping=!1,this.webglObj&&(this.webglObj.destroy(),this.offscreenCanvas=null,this.offscreenCanvasGL=null,this.offscreenCanvasCtx=null),e=[],r=[],delete this.playAudioPlanar,delete this.draw},pushBuffer:function(r,t){t.type===w?e.push({ts:t.ts,payload:r,decoder:o,type:w}):t.type===b&&e.push({ts:t.ts,payload:r,decoder:a,type:b,isIFrame:t.isIFrame})}},o=new s.AudioDecoder(n),a=new s.VideoDecoder(n);postMessage({cmd:_}),self.onmessage=function(e){var r=e.data;switch(r.cmd){case _:try{n.opt=Object.assign(n.opt,JSON.parse(r.opt))}catch(e){}o.sample_rate=r.sampleRate,n.init();break;case T:n.pushBuffer(r.buffer,r.options);break;case k:o.decode(r.buffer,r.ts);break;case S:a.decode(r.buffer,r.ts);break;case C:n.close();break;case P:n.opt[r.key]=r.value}}}}));
... ...
web_src/static/js/jessibuca/decoder.wasm 0 → 100644
No preview for this file type
web_src/static/js/jessibuca/jessibuca.js 0 → 100644
  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={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},c="init",l="initVideo",u="render",h="playAudio",p="initAudio",m="audioCode",g="videoCode",f="wasmError",b="Invalid NAL unit size",y=1,v=2,w=8,S=9,E="init",B="decode",C="audioDecode",R="close",k="updateConfig",T={fullscreen:"fullscreen$2",webFullscreen:"webFullscreen",decoderWorkerInit:"decoderWorkerInit",play:"play",playing:"playing",pause:"pause",mute:"mute",load:"load",loading:"loading",videoInfo:"videoInfo",timeUpdate:"timeUpdate",audioInfo:"audioInfo",log:"log",error:"error",kBps:"kBps",timeout:"timeout",delayTimeout:"delayTimeout",loadingTimeout:"loadingTimeout",stats:"stats",performance:"performance",record:"record",recording:"recording",recordingTimestamp:"recordingTimestamp",recordStart:"recordStart",recordEnd:"recordEnd",recordCreateError:"recordCreateError",buffer:"buffer",videoFrame:"videoFrame",start:"start",metadata:"metadata",resize:"resize",streamEnd:"streamEnd",streamSuccess:"streamSuccess",streamMessage:"streamMessage",streamError:"streamError",volumechange:"volumechange",destroy:"destroy",mseSourceOpen:"mseSourceOpen",mseSourceClose:"mseSourceClose",mseSourceBufferError:"mseSourceBufferError",mseSourceBufferBusy:"mseSourceBufferBusy",mseSourceBufferFull:"mseSourceBufferFull",videoWaiting:"videoWaiting",videoTimeUpdate:"videoTimeUpdate",videoSyncAudio:"videoSyncAudio",playToRenderTimes:"playToRenderTimes"},I={load:T.load,timeUpdate:T.timeUpdate,videoInfo:T.videoInfo,audioInfo:T.audioInfo,error:T.error,kBps:T.kBps,log:T.log,start:T.start,timeout:T.timeout,loadingTimeout:T.loadingTimeout,delayTimeout:T.delayTimeout,fullscreen:"fullscreen",play:T.play,pause:T.pause,mute:T.mute,stats:T.stats,performance:T.performance,recordingTimestamp:T.recordingTimestamp,recordStart:T.recordStart,recordEnd:T.recordEnd,playToRenderTimes:T.playToRenderTimes},x={playError:"playIsNotPauseOrUrlIsNull",fetchError:"fetchError",websocketError:"websocketError",webcodecsH265NotSupport:"webcodecsH265NotSupport",webcodecsDecodeError:"webcodecsDecodeError",webcodecsWidthOrHeightChange:"webcodecsWidthOrHeightChange",mediaSourceH265NotSupport:"mediaSourceH265NotSupport",mediaSourceFull:T.mseSourceBufferFull,mseSourceBufferError:T.mseSourceBufferError,mediaSourceAppendBufferError:"mediaSourceAppendBufferError",mediaSourceBufferListLarge:"mediaSourceBufferListLarge",mediaSourceAppendBufferEndTimeout:"mediaSourceAppendBufferEndTimeout",wasmDecodeError:"wasmDecodeError",webglAlignmentError:"webglAlignmentError"},D="notConnect",j="open",L="close",F="error",O={download:"download",base64:"base64",blob:"blob"},V={7:"H264(AVC)",12:"H265(HEVC)"},M=12,U={10:"AAC",7:"ALAW",8:"MULAW"},Q=38,W=0,J=1,G=2,P="mp4",N="webm",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";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;r<i;r++)o[r-1]=arguments[r];console.log(`Jessibuca: [${t}]`,...o)}},this.warn=function(t){if(e._opt.debug){for(var i=arguments.length,o=new Array(i>1?i-1:0),r=1;r<i;r++)o[r-1]=arguments[r];console.warn(`Jessibuca: [${t}]`,...o)}},this.error=function(e){for(var t=arguments.length,i=new Array(t>1?t-1:0),o=1;o<t;o++)i[o-1]=arguments[o];console.error(`Jessibuca: [${e}]`,...i)}}}class ne{constructor(e){this.destroys=[],this.proxy=this.proxy.bind(this),this.master=e}proxy(e,t,i){let o=arguments.length>3&&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 Ae=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={};o<r;o++)if((e=i[o])&&e[1]in t){for(o=0;o<e.length;o++)s[i[0][o]]=e[o];return s}return!1}(),r={change:o.fullscreenchange,error:o.fullscreenerror},s={request:function(e,i){return new Promise(function(r,s){var a=function(){this.off("change",a),r()}.bind(this);this.on("change",a);var n=(e=e||t.documentElement)[o.requestFullscreen](i);n instanceof Promise&&n.then(a).catch(s)}.bind(this))},exit:function(){return new Promise(function(e,i){if(this.isFullscreen){var r=function(){this.off("change",r),e()}.bind(this);this.on("change",r);var s=t[o.exitFullscreen]();s instanceof Promise&&s.then(r).catch(i)}else e()}.bind(this))},toggle:function(e,t){return this.isFullscreen?this.exit():this.request(e,t)},onchange:function(e){this.on("change",e)},onerror:function(e){this.on("error",e)},on:function(e,i){var o=r[e];o&&t.addEventListener(o,i,!1)},off:function(e,i){var o=r[e];o&&t.removeEventListener(o,i,!1)},raw:o};o?(Object.defineProperties(s,{isFullscreen:{get:function(){return Boolean(t[o.fullscreenElement])}},element:{enumerable:!0,get:function(){return t[o.fullscreenElement]}},isEnabled:{enumerable:!0,get:function(){return Boolean(t[o.fullscreenEnabled])}}}),i?e.exports=s:window.screenfull=s):i?e.exports={isEnabled:!1}:window.screenfull={isEnabled:!1}}()}));function de(){}function ce(){let e=arguments.length>0&&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 le(){return(new Date).getTime()}function ue(e,t,i){return Math.max(Math.min(e,Math.max(t,i)),Math.min(t,i))}function he(e,t,i){if(e)return"object"==typeof t&&Object.keys(t).forEach((i=>{he(e,i,t[i])})),e.style[t]=i,e}function pe(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 me(){return performance&&"function"==typeof performance.now?performance.now():Date.now()}function ge(e){let t=0,i=me();return o=>{t+=o;const r=me(),s=r-i;s>=1e3&&(e(t/s*1e3),i=r,t=0)}}function fe(){return/iphone|ipod|android.*mobile|windows.*phone|blackberry.*mobile/i.test(window.navigator.userAgent.toLowerCase())}function be(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 ye(e){return null==e}function ve(e){return!ye(e)}Ae.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 we{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;n<s;n++)a[n]=arguments[n];t.apply(i,a)}return r._=t,this.on(e,r,i)}emit(e){const t=((this.e||(this.e={}))[e]||[]).slice();for(var i=arguments.length,o=new Array(i>1?i-1:0),r=1;r<i;r++)o[r-1]=arguments[r];for(let e=0;e<t.length;e+=1)t[e].fn.apply(t[e].ctx,o);return this}off(e,t){const i=this.e||(this.e={});if(!e)return Object.keys(i).forEach((e=>{delete i[e]})),void delete this.e;const o=i[e],r=[];if(o&&t)for(let e=0,i=o.length;e<i;e+=1)o[e].fn!==t&&o[e].fn._!==t&&r.push(o[e]);return r.length?i[e]=r:delete i[e],this}}class Se extends we{constructor(){super(),this.init=!1}resetInit(){this.init=!1,this.videoInfo={width:"",height:"",encType:"",encTypeCode:""}}destroy(){this.resetInit(),this.player.$container.removeChild(this.$videoElement),this.off()}updateVideoInfo(e){e.encTypeCode&&(this.videoInfo.encType=V[e.encTypeCode]),e.width&&(this.videoInfo.width=e.width),e.height&&(this.videoInfo.height=e.height),this.videoInfo.encType&&this.videoInfo.height&&this.videoInfo.width&&!this.init&&(this.player.emit(T.videoInfo,this.videoInfo),this.init=!0)}play(){}pause(){}clearView(){}}var Ee="object"==typeof window&&window.window===window?window:"object"==typeof self&&self.self===self?self:"object"==typeof global&&global.global===global?global:void 0;function Be(e,t,i){var o=new XMLHttpRequest;o.open("GET",e),o.responseType="blob",o.onload=function(){Te(o.response,t,i)},o.onerror=function(){console.error("could not download file")},o.send()}function Ce(e){var t=new XMLHttpRequest;t.open("HEAD",e,!1);try{t.send()}catch(e){}return t.status>=200&&t.status<=299}function Re(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 ke=Ee.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),Te="object"!=typeof window||window!==Ee?function(){}:"download"in HTMLAnchorElement.prototype&&!ke?function(e,t,i){var o=Ee.URL||Ee.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?Ce(r.href)?Be(e,t,i):Re(r,r.target="_blank"):Re(r)):(r.href=o.createObjectURL(e),setTimeout((function(){o.revokeObjectURL(r.href)}),4e4),setTimeout((function(){Re(r)}),0))}:"msSaveOrOpenBlob"in navigator?function(e,t,i){if(t=t||e.name||"download","string"==typeof e)if(Ce(e))Be(e,t,i);else{var o=document.createElement("a");o.href=e,o.target="_blank",setTimeout((function(){Re(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 Be(e,t,i);var r="application/octet-stream"===e.type,s=/constructor/i.test(Ee.HTMLElement)||Ee.safari,a=/CriOS\/[\d]+/.test(navigator.userAgent);if((a||r&&s||ke)&&"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=Ee.URL||Ee.webkitURL,d=A.createObjectURL(e);o?o.location=d:location.href=d,o=null,setTimeout((function(){A.revokeObjectURL(d)}),4e4)}};class Ie extends Se{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<i.length;){const r=i[o];try{let i={preserveDrawingBuffer:!0};t=e.getContext(r,i)}catch(e){t=null}t&&"function"==typeof t.getParameter||(t=null),++o}return t}(this.$videoElement);const e=((e,t)=>{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.deleteBuffer(p)}catch(e){}}}})(this.contextGl,this.player._opt.openWebglAlignment);this.contextGlRender=e.render,this.contextGlDestroy=e.destroy}_initContext2D(){this.context2D=this.$videoElement.getContext("2d")}_initCanvasRender(){this.player._opt.useWCS&&!this._supportOffscreen()?(this.renderType=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)}}screenshot(e,t,i,o){e=e||le(),o=o||O.download;const r={png:"image/png",jpeg:"image/jpeg",webp:"image/webp"};let s=.92;!r[t]&&O[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),n=ce(a);return o===O.base64?a:o===O.blob?n:void(o===O.download&&Te(n,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&&(fe()&&this.player.fullscreen?t-=Q:i-=Q);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 xe extends Se{constructor(e){super(),this.player=e;const t=document.createElement("video");t.muted=!0,t.style.position="absolute",t.style.top=0,t.style.left=0,e.$container.appendChild(t),this.videoInfo={width:"",height:"",encType:""};const i=this.player._opt;i.useWCS&&i.wcsUseVideoRender&&(this.trackGenerator=new MediaStreamTrackGenerator({kind:"video"}),t.srcObject=new MediaStream([this.trackGenerator]),this.vwriter=this.trackGenerator.writable.getWriter()),this.$videoElement=t,this.resize();const{proxy:o}=this.player.events;o(this.$videoElement,"canplay",(()=>{this.player.debug.log("Video","canplay")})),o(this.$videoElement,"waiting",(()=>{this.player.emit(T.videoWaiting)})),o(this.$videoElement,"timeupdate",(e=>{const t=parseInt(e.timeStamp,10);this.player.emit(T.timeUpdate,t)})),this.player.debug.log("Video","init")}destroy(){super.destroy(),this.$videoElement&&(this.$videoElement.src="",this.$videoElement.removeAttribute("src"),this.$videoElement=null),this.trackGenerator&&(this.trackGenerator=null),this.vwriter&&(this.vwriter=null),this.player.debug.log("Video","destroy")}play(){setTimeout((()=>{this.$videoElement.play().then((()=>{this.player.debug.log("Video","play")})).catch((e=>{this.player.debug.warn("Video","play",e)}))}),100)}pause(e){e?this.$videoElement&&this.$videoElement.pause():setTimeout((()=>{this.$videoElement&&this.$videoElement.pause()}),100)}clearView(){}screenshot(e,t,i,o){e=e||le(),o=o||O.download;let r=.92;!{png:"image/png",jpeg:"image/jpeg",webp:"image/webp"}[t]&&O[t]&&(o=t,t="png",i=void 0),"string"==typeof i&&(o=i,i=void 0),void 0!==i&&(r=Number(i));const s=this.$videoElement;let a=document.createElement("canvas");a.width=s.videoWidth,a.height=s.videoHeight;a.getContext("2d").drawImage(s,0,0,a.width,a.height);const n=a.toDataURL(O[t]||O.png,r),A=ce(n);return o===O.base64?n:o===O.blob?A:void(o===O.download&&Te(A,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&&(fe()&&this.player.fullscreen?e-=Q:t-=Q),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"}}class De{constructor(e){return new(De.getLoaderFactory(e._opt))(e)}static getLoaderFactory(e){return e.useMSE||e.useWCS&&!e.useOffscreen&&e.wcsUseVideoRender?xe:Ie}}class je extends we{constructor(e){super(),this.bufferList=[],this.player=e,this.scriptNode=null,this.hasInitScriptNode=!1,this.audioContextChannel=null,this.audioContext=new(window.AudioContext||window.webkitAudioContext),this.gainNode=this.audioContext.createGain();const t=this.audioContext.createBufferSource();t.buffer=this.audioContext.createBuffer(1,1,22050),t.connect(this.audioContext.destination),t.noteOn?t.noteOn(0):t.start(0),this.audioBufferSourceNode=t,this.mediaStreamAudioDestinationNode=this.audioContext.createMediaStreamDestination(),this.audioEnabled(!0),this.gainNode.gain.value=0,this.playing=!1,this.audioSyncVideoOption={diff:null},this.audioInfo={encType:"",channels:"",sampleRate:""},this.init=!1,this.hasAudio=!1,this.on(T.videoSyncAudio,(e=>{this.audioSyncVideoOption=e})),this.player.debug.log("AudioContext","init")}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=de,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=U[e.encTypeCode]),e.channels&&(this.audioInfo.channels=e.channels),e.sampleRate&&(this.audioInfo.sampleRate=e.sampleRate),this.audioInfo.sampleRate&&this.audioInfo.channels&&this.audioInfo.encType&&!this.init&&(this.player.emit(T.audioInfo,this.audioInfo),this.init=!0)}get isPlaying(){return this.playing}get isMute(){return 0===this.gainNode.gain.value||this.isStateSuspended()}get volume(){return this.gainNode.gain.value}get bufferSize(){return this.bufferList.length}initScriptNode(){if(this.playing=!0,this.hasInitScriptNode)return;const e=this.audioInfo.channels,t=this.audioContext.createScriptProcessor(1024,0,e);t.onaudioprocess=t=>{const i=t.outputBuffer;if(this.bufferList.length&&this.playing){if(!this.player._opt.useWCS&&!this.player._opt.useMSE&&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;o<e;o++){const e=t.buffer[o],r=i.getChannelData(o);for(let t=0;t<1024;t++)r[t]=e[t]||0}}},t.connect(this.gainNode),this.scriptNode=t,this.gainNode.connect(this.audioContext.destination),this.gainNode.connect(this.mediaStreamAudioDestinationNode),this.hasInitScriptNode=!0}mute(e){e?(this.isMute||this.player.emit(T.mute,e),this.setVolume(0),this.audioEnabled(!1),this.clear()):(this.isMute&&this.player.emit(T.mute,e),this.setVolume(.5),this.audioEnabled(!0))}setVolume(e){e=parseFloat(e).toFixed(2),isNaN(e)||(this.audioEnabled(!0),e=ue(e,0,1),this.gainNode.gain.value=e,this.gainNode.gain.setValueAtTime(e,this.audioContext.currentTime),this.player.emit(T.volumechange,this.player.volume))}closeAudio(){this.hasInitScriptNode&&(this.scriptNode&&this.scriptNode.disconnect(this.gainNode),this.gainNode&&this.gainNode.disconnect(this.audioContext.destination),this.gainNode&&this.gainNode.disconnect(this.mediaStreamAudioDestinationNode)),this.clear()}audioEnabled(e){e?"suspended"===this.audioContext.state&&this.audioContext.resume():"running"===this.audioContext.state&&this.audioContext.suspend()}isStateRunning(){return"running"===this.audioContext.state}isStateSuspended(){return"suspended"===this.audioContext.state}clear(){this.bufferList=[]}play(e,t){this.isMute||(this.hasAudio=!0,this.bufferList.push({buffer:e,ts:t}),this.bufferList.length>20&&(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 Le{constructor(e){return new(Le.getLoaderFactory())(e)}static getLoaderFactory(){return je}}class Fe extends we{constructor(e){super(),this.player=e,this.playing=!1,this.abortController=new AbortController,this.streamRate=ge((t=>{e.emit(T.kBps,(t/1024).toFixed(2))})),e.debug.log("FetchStream","init")}destroy(){this.abort(),this.off(),this.streamRate=null,this.player.debug.log("FetchStream","destroy")}fetchStream(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const{demux:i}=this.player;this.player._times.streamStart=le();const o=Object.assign({signal:this.abortController.signal},{headers:t.headers||{}});fetch(e,o).then((e=>{const t=e.body.getReader();this.emit(T.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();this.abort(),-1===t.indexOf(se)&&(this.emit(x.fetchError,e),this.player.emit(T.error,x.fetchError))}))};o()})).catch((e=>{this.abort(),this.emit(x.fetchError,e),this.player.emit(T.error,x.fetchError)}))}abort(){this.abortController&&(this.abortController.abort(),this.abortController=null)}}class Oe extends we{constructor(e){super(),this.player=e,this.socket=null,this.socketStatus=D,this.wsUrl=null,this.streamRate=ge((t=>{e.emit(T.kBps,(t/1024).toFixed(2))})),e.debug.log("WebsocketLoader","init")}destroy(){this.socket&&(this.socket.close(),this.socket=null),this.socketStatus=D,this.streamRate=null,this.wsUrl=null,this.off(),this.player.debug.log("websocketLoader","destroy")}_createWebSocket(){const e=this.player,{debug:t,events:{proxy:i},demux:o}=e;this.socket=new WebSocket(this.wsUrl),this.socket.binaryType="arraybuffer",i(this.socket,"open",(()=>{this.emit(T.streamSuccess),t.log("websocketLoader","socket open"),this.socketStatus=j})),i(this.socket,"message",(e=>{this.streamRate&&this.streamRate(e.data.byteLength),this._handleMessage(e.data)})),i(this.socket,"close",(()=>{t.log("websocketLoader","socket close"),this.emit(T.streamEnd),this.socketStatus=L})),i(this.socket,"error",(e=>{t.log("websocketLoader","socket error"),this.emit(x.websocketError,e),this.player.emit(T.error,x.websocketError),this.socketStatus=F,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=le(),this.wsUrl=e,this._createWebSocket()}}class Ve{constructor(e){return new(Ve.getLoaderFactory(e._opt.protocol))(e)}static getLoaderFactory(e){return e===a?Fe:e===s?Oe:void 0}}var Me=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<i;o++)t.advertisement.push({duration:o,image:e[o]})},blob:null,bufferSize:0,sampleRate:0,buffer:null,reset:function(){"recording"!==s.state||t.disableLogs||console.warn("Stop an active recorder."),h&&"function"==typeof h.clearRecordedData&&h.clearRecordedData(),h=null,u("inactive"),s.blob=null},onStateChanged:function(e){t.disableLogs||console.log("Recorder state changed:",e)},state:"inactive",getState:function(){return s.state},destroy:function(){var e=t.disableLogs;t={disableLogs:!0},s.reset(),u("destroyed"),g=s=null,E.AudioContextConstructor&&(E.AudioContextConstructor.close(),E.AudioContextConstructor=null),t.disableLogs=e,t.disableLogs||console.log("RecordRTC is destroyed.")},version:"5.6.2"};if(!this)return s=g,g;for(var f in g)this[f]=g[f];return s=this,g}function o(e,t){return t.recorderType||t.type||(t.audio&&t.video?t.type="video":t.audio&&!t.video&&(t.type="audio")),t.recorderType&&!t.type&&(t.recorderType===T||t.recorderType===k||t.recorderType===F?t.type="video":t.recorderType===D?t.type="gif":t.recorderType===R?t.type="audio":t.recorderType===C&&(w(e,"audio").length&&w(e,"video").length||!w(e,"audio").length&&w(e,"video").length?t.type="video":w(e,"audio").length&&!w(e,"video").length&&(t.type="audio"))),"undefined"!=typeof MediaRecorder&&"requestData"in MediaRecorder.prototype&&(t.mimeType||(t.mimeType="video/webm"),t.type||(t.type=t.mimeType.split("/")[0]),t.bitsPerSecond),t.type||(t.mimeType&&(t.type=t.mimeType.split("/")[0]),t.type||(t.type="audio")),t}function r(e,t){var i;return(m||u||h)&&(i=R),"undefined"!=typeof MediaRecorder&&"requestData"in MediaRecorder.prototype&&!m&&(i=C),"video"===t.type&&(m||h)&&(i=T,"undefined"!=typeof ReadableStream&&(i=F)),"gif"===t.type&&(i=D),"canvas"===t.type&&(i=k),B()&&i!==k&&i!==D&&"undefined"!=typeof MediaRecorder&&"requestData"in MediaRecorder.prototype&&(w(e,"video").length||w(e,"audio").length)&&("audio"===t.type?"function"==typeof MediaRecorder.isTypeSupported&&MediaRecorder.isTypeSupported("audio/webm")&&(i=C):"function"==typeof MediaRecorder.isTypeSupported&&MediaRecorder.isTypeSupported("video/webm")&&(i=C)),e instanceof Array&&e.length&&(i=L),t.recorderType&&(i=t.recorderType),!t.disableLogs&&i&&i.name&&console.log("Using recorderType:",i.name||i.constructor.name),!i&&g&&(i=C),i}function s(e){this.addStream=function(t){t&&(e=t)},this.mediaType={audio:!0,video:!0},this.startRecording=function(){var t,o=this.mediaType,r=this.mimeType||{audio:null,video:null,gif:null};if("function"!=typeof o.audio&&B()&&!w(e,"audio").length&&(o.audio=!1),"function"!=typeof o.video&&B()&&!w(e,"video").length&&(o.video=!1),"function"!=typeof o.gif&&B()&&!w(e,"video").length&&(o.gif=!1),!o.audio&&!o.video&&!o.gif)throw"MediaStream must have either audio or video tracks.";if(o.audio&&(t=null,"function"==typeof o.audio&&(t=o.audio),this.audioRecorder=new i(e,{type:"audio",bufferSize:this.bufferSize,sampleRate:this.sampleRate,numberOfAudioChannels:this.numberOfAudioChannels||2,disableLogs:this.disableLogs,recorderType:t,mimeType:r.audio,timeSlice:this.timeSlice,onTimeStamp:this.onTimeStamp}),o.video||this.audioRecorder.startRecording()),o.video){t=null,"function"==typeof o.video&&(t=o.video);var s=e;if(B()&&o.audio&&"function"==typeof o.audio){var a=w(e,"video")[0];p?((s=new f).addTrack(a),t&&t===T&&(t=C)):(s=new f).addTrack(a)}this.videoRecorder=new i(s,{type:"video",video:this.video,canvas:this.canvas,frameInterval:this.frameInterval||10,disableLogs:this.disableLogs,recorderType:t,mimeType:r.video,timeSlice:this.timeSlice,onTimeStamp:this.onTimeStamp,workerPath:this.workerPath,webAssemblyPath:this.webAssemblyPath,frameRate:this.frameRate,bitrate:this.bitrate}),o.audio||this.videoRecorder.startRecording()}if(o.audio&&o.video){var n=this,A=!0===B();(o.audio instanceof R&&o.video||!0!==o.audio&&!0!==o.video&&o.audio!==o.video)&&(A=!1),!0===A?(n.audioRecorder=null,n.videoRecorder.startRecording()):n.videoRecorder.initRecorder((function(){n.audioRecorder.initRecorder((function(){n.videoRecorder.startRecording(),n.audioRecorder.startRecording()}))}))}o.gif&&(t=null,"function"==typeof o.gif&&(t=o.gif),this.gifRecorder=new i(e,{type:"gif",frameRate:this.frameRate||200,quality:this.quality||10,disableLogs:this.disableLogs,recorderType:t,mimeType:r.gif}),this.gifRecorder.startRecording())},this.stopRecording=function(e){e=e||function(){},this.audioRecorder&&this.audioRecorder.stopRecording((function(t){e(t,"audio")})),this.videoRecorder&&this.videoRecorder.stopRecording((function(t){e(t,"video")})),this.gifRecorder&&this.gifRecorder.stopRecording((function(t){e(t,"gif")}))},this.pauseRecording=function(){this.audioRecorder&&this.audioRecorder.pauseRecording(),this.videoRecorder&&this.videoRecorder.pauseRecording(),this.gifRecorder&&this.gifRecorder.pauseRecording()},this.resumeRecording=function(){this.audioRecorder&&this.audioRecorder.resumeRecording(),this.videoRecorder&&this.videoRecorder.resumeRecording(),this.gifRecorder&&this.gifRecorder.resumeRecording()},this.getBlob=function(e){var t={};return this.audioRecorder&&(t.audio=this.audioRecorder.getBlob()),this.videoRecorder&&(t.video=this.videoRecorder.getBlob()),this.gifRecorder&&(t.gif=this.gifRecorder.getBlob()),e&&e(t),t},this.destroy=function(){this.audioRecorder&&(this.audioRecorder.destroy(),this.audioRecorder=null),this.videoRecorder&&(this.videoRecorder.destroy(),this.videoRecorder=null),this.gifRecorder&&(this.gifRecorder.destroy(),this.gifRecorder=null)},this.getDataURL=function(e){function t(e,t){if("undefined"!=typeof Worker){var i=function(e){var t,i=l.createObjectURL(new Blob([e.toString(),"this.onmessage = function (eee) {"+e.name+"(eee.data);}"],{type:"application/javascript"})),o=new Worker(i);if(void 0!==l)t=l;else{if("undefined"==typeof webkitURL)throw"Neither URL nor webkitURL detected.";t=webkitURL}return t.revokeObjectURL(i),o}((function(e){postMessage((new FileReaderSync).readAsDataURL(e))}));i.onmessage=function(e){t(e.data)},i.postMessage(e)}else{var o=new FileReader;o.readAsDataURL(e),o.onload=function(e){t(e.target.result)}}}this.getBlob((function(i){i.audio&&i.video?t(i.audio,(function(o){t(i.video,(function(t){e({audio:o,video:t})}))})):i.audio?t(i.audio,(function(t){e({audio:t})})):i.video&&t(i.video,(function(t){e({video:t})}))}))},this.writeToDisk=function(){i.writeToDisk({audio:this.audioRecorder,video:this.videoRecorder,gif:this.gifRecorder})},this.save=function(e){(e=e||{audio:!0,video:!0,gif:!0}).audio&&this.audioRecorder&&this.audioRecorder.save("string"==typeof e.audio?e.audio:""),e.video&&this.videoRecorder&&this.videoRecorder.save("string"==typeof e.video?e.video:""),e.gif&&this.gifRecorder&&this.gifRecorder.save("string"==typeof e.gif?e.gif:"")}}i.version="5.6.2",t.exports=i,i.getFromDisk=function(e,t){if(!t)throw"callback is mandatory.";console.log("Getting recorded "+("all"===e?"blobs":e+" blob ")+" from disk!"),x.Fetch((function(i,o){"all"!==e&&o===e+"Blob"&&t&&t(i),"all"===e&&t&&t(i,o.replace("Blob",""))}))},i.writeToDisk=function(e){console.log("Writing recorded blob(s) to disk!"),(e=e||{}).audio&&e.video&&e.gif?e.audio.getDataURL((function(t){e.video.getDataURL((function(i){e.gif.getDataURL((function(e){x.Store({audioBlob:t,videoBlob:i,gifBlob:e})}))}))})):e.audio&&e.video?e.audio.getDataURL((function(t){e.video.getDataURL((function(e){x.Store({audioBlob:t,videoBlob:e})}))})):e.audio&&e.gif?e.audio.getDataURL((function(t){e.gif.getDataURL((function(e){x.Store({audioBlob:t,gifBlob:e})}))})):e.video&&e.gif?e.video.getDataURL((function(t){e.gif.getDataURL((function(e){x.Store({videoBlob:t,gifBlob:e})}))})):e.audio?e.audio.getDataURL((function(e){x.Store({audioBlob:e})})):e.video?e.video.getDataURL((function(e){x.Store({videoBlob:e})})):e.gif&&e.gif.getDataURL((function(e){x.Store({gifBlob:e})}))},s.getFromDisk=i.getFromDisk,s.writeToDisk=i.writeToDisk,i.MRecordRTC=s;var a;(a=void 0!==e?e:null)&&"undefined"==typeof window&&void 0!==e&&(e.navigator={userAgent:"Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45",getUserMedia:function(){}},e.console||(e.console={}),void 0!==e.console.log&&void 0!==e.console.error||(e.console.error=e.console.log=e.console.log||function(){console.log(arguments)}),"undefined"==typeof document&&(a.document={documentElement:{appendChild:function(){return""}}},document.createElement=document.captureStream=document.mozCaptureStream=function(){var e={getContext:function(){return e},play:function(){},pause:function(){},drawImage:function(){},toDataURL:function(){return""},style:{}};return e},a.HTMLVideoElement=function(){}),"undefined"==typeof location&&(a.location={protocol:"file:",href:"",hash:""}),"undefined"==typeof screen&&(a.screen={width:0,height:0}),void 0===l&&(a.URL={createObjectURL:function(){return""},revokeObjectURL:function(){return""}}),a.window=e);var n=window.requestAnimationFrame;if(void 0===n)if("undefined"!=typeof webkitRequestAnimationFrame)n=webkitRequestAnimationFrame;else if("undefined"!=typeof mozRequestAnimationFrame)n=mozRequestAnimationFrame;else if("undefined"!=typeof msRequestAnimationFrame)n=msRequestAnimationFrame;else if(void 0===n){var A=0;n=function(e,t){var i=(new Date).getTime(),o=Math.max(0,16-(i-A)),r=setTimeout((function(){e(i+o)}),o);return A=i+o,r}}var d=window.cancelAnimationFrame;void 0===d&&("undefined"!=typeof webkitCancelAnimationFrame?d=webkitCancelAnimationFrame:"undefined"!=typeof mozCancelAnimationFrame?d=mozCancelAnimationFrame:"undefined"!=typeof msCancelAnimationFrame?d=msCancelAnimationFrame:void 0===d&&(d=function(e){clearTimeout(e)}));var c=window.AudioContext;void 0===c&&("undefined"!=typeof webkitAudioContext&&(c=webkitAudioContext),"undefined"!=typeof mozAudioContext&&(c=mozAudioContext));var l=window.URL;void 0===l&&"undefined"!=typeof webkitURL&&(l=webkitURL),"undefined"!=typeof navigator&&void 0===navigator.getUserMedia&&(void 0!==navigator.webkitGetUserMedia&&(navigator.getUserMedia=navigator.webkitGetUserMedia),void 0!==navigator.mozGetUserMedia&&(navigator.getUserMedia=navigator.mozGetUserMedia));var u=!(-1===navigator.userAgent.indexOf("Edge")||!navigator.msSaveBlob&&!navigator.msSaveOrOpenBlob),h=!!window.opera||-1!==navigator.userAgent.indexOf("OPR/"),p=navigator.userAgent.toLowerCase().indexOf("firefox")>-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;a<o-1;a++){var n=a*s,A=Number(Math.floor(n)).toFixed(),d=Number(Math.ceil(n)).toFixed(),l=n-A;r[a]=c(e[A],e[d],l)}return r[o-1]=e[e.length-1],r}function c(e,t,i){return e+(t-e)*i}function l(e,t){for(var i=new Float64Array(t),o=0,r=e.length,s=0;s<r;s++){var a=e[s];i.set(a,o),o+=a.length}return i}function u(e,t,i){for(var o=i.length,r=0;r<o;r++)e.setUint8(t+r,i.charCodeAt(r))}2===o&&(r=l(r,n),s=l(s,n),A&&(r=d(r,A,a),s=d(s,A,a))),1===o&&(r=l(r,n),A&&(r=d(r,A,a))),A&&(a=A),2===o&&(i=function(e,t){for(var i=e.length+t.length,o=new Float64Array(i),r=0,s=0;s<i;)o[s++]=e[r],o[s++]=t[r],r++;return o}(r,s)),1===o&&(i=r);var h=i.length,p=new ArrayBuffer(44+2*h),m=new DataView(p);u(m,0,"RIFF"),m.setUint32(4,36+2*h,!0),u(m,8,"WAVE"),u(m,12,"fmt "),m.setUint32(16,16,!0),m.setUint16(20,1,!0),m.setUint16(22,o,!0),m.setUint32(24,a,!0),m.setUint32(28,a*o*2,!0),m.setUint16(32,2*o,!0),m.setUint16(34,16,!0),u(m,36,"data"),m.setUint32(40,2*h,!0);for(var g=h,f=44,b=0;b<g;b++)m.setInt16(f,32767*i[b],!0),f+=2;if(t)return t({buffer:p,view:m});postMessage({buffer:p,view:m})}if(e.noWorker)i(e,(function(e){t(e.buffer,e.view)}));else{var o,r,s,a=(o=i,r=l.createObjectURL(new Blob([o.toString(),";this.onmessage = function (eee) {"+o.name+"(eee.data);}"],{type:"application/javascript"})),(s=new Worker(r)).workerURL=r,s);a.onmessage=function(e){t(e.data.buffer,e.data.view),l.revokeObjectURL(a.workerURL),a.terminate()},a.postMessage(e)}}!0===t.leftChannel&&(d=1),1===t.numberOfAudioChannels&&(d=1),(!d||d<1)&&(d=2),t.disableLogs||console.log("StereoAudioRecorder is set to record number of channels: "+d),void 0===t.checkForInactiveTracks&&(t.checkForInactiveTracks=!0),this.record=function(){if(!1===u())throw"Please make sure MediaStream is active.";v(),E=y=!1,n=!0,void 0!==t.timeSlice&&C()},this.stop=function(e){e=e||function(){},n=!1,h({desiredSampRate:c,sampleRate:b,numberOfAudioChannels:d,internalInterleavedLength:A,leftBuffers:s,rightBuffers:1===d?[]:a,noWorker:t.noWorker},(function(t,i){r.blob=new Blob([i],{type:"audio/wav"}),r.buffer=new ArrayBuffer(i.buffer.byteLength),r.view=i,r.sampleRate=c||b,r.bufferSize=f,r.length=A,E=!1,e&&e(r.blob)}))},void 0===i.Storage&&(i.Storage={AudioContextConstructor:null,AudioContext:window.AudioContext||window.webkitAudioContext}),i.Storage.AudioContextConstructor&&"closed"!==i.Storage.AudioContextConstructor.state||(i.Storage.AudioContextConstructor=new i.Storage.AudioContext);var p=i.Storage.AudioContextConstructor,m=p.createMediaStreamSource(e),g=[0,256,512,1024,2048,4096,8192,16384],f=void 0===t.bufferSize?4096:t.bufferSize;if(-1===g.indexOf(f)&&(t.disableLogs||console.log("Legal values for buffer-size are "+JSON.stringify(g,null,"\t"))),p.createJavaScriptNode)o=p.createJavaScriptNode(f,d,d);else{if(!p.createScriptProcessor)throw"WebAudio API has no support on this browser.";o=p.createScriptProcessor(f,d,d)}m.connect(o),t.bufferSize||(f=o.bufferSize);var b=void 0!==t.sampleRate?t.sampleRate:p.sampleRate||44100;(b<22050||b>96e3)&&(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;t<r;t+=4){var i={r:e.data[t],g:e.data[t+1],b:e.data[t+2]};Math.sqrt(Math.pow(i.r-p,2)+Math.pow(i.g-m,2)+Math.pow(i.b-g,2))<=f*b&&o++}a()},n.src=e[i].image}},callback:function(){(l=l.concat(e.slice(h))).length<=0&&l.push(e[e.length-1]),r(l)}}).length,function e(){++n!==A?setTimeout((function(){a.functionToLoop(e,n)}),1):a.callback()}()}(t=t||{}).frameInterval||(t.frameInterval=10),t.disableLogs||console.log("Using frames-interval:",t.frameInterval),this.record=function(){t.width||(t.width=320),t.height||(t.height=240),t.video||(t.video={width:t.width,height:t.height}),t.canvas||(t.canvas={width:t.width,height:t.height}),c.width=t.canvas.width||320,c.height=t.canvas.height||240,l=c.getContext("2d"),t.video&&t.video instanceof HTMLVideoElement?(n=t.video.cloneNode(),t.initCallback&&t.initCallback()):(n=document.createElement("video"),S(e,n),n.onloadedmetadata=function(){t.initCallback&&t.initCallback()},n.width=t.video.width,n.height=t.video.height),n.muted=!0,n.play(),A=(new Date).getTime(),d=new I.Video,t.disableLogs||(console.log("canvas resolutions",c.width,"*",c.height),console.log("video width/height",n.width||c.width,"*",n.height||c.height)),i(t.frameInterval)};var r=!1;this.stop=function(e){e=e||function(){},r=!0;var i=this;setTimeout((function(){o(d.frames,-1,null,null,(function(o){d.frames=o,t.advertisement&&t.advertisement.length&&(d.frames=t.advertisement.concat(d.frames)),d.compile((function(t){i.blob=t,i.blob.forEach&&(i.blob=new Blob([],{type:"video/webm"})),e&&e(i.blob)}))}))}),10)};var s=!1;function a(){d.frames=[],r=!0,s=!1}this.pause=function(){s=!0},this.resume=function(){s=!1,r&&this.record()},this.clearRecordedData=function(){r||this.stop(a),a()},this.name="WhammyRecorder",this.toString=function(){return this.name};var n,A,d,c=document.createElement("canvas"),l=c.getContext("2d")}void 0!==c?E.AudioContext=c:"undefined"!=typeof webkitAudioContext&&(E.AudioContext=webkitAudioContext),i.Storage=E,i.MediaStreamRecorder=C,i.StereoAudioRecorder=R,i.CanvasRecorder=k,i.WhammyRecorder=T;var I=function(){function e(e){this.frames=[],this.duration=e||1,this.quality=.8}function t(e){function t(e,t,i){return[{data:e,id:231}].concat(i.map((function(e){var i=function(e){var t=0;e.keyframe&&(t|=128);e.invisible&&(t|=8);e.lacing&&(t|=e.lacing<<1);e.discardable&&(t|=1);if(e.trackNum>127)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;i<e.length;i+=8)t.push(parseInt(e.substr(i,8),2));return new Uint8Array(t)}function r(e){for(var t=[],s=0;s<e.length;s++){var a=e[s].data;"object"==typeof a&&(a=r(a)),"number"==typeof a&&(a=o(a.toString(2))),"string"==typeof a&&(a=new Uint8Array(a.split("").map((function(e){return e.charCodeAt(0)}))));var n=a.size||a.byteLength||a.length,A=Math.ceil(Math.ceil(Math.log(n)/Math.log(2))/8),d=n.toString(2),c=new Array(7*A+7+1-d.length).join("0")+d,l=new Array(A).join("0")+"1"+c;t.push(i(e[s].id)),t.push(o(l)),t.push(a)}return new Blob(t,{type:"video/webm"})}function s(e,t){return parseInt(e.substr(t+4,4).split("").map((function(e){var t=e.charCodeAt(0).toString(2);return new Array(8-t.length+1).join("0")+t})).join(""),2)}function a(e){for(var t=0,i={};t<e.length;){var o=e.substr(t,4),r=s(e,t),n=e.substr(t+4+4,r);t+=8+r,i[o]=i[o]||[],"RIFF"===o||"LIST"===o?i[o].push(a(n)):i[o].push(n)}return i}var n=new function(e){var i=function(e){if(!e[0])return void postMessage({error:"Something went wrong. Maybe WebP format is not supported in the current browser."});for(var t=e[0].width,i=e[0].height,o=e[0].duration,r=1;r<e.length;r++)o+=e[r].duration;return{duration:o,width:t,height:i}}(e);if(!i)return[];for(var o,s=[{id:440786851,data:[{data:1,id:17030},{data:1,id:17143},{data:4,id:17138},{data:8,id:17139},{data:"webm",id:17026},{data:2,id:17031},{data:2,id:17029}]},{id:408125543,data:[{id:357149030,data:[{data:1e6,id:2807729},{data:"whammy",id:19840},{data:"whammy",id:22337},{data:(o=i.duration,[].slice.call(new Uint8Array(new Float64Array([o]).buffer),0).map((function(e){return String.fromCharCode(e)})).reverse().join("")),id:17545}]},{id:374648427,data:[{id:174,data:[{data:1,id:215},{data:1,id:29637},{data:0,id:156},{data:"und",id:2274716},{data:"V_VP8",id:134},{data:"VP8",id:2459272},{data:1,id:131},{id:224,data:[{data:i.width,id:176},{data:i.height,id:186}]}]}]}]}],a=0,n=0;a<e.length;){var A=[],d=0;do{A.push(e[a]),d+=e[a].duration,a++}while(a<e.length&&d<3e4);var c={id:524531317,data:t(n,0,A)};s[1].data.push(c),n+=d}return r(s)}(e.map((function(e){var t=function(e){for(var t=e.RIFF[0].WEBP[0],i=t.indexOf("*"),o=0,r=[];o<4;o++)r[o]=t.charCodeAt(i+3+o);return{width:16383&(r[1]<<8|r[0]),height:16383&(r[3]<<8|r[2]),data:t,riff:e}}(a(atob(e.image.slice(23))));return t.duration=e.duration,t})));postMessage(n)}return e.prototype.add=function(e,t){if("canvas"in e&&(e=e.canvas),"toDataURL"in e&&(e=e.toDataURL("image/webp",this.quality)),!/^data:image\/webp;base64,/gi.test(e))throw"Input must be formatted properly as a base64 encoded DataURI of type image/webp";this.frames.push({image:e,duration:t||this.duration})},e.prototype.compile=function(e){var i,o,r,s=(i=t,o=l.createObjectURL(new Blob([i.toString(),"this.onmessage = function (eee) {"+i.name+"(eee.data);}"],{type:"application/javascript"})),r=new Worker(o),l.revokeObjectURL(o),r);s.onmessage=function(t){t.data.error?console.error(t.data.error):e(t.data)},s.postMessage(this.frames)},{Video:e}}();i.Whammy=I;var x={init:function(){var e=this;if("undefined"!=typeof indexedDB&&void 0!==indexedDB.open){var t,i=this.dbName||location.href.replace(/\/|:|#|%|\.|\[|\]/g,""),o=indexedDB.open(i,1);o.onerror=e.onError,o.onsuccess=function(){((t=o.result).onerror=e.onError,t.setVersion)?1!==t.version?t.setVersion(1).onsuccess=function(){r(t),s()}:s():s()},o.onupgradeneeded=function(e){r(e.target.result)}}else console.error("IndexedDB API are not available in this browser.");function r(t){t.createObjectStore(e.dataStoreName)}function s(){var i=t.transaction([e.dataStoreName],"readwrite");function o(t){i.objectStore(e.dataStoreName).get(t).onsuccess=function(i){e.callback&&e.callback(i.target.result,t)}}e.videoBlob&&i.objectStore(e.dataStoreName).put(e.videoBlob,"videoBlob"),e.gifBlob&&i.objectStore(e.dataStoreName).put(e.gifBlob,"gifBlob"),e.audioBlob&&i.objectStore(e.dataStoreName).put(e.audioBlob,"audioBlob"),o("audioBlob"),o("videoBlob"),o("gifBlob")}},Fetch:function(e){return this.callback=e,this.init(),this},Store:function(e){return this.audioBlob=e.audioBlob,this.videoBlob=e.videoBlob,this.gifBlob=e.gifBlob,this.init(),this},onError:function(e){console.error(JSON.stringify(e,null,"\t"))},dataStoreName:"recordRTC",dbName:null};function D(e,t){if("undefined"==typeof GIFEncoder){var i=document.createElement("script");i.src="https://www.webrtc-experiment.com/gif-recorder.js",(document.body||document.documentElement).appendChild(i)}t=t||{};var o=e instanceof CanvasRenderingContext2D||e instanceof HTMLCanvasElement;this.record=function(){"undefined"!=typeof GIFEncoder&&A?(o||(t.width||(t.width=c.offsetWidth||320),t.height||(t.height=c.offsetHeight||240),t.video||(t.video={width:t.width,height:t.height}),t.canvas||(t.canvas={width:t.width,height:t.height}),s.width=t.canvas.width||320,s.height=t.canvas.height||240,c.width=t.video.width||320,c.height=t.video.height||240),(u=new GIFEncoder).setRepeat(0),u.setDelay(t.frameRate||200),u.setQuality(t.quality||10),u.start(),"function"==typeof t.onGifRecordingStarted&&t.onGifRecordingStarted(),h=n((function e(i){if(!0!==p.clearedRecordedData){if(r)return setTimeout((function(){e(i)}),100);h=n(e),void 0===typeof l&&(l=i),i-l<90||(!o&&c.paused&&c.play(),o||a.drawImage(c,0,0,s.width,s.height),t.onGifPreview&&t.onGifPreview(s.toDataURL("image/png")),u.addFrame(a),l=i)}})),t.initCallback&&t.initCallback()):setTimeout(p.record,1e3)},this.stop=function(e){e=e||function(){},h&&d(h),this.blob=new Blob([new Uint8Array(u.stream().bin)],{type:"image/gif"}),e(this.blob),u.stream().bin=[]};var r=!1;this.pause=function(){r=!0},this.resume=function(){r=!1},this.clearRecordedData=function(){p.clearedRecordedData=!0,u&&(u.stream().bin=[])},this.name="GifRecorder",this.toString=function(){return this.name};var s=document.createElement("canvas"),a=s.getContext("2d");o&&(e instanceof CanvasRenderingContext2D?s=(a=e).canvas:e instanceof HTMLCanvasElement&&(a=e.getContext("2d"),s=e));var A=!0;if(!o){var c=document.createElement("video");c.muted=!0,c.autoplay=!0,c.playsInline=!0,A=!1,c.onloadedmetadata=function(){A=!0},S(e,c),c.play()}var l,u,h=null,p=this}function j(e,t){t=t||"multi-streams-mixer";var i=[],o=!1,r=document.createElement("canvas"),s=r.getContext("2d");r.style.opacity=0,r.style.position="absolute",r.style.zIndex=-1,r.style.top="-1000em",r.style.left="-1000em",r.className=t,(document.body||document.documentElement).appendChild(r),this.disableLogs=!1,this.frameInterval=10,this.width=360,this.height=240,this.useGainNode=!0;var a=this,n=window.AudioContext;void 0===n&&("undefined"!=typeof webkitAudioContext&&(n=webkitAudioContext),"undefined"!=typeof mozAudioContext&&(n=mozAudioContext));var A=window.URL;void 0===A&&"undefined"!=typeof webkitURL&&(A=webkitURL),"undefined"!=typeof navigator&&void 0===navigator.getUserMedia&&(void 0!==navigator.webkitGetUserMedia&&(navigator.getUserMedia=navigator.webkitGetUserMedia),void 0!==navigator.mozGetUserMedia&&(navigator.getUserMedia=navigator.mozGetUserMedia));var d=window.MediaStream;void 0===d&&"undefined"!=typeof webkitMediaStream&&(d=webkitMediaStream),void 0!==d&&void 0===d.prototype.stop&&(d.prototype.stop=function(){this.getTracks().forEach((function(e){e.stop()}))});var c={};function l(){if(!o){var e=i.length,t=!1,s=[];if(i.forEach((function(e){e.stream||(e.stream={}),e.stream.fullcanvas?t=e:s.push(e)})),t)r.width=t.stream.width,r.height=t.stream.height;else if(s.length){r.width=e>1?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 Ue extends we{constructor(e){super(),this.player=e,this.fileName="",this.fileType=N,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,P!==t&&N!==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=Me(e,t)}catch(t){e.error("Recorder","startRecord error",t),this.emit(T.recordCreateError)}this.recorder&&(this.isRecording=!0,this.player.emit(T.recording,!0),this.recorder.startRecording(),e.log("Recorder","start recording"),this.player.emit(T.recordStart),this.recordingInterval=window.setInterval((()=>{this.recordingTimestamp+=1,this.player.emit(T.recordingTimestamp,this.recordingTimestamp)}),1e3))}stopRecordAndSave(){this.recorder&&this.isRecording&&this.recorder.stopRecording((()=>{this.player.debug.log("Recorder","stop recording"),this.player.emit(T.recordEnd);const e=(this.fileName||le())+"."+(this.fileType||N);Te(this.recorder.getBlob(),e),this._reset(),this.player.emit(T.recording,!1)}))}_reset(){this.isRecording=!1,this.recordingTimestamp=0,this.recorder&&(this.recorder.destroy(),this.recorder=null),this.fileName=null,this.recordingInterval&&clearInterval(this.recordingInterval),this.recordingInterval=null}}class Qe{constructor(e){return new(Qe.getLoaderFactory())(e)}static getLoaderFactory(){return Ue}}class We{constructor(e){this.player=e,this.decoderWorker=new Worker(e._opt.decoder),this._initDecoderWorker(),e.debug.log("decoderWorker","init")}destroy(){this.decoderWorker.postMessage({cmd:R}),this.decoderWorker.terminate(),this.decoderWorker=null,this.player.debug.log("decoderWorker","destroy")}_initDecoderWorker(){const{debug:e,events:{proxy:t}}=this.player;this.decoderWorker.onmessage=t=>{const i=t.data;switch(i.cmd){case c:e.log("decoderWorker","onmessage:",c),this.player.loaded||this.player.emit(T.load),this.player.emit(T.decoderWorkerInit),this._initWork();break;case g:e.log("decoderWorker","onmessage:",g,i.code),this.player._times.decodeStart||(this.player._times.decodeStart=le()),this.player.video.updateVideoInfo({encTypeCode:i.code});break;case m:e.log("decoderWorker","onmessage:",m,i.code),this.player.audio&&this.player.audio.updateAudioInfo({encTypeCode:i.code});break;case l:if(e.log("decoderWorker","onmessage:",l,`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(x.webglAlignmentError);this.player.video.initCanvasViewSize();break;case p:e.log("decoderWorker","onmessage:",p,`channels:${i.channels},sampleRate:${i.sampleRate}`),this.player.audio&&(this.player.audio.updateAudioInfo(i),this.player.audio.initScriptNode(i));break;case u:this.player.handleRender(),this.player.video.render(i),this.player.emit(T.timeUpdate,i.ts),this.player.updateStats({fps:!0,ts:i.ts,buf:i.delay}),this.player._times.videoStart||(this.player._times.videoStart=le(),this.player.handlePlayToRenderTimes());break;case h:this.player.playing&&this.player.audio&&this.player.audio.play(i.buffer,i.ts);break;case f:i.message&&-1!==i.message.indexOf(b)&&(this.player.emit(T.error,x.wasmDecodeError),this.player.emit(x.wasmDecodeError));break;default:this.player[i.cmd]&&this.player[i.cmd](i)}}}_initWork(){const e={debug:this.player._opt.debug,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:E,opt:JSON.stringify(e),sampleRate:this.player.audio&&this.player.audio.audioContext.sampleRate||0})}decodeVideo(e,t,i){const o={type:v,ts:Math.max(t,0),isIFrame:i};this.decoderWorker.postMessage({cmd:B,buffer:e,options:o},[e.buffer])}decodeAudio(e,t){this.player._opt.useWCS&&!this.player._opt.useOffscreen||this.player._opt.useMSE?this._decodeAudioNoDelay(e,t):this._decodeAudio(e,t)}_decodeAudio(e,t){const i={type:y,ts:Math.max(t,0)};this.decoderWorker.postMessage({cmd:B,buffer:e,options:i},[e.buffer])}_decodeAudioNoDelay(e,t){this.decoderWorker.postMessage({cmd:C,buffer:e,ts:Math.max(t,0)},[e.buffer])}updateWorkConfig(e){this.decoderWorker.postMessage({cmd:k,key:e.key,value:e.value})}}class Je extends we{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===y&&0===e.payload[1]&&this._doDecoderDecode(e);!e.isIFrame&&this.bufferList.length;)e=this.bufferList.shift(),e.type===y&&0===e.payload[1]&&this._doDecoderDecode(e);e.isIFrame&&this.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===v&&(a.isIFrame=o),this.pushBuffer(e,a)):t===v?s.decoderWorker&&s.decoderWorker.decodeVideo(e,i,o):t===y&&s._opt.hasAudio&&s.decoderWorker&&s.decoderWorker.decodeAudio(e,i)}_doDecoderDecode(e){const t=this.player,{webcodecsDecoder:i,mseDecoder:o}=t;e.type===y?t._opt.hasAudio&&t.decoderWorker&&t.decoderWorker.decodeAudio(e.payload,e.ts):e.type===v&&(t._opt.useWCS&&!t._opt.useOffscreen?i.decodeVideo(e.payload,e.ts,e.isIFrame):t._opt.useMSE&&o.decodeVideo(e.payload,e.ts,e.isIFrame,e.cts))}pushBuffer(e,t){t.type===y?this.bufferList.push({ts:t.ts,payload:e,type:y}):t.type===v&&this.bufferList.push({ts:t.ts,cts:t.cts,payload:e,type:v,isIFrame:t.isIFrame})}close(){}}class Ge extends Je{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 w:o._opt.hasAudio&&(o.updateStats({abps:n.byteLength}),n.byteLength>0&&this._doDecode(n,y,a));break;case S:if(o._times.demuxStart||(o._times.demuxStart=le()),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,v,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 Pe extends Je{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 y:if(t._opt.hasAudio){const i=new Uint8Array(e,5);t.updateStats({abps:i.byteLength}),i.byteLength>0&&this._doDecode(i,o,r)}break;case v:if(t._opt.hasVideo)if(t._times.demuxStart||(t._times.demuxStart=le()),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 Ne{constructor(e){return new(Ne.getLoaderFactory(e._opt.demuxType))(e)}static getLoaderFactory(e){return e===A?Pe:e===n?Ge:void 0}}class ze{constructor(e){this.TAG="ExpGolomb",this._buffer=e,this._buffer_index=0,this._total_bytes=e.byteLength,this._total_bits=8*e.byteLength,this._current_word=0,this._current_word_bits_left=0}destroy(){this._buffer=null}_fillCurrentWord(){let e=this._total_bytes-this._buffer_index,t=Math.min(4,e),i=new Uint8Array(4);i.set(this._buffer.subarray(this._buffer_index,this._buffer_index+t)),this._current_word=new DataView(i.buffer).getUint32(0,!1),this._buffer_index+=t,this._current_word_bits_left=8*t}readBits(e){if(e<=this._current_word_bits_left){let t=this._current_word>>>32-e;return this._current_word<<=e,this._current_word_bits_left-=e,t}let t=this._current_word_bits_left?this._current_word:0;t>>>=32-this._current_word_bits_left;let i=e-this._current_word_bits_left;this._fillCurrentWord();let o=Math.min(i,this._current_word_bits_left),r=this._current_word>>>32-o;return this._current_word<<=o,this._current_word_bits_left-=o,t=t<<o|r,t}readBool(){return 1===this.readBits(1)}readByte(){return this.readBits(8)}_skipLeadingZero(){let e;for(e=0;e<this._current_word_bits_left;e++)if(0!=(this._current_word&2147483648>>>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 He{static _ebsp2rbsp(e){let t=e,i=t.byteLength,o=new Uint8Array(i),r=0;for(let e=0;e<i;e++)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=He._ebsp2rbsp(e),i=new ze(t);i.readByte();let o=i.readByte();i.readByte();let r=i.readByte();i.readUEG();let s=He.getProfileString(o),a=He.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;t<e;t++)i.readBool()&&(t<6?He._skipScalingList(i,16):He._skipScalingList(i,64))}i.readUEG();let l=i.readUEG();if(0===l)i.readUEG();else if(1===l){i.readBits(1),i.readSEG(),i.readSEG();let e=i.readUEG();for(let t=0;t<e;t++)i.readSEG()}let u=i.readUEG();i.readBits(1);let h=i.readUEG(),p=i.readUEG(),m=i.readBits(1);0===m&&i.readBits(1),i.readBits(1);let g=0,f=0,b=0,y=0;i.readBool()&&(g=i.readUEG(),f=i.readUEG(),b=i.readUEG(),y=i.readUEG());let v=1,w=1,S=0,E=!0,B=0,C=0;if(i.readBool()){if(i.readBool()){let e=i.readByte(),t=[1,12,10,16,40,24,20,32,80,18,15,64,160,4,3,2],o=[1,11,11,11,33,11,11,11,33,11,11,33,99,3,2,1];e>0&&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:He.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;s++)0!==o&&(r=e.readSEG(),o=(i+r+256)%256),i=0===o?i:o}static getProfileString(e){switch(e){case 66:return"Baseline";case 77:return"Main";case 88:return"Extended";case 100:return"High";case 110:return"High10";case 122:return"High422";case 244:return"High444";default:return"Unknown"}}static getLevelString(e){return(e/10).toFixed(1)}static getChromaFormatString(e){switch(e){case 420:return"4:2:0";case 422:return"4:2:2";case 444:return"4:4:4";default:return"Unknown"}}}function Ye(e){const t={},i=new DataView(e.buffer);let o=i.getUint8(0),r=i.getUint8(1);if(i.getUint8(2),i.getUint8(3),1!==o||0===r)return;const s=1+(3&i.getUint8(4));if(3!==s&&4!==s)return;let a=31&i.getUint8(5);if(0===a)return;let n=6;for(let o=0;o<a;o++){let r=i.getUint16(n,!1);if(n+=2,0===r)continue;let s=new Uint8Array(e.buffer,n,r);n+=r;let a=He.parseSPS(s);if(0!==o)continue;t.codecWidth=a.codec_size.width,t.codecHeight=a.codec_size.height,t.presentWidth=a.present_size.width,t.presentHeight=a.present_size.height,t.profile=a.profile_string,t.level=a.level_string,t.bitDepth=a.bit_depth,t.chromaFormat=a.chroma_format,t.sarRatio=a.sar_ratio,t.frameRate=a.frame_rate,!1!==a.frame_rate.fixed&&0!==a.frame_rate.fps_num&&0!==a.frame_rate.fps_den||(t.frameRate={});let A=t.frameRate.fps_den,d=t.frameRate.fps_num;t.refSampleDuration=t.timescale*(A/d);let c=s.subarray(1,4),l="avc1.";for(let e=0;e<3;e++){let t=c[e].toString(16);t.length<2&&(t="0"+t),l+=t}t.codec=l}let A=i.getUint8(n);if(0!==A){n++;for(let t=0;t<A;t++){let t=i.getUint16(n,!1);n+=2,0!==t&&(new Uint8Array(e.buffer,n,t),n+=t)}return t.videoType="avc",t}}class Xe extends we{constructor(e){super(),this.player=e,this.hasInit=!1,this.isDecodeFirstIIframe=!1,this.isInitInfo=!1,this.decoder=null,this.initDecoder(),e.debug.log("Webcodecs","init")}destroy(){this.decoder&&(this.decoder.close(),this.decoder=null),this.hasInit=!1,this.isInitInfo=!1,this.isDecodeFirstIIframe=!1,this.off(),this.player.debug.log("Webcodecs","destroy")}initDecoder(){const e=this;this.decoder=new VideoDecoder({output(t){e.handleDecode(t)},error(t){e.handleError(t)}})}handleDecode(e){this.isInitInfo||(this.player.video.updateVideoInfo({width:e.codedWidth,height:e.codedHeight}),this.player.video.initCanvasViewSize(),this.isInitInfo=!0),this.player._times.videoStart||(this.player._times.videoStart=le(),this.player.handlePlayToRenderTimes()),this.player.handleRender(),this.player.video.render({videoFrame:e}),this.player.updateStats({fps:!0,ts:0,buf:this.player.demux.delay}),setTimeout((function(){e.close?e.close():e.destroy()}),100)}handleError(e){this.player.debug.error("Webcodecs","VideoDecoder handleError",e)}decodeVideo(e,t,i){if(this.hasInit){if(i&&0===e[1]){const t=Ye(e.slice(5)),i=this.player.video.videoInfo;if(t.codecWidth!==i.width||t.codecHeight!==i.height)return this.player.debug.log("Webcodecs",`width or height is update, width ${i.width}-> ${t.codecWidth}, height ${i.height}-> ${t.codecHeight}`),void this.player.emit(x.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(T.timeUpdate,t);try{this.decoder.decode(o)}catch(e){this.player.debug.error("Webcodecs","VideoDecoder",e),-1!==e.toString().indexOf(re)&&this.player.emit(x.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===M)return void this.emit(x.webcodecsH265NotSupport);this.player._times.decodeStart||(this.player._times.decodeStart=le());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 qe={play:"播放",pause:"暂停",audio:"",mute:"",screenshot:"截图",loading:"加载",fullscreen:"全屏",fullscreenExit:"退出全屏",record:"录制",recordStop:"停止录制"};var Ze=Object.keys(qe).reduce(((e,t)=>(e[t]=`\n <i class="jessibuca-icon jessibuca-icon-${t}"></i>\n ${qe[t]?`<span class="icon-title-tips"><span class="icon-title">${qe[t]}</span></span>`:""}\n`,e)),{}),Ke=(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",he(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(T.resize),n())}))})),e.$container.appendChild(o),e.on(T.destroy,(()=>{e.$container.removeChild(o)})),e.on(T.volumechange,(()=>{!function(e){if(0===e)he(t.$volumeOn,"display","none"),he(t.$volumeOff,"display","flex"),he(t.$volumeHandle,"top","48px");else if(t.$volumeHandle&&t.$volumePanel){const i=pe(t.$volumePanel,"height")||60,o=pe(t.$volumeHandle,"height"),r=i-(i-o)*e-o;he(t.$volumeHandle,"top",`${r}px`),he(t.$volumeOn,"display","flex"),he(t.$volumeOff,"display","none")}t.$volumePanelText&&(t.$volumePanelText.innerHTML=parseInt(100*e))}(e.volume)})),e.on(T.loading,(e=>{he(t.$loading,"display",e?"flex":"none"),he(t.$poster,"display","none"),e&&he(t.$playBig,"display","none")}));const a=i=>{let o=!0===(r=i)||!1===r?i:e.fullscreen;var r;he(t.$fullscreenExit,"display",o?"flex":"none"),he(t.$fullscreen,"display",o?"none":"flex")},n=()=>{fe()&&t.$controls&&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{Ae.on("change",a),e.events.destroys.push((()=>{Ae.off("change",a)}))}catch(e){}e.on(T.webFullscreen,(e=>{a(e),n()})),e.on(T.recording,(()=>{he(t.$record,"display",e.recording?"none":"flex"),he(t.$recordStop,"display",e.recording?"flex":"none"),he(t.$recording,"display",e.recording?"flex":"none")})),e.on(T.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(T.playing,(e=>{he(t.$play,"display",e?"none":"flex"),he(t.$playBig,"display",e?"none":"block"),he(t.$pause,"display",e?"flex":"none"),he(t.$screenshot,"display",e?"flex":"none"),he(t.$record,"display",e?"flex":"none"),he(t.$fullscreen,"display",e?"flex":"none"),e||t.$speed&&(t.$speed.innerHTML=be(""))})),e.on(T.kBps,(e=>{const i=be(e);t.$speed&&(t.$speed.innerHTML=i)}))};function _e(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))}}_e('@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,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAFvklEQVRoQ+2ZaaiVVRSGn9fS0iabCNO0eSaosAmplKJRxMiygSQCixQipBKMoDRBon5EI/0pQ8JuRQTVj4omo+FH04/muVum2GCDWVYr3ss+8t3vfud8+3guXi6cBYc7nD2sd6+11/BuMcxFw1x/ugCG2oL9LBAR44HeFkr9B/wMbAOMBT4B9gC+BiZL+rfZ3Ijw+PuB6cA6YFdgAzAy/V41NQB/rpL0QNWAAS4UEVbQm+XKj8B4SX/VTYiIicC9wMnAjnXjC9/fKemaWgARsSfwEbBbxeDPgAOBL4AdgF2AD4ETJP2dq0xEHArcA4yGvjv4D/Br2vOo9P/ycosl3ZQD4IDkFiMqBl8LPASMkfRdREwFVknalKt8Y1xETJDUGxFea0NE2CX9aWbF+ZLuzgEwBlgPbNtEqYuAlZLsl4MmEWGL/t5iwQWS7sgB4Iv1TcE//yyZ1Ke9AOiR9MNgIGihvAOCrWJZKGlZDoCjgTdTZLDy1wGLS1HCkehF4DxJ9t0tlhbKXwbcAByRFp8taWUOgN2B94G9AZ/A9sD5wIPAdqUFngAuBTZuiUu1UH4O8DjwVQrR3nZuVhiNCEcFT3S4swX2k7QmImYDs3zqJRCOzfOBTe2AaKW8pOUR4cPy/tbH9+0cSc/mWMATfkp5wAtMlLQuAXNo7QEcfYqyBLjZFssBUad8IVI5bDsqWs7OAuCREeHselCaeLgkx/o+iQi71lPAsSUQyyQtrLsM6SB8h8oyxydf2Meu/CrgnGGZJcluNUDKpYRN9zEwCVgLjJPUb8OIODiBOKSw2lhJDr8tJSIc5ZzE7JIN6ad8OijrNQ9w8nJynSrppRwAjXhs5e0+lYklIo4DHgP2AUa1k8wiwjnmGeB0YIDyBSv4MB2yHQnPkvRGDgAjfxs4vq48iIhpwCuSXAq0JRHh6HZB0W2qFnCmBu4CludaYCen8zrl29K2w8Hp0o+U9EutBTrca0imdzuyITn2wqZdC3Qt0OEJDAsXcnHXLKmWSwn/PUmSK9JaiYgR5VKjdlKbAyJiL+DU3H7AtIpLhMslublvKinBXAg83E4pkWodZ2J3WO60XPVWSlLend9MSU9mJbKI+DxxPzPcvDdJ8Y2a6TfgCjcguZaIiFHA94ArTnd7S6oyf0TsC3yZ+oFLJD1SCyAVWp8Cnvxy6oRcXm+Winp+DXClK9S6fiAiXKrYPYu0jYu128tzI6LRD7gzPFPS8zkAXAGaHXDF6InTi41Ei2akablbAm8XfQ44rKSMmTezdn2SgLpinQK4nJ8i6fVaAGmyS2nX4JbNnVBuJ1V3RyPCzZD7abetDdmYXNFsRx/PFBEeMzMNmCbJRMIAqWpoDGDnNNIlb89gKV844VMSiKIrmdL8ILEdayPCljotMXeOQq/lADDdZ17IhK1daAbgTqiKdGrajNRZIZ2wSV732GW2w9HGbMcL7kvSJb5a0n05AEzqOnw69hqAT2pVxcSOlE8AbP2LgVvMfiQGorGVm5hjgJPSP26TdH0OADft3wJV3GhjfsfKF1zJILzX08AZLSy3SNLSHACOPnaXslkHXfmiMqnZd5xvBuJWSTfmAHCC8h2ootfdYJshnpASkX+eCKxo9bBRtWkKk3OBt5KrmgO1JUwf2n3LslTSohwAjs/vmmmoGGyGYnW64Da9SwBfdlOBLieyGOtCeeAt/K7gvbyWyQEnuiqZJ8l0zAAph9FxgMuHdqpUx23XTivqoo/fBdIdqxta/r5foit+WQZgF/IlNgFlxfx+VaS57V5O8eaD/Jbmu2Lqw+H3XEn+rlLS6887iTz285ILOruL1zwyrWFrFHWyVXwv+/JRjgVM5Vnp/ZN7GIyTmgsvb/iopNVObJL+8IIpyfnOrK+j2yNidKP6jAiD8CF5Xc+fnA7PXtB4o3Od1SvpvWYH046rtGv2rTK+C2CrHHOLTboW6FqgwxP4Hz4mJ0+J869tAAAAAElFTkSuQmCC") 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 $e{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?`<div class="jessibuca-poster" style="background-image: url(${i.background})"></div>`:""}\n <div class="jessibuca-loading">\n ${Ze.loading}\n ${i.loadingText?`<div class="jessibuca-loading-text">${i.loadingText}</div>`:""}\n </div>\n ${i.hasControl&&o.play?'<div class="jessibuca-play-big"></div>':""}\n ${i.hasControl?`\n <div class="jessibuca-recording">\n <div class="jessibuca-recording-red-point"></div>\n <div class="jessibuca-recording-time">00:00:01</div>\n <div class="jessibuca-icon-recordStop jessibuca-recording-stop">${Ze.recordStop}</div>\n </div>\n `:""}\n ${i.hasControl?`\n <div class="jessibuca-controls">\n <div class="jessibuca-controls-bottom">\n <div class="jessibuca-controls-left">\n ${i.showBandwidth?'<div class="jessibuca-controls-item jessibuca-speed"></div>':""}\n </div>\n <div class="jessibuca-controls-right">\n ${o.audio?`\n <div class="jessibuca-controls-item jessibuca-volume">\n ${Ze.audio}\n ${Ze.mute}\n <div class="jessibuca-volume-panel-wrap">\n <div class="jessibuca-volume-panel">\n <div class="jessibuca-volume-panel-handle"></div>\n </div>\n <div class="jessibuca-volume-panel-text"></div>\n </div>\n </div>\n `:""}\n ${o.play?`<div class="jessibuca-controls-item jessibuca-play">${Ze.play}</div><div class="jessibuca-controls-item jessibuca-pause">${Ze.pause}</div>`:""}\n ${o.screenshot?`<div class="jessibuca-controls-item jessibuca-screenshot">${Ze.screenshot}</div>`:""}\n ${o.record?` <div class="jessibuca-controls-item jessibuca-record">${Ze.record}</div><div class="jessibuca-controls-item jessibuca-record-stop">${Ze.recordStop}</div>`:""}\n ${o.fullscreen?`<div class="jessibuca-controls-item jessibuca-fullscreen">${Ze.fullscreen}</div><div class="jessibuca-controls-item jessibuca-fullscreen-exit">${Ze.fullscreenExit}</div>`:""}\n </div>\n </div>\n </div>\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()}),Ke(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 ue(i-e.y-r/2,0,o-r/2)/(o-r)}i(window,["click","contextmenu"],(i=>{i.composedPath().indexOf(e.$container)>-1?t.isFocus=!0:t.isFocus=!1})),i(window,"orientationchange",(()=>{setTimeout((()=>{e.resize()}),300)})),i(t.$controls,"click",(e=>{e.stopPropagation()})),i(t.$pause,"click",(t=>{e.pause()})),i(t.$play,"click",(t=>{e.play(),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(),he(t.$volumeOn,"display","none"),he(t.$volumeOff,"display","block");const o=e.volume;e.volume=0,e._lastVolume=o})),i(t.$volumeOff,"click",(i=>{i.stopPropagation(),he(t.$volumeOn,"display","block"),he(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||he(t.$controls,"display","block")})),i(e.$container,"mouseout",(()=>{he(t.$controls,"display","none")})))})(e,this),e._opt.hotKey&&((e,t)=>{const{events:{proxy:i}}=e,o={};function r(e,t){o[e]?o[e].push(t):o[e]=[t]}r(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.$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`}}}_e(".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}");var et=e=>{const{_opt:t,debug:i,events:{proxy:o}}=e;t.supportDblclickFullscreen&&o(e.$container,"dblclick",(t=>{const i=function(e){const t=e||window.event;return t.target||t.srcElement}(t),o=i.nodeName.toLowerCase();"canvas"!==o&&"video"!==o||(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()}))};class tt{static init(){tt.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 tt.types)tt.types.hasOwnProperty(e)&&(tt.types[e]=[e.charCodeAt(0),e.charCodeAt(1),e.charCodeAt(2),e.charCodeAt(3)]);let e=tt.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<r;e++)t+=o[e].byteLength;i=new Uint8Array(t),i[0]=t>>>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<r;e++)i.set(o[e],s),s+=o[e].byteLength;return i}static generateInitSegment(e){let t=tt.box(tt.types.ftyp,tt.constants.FTYP),i=tt.moov(e),o=new Uint8Array(t.byteLength+i.byteLength);return o.set(t,0),o.set(i,t.byteLength),o}static moov(e){let t=tt.mvhd(e.timescale,e.duration),i=tt.trak(e),o=tt.mvex(e);return tt.box(tt.types.moov,t,i,o)}static mvhd(e,t){return tt.box(tt.types.mvhd,new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,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 tt.box(tt.types.trak,tt.tkhd(e),tt.mdia(e))}static tkhd(e){let t=e.id,i=e.duration,o=e.presentWidth,r=e.presentHeight;return tt.box(tt.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 tt.box(tt.types.mdia,tt.mdhd(e),tt.hdlr(e),tt.minf(e))}static mdhd(e){let t=e.timescale,i=e.duration;return tt.box(tt.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?tt.constants.HDLR_AUDIO:tt.constants.HDLR_VIDEO,tt.box(tt.types.hdlr,t)}static minf(e){let t=null;return t="audio"===e.type?tt.box(tt.types.smhd,tt.constants.SMHD):tt.box(tt.types.vmhd,tt.constants.VMHD),tt.box(tt.types.minf,t,tt.dinf(),tt.stbl(e))}static dinf(){return tt.box(tt.types.dinf,tt.box(tt.types.dref,tt.constants.DREF))}static stbl(e){return tt.box(tt.types.stbl,tt.stsd(e),tt.box(tt.types.stts,tt.constants.STTS),tt.box(tt.types.stsc,tt.constants.STSC),tt.box(tt.types.stsz,tt.constants.STSZ),tt.box(tt.types.stco,tt.constants.STCO))}static stsd(e){return"audio"===e.type?tt.box(tt.types.stsd,tt.constants.STSD_PREFIX,tt.mp4a(e)):"avc"===e.videoType?tt.box(tt.types.stsd,tt.constants.STSD_PREFIX,tt.avc1(e)):tt.box(tt.types.stsd,tt.constants.STSD_PREFIX,tt.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 tt.box(tt.types.mp4a,o,tt.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 tt.box(tt.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 tt.box(tt.types.avc1,r,tt.box(tt.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 tt.box(tt.types.hvc1,r,tt.box(tt.types.hvcC,t))}static mvex(e){return tt.box(tt.types.mvex,tt.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 tt.box(tt.types.trex,i)}static moof(e,t){return tt.box(tt.types.moof,tt.mfhd(e.sequenceNumber),tt.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 tt.box(tt.types.mfhd,t)}static traf(e,t){let i=e.id,o=tt.box(tt.types.tfhd,new Uint8Array([0,0,0,0,i>>>24&255,i>>>16&255,i>>>8&255,255&i])),r=tt.box(tt.types.tfdt,new Uint8Array([0,0,0,0,t>>>24&255,t>>>16&255,t>>>8&255,255&t])),s=tt.sdtp(e),a=tt.trun(e,s.byteLength+16+16+8+16+8+8);return tt.box(tt.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,tt.box(tt.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),tt.box(tt.types.trun,i)}static mdat(e){return tt.box(tt.types.mdat,e)}}tt.init();class it extends we{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(T.mseSourceOpen)})),i(this.mediaSource,"sourceclose",(()=>{this.player.emit(T.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]){e[0];let t={};t=Ye(e);const i=this.player.video.videoInfo;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===M)return void this.emit(x.mediaSourceH265NotSupport);r._times.decodeStart||(r._times.decodeStart=le()),this._decodeConfigurationRecord(e,t,i,o),this.hasInit=!0}}_decodeConfigurationRecord(e,t,i,o){let r=e.slice(5),s={};s=Ye(r);const a={id:1,type:"video",timescale:1e3,duration:0,avcc:r,codecWidth:s.codecWidth,codecHeight:s.codecHeight,videoType:s.videoType},n=tt.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(tt.types.mdat,4),i.set(this.cacheTrack.data,8),this.cacheTrack.duration=t-this.cacheTrack.dts;let o=tt.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=le(),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(T.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(x.mediaSourceFull)):11===e.code?(this.stop(),this.mediaSourceAppendBufferError=!0,this.emit(x.mediaSourceAppendBufferError)):(t.error("MediaSource","appendBuffer error",e),this.player.emit(T.mseSourceBufferError,e))}else this.isStateClosed?this.player.emit(x.mseSourceBufferError,"mediaSource is not attached to video or mediaSource is closed"):this.isStateEnded?this.player.emit(x.mseSourceBufferError,"mediaSource is closed"):!0===this.sourceBuffer.updating&&this.player.emit(T.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(){if(this.isStateOpen)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 ot=()=>"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,rt=()=>"wakeLock"in navigator;class st{constructor(e){if(this.player=e,this.enabled=!1,rt()){this._wakeLock=null;const e=()=>{null!==this._wakeLock&&"visible"===document.visibilityState&&this.enable()};document.addEventListener("visibilitychange",e),document.addEventListener("fullscreenchange",e)}else ot()?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(rt())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(ot())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;rt()?(this._wakeLock&&this._wakeLock.release(),this._wakeLock=null):ot()?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 we{constructor(e,t){var i;super(),this.$container=e,this._opt=Object.assign({},d,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 ne(this),this.video=new De(this),this._opt.hasAudio&&(this.audio=new Le(this)),this.recorder=new Qe(this),this._onlyMseOrWcsVideo()?this.loaded=!0:this.decoderWorker=new We(this),this.stream=null,this.demux=null,this._lastVolume=null,this._opt.useWCS&&(this.webcodecsDecoder=new Xe(this),this.loaded=!0),this._opt.useMSE&&(this.mseDecoder=new it(this),this.loaded=!0),this.control=new $e(this),fe()&&(this.keepScreenOn=new st(this)),(e=>{try{const t=t=>{t.target===e.$container&&(e.emit(I.fullscreen,e.fullscreen),e.fullscreen?e._opt.useMSE&&e.resize():e.resize())};Ae.on("change",t),e.events.destroys.push((()=>{Ae.off("change",t)}))}catch(e){}if(e.on(T.decoderWorkerInit,(()=>{e.debug.log("player","has loaded"),e.loaded=!0})),e.on(T.play,(()=>{e.loading=!1})),e.on(T.fullscreen,(t=>{if(t)try{Ae.request(e.$container).then((()=>{})).catch((t=>{e.webFullscreen=!0}))}catch(t){e.webFullscreen=!0}else try{Ae.exit().then((()=>{})).catch((()=>{e.webFullscreen=!1}))}catch(t){e.webFullscreen=!1}})),e.on(T.webFullscreen,(t=>{t?e.$container.classList.add("jessibuca-fullscreen-web"):e.$container.classList.remove("jessibuca-fullscreen-web"),e.emit(I.fullscreen,e.fullscreen)})),e.on(T.resize,(()=>{e.video.resize()})),e._opt.debug){const t=[T.timeUpdate];Object.keys(T).forEach((i=>{e.on(T[i],(o=>{t.includes(i)||e.debug.log("player events",T[i],o)}))})),Object.keys(x).forEach((t=>{e.on(x[t],(i=>{e.debug.log("player event error",x[t],i)}))}))}})(this),et(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){fe()?(this.emit(T.webFullscreen,e),setTimeout((()=>{this.updateOption({rotate:e?270:0}),this.resize()}),10)):this.emit(T.fullscreen,e)}get fullscreen(){return document.isFullScreen||document.mozIsFullScreen||document.webkitIsFullScreen||this.webFullscreen}set webFullscreen(e){this.emit(T.webFullscreen,e)}get webFullscreen(){return this.$container.classList.contains("jessibuca-fullscreen-web")}set loaded(e){this._hasLoaded=e}get loaded(){return this._hasLoaded}set playing(e){e&&(this.loading=!1),this.playing!==e&&(this._playing=e,this.emit(T.playing,e),this.emit(T.volumechange,this.volume),e?this.emit(T.play):this.emit(T.pause))}get playing(){return this._playing}get volume(){return this.audio&&this.audio.volume||0}set volume(e){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(T.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(T.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 Ve(this)),this.audio||this._opt.hasAudio&&(this.audio=new Le(this)),this.demux||(this.demux=new Ne(this)),this._opt.useWCS&&(this.webcodecsDecoder||(this.webcodecsDecoder=new Xe(this))),this._opt.useMSE&&(this.mseDecoder||(this.mseDecoder=new it(this))),this.decoderWorker||this._onlyMseOrWcsVideo()?e():(this.decoderWorker=new We(this),this.once(T.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=le(),e||(e=this._opt.url),this._opt.url=e,this.clearCheckHeartTimeout(),this.init().then((()=>{this._times.playStart=le(),this._opt.isNotMute&&this.mute(!1),this.webcodecsDecoder&&this.webcodecsDecoder.once(x.webcodecsH265NotSupport,(()=>{this.emit(x.webcodecsH265NotSupport),this._opt.autoWasm||this.emit(T.error,x.webcodecsH265NotSupport)})),this.mseDecoder&&(this.mseDecoder.once(x.mediaSourceH265NotSupport,(()=>{this.emit(x.mediaSourceH265NotSupport),this._opt.autoWasm||this.emit(T.error,x.mediaSourceH265NotSupport)})),this.mseDecoder.once(x.mediaSourceFull,(()=>{this.emit(x.mediaSourceFull)})),this.mseDecoder.once(x.mediaSourceAppendBufferError,(()=>{this.emit(x.mediaSourceAppendBufferError)})),this.mseDecoder.once(x.mediaSourceBufferListLarge,(()=>{this.emit(x.mediaSourceBufferListLarge)})),this.mseDecoder.once(x.mediaSourceAppendBufferEndTimeout,(()=>{this.emit(x.mediaSourceAppendBufferEndTimeout)}))),this.enableWakeLock(),this.stream.fetchStream(e,t),this.checkLoadingTimeout(),this.stream.once(x.fetchError,(e=>{o(e)})),this.stream.once(x.websocketError,(e=>{o(e)})),this.stream.once(T.streamEnd,(()=>{o()})),this.stream.once(x.hlsError,(e=>{o(e)})),this.stream.once(T.streamSuccess,(()=>{i(),this._times.streamResponse=le(),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((()=>{this.pause().then((()=>{this.emit(T.timeout,T.delayTimeout),this.emit(T.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.pause().then((()=>{this.emit(T.timeout,T.loadingTimeout),this.emit(T.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(T.start),this.loading=!1,this.clearCheckLoadingTimeout()),this.playing||(this.playing=!0),this.checkHeart()}updateStats(e){e=e||{},this._startBpsTime||(this._startBpsTime=le()),ve(e.ts)&&(this._stats.ts=e.ts),ve(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=le();t-this._startBpsTime<1e3||(this.emit(T.stats,this._stats),this.emit(T.performance,function(e){let t=0;return e>=24?t=2:e>=15&&(t=1),t}(this._stats.fps)),this._stats.fps=0,this._stats.abps=0,this._stats.vbps=0,this._startBpsTime=t)}resetStats(){this._startBpsTime=null,this._stats={buf:0,fps:0,abps:0,vbps:0,ts:0}}enableWakeLock(){this._opt.keepScreenOn&&this.keepScreenOn&&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(T.playToRenderTimes,e)}getOption(){return this._opt}}class nt extends we{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`);i.classList.add("jessibuca-container"),delete t.container,ve(t.videoBuffer)&&(t.videoBuffer=1e3*Number(t.videoBuffer)),ve(t.timeout)&&(ye(t.loadingTimeout)&&(t.loadingTimeout=t.timeout),ye(t.heartTimeout)&&(t.heartTimeout=t.timeout)),this._opt=t,this.$container=i,this._loadingTimeoutReplayTimes=0,this._heartTimeoutReplayTimes=0,this.events=new ne(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(I).forEach((e=>{this.player.on(I[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 W:t.isFullResize=!1,t.isResize=!1;break;case J:t.isFullResize=!1,t.isResize=!0;break;case G: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(T.error,x.playError),void o();if(e){if(!this._opt.url)return this._play(e,t);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((()=>{this.player.pause().then((()=>{o()}))}))):this.player.pause().then((()=>(this.clearView(),this._play(e,t)))).catch((()=>{o()}))}else this.player.play(this._opt.url,this._opt.playOptions).then((()=>{i(),this.player.resumeAudioAfterPause()})).catch((()=>{this.player.pause().then((()=>{o()}))}))}))}_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(x.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(x.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(x.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(x.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(x.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(x.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(x.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(x.mseSourceBufferError,(()=>{this.pause().then((()=>{this.player.debug.log("Jessibuca","mseSourceBufferError close success")}))})),this.player.once(x.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(x.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(x.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(x.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(T.delayTimeout,(()=>{this.player._opt.heartTimeoutReplay&&(this._heartTimeoutReplayTimes<this.player._opt.heartTimeoutReplayTimes||-1===this.player._opt.heartTimeoutReplayTimes)&&(this.player.debug.log("Jessibuca",`delay timeout replay time is ${this._heartTimeoutReplayTimes}`),this._heartTimeoutReplayTimes+=1,this.play(e,t).then((()=>{this._heartTimeoutReplayTimes=0})).catch((()=>{})))})),this.player.on(T.loadingTimeout,(()=>{this.player._opt.loadingTimeoutReplay&&(this._loadingTimeoutReplayTimes<this.player._opt.loadingTimeoutReplayTimes||-1===this.player._opt.loadingTimeoutReplayTimes)&&(this.player.debug.log("Jessibuca",`loading timeout replay time is ${this._loadingTimeoutReplayTimes}`),this._loadingTimeoutReplayTimes+=1,this.play(e,t).then((()=>{this._loadingTimeoutReplayTimes=0})).catch((()=>{})))})),this.hasLoaded()?this.player.play(e,t).then((()=>{i()})).catch((()=>{this.player.pause().then((()=>{o()}))})):this.player.once(T.decoderWorkerInit,(()=>{this.player.play(e,t).then((()=>{i()})).catch((()=>{this.player.pause().then((()=>{o()}))}))}))}))}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.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(nt,"ERROR",x),r(nt,"TIMEOUT",{loadingTimeout:T.loadingTimeout,delayTimeout:T.delayTimeout}),window.Jessibuca=nt,nt}));
... ...