ucservice 1.5.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -298,7 +298,14 @@
298
298
  }
299
299
  }
300
300
  return e.returnValue;
301
- }
301
+ },
302
+
303
+ /**
304
+ * 清空事件
305
+ */
306
+ removeAllListeners: function () {
307
+ this._listeners = {};
308
+ }
302
309
  }
303
310
 
304
311
  let videoListener = new Listener();
@@ -916,20 +923,20 @@
916
923
  * 添加事件
917
924
  */
918
925
  addListener: function(type,handler,key){
919
- videoListener.addListener(type,handler,key);
926
+ this.videoListener.addListener(type,handler,key);
920
927
  },
921
928
 
922
929
  /**
923
930
  * 删除事件
924
931
  */
925
932
  removeListener: function(type,handler){
926
- videoListener.removeListener(type,handler);
933
+ this.videoListener.removeListener(type,handler);
927
934
  },
928
935
  /**
929
936
  * 派发事件
930
937
  */
931
938
  dispatch: function(e,options){
932
- videoListener.dispatch(e,options);
939
+ this.videoListener.dispatch(e,options);
933
940
  },
934
941
 
935
942
  /**
@@ -1306,10 +1313,12 @@
1306
1313
  */
1307
1314
  function VideoWebRtc($dom,opts){
1308
1315
  let _self = this;
1316
+ _self.videoListener = new Listener();
1309
1317
  if (!opts) {
1310
1318
  console.error('VideoWebRtc的参数opts不能为空');
1311
1319
  return;
1312
1320
  }
1321
+ _self.configOpt = (opts && opts.configOpt) || 3;
1313
1322
  hasMediaDevices();
1314
1323
  //检测声卡设备为异步方法,耗时在几十毫秒左右,因此需要做延迟
1315
1324
  //默认检查次数为40 间隔为20ms
@@ -1347,6 +1356,11 @@
1347
1356
  // console.info('该视频正在播放');
1348
1357
  // return false;
1349
1358
  // }
1359
+ // 启用播放队列时,当前位置正在关闭上一个视频,则加入播放队列
1360
+ if (me.waitPlayQueueSwitch && me.VIDEO_DATA[index].isClosing) {
1361
+ me._addWaitingVideo(index, video, id, opts);
1362
+ return ;
1363
+ }
1350
1364
  console.log("play >>> " + "video=" + video + "; id=" + id + "; index=" + index);
1351
1365
  me.dispatch('beforeplay',{index:index, video:video, id:id});
1352
1366
 
@@ -1441,7 +1455,7 @@
1441
1455
  */
1442
1456
  playInChoice: function(video, id, opts){
1443
1457
  let me = this;
1444
- let selView = $('[video-flag="'+me._opts.windowsBeginIndex+'"]>li.sel');
1458
+ let selView = $(me.selector + ' li.sel');
1445
1459
  if(selView.length){
1446
1460
  if (me.VIDEO_DATA[selView.index()].playing) {
1447
1461
  me.close(selView.index());
@@ -1498,6 +1512,7 @@
1498
1512
  this.VIDEO_DATA[index].close();
1499
1513
 
1500
1514
  if (!isSave) {
1515
+ this.VIDEO_DATA[index].closeType = 'close';
1501
1516
  this.dispatch('afterclose', this.VIDEO_DATA[index]);
1502
1517
  }
1503
1518
 
@@ -1524,6 +1539,7 @@
1524
1539
  me.close(videoData[i].index);
1525
1540
 
1526
1541
  if (!isSave) {
1542
+ videoData[i].closeType = 'close';
1527
1543
  me.dispatch('afterclose', videoData[i]);
1528
1544
  }
1529
1545
  }
@@ -1535,12 +1551,14 @@
1535
1551
  */
1536
1552
  closeAll: function(isSave){
1537
1553
  let me = this;
1538
-
1554
+ // 清除所有等待播放队列
1555
+ me._removeAllWaitingVideo();
1539
1556
  let openVideos = [];
1540
1557
  for(let i = me._opts.windowsBeginIndex; i < me._opts.windowsBeginIndex + me._opts.windowsNum; i++){
1541
- if(me.VIDEO_DATA[i].playing){
1542
- me.close(i, isSave)
1543
- }
1558
+ // if(me.VIDEO_DATA[i].playing){
1559
+ // me.close(i, isSave)
1560
+ // }
1561
+ me.close(i, isSave);
1544
1562
  }
1545
1563
 
1546
1564
  // if(!openVideos.length) return;
@@ -1582,8 +1600,8 @@
1582
1600
  me.setChoiceWindow(me._getMinIndexFreeWindow());
1583
1601
  }
1584
1602
 
1585
- $('[video-flag="'+me._opts.windowsBeginIndex+'"]').removeClass("mode-" + me._opts.windows).addClass("mode-" + num);
1586
- let _li = $('[video-flag="'+me._opts.windowsBeginIndex+'"]>li').hide();
1603
+ $(me.selector + ' .video-main').removeClass("mode-" + me._opts.windows).addClass("mode-" + num);
1604
+ let _li = $(me.selector + ' li').hide();
1587
1605
  me._opts.windows = num;
1588
1606
  for(let i = 0; i < num; i++){
1589
1607
  _li.eq(i).show();
@@ -1639,23 +1657,34 @@
1639
1657
  this._polltimer = new PollTimer(function(firstRun){
1640
1658
  me.closeAll();
1641
1659
 
1642
- wait(500).then(function(){
1643
- for(let i = 0; i < windowNum; i++) {
1660
+ if (firstRun) {
1661
+ for(let i = 0; i < windowNum; i++) {
1644
1662
  if(nowNum == maxNum){
1645
- nowNum = 0;
1646
- if(maxNum < windowNum) {
1647
- break;
1648
- }
1663
+ nowNum = 0;
1664
+ if(maxNum < windowNum) {
1665
+ break;
1666
+ }
1649
1667
  }
1650
-
1668
+
1651
1669
  me.playByOrder(array[nowNum].video, array[nowNum].id, array[nowNum].opts);
1652
1670
  nowNum++;
1653
- }
1654
- if (firstRun) {
1671
+ }
1655
1672
  me.dispatch('startpoll', array);
1656
- }
1657
- })
1658
-
1673
+ }else {
1674
+ wait(1500).then(function(){
1675
+ for(let i = 0; i < windowNum; i++) {
1676
+ if(nowNum == maxNum){
1677
+ nowNum = 0;
1678
+ if(maxNum < windowNum) {
1679
+ break;
1680
+ }
1681
+ }
1682
+
1683
+ me.playByOrder(array[nowNum].video, array[nowNum].id, array[nowNum].opts);
1684
+ nowNum++;
1685
+ }
1686
+ })
1687
+ }
1659
1688
  }, time);
1660
1689
  },
1661
1690
 
@@ -1780,9 +1809,9 @@
1780
1809
  */
1781
1810
  getInChoiceVideo: function() {
1782
1811
  let me = this;
1783
- let selView = $('[video-flag="'+me._opts.windowsBeginIndex+'"]>li.sel');
1784
- if(selView.length && me.VIDEO_DATA[selView.index()].video){
1785
- return me.VIDEO_DATA[selView.index()];
1812
+ let selView = $(me.selector + ' li.sel');
1813
+ if(selView.length && me.VIDEO_DATA[selView.attr("index")].video){
1814
+ return me.VIDEO_DATA[selView.attr("index")];
1786
1815
  }
1787
1816
  return undefined;
1788
1817
  },
@@ -1818,13 +1847,21 @@
1818
1847
 
1819
1848
  let canvasObj = document.getElementById('myCanvas');
1820
1849
  let videoObj = dataObj.tagBox.get(0);
1821
-
1822
- canvasObj.width = dataObj.tagBox.parent().width();
1823
- canvasObj.height = dataObj.tagBox.parent().height();
1850
+ if (videoObj == undefined) {
1851
+ return;
1852
+ }
1853
+ // 取流中 取视频框尺寸
1854
+ // 播放中 取视频画面尺寸
1855
+ if (dataObj.tagBox.parent().find(".stream-loading").length != 0) {
1856
+ canvasObj.width = dataObj.tagBox.width();
1857
+ canvasObj.height = dataObj.tagBox.height();
1858
+ } else {
1859
+ canvasObj.width = videoObj.videoWidth;
1860
+ canvasObj.height = videoObj.videoHeight;
1861
+ }
1824
1862
 
1825
1863
  let ctx = canvasObj.getContext("2d");
1826
- ctx.drawImage(videoObj, 0, 0, dataObj.tagBox.parent().width(),
1827
- dataObj.tagBox.parent().height());
1864
+ ctx.drawImage(videoObj, 0, 0, videoObj.videoWidth, videoObj.videoHeight);
1828
1865
 
1829
1866
  let dataUrl = canvasObj.toDataURL('image/jpeg');
1830
1867
  callback ? callback(dataUrl) : null;
@@ -1874,7 +1911,8 @@
1874
1911
  if (index < 0) {
1875
1912
  console.error('窗口编号获取失败!')
1876
1913
  }
1877
-
1914
+ //保存对讲号码
1915
+ window.pocNo = pocNo;
1878
1916
  return me.VIDEO_DATA[index].pocCall("open_poccall", pocNo, centerTel);
1879
1917
  },
1880
1918
 
@@ -1899,6 +1937,7 @@
1899
1937
  }
1900
1938
 
1901
1939
  me.VIDEO_DATA[index].stream = null;
1940
+ window.pocNo = null;
1902
1941
  return me.VIDEO_DATA[index].pocCall("close_poccall", pocNo, centerTel);
1903
1942
  },
1904
1943
 
@@ -1933,22 +1972,28 @@
1933
1972
  * 添加事件
1934
1973
  */
1935
1974
  addListener: function(type, handler, key){
1936
- videoListener.addListener(type, handler, key);
1975
+ this.videoListener.addListener(type, handler, key);
1937
1976
  },
1938
1977
 
1939
1978
  /**
1940
1979
  * 删除事件
1941
1980
  */
1942
1981
  removeListener: function(type, handler){
1943
- videoListener.removeListener(type, handler);
1982
+ this.videoListener.removeListener(type, handler);
1944
1983
  },
1945
1984
  /**
1946
1985
  * 派发事件
1947
1986
  */
1948
1987
  dispatch: function(e, options){
1949
- videoListener.dispatch(e, options);
1988
+ this.videoListener.dispatch(e, options);
1950
1989
  },
1951
1990
 
1991
+ /**
1992
+ * 清空事件
1993
+ */
1994
+ removeAllListeners: function() {
1995
+ this.videoListener.removeAllListeners();
1996
+ },
1952
1997
  /**
1953
1998
  * 获取编号最小的空闲窗口的编号
1954
1999
  */
@@ -1956,7 +2001,7 @@
1956
2001
  let me = this;
1957
2002
  index = index || me._opts.windowsBeginIndex;
1958
2003
  for (; index < me._opts.windowsBeginIndex + me._opts.windows ; index++) {
1959
- if (!me.VIDEO_DATA[index].playing) {
2004
+ if (!me.VIDEO_DATA[index].playing && !me.VIDEO_DATA[index].isWaiting) {
1960
2005
  return index;
1961
2006
  }
1962
2007
  }
@@ -1968,7 +2013,7 @@
1968
2013
  */
1969
2014
  _getChoiceWindow: function() {
1970
2015
  let me = this;
1971
- let selView = $('[video-flag="' + me._opts.windowsBeginIndex+'"]>li.sel');
2016
+ let selView = $(me.selector + ' li.sel');
1972
2017
  if (selView.length) {
1973
2018
  return selView.index();
1974
2019
  }
@@ -1997,8 +2042,8 @@
1997
2042
  _clickWindow: function() {
1998
2043
  let me = this;
1999
2044
  //视频窗口选中
2000
- $('[video-flag="'+me._opts.windowsBeginIndex+'"]>li').click(function(){
2001
- $('[video-flag="'+me._opts.windowsBeginIndex+'"]>li').removeClass("sel");
2045
+ $(me.selector + ' li').click(function(){
2046
+ $(me.selector + ' li').removeClass("sel");
2002
2047
  $(this).addClass("sel");
2003
2048
 
2004
2049
  //如果视频正在播放则分发点击消息
@@ -2006,6 +2051,33 @@
2006
2051
  me.dispatch('click', me.getInChoiceVideo());
2007
2052
  }
2008
2053
  });
2054
+ // 设置视频窗可拖拽
2055
+ let src = null;
2056
+ $(me.selector + ' li').bind("dragstart", function (ev) {
2057
+ src = $(this);
2058
+ })
2059
+
2060
+ $(me.selector + ' li').bind("dragover", function (ev) {
2061
+ ev.preventDefault();
2062
+ })
2063
+
2064
+ $(me.selector + ' li').bind("drop", function (ev) {
2065
+ ev.preventDefault();
2066
+ if(src.prop("outerHTML") === $(this).prop("outerHTML")){
2067
+ return;
2068
+ }
2069
+ let target = $(this);
2070
+ let srcIndex = src.index();
2071
+ let targetIndex = target.index();
2072
+
2073
+ if (srcIndex > targetIndex) {
2074
+ src.insertBefore(target);
2075
+ target.insertAfter($(me.selector +' .video-main li.screen').eq(srcIndex));
2076
+ } else {
2077
+ src.insertAfter(target);
2078
+ target.insertBefore($(me.selector +' .video-main li.screen').eq(srcIndex));
2079
+ }
2080
+ })
2009
2081
  },
2010
2082
 
2011
2083
  /*
@@ -2034,7 +2106,11 @@
2034
2106
  conf: {},
2035
2107
  pollInterval: 10,
2036
2108
  windowsNum: 16,
2037
- isVideoTag: true //true生成video标签 false生产audio标签 默认true
2109
+ isVideoTag: true, //true生成video标签 false生产audio标签 默认true
2110
+ showVideoInfo: 1, //显示分辨率、码率等信息,1-显示左下角 2-显示左上角,为0时控制_showVideoInfo函数处理,默认1
2111
+ waitPlayQueueSwitch: false, //播放队列开关,默认false
2112
+ defaultBusinessType: 0, // 配置的默认业务类型 0:调度主线 1:勤指
2113
+ videoTipTimeOut: 5, // 超时无首屏检测时间
2038
2114
  };
2039
2115
 
2040
2116
  $.extend(me._opts,opts);
@@ -2053,23 +2129,32 @@
2053
2129
  }
2054
2130
 
2055
2131
  //获取起始的窗口位置
2056
- me._opts.windowsBeginIndex = windowsSum;
2057
- //窗口总数
2058
- windowsSum += me._opts.windowsNum;
2132
+ me._opts.windowsBeginIndex = 0;
2133
+ // 对讲号码初始化
2134
+ window.pocNo = 0;
2135
+ // 播放队列开关 默认打开
2136
+ me.waitPlayQueueSwitch = opts.waitPlayQueueSwitch == undefined ? false : opts.waitPlayQueueSwitch;
2059
2137
 
2060
2138
  //是否提示
2061
2139
  me._opts.showPrompt === false && (showPrompt = false);
2062
- //初始化界面
2063
- me._opts.isVideoTag ? me._initVideoView($dom) : me._initAudioView($dom);
2140
+ // 勤指业务下 设备检测
2141
+ me._opts.defaultBusinessType == 1 && me._checkDevices();
2142
+ //初始化界面
2143
+ me._opts.isVideoTag ? me._initVideoView($dom, me._opts.showVideoInfo) : me._initAudioView($dom);
2144
+
2064
2145
  //初始化Janus
2065
2146
  me._initJanus();
2066
2147
  //初始化按钮点击事件
2067
2148
  me._initBtnEvent();
2149
+ // 初始化事件监听
2150
+ me._initEventListen();
2068
2151
 
2069
2152
  //设置鼠标点击选中窗口
2070
2153
  me._clickWindow();
2071
- //显示视频信息:名称、分辨率、码率、丢包率
2072
- me._showVideoInfo();
2154
+ if (me._opts.showVideoInfo != 0) {
2155
+ //显示视频信息:名称、分辨率、码率、丢包率
2156
+ me._showVideoInfo();
2157
+ }
2073
2158
  //双击某个视频全屏
2074
2159
  me._dbClickFullScreen();
2075
2160
  },
@@ -2172,36 +2257,7 @@
2172
2257
  */
2173
2258
  _initBtnEvent: function() {
2174
2259
  let me = this;
2175
-
2176
- // 设置视频窗可拖拽
2177
- var src = null;
2178
- $(me.selector + ' li').bind("dragstart", function (ev) {
2179
- src = $(this);
2180
- })
2181
-
2182
- $(me.selector + ' li').bind("dragover", function (ev) {
2183
- ev.preventDefault();
2184
- })
2185
-
2186
- $(me.selector + ' li').bind("drop", function (ev) {
2187
- ev.preventDefault();
2188
- if(src.prop("outerHTML") === $(this).prop("outerHTML")){
2189
- return;
2190
- }
2191
- var target = $(this);
2192
- var srcIndex = src.index();
2193
- var targetIndex = target.index();
2194
-
2195
- if (srcIndex > targetIndex) {
2196
- src.insertBefore(target);
2197
- target.insertAfter($(me.selector +' .video-main li.screen').eq(srcIndex));
2198
- } else {
2199
- src.insertAfter(target);
2200
- target.insertBefore($(me.selector +' .video-main li.screen').eq(srcIndex));
2201
- }
2202
- })
2203
-
2204
- let videoFlagObj = $('[video-flag="'+me._opts.windowsBeginIndex+'"]');
2260
+ let videoFlagObj = $(me.selector + ' .video-main');
2205
2261
 
2206
2262
  //锁定视频事件
2207
2263
  videoFlagObj.find("button[name='lockVideo']").click(function(){
@@ -2219,6 +2275,8 @@
2219
2275
  me.VIDEO_DATA[index].isLockVideo = true;
2220
2276
  $(this).attr('class', 'lock-video-btn');
2221
2277
  }
2278
+ //jquery方式阻止默认事件 & 冒泡事件
2279
+ return false;
2222
2280
  });
2223
2281
  videoFlagObj.find('button[name=rotateVideo]').click(function(){
2224
2282
  let rotateIndex = Number($(this).attr("rotate-index"));
@@ -2227,12 +2285,16 @@
2227
2285
  $(this).attr("rotate-index", rotateIndex);
2228
2286
  let li = $(this).parent().parent()[0];
2229
2287
  me.videoRotateSize(li, rotateIndex)
2288
+ //jquery方式阻止默认事件 & 冒泡事件
2289
+ return false;
2230
2290
  })
2231
2291
 
2232
2292
  //接收音频事件
2233
2293
  videoFlagObj.find("button[name='recvAudio']").click(function(){
2234
2294
  let _self = this;
2235
2295
  let index = Number($(this).parent().parent().attr("index"));
2296
+ // 优先当前窗口参数设置的业务类型 其次配置的默认业务类型
2297
+ let businessType = me.VIDEO_DATA[index].opts.businessType == undefined ? me._opts.defaultBusinessType : me.VIDEO_DATA[index].opts.businessType;
2236
2298
 
2237
2299
  if (!me.VIDEO_DATA[index].playing) {
2238
2300
  promptAlarm('请选择播放的视频源!');
@@ -2243,8 +2305,9 @@
2243
2305
  //发送停止播放音频的请求
2244
2306
  me.VIDEO_DATA[index].operateAudio("stop_audio")
2245
2307
  $(this).attr('class', 'unrecv-audio-btn');
2246
- me.VIDEO_DATA[index].tagBox.muted = false;
2308
+ me.VIDEO_DATA[index].tagBox.prop("muted", true);
2247
2309
  } else {
2310
+ if (businessType == 0) {
2248
2311
  //发送前先关闭其它音频通道(包括接收和发送音频)
2249
2312
  let lis = $($(_self).parent().parent().parent()).children();
2250
2313
  for (let i = 0; i < lis.length; i++) {
@@ -2269,21 +2332,33 @@
2269
2332
  //发送播放音频的请求
2270
2333
  $(_self).attr('class', 'recv-audio-btn');
2271
2334
  setTimeout(function () {
2272
- me.VIDEO_DATA[index].operateAudio("recv_audio");
2273
- me.VIDEO_DATA[index].tagBox.muted = true;
2335
+ if(!me.VIDEO_DATA[index].isClosing){
2336
+ me.VIDEO_DATA[index].operateAudio("recv_audio");
2337
+ me.VIDEO_DATA[index].tagBox.prop("muted", false);
2338
+ }
2274
2339
  }, 1000);
2340
+ } else if (businessType == 1) {
2341
+ // poc新模式支持回传多个音频,因此不关闭
2342
+ //发送播放音频的请求
2343
+ $(_self).attr('class', 'recv-audio-btn');
2344
+ me.VIDEO_DATA[index].operateAudio("recv_audio");
2345
+ me.VIDEO_DATA[index].tagBox.prop("muted", false)
2346
+ }
2275
2347
  }
2276
2348
  });
2277
2349
 
2278
2350
  //发送音频事件
2279
2351
  videoFlagObj.find("button[name='sendAudio']").click(function(){
2280
2352
  let index = Number($(this).parent().parent().attr("index"));
2353
+ // 优先当前窗口参数设置的业务类型 其次配置的默认业务类型 最后默认调度业务
2354
+ let businessType = me.VIDEO_DATA[index].opts.businessType == undefined ? me._opts.defaultBusinessType : me.VIDEO_DATA[index].opts.businessType;
2281
2355
 
2282
2356
  if (!me.VIDEO_DATA[index].playing) {
2283
2357
  promptAlarm('请选择播放的视频源!');
2284
2358
  return;
2285
2359
  }
2286
-
2360
+ if (businessType == 0) {
2361
+ // 主线业务
2287
2362
  if ($(this).attr('class') == 'send-audio-btn') {
2288
2363
  me.VIDEO_DATA[index].operateAudio("unsend_audio")
2289
2364
  $(this).attr('class', 'unsend-audio-btn');
@@ -2293,23 +2368,53 @@
2293
2368
  $(this).attr('class', 'send-audio-btn');
2294
2369
  me.VIDEO_DATA[index].stream.getAudioTracks()[0].enabled = true;
2295
2370
  }
2371
+ } else if (businessType == 1) {
2372
+ // 勤指业务
2373
+ if ($(this).attr('class') == 'send-audio-btn') {
2374
+ $(this).attr('class', 'unsend-audio-btn');
2375
+ //关闭点对点对讲
2376
+ me.VIDEO_DATA[index].ptopPoc("close_ptop_poc", window.pocNo);
2377
+ } else {
2378
+ $(this).attr('class', 'send-audio-btn');
2379
+ //发起点对点对讲
2380
+ me.VIDEO_DATA[index].ptopPoc("open_ptop_poc", window.pocNo);
2381
+ }
2382
+ }
2383
+ //jquery方式阻止默认事件 & 冒泡事件
2384
+ return false;
2296
2385
  });
2297
2386
 
2298
2387
  //关闭视频事件
2299
2388
  videoFlagObj.find("button[name='closeVideo']").click(function(){
2300
2389
  let index = Number($(this).parent().parent().attr("index"));
2301
2390
  me.close(index);
2391
+ //jquery方式阻止默认事件 & 冒泡事件
2392
+ return false;
2393
+ });
2394
+ },
2395
+ /**
2396
+ * 初始化事件监听
2397
+ */
2398
+ _initEventListen: function () {
2399
+ let _self = this;
2400
+ // 打开视频 {index, video, id, opts} index(视频窗口编号,从0开始) video(视频设备id,即devId) id(标识,空的话使用设备id) opts(其他参数)
2401
+ _self.addListener('openVideo', function (e) {
2402
+ _self.play(e.index, e.video, e.id, e.opts);
2403
+ })
2404
+
2405
+ // janus通知后台已关闭视频
2406
+ _self.addListener('notifyCloseVideo', function (e) {
2407
+ _self._notifyWaitingVideo(e.index);
2302
2408
  });
2303
2409
  },
2304
-
2305
2410
  /**
2306
2411
  * 双击某个视频全屏
2307
2412
  */
2308
2413
  _dbClickFullScreen: function() {
2309
2414
  let me = this;
2310
- $('[video-flag="'+me._opts.windowsBeginIndex+'"]').find('.video-box').on("dblclick",function(){
2311
- me._fullScreenEvent(this);
2312
- });
2415
+ $(me.selector + ' .video-main').find('.video-box').on("dblclick",function(){
2416
+ me._fullScreenEvent(this);
2417
+ });
2313
2418
  },
2314
2419
 
2315
2420
  /**
@@ -2342,10 +2447,17 @@
2342
2447
  _showVideoInfo: function() {
2343
2448
  let me = this;
2344
2449
  function clock(){
2450
+ if(!me.VIDEO_DATA || !me.VIDEO_DATA.length) return;
2345
2451
  for (let i = 0; i < me.VIDEO_DATA.length; i++) {
2346
2452
  if (me.VIDEO_DATA[i].playing) {
2347
2453
  let number = Number(i) + Number(1);
2348
- let videoObj = document.getElementById('video-' + number);
2454
+ //获取视频video标签
2455
+ let $videoList = $(me.selector + " .video-main #video-" + number);
2456
+ if(!$videoList.length) {
2457
+ console.error("can not find dom by id [video-" + number + "]");
2458
+ continue ;
2459
+ }
2460
+ let videoObj = $videoList[0];
2349
2461
 
2350
2462
  if (!videoObj) continue;
2351
2463
 
@@ -2360,6 +2472,7 @@
2360
2472
 
2361
2473
  let config = me.VIDEO_DATA[i].sipcall.webrtcStuff;
2362
2474
  me._setPacketsLostRate(i, config, me);
2475
+ me._setFrameDecoded(i, config, me);
2363
2476
  let resolution = width + "×" + height;
2364
2477
  switch(height + '') {
2365
2478
  case '1080':
@@ -2378,11 +2491,24 @@
2378
2491
  break;
2379
2492
  }
2380
2493
 
2381
- let videoInfoObj = document.getElementById('info-' + number);
2494
+ //获取文案显示dom元素
2495
+ let $videoInfoList = $(me.selector + " .video-main #info-" + number);
2496
+ if(!$videoInfoList.length) {
2497
+ console.error("can not find dom by id [info-" + number + "]");
2498
+ continue ;
2499
+ }
2500
+ let videoInfoObj = $videoInfoList[0];
2382
2501
  let videoName = me.VIDEO_DATA[i].opts && me.VIDEO_DATA[i].opts.name ?
2383
2502
  me.VIDEO_DATA[i].opts.name : me.VIDEO_DATA[i].video;
2384
2503
  videoInfoObj.innerHTML = videoName + '&nbsp;&nbsp;' + resolution +
2385
2504
  '<br>丢包率:' + me.VIDEO_DATA[i].packetsLostRate + '&nbsp;&nbsp;' + bitrate.replace("kbits/sec", "kbps");
2505
+ //根据视频框大小自动计算 显示文案的文字大小
2506
+ let fontHeightSize = $(me.selector + " .video-main #video-" + number).height() / 15;
2507
+ let fontWidthSize = $(me.selector + " .video-main #video-" + number).width() / 18;
2508
+ //长和宽 除以 15, 取小的值做文字大小样式
2509
+ let fontSize = fontHeightSize < fontWidthSize? fontHeightSize:fontWidthSize;
2510
+ fontSize = (!fontSize || fontSize > 20)? 20 : fontSize;
2511
+ $("#info-" + number).css("font-size", fontSize+"px");
2386
2512
  }
2387
2513
  }
2388
2514
  }
@@ -2438,23 +2564,163 @@
2438
2564
  '<br>' + bitrate + '&nbsp;&nbsp;Lost:' + res.packetsLost;*/
2439
2565
  }
2440
2566
  }
2441
-
2442
2567
  });
2443
- });
2444
- }
2445
- },
2568
+ });
2569
+ }
2570
+ },
2571
+
2572
+ /**
2573
+ * 设置当前网络信号提示
2574
+ * @param index 当前打开视频的索引
2575
+ * @param config
2576
+ * @param me
2577
+ * @private
2578
+ */
2579
+ _setFrameDecoded: function(index, config, me) {
2580
+ if (config.pc !== null && typeof config.pc !== 'undefined' && config.pc.getStats) {
2581
+ config.pc.getStats().then(function (stats) {
2582
+ stats.forEach(function (res) {
2583
+ if (res) {
2584
+ let inStats = false;
2585
+ // Check if these are statistics on incoming media
2586
+ if ((res.mediaType === 'video' || res.id.toLowerCase().indexOf('video') > -1) &&
2587
+ res.type === 'inbound-rtp' && res.id.indexOf('rtcp') < 0) {
2588
+ // New stats
2589
+ inStats = true;
2590
+ } else if (res.type === 'ssrc' && res.bytesReceived &&
2591
+ (res.googCodecName === 'VP8' || res.googCodecName === '')) {
2592
+ // Older Chromer versions
2593
+ inStats = true;
2594
+ }
2595
+ // Parse stats now
2596
+ if (inStats) {
2597
+ if (res.hasOwnProperty('framesDecoded')) {
2598
+ if (res.framesDecoded <= 1) return;
2599
+ if (typeof me.VIDEO_DATA[index].framesDecodedCount === 'undefined') {
2600
+ me.VIDEO_DATA[index].framesDecodedCount = 0;
2601
+ }
2602
+ if (typeof me.VIDEO_DATA[index].framesDecodedLast === 'undefined') {
2603
+ me.VIDEO_DATA[index].framesDecodedLast = res.framesDecoded;
2604
+ }
2605
+ if (res.framesDecoded > me.VIDEO_DATA[index].framesDecodedLast) {
2606
+ if (typeof me.VIDEO_DATA[index].tipDom !== 'undefined' && me.VIDEO_DATA[index].tipDom.style.display === 'block') {
2607
+ me.VIDEO_DATA[index].tipDom.style.display = 'none';
2608
+ }
2609
+ }
2610
+ me.VIDEO_DATA[index].framesDecodedCount++;
2611
+ // 定时检测
2612
+ if (me.VIDEO_DATA[index].framesDecodedCount === me.configOpt) {
2613
+ if (res.framesDecoded - me.VIDEO_DATA[index].framesDecodedLast === 0) { // 卡顿
2614
+ if (typeof me.VIDEO_DATA[index].tipDom === 'undefined') {
2615
+ var numIndex = index + 1;
2616
+ me.VIDEO_DATA[index].tipDom = document.getElementById('frame-decoded-' + numIndex);
2617
+ }
2618
+ me.VIDEO_DATA[index].tipDom.style.display = 'block';
2619
+ }
2620
+ me.VIDEO_DATA[index].framesDecodedCount = 0;
2621
+ me.VIDEO_DATA[index].framesDecodedLast = res.framesDecoded;
2622
+ } else {
2623
+ me.VIDEO_DATA[index].framesDecodedLast = res.framesDecoded;
2624
+ }
2625
+ }
2626
+ }
2627
+ }
2628
+
2629
+ });
2630
+ });
2631
+ }
2632
+ },
2633
+
2634
+
2635
+ /**
2636
+ * 设备可用性检查(扬声器、麦克风、分辨率、浏览器类型)
2637
+ * 1. 未检测(status == undefined)询问进入检测界面
2638
+ * 2. 检测完成按相应结果给予相应提示
2639
+ * 3. 拒绝检测按所有检测通过处理
2640
+ * 4. 程序自行检测,并将是否加载声卡结果放入window对象
2641
+ * @private
2642
+ */
2643
+ _checkDevices: function() {
2644
+ let me = this;
2645
+ let tipCountKey = "DEVICES_CHECK_TIP_COUNT";
2646
+ let statusKey = "DEVICES_CHECK_STATUS";
2647
+ let checkCompleted = "completed";
2648
+ let checkRejected = "rejected";
2649
+ let microResultKey = "MICRO_RESULT";
2650
+ let speakerResultKey = "SPEAKER_RESULT";
2651
+ let screenResultKey = "SCREEN_RESULT";
2652
+ let resultPass = "pass";
2653
+ let resultNotPass = "not pass";
2654
+
2655
+ let tipCount = sessionStorage.getItem(tipCountKey);
2656
+ let status = localStorage.getItem(statusKey);
2657
+ if ((tipCount == undefined || tipCount == "0") && status == undefined) {
2658
+ sessionStorage.setItem(tipCountKey, "1");
2659
+ //弹窗讯问是否进入检测界面
2660
+ if (confirm("您还没有检测硬件设备,是否先进行检测?")) {
2661
+ window.open("/scooper-video/new/checkDevices");
2662
+ } else {
2663
+ localStorage.setItem(statusKey, checkRejected);
2664
+ }
2665
+ return;
2666
+ }
2667
+
2668
+ if (status == checkCompleted) {
2669
+ let microResult = (localStorage.getItem(microResultKey) == undefined) ? resultPass : localStorage.getItem(microResultKey);
2670
+ let speakerResult = (localStorage.getItem(speakerResultKey) == undefined) ? resultPass : localStorage.getItem(speakerResultKey);
2671
+ let screenResult = (localStorage.getItem(screenResultKey) == undefined) ? resultPass : localStorage.getItem(screenResultKey);
2672
+
2673
+ let msg = "";
2674
+ if(speakerResult == resultNotPass){
2675
+ msg += "检测到扬声器异常,可能影响系统某些功能的使用!";
2676
+ console.error(msg);
2677
+ }
2678
+ if (microResult == resultNotPass) {
2679
+ msg += "检测到麦克风异常,可能影响系统某些功能的使用!";
2680
+ console.error(msg);
2681
+ }
2682
+ if (screenResult === resultNotPass) {
2683
+ msg += "您的屏幕分辨率过低,可能导致页面布局混乱!";
2684
+ //提示消息
2685
+ promptAlarm(msg);
2686
+ }
2687
+ let userAgent = navigator.userAgent;
2688
+ if (userAgent.toLowerCase().indexOf("chrome") < 0 && userAgent.toLowerCase().indexOf("firefox") < 0 && userAgent.toLowerCase().indexOf("edge") < 0) {
2689
+ msg += "当前浏览器下部分功能可能存在兼容问题,建议使用谷歌浏览器!";
2690
+ promptAlarm(msg);
2691
+ }
2692
+
2693
+ if (msg != "") {
2694
+ //提示并派发消息
2695
+ // promptAlarm(msg);
2696
+ //防止外部未监听消息就派发事件
2697
+ setTimeout(function () {
2698
+ me.videoListener.dispatch('msginfo', {'msg' : msg});
2699
+ }, 3000);
2700
+ }
2701
+ }
2702
+
2703
+ },
2704
+
2446
2705
 
2447
2706
  /**
2448
2707
  * 视频界面的初始化,在传过来的dom中创建界面
2449
2708
  */
2450
- _initVideoView: function($dom) {
2709
+ _initVideoView: function($dom, infoPosition) {
2451
2710
  let me = this;
2452
- let objClass = (me._opts.windowsNum == 1 ? 'video-main-full mode-' : 'video-main mode-') + me._opts.windows;
2711
+ let position;
2712
+ switch (infoPosition) {
2713
+ case 1: position = "info-bottom"; break;
2714
+ case 2: position = "info-top"; break;
2715
+ default : position = "info-bottom";
2716
+ }
2717
+ let objClass = (me._opts.windowsNum == 1 ? 'video-main video-main-full mode-' : 'video-main mode-') + me._opts.windows;
2453
2718
  let videoHtml = '<ul class="' + objClass +'" id="video-main-web-rtc" video-flag="'+me._opts.windowsBeginIndex+'">';
2454
2719
  let index = me._opts.windowsBeginIndex + 1;
2455
2720
  for (let i = index; i < index + me._opts.windowsNum; i++) {
2456
- videoHtml += '<li class="screen-' + i +'" index="' + (i - 1) +'" draggable="true"><video class="video-box" id="video-' + i +'" autoplay ></video>' +
2457
- '<div class="info hide" id="info-' + i +'"></div>' +
2721
+ videoHtml += '<li class="screen screen-' + i +'" index="' + (i - 1) +'" draggable="true"><video muted class="video-box" id="video-' + i +'" autoplay ></video>' +
2722
+ '<div class="hide info ' + position + '" id="info-' + i +'"></div>' +
2723
+ '<div class="hide frame-decoded" id="frame-decoded-' + i + '"style="position:absolute;float:left;color:#fff;width:67%;z-index:1;top:135px;left:180px">网络信号不佳</div>' +
2458
2724
  '<div class="operate-btn">';
2459
2725
  //videoHtml +='<button type="button" class="unlock-video-btn hide" name="lockVideo"></button>';
2460
2726
  checkUserMediaAvailable() && (videoHtml += '<button type="button" class="rotate-btn hide" name="rotateVideo" rotate-index="0" title="视频旋转"></button><button type="button" class="unsend-audio-btn hide" name="sendAudio"></button>');
@@ -2474,7 +2740,7 @@
2474
2740
  */
2475
2741
  _initAudioView: function($dom) {
2476
2742
  let me = this;
2477
- let objClass = (me._opts.windowsNum == 1 ? 'video-main-full mode-' : 'video-main mode-') + me._opts.windows;
2743
+ let objClass = (me._opts.windowsNum == 1 ? 'video-main video-main-full mode-' : 'video-main mode-') + me._opts.windows;
2478
2744
  let videoHtml = '<ul class="' + objClass +'" id="video-main-web-rtc" video-flag="'+me._opts.windowsBeginIndex+'">';
2479
2745
  let index = me._opts.windowsBeginIndex + 1;
2480
2746
  for (let i = index; i < index + me._opts.windowsNum; i++) {
@@ -2523,6 +2789,7 @@
2523
2789
 
2524
2790
  console.error('Janus服务连接失败:'+ error)
2525
2791
  me.closeAll();
2792
+ me._removeAllWaitingVideo();
2526
2793
  me._initJanus();
2527
2794
  },
2528
2795
  destroyed: function() {
@@ -2533,6 +2800,19 @@
2533
2800
  }
2534
2801
  });
2535
2802
  },
2803
+ /**
2804
+ * 销毁所有
2805
+ */
2806
+ destory: function() {
2807
+ var me = this;
2808
+ me.destoryJanus();
2809
+ $(me.selector).empty();
2810
+ me.videoListener = null;
2811
+ me.VIDEO_DATA = null;
2812
+ me.selector = null;
2813
+ //destoryJanus会触发回调,需要使用到opts
2814
+ // me._opts = null;
2815
+ },
2536
2816
 
2537
2817
  /**
2538
2818
  * 销毁:Janus销毁、资源释放
@@ -2541,7 +2821,7 @@
2541
2821
  let me = this;
2542
2822
  me._opts.janus.destroy({
2543
2823
  success: function() {
2544
- window.windowsSum = 0;
2824
+ // window.windowsSum = 0;
2545
2825
  //userToken = null;
2546
2826
  registered = false;
2547
2827
  },
@@ -2549,7 +2829,10 @@
2549
2829
  notifyDestroyed: true,
2550
2830
 
2551
2831
  });
2832
+ me.removeAllListeners();
2552
2833
  },
2834
+
2835
+ reLoginJanusItvIndex : -1,
2553
2836
 
2554
2837
  /**
2555
2838
  * 初始化插件、登陆,初始化窗口
@@ -2607,59 +2890,271 @@
2607
2890
  console.log("result event:" + event);
2608
2891
  if(event === 'registration_failed') {
2609
2892
  promptFailed("janus登陆失败: " + result["code"] + " " + result["reason"]);
2893
+ if(me.reLoginJanusItvIndex == -1) {
2894
+ me.reLoginJanusItvIndex = setInterval(function () {
2895
+ console.log("尝试重新登陆janus!");
2896
+ me._initJanus(janus);
2897
+ }, 5000);
2898
+ }
2899
+ return;
2900
+ }
2901
+ if (event === 'video_server_closed') {
2902
+ if(me.reLoginJanusItvIndex == -1) {
2903
+ me.reLoginJanusItvIndex = setInterval(function () {
2904
+ console.log("尝试重新登陆janus!");
2905
+ me._initJanus(janus);
2906
+ }, 5000);
2907
+ }
2610
2908
  return;
2611
2909
  }
2612
2910
  if(event === 'registered') {
2613
2911
  let userToken = result["userToken"];
2614
2912
  console.log('janus登陆成功')
2615
2913
  registered = true;
2914
+ if (me.reLoginJanusItvIndex != -1) {
2915
+ clearInterval(me.reLoginJanusItvIndex);
2916
+ me.reLoginJanusItvIndex = -1;
2917
+ }
2616
2918
 
2617
2919
  //生成主界面
2920
+ //相对序号,用于标识当前janus连接初始化的序号窗口
2921
+ let relativeIndex = 0;
2618
2922
  for(let i = me._opts.windowsBeginIndex; i < me._opts.windowsBeginIndex + me._opts.windowsNum; i++){
2619
2923
  me.VIDEO_DATA[i] = new SVideo({
2620
2924
  index: i,
2621
2925
  janus: janus,
2622
2926
  userToken: userToken,
2623
2927
  windowsNum: me._opts.windowsNum,
2928
+ relativeIndex: relativeIndex++,
2624
2929
  flag: me._opts.flag,
2625
2930
  isVideoTag: me._opts.isVideoTag,
2626
- janusPlugin: me._opts.conf.janusPlugin
2627
- });
2931
+ janusPlugin: me._opts.conf.janusPlugin,
2932
+ videoTipTimeOut: me._opts.videoTipTimeOut,
2933
+ parentSelector: me.selector
2934
+ }, me.videoListener);
2628
2935
  }
2629
2936
  }
2630
2937
  }
2631
2938
  }
2632
2939
  });
2633
2940
  },
2941
+ /* ----------------------------- 播放队列 --------------------------- */
2942
+
2943
+ // 记录等待中的视频
2944
+ WAITING_DATA: [],
2945
+
2946
+ // 播放队列开关 默认关闭
2947
+ waitPlayQueueSwitch: false,
2948
+
2949
+ /**
2950
+ * 添加等待视频数据
2951
+ * 同一位置再次打开,则覆盖上一次的
2952
+ * 参数:index(视频窗口编号,从0开始) video(视频设备id,即devId) id(标识,空的话使用设备id) opts(其他参数)
2953
+ */
2954
+ _addWaitingVideo: function (index, video, id, opts) {
2955
+ let _self = this;
2956
+ let isOverride = false;
2957
+ $.each(_self.WAITING_DATA, function (i, obj) {
2958
+ if (obj.index == index) {
2959
+ console.log("播放队列中 index = " + index + ", video = " + obj.video + " 替换为 video = " + video);
2960
+ _self.WAITING_DATA[i] = {
2961
+ index: index,
2962
+ video: video,
2963
+ id: id,
2964
+ opts: opts
2965
+ }
2966
+ isOverride = true;
2967
+ return false;
2968
+ }
2969
+ });
2970
+ if (!isOverride) {
2971
+ _self.WAITING_DATA.push({
2972
+ index: index,
2973
+ video: video,
2974
+ id: id,
2975
+ opts: opts
2976
+ });
2977
+ console.log("index = " + index + ", video = " + video + " 加入播放队列");
2978
+ }
2979
+ _self.VIDEO_DATA[index].isWaiting = true;
2980
+ },
2981
+
2982
+ /**
2983
+ * 删除等待视频数据
2984
+ */
2985
+ _removeWaitingVideo: function (index, video) {
2986
+ let _self = this;
2987
+ $.each(_self.WAITING_DATA, function (i, obj) {
2988
+ if (obj.index == index && obj.video == video) {
2989
+ _self.WAITING_DATA.splice(i, 1);
2990
+ _self.VIDEO_DATA[index].isWaiting = false;
2991
+ console.log("index = " + index + ", video = " + video + " 从播放队列移除");
2992
+ return false;
2993
+ }
2994
+ });
2995
+ },
2996
+
2997
+ /**
2998
+ * 删除所有等待播放视频的数据
2999
+ */
3000
+ _removeAllWaitingVideo: function () {
3001
+ let _self = this;
3002
+ _self.WAITING_DATA = [];
3003
+ let index = _self._opts.windowsBeginIndex;
3004
+ for (; index < _self._opts.windowsBeginIndex + _self._opts.windows ; index++) {
3005
+ _self.VIDEO_DATA[index].isWaiting = false;
3006
+ }
3007
+ },
3008
+
3009
+ /**
3010
+ * 上一个视频关闭后,如果该位置由等待视频,通知打开新视频
3011
+ * @param index
3012
+ */
3013
+ _notifyWaitingVideo: function (index) {
3014
+ let _self = this;
3015
+ $.each(_self.WAITING_DATA, function (i, obj) {
3016
+ if (obj.index == index) {
3017
+ _self.play(obj.index, obj.video, obj.id, obj.opts);
3018
+ _self._removeWaitingVideo(obj.index, obj.video);
3019
+ return false;
3020
+ }
3021
+ });
3022
+ }
2634
3023
 
2635
3024
  }
2636
3025
 
2637
- let FAILED_CODE = {
2638
- 1001 : "账号或密码错误",
2639
- 1002 : "该帐号已连接",
2640
- 1004 : "没有鉴权",
2641
- 2001 : "未携带token或token错误",
2642
- 2002 : "视频源id不存在",
2643
- 2003 : "操作指令非法(该账号不允许执行这个指令,或者不支持该指令)",
2644
- 3001 : "nat通道未建立",
2645
- 3002 : "该视频流已在传输",
2646
- 3003 : "无法获取远端视频流",
2647
- 4001 : "视频流断开连接",
2648
- 4002 : "获取视频失败",
2649
- 5000 : "内部错误",
2650
- 8001 : "设备不支持该指令",
2651
- 453 : "videoServer连接失败",
2652
- 454 : "录像回放窗口已占用,请先停止播放",
2653
- 455 : "无视频录像,操作失败",
2654
- 5001 : "呼叫异常断开",
2655
- 5002 : "禁止修改分辨率",
2656
- 4003 : "音频被占用",
2657
- 4004 : "音频被占用",
2658
- 4005 : "客户端已打开对讲",
2659
- 4006 : "终端离开云眼音频"
2660
- }
3026
+ let CONST_CODE = {
3027
+ //打开失败错误码,面板显示,不做浮窗提示框显示
3028
+ VIDEO_CODE: {
3029
+ 2002 : "打开失败,未找到该ID的视频源",
3030
+ 2003 : "打开失败,请确认视频服务版本",
3031
+ 3001 : "打开失败,未接收到引流包,请检查端口映射",
3032
+ 3002 : "",
3033
+ 3003 : "打开失败,对方拒绝视频请求",
3034
+ 4001 : "打开失败,视频流中断",
3035
+ 4002 : "打开失败,终端挂机或超时未接听",
3036
+ 7000 : "打开失败,无法连接视频设备",
3037
+ 7001 : "打开失败,无法连接视频设备",
3038
+ 7002 : "打开失败,无法连接视频设备"
3039
+ },
3040
+
3041
+ OTHER_CODE: {
3042
+ 453 : "videoServer连接失败,错误码453",
3043
+ 454 : "视频窗口已被占用,请选择其它窗口打开或停止播放",
3044
+ 455 : "操作失败,该段时间无视频录像",
3045
+ 499 : "操作异常,错误码499",
3046
+ 440 : "操作异常,错误码440",
3047
+ 441 : "操作异常,错误码441",
3048
+ 442 : "操作异常,错误码442",
3049
+ 443 : "操作异常,错误码443",
3050
+ 444 : "操作异常,错误码444",
3051
+ 445 : "操作异常,错误码445",
3052
+ 446 : "操作异常,错误码446",
3053
+ 447 : "操作异常,错误码447",
3054
+ 448 : "操作异常,错误码448",
3055
+ 449 : "操作异常,错误码449",
3056
+ 450 : "操作异常,错误码450",
3057
+ 451 : "操作异常,错误码451",
3058
+ 452 : "操作异常,错误码452",
3059
+ 456 : "设备已处于对讲状态或不支持对讲",
3060
+ 6001 : "录像失败"
3061
+ },
3062
+ FAILED_CODE: {
3063
+ 1001 : "账号或密码错误",
3064
+ 1002 : "该帐号已连接",
3065
+ 1004 : "没有鉴权",
3066
+ 2001 : "未携带token或token错误",
3067
+ 2002 : "视频源id不存在",
3068
+ 2003 : "操作指令非法(该账号不允许执行这个指令,或者不支持该指令)",
3069
+ 3001 : "nat通道未建立",
3070
+ 3002 : "该视频流已在传输",
3071
+ 3003 : "无法获取远端视频流",
3072
+ 4001 : "视频流异常断开连接",
3073
+ 4002 : "获取视频失败",
3074
+ 5000 : "内部错误",
3075
+ 8001 : "该设备不支持云台操作",
3076
+ 453 : "videoServer连接失败",
3077
+ 454 : "录像回放窗口已占用,请先停止播放",
3078
+ 455 : "无视频录像,操作失败",
3079
+ 5001 : "呼叫异常断开",
3080
+ 5002 : "禁止修改分辨率",
3081
+ 401 : "禁止修改分辨率",
3082
+ 4003 : "音频重置",
3083
+ 4004 : "音频被占用",
3084
+ 4005 : "客户端已打开对讲",
3085
+ 4006 : "终端离开云眼音频",
3086
+ 4008 : "终端拒绝打开视频",
3087
+ 6001 : "录像失败",
3088
+ 499 : "内部创建失败",
3089
+ 440 : "请求消息内容为空",
3090
+ 441 : "json格式错误",
3091
+ 442 : "无效的请求",
3092
+ 443 : "消息内容缺失",
3093
+ 444 : "消息缺少参数",
3094
+ 445 : "账户已登陆",
3095
+ 446 : "收到的videoserver消息有误",
3096
+ 447 : "消息参数错误",
3097
+ 448 : "SDP创建失败",
3098
+ 449 : "未使用",
3099
+ 450 : "创建端口失败",
3100
+ 451 : "SDP创建失败",
3101
+ 452 : "未使用加密的RTP",
3102
+ 456 : "对讲未打开",
3103
+ 7000 : "URL账号密码错误(RTSP)",
3104
+ 7001 : "URL地址不通(RTSP)",
3105
+ 7002 : "URL格式错误(RTSP)"
3106
+ },
2661
3107
 
2662
- let STATUS_CODE_ARR = ['5002', '4003', '4004', '4005', '4006'];
3108
+ STATUS_CODE_ARR: ['5002', '4003', '4004', '4005', '4006']
3109
+ }
3110
+ /**
3111
+ * 显示播放错误内容, 4002特殊处理
3112
+ * @param videoObj 视频对象
3113
+ * @param code 状态码(包含错误码)
3114
+ * @param isShowReplayBtn 是否显示重新播放按钮
3115
+ * @param video 打开错误的视频Id
3116
+ */
3117
+ function showResult(videoObj, code, isShowReplayBtn, video) {
3118
+ let videoDom = videoObj.tagBox.parent();
3119
+
3120
+ videoDom.addClass("result");
3121
+ videoDom.find('.operate-btn .close-btn').show();
3122
+
3123
+ if (CONST_CODE.FAILED_CODE[code] && code != "4002") {
3124
+ let resultHtml = "<div class='real-result'>错误码" + code + ": " + CONST_CODE.FAILED_CODE[code] + "</div>"
3125
+ videoDom.append(resultHtml);
3126
+ }
3127
+
3128
+ let resultHtml = "<div class='result'><div class='result-tip'>" + CONST_CODE.VIDEO_CODE[code] + "</div>";
3129
+ if(isShowReplayBtn && code != "4002") {
3130
+ resultHtml += "<div class='result-replay'><span id='replay-" + videoObj.index + "' class='replay'>点击重试</span></div>"
3131
+ }
3132
+ resultHtml += "</div>";
3133
+ videoDom.append(resultHtml);
3134
+ if(isShowReplayBtn && code != "4002") {
3135
+ $("#replay-" + videoObj.index).unbind("click").click(function () {
3136
+ videoObj.videoListener.dispatch("openVideo", {index: videoObj.index, video: video, id: videoObj.id, opts: videoObj.opts});
3137
+ });
3138
+ }
3139
+ if (code == "4002") {
3140
+ setTimeout(function () {
3141
+ clearResult(videoDom);
3142
+ }, 5000);
3143
+ }
3144
+ }
3145
+
3146
+ /**
3147
+ * 清除显示内容 videoDom: 视频Dom对象
3148
+ */
3149
+ function clearResult(videoDom, videoObj) {
3150
+ videoDom.removeClass("result");
3151
+ videoDom.find(".real-result").remove();
3152
+ videoDom.find(".result").remove();
3153
+ if (!videoObj || !videoObj.playing) {
3154
+ // bug id 18654: 增加是否正在播放判断
3155
+ videoDom.find('.operate-btn .close-btn').hide();
3156
+ }
3157
+ }
2663
3158
 
2664
3159
  //window.VIDEO_DATA = [];//记录视频各分屏的状态
2665
3160
  window.windowsSum = 0; //当前页面的视频总数,可能有多个VideoWebRtc对象
@@ -2667,16 +3162,23 @@
2667
3162
  let registered = false;
2668
3163
  let recordAvBusinessId = null;
2669
3164
 
2670
- function SVideo (opts) {
3165
+ function SVideo (opts, videoListener) {
2671
3166
  this.janus = opts.janus;
2672
3167
  this.userToken = opts.userToken;
2673
3168
  this.sipcall = null;
2674
3169
  this.index = opts.index;
2675
- this.tagBox = opts.isVideoTag ? $('#video-' + (this.index + 1)) : $('#audio-' + (this.index + 1));
3170
+ this.relativeIndex = opts.relativeIndex;
3171
+ // this.tagBox = opts.isVideoTag ? $('#video-' + (this.index + 1)) : $('#audio-' + (this.index + 1));
3172
+ this.tagBox = opts.isVideoTag ? $(opts.parentSelector + ' #video-' + (this.index + 1)) : $('#audio-' + (this.index + 1));
2676
3173
  this.windowsNum = opts.windowsNum;
2677
3174
  this.flag = opts.flag;
2678
3175
  this.isVideoTag = opts.isVideoTag;
2679
3176
  this.janusPlugin = opts.janusPlugin;
3177
+ this.isClosing = false; // 当前分屏是否正在关闭视频
3178
+ this.isWaiting = false; // 当前分屏是否有视频在等待播放
3179
+ this.playSucTimeOutIndex = -1; // 超时提示定时器index
3180
+ this.videoTipTimeOut = opts.videoTipTimeOut; // 超时无首屏时间
3181
+ this.videoListener = videoListener;
2680
3182
  this.init();
2681
3183
  }
2682
3184
 
@@ -2699,8 +3201,8 @@
2699
3201
  console.log("视频插件初始化成功:" + self.index);
2700
3202
  self.sipcall = pluginHandle;
2701
3203
 
2702
- if (self.index + 1 >= self.windowsNum) {
2703
- videoListener.dispatch('initsucc', self);
3204
+ if (self.relativeIndex + 1 >= self.windowsNum) {
3205
+ self.videoListener.dispatch('initsucc', self);
2704
3206
  }
2705
3207
  },
2706
3208
 
@@ -2717,10 +3219,15 @@
2717
3219
  promptFailed("请先登陆");
2718
3220
  }
2719
3221
 
2720
- if (msg.error_code) {
2721
- console.error(msg.error_code + ' ' + msg.error);
2722
- promptFailed(FAILED_CODE[msg.error_code] || '错误码:' + msg.error_code);
2723
- videoListener.dispatch('msginfo', {'msg' : FAILED_CODE[msg.error_code] || '错误码:' + msg.error_code});
3222
+ let video = self.video;
3223
+ if (msg.error_code) {
3224
+ console.error("错误码:" + msg.error_code + " " + CONST_CODE.FAILED_CODE[msg.error_code]);
3225
+ if (CONST_CODE.VIDEO_CODE[msg.error_code]) {
3226
+ showResult(self, msg.error_code, true, video);
3227
+ } else {
3228
+ promptFailed(CONST_CODE.OTHER_CODE[msg.error_code] || CONST_CODE.FAILED_CODE[msg.error_code] || '错误码:' + msg.error_code);
3229
+ }
3230
+ self.videoListener.dispatch('msginfo', {'code': msg.error_code, 'msg' : CONST_CODE.OTHER_CODE[msg.error_code] || CONST_CODE.FAILED_CODE[msg.error_code] || '错误码:' + msg.error_code});
2724
3231
  return false;
2725
3232
  }
2726
3233
 
@@ -2729,21 +3236,28 @@
2729
3236
  if (result.error_code && result.error_code != 0) {
2730
3237
  //videoListener.dispatch('afterclose', self);
2731
3238
 
2732
- if (!STATUS_CODE_ARR.includes(result.error_code + '')) {
3239
+ if (!CONST_CODE.STATUS_CODE_ARR.includes(result.error_code + '')) {
2733
3240
  //videoListener.dispatch('afterclose', self);
2734
3241
  //播放失败 关闭视频
2735
- let video = self.video;
3242
+
2736
3243
  self.close('err');
2737
3244
 
2738
3245
  if (self.isLockVideo) {
2739
- self.play(video);
3246
+ setTimeout(function () {
3247
+ self.play(video);
3248
+ }, 1500);
2740
3249
  } else {
2741
- videoListener.dispatch('afterclose', self);
3250
+ self.closeType = 'error';
3251
+ self.videoListener.dispatch('afterclose', self);
2742
3252
  }
2743
3253
  }
2744
3254
 
2745
- videoListener.dispatch('msginfo', {'msg' : FAILED_CODE[result.error_code] || '错误码:' + result.error_code});
2746
- promptFailed(FAILED_CODE[result.error_code] || '错误码:' + result.error_code);
3255
+ self.videoListener.dispatch('msginfo', {'code': result.error_code, 'msg' : CONST_CODE.OTHER_CODE[result.error_code] || CONST_CODE.FAILED_CODE[result.error_code] || '错误码:' + result.error_code});
3256
+ if (CONST_CODE.VIDEO_CODE[result.error_code]) {
3257
+ showResult(self, result.error_code, true, video);
3258
+ } else {
3259
+ promptFailed(CONST_CODE.OTHER_CODE[result.error_code] || CONST_CODE.FAILED_CODE[result.error_code] || '错误码:' + result.error_code);
3260
+ }
2747
3261
 
2748
3262
  self.tagBox.parent().find('.recv-audio-btn').attr("class", "unrecv-audio-btn");
2749
3263
  self.tagBox.parent().find('.send-audio-btn').attr("class", "unsend-audio-btn");
@@ -2769,11 +3283,17 @@
2769
3283
  },
2770
3284
  error: function(error) {
2771
3285
  console.error(error);
2772
- let body = { "request": "decline", "code": 480 };
2773
- self.sipcall.send({"message": body});
3286
+ // let body = { "request": "decline", "code": 480 };
3287
+ // self.sipcall.send({"message": body});
2774
3288
  }
2775
3289
  });
2776
3290
  break;
3291
+ case 'accepted' :
3292
+ self.playSucTimeOutIndex = setTimeout(function () {
3293
+ let _li = self.tagBox.parent();
3294
+ _li.append('<div class="video-tip">网络环境较差,可能无法正常加载视频</div>');
3295
+ }, self.videoTipTimeOut * 1000);
3296
+ break;
2777
3297
  //呼叫失败
2778
3298
  case 'callfaild':
2779
3299
  //promptFailed(FAILED_CODE[result.error_code] || '错误码:' + result.error_code);
@@ -2788,19 +3308,31 @@
2788
3308
  case 'start_av_record':
2789
3309
  if (result && result.filename) {
2790
3310
  self.videoFileName = result.filename;
2791
- videoListener.dispatch('recordvideo', self);
3311
+ self.videoListener.dispatch('startRecordVideo', self);
2792
3312
  console.log('录像地址:' + result.filename);
2793
3313
  }
2794
3314
  break;
3315
+ case 'stop_av_record':
3316
+ if (result && result.filename) {
3317
+ self.videoFileName = result.filename;
3318
+ self.videoListener.dispatch('stopRecordVideo', self);
3319
+ console.log('录像地址:' + result.filename);
3320
+ }
3321
+ break;
2795
3322
  //修改分辨率
2796
3323
  case 'ser_notify_resolution':
2797
3324
  if (result && result.resolution) {
2798
3325
  self.resolution = result.resolution;
2799
- videoListener.dispatch('notifyresolution', self);
3326
+ self.videoListener.dispatch('notifyresolution', self);
2800
3327
  console.log('接收notifyresolution:' + result.resolution);
2801
3328
  //promptAlarm('当前分辨率:' + result.resolution);
2802
3329
  }
2803
3330
  break;
3331
+ case 'change_resolution':
3332
+ if(result) {
3333
+ self.videoListener.dispatch('notifyResolutionChange', self);
3334
+ }
3335
+ break;
2804
3336
  //建立预呼叫响应
2805
3337
  case 'open_poccall':
2806
3338
  console.log('建立预呼叫');
@@ -2826,8 +3358,32 @@
2826
3358
  console.log('预呼叫异常断开');
2827
3359
  break;
2828
3360
  case 'ser_stop_audio':
3361
+ self.tagBox.parent().find('.recv-audio-btn').attr("class", "unrecv-audio-btn");
2829
3362
  console.log('音频异常关闭!');
2830
3363
  break;
3364
+ case 'close_down_audio':
3365
+ self.tagBox.parent().find('.send-audio-btn').attr("class", "unsend-audio-btn");
3366
+ promptSuccess("通话时间到");
3367
+ console.log('通话时间到!');
3368
+ break;
3369
+ case 'ser_notify_dispatch_close_ptop_poc':
3370
+ self.tagBox.parent().find('.send-audio-btn').attr("class", "unsend-audio-btn");
3371
+ console.log('调度通知点对点对讲关闭!');
3372
+ break;
3373
+ case 'ser_close_ptop_poc':
3374
+ self.tagBox.parent().find('.send-audio-btn').attr("class", "unsend-audio-btn");
3375
+ console.log('点对点对讲关闭!');
3376
+ break;
3377
+ case 'ser_open_ptop_poc':
3378
+ self.tagBox.parent().find('.unsend-audio-btn').attr("class", "send-audio-btn");
3379
+ console.log('点对点对讲打开!');
3380
+ break;
3381
+ // 视频关闭通知
3382
+ case 'ser_close_video':
3383
+ console.log('视频框 ' + self.index + ' 视频已关闭');
3384
+ self.isClosing = false;
3385
+ self.videoListener.dispatch('notifyCloseVideo', self);
3386
+ break;
2831
3387
  default:
2832
3388
  console.log('event:' + event);
2833
3389
  break;
@@ -2883,7 +3439,9 @@
2883
3439
  self.tagBox.bind("canplay play playing", function () {
2884
3440
  if(self.video){
2885
3441
  if (self.tagBox.is(':visible')) return;
2886
-
3442
+ // 清除播放监测定时任务,清除内容
3443
+ self.playSucTimeOutIndex > 0 && clearTimeout(self.playSucTimeOutIndex);
3444
+ self.tagBox.parent().find(".video-tip").remove();
2887
3445
  console.log('分屏=' + (self.index + 1) + ', ' + self.video + ' 播放成功');
2888
3446
  promptSuccess('分屏' + (self.index + 1) + ' 播放成功');
2889
3447
  self.tagBox.show();
@@ -2930,13 +3488,20 @@
2930
3488
  };
2931
3489
  this.sipcall.send({"message": body });
2932
3490
  this.video = video;
3491
+ this.isClosing = false;
2933
3492
  this.playing = true;
2934
3493
  this.packetsLostRate = '0.00%';
2935
3494
  this.packetsLostSum = 0;
2936
3495
  this.packetsReceivedSum = 0;
3496
+ let numIndex = this.index + 1;
3497
+ let framesDecodedDom = document.getElementById("frame-decoded-" + numIndex);
3498
+ framesDecodedDom.style.display = 'none';
3499
+ this.framesDecodedLast = undefined;
3500
+ this.framesDecodedCount = 0;
2937
3501
 
2938
3502
  //loading
2939
3503
  let _li = this.tagBox.parent();
3504
+ clearResult(_li, this);
2940
3505
  if(!_li.hasClass("loading")){
2941
3506
  _li.addClass("loading").append('<div class="stream-loading">等待数据流传送...</div>');
2942
3507
  }
@@ -2950,6 +3515,8 @@
2950
3515
  this.tagBox.parent().find('.lock-video-btn').attr("class", "unlock-video-btn");
2951
3516
 
2952
3517
  }
3518
+ //清除结果提示内容
3519
+ clearResult(this.tagBox.parent(), this);
2953
3520
 
2954
3521
  //let reqType = type ? type : "hangup";
2955
3522
  if(!this.playing) return;
@@ -2962,6 +3529,7 @@
2962
3529
  this.sipcall.send({"message": _hangup});
2963
3530
  this.sipcall.hangup();
2964
3531
  this.tagBox.hide();
3532
+ this.isClosing = true;
2965
3533
  this.playing = false;
2966
3534
  this.video = null;
2967
3535
  this.packetsLostRate = '0.00%';
@@ -2977,6 +3545,15 @@
2977
3545
  this.tagBox.parent().removeClass("loading").find(".stream-loading").remove();
2978
3546
  //清除object-fit
2979
3547
  this.tagBox.css('object-fit', '');
3548
+
3549
+ // 清除内容提示
3550
+ this.playSucTimeOutIndex > 0 && clearTimeout(this.playSucTimeOutIndex);
3551
+ this.tagBox.parent().find(".video-tip").remove();
3552
+ var numIndex = Number(this.index) + 1;
3553
+ var framesDecodedDom = document.getElementById("frame-decoded-" + numIndex);
3554
+ framesDecodedDom.style.display = 'none';
3555
+ this.framesDecodedLast = undefined;
3556
+ this.framesDecodedCount = 0;
2980
3557
  },
2981
3558
 
2982
3559
  /**
@@ -3081,6 +3658,7 @@
3081
3658
 
3082
3659
  //loading
3083
3660
  let _li = this.tagBox.parent();
3661
+ clearResult(_li, this);
3084
3662
  if(!_li.hasClass("loading")){
3085
3663
  _li.addClass("loading").append('<div class="stream-loading">等待数据流传送...</div>');
3086
3664
  }
@@ -3222,26 +3800,28 @@
3222
3800
 
3223
3801
  self.sipcall.send({"message": body });
3224
3802
  console.log("预呼叫操作="+ requestType +", 对讲号码=" + pocNo + ",调度中心号码=" + centerTel);
3803
+ },
3804
+
3805
+ /**
3806
+ * 点对点对讲
3807
+ * requstType: open_ptop_poc(开启) close_poc_poc(关闭)
3808
+ * pocno
3809
+ */
3810
+ ptopPoc: function(requestType, pocNo) {
3811
+ let self = this;
3812
+
3813
+ let body = {
3814
+ request: requestType,
3815
+ pocno: pocNo,
3816
+ playseq: self.index,
3817
+ pocmember: self.video,
3818
+ userToken: self.userToken,
3819
+ };
3820
+
3821
+ self.sipcall.send({"message": body });
3822
+ console.log("点对点对讲操作="+ requestType +", 主叫号码=" + pocNo + ",被叫号码=" + self.video);
3225
3823
  }
3226
3824
  }
3227
3825
 
3228
3826
  return isIE() ? VideoOcx : VideoWebRtc;
3229
- }));
3230
-
3231
-
3232
-
3233
-
3234
-
3235
-
3236
-
3237
-
3238
-
3239
-
3240
-
3241
-
3242
-
3243
-
3244
-
3245
-
3246
-
3247
-
3827
+ }));