Commit 34d9ab96ba84808fb56548f19914fce60b75ec87

Authored by xp.Huang
2 parents fdd6f2c2 478e5b14

Merge branch 'perf/video-player-change' into 'main_dev'

perf: 变更视频组件&&支持h265

See merge request yunteng/thingskit-view!223
@@ -57,7 +57,11 @@ @@ -57,7 +57,11 @@
57 "vue-router": "4.0.12", 57 "vue-router": "4.0.12",
58 "vue3-lazyload": "^0.2.5-beta", 58 "vue3-lazyload": "^0.2.5-beta",
59 "vue3-sketch-ruler": "^1.3.3", 59 "vue3-sketch-ruler": "^1.3.3",
60 - "vuedraggable": "^4.1.0" 60 + "vuedraggable": "^4.1.0",
  61 + "xgplayer": "^3.0.14",
  62 + "xgplayer-flv": "^3.0.14",
  63 + "xgplayer-hls": "^3.0.14",
  64 + "xgplayer-mp4": "^3.0.14"
61 }, 65 },
62 "devDependencies": { 66 "devDependencies": {
63 "@commitlint/cli": "^17.0.2", 67 "@commitlint/cli": "^17.0.2",
1 -lockfileVersion: '6.1' 1 +lockfileVersion: '6.0'
2 2
3 settings: 3 settings:
4 autoInstallPeers: true 4 autoInstallPeers: true
@@ -11,6 +11,9 @@ dependencies: @@ -11,6 +11,9 @@ dependencies:
11 '@amap/amap-jsapi-types': 11 '@amap/amap-jsapi-types':
12 specifier: ^0.0.8 12 specifier: ^0.0.8
13 version: 0.0.8 13 version: 0.0.8
  14 + '@fingerprintjs/fingerprintjs':
  15 + specifier: ^3.4.1
  16 + version: 3.4.2
14 '@types/color': 17 '@types/color':
15 specifier: ^3.0.3 18 specifier: ^3.0.3
16 version: 3.0.3 19 version: 3.0.3
@@ -41,6 +44,9 @@ dependencies: @@ -41,6 +44,9 @@ dependencies:
41 dom-helpers: 44 dom-helpers:
42 specifier: ^5.2.1 45 specifier: ^5.2.1
43 version: 5.2.1 46 version: 5.2.1
  47 + echarts-gl:
  48 + specifier: ^2.0.9
  49 + version: 2.0.9(echarts@5.3.3)
44 echarts-liquidfill: 50 echarts-liquidfill:
45 specifier: ^3.1.0 51 specifier: ^3.1.0
46 version: 3.1.0(echarts@5.3.3) 52 version: 3.1.0(echarts@5.3.3)
@@ -50,6 +56,9 @@ dependencies: @@ -50,6 +56,9 @@ dependencies:
50 echarts-wordcloud: 56 echarts-wordcloud:
51 specifier: ^2.0.0 57 specifier: ^2.0.0
52 version: 2.0.0(echarts@5.3.3) 58 version: 2.0.0(echarts@5.3.3)
  59 + flv.js:
  60 + specifier: ^1.6.2
  61 + version: 1.6.2
53 gsap: 62 gsap:
54 specifier: ^3.11.3 63 specifier: ^3.11.3
55 version: 3.11.3 64 version: 3.11.3
@@ -89,6 +98,9 @@ dependencies: @@ -89,6 +98,9 @@ dependencies:
89 video.js: 98 video.js:
90 specifier: ^7.20.3 99 specifier: ^7.20.3
91 version: 7.21.4 100 version: 7.21.4
  101 + videojs-flvjs-es6:
  102 + specifier: ^1.0.1
  103 + version: 1.0.1
92 vue: 104 vue:
93 specifier: ^3.2.31 105 specifier: ^3.2.31
94 version: 3.2.37 106 version: 3.2.37
@@ -113,6 +125,18 @@ dependencies: @@ -113,6 +125,18 @@ dependencies:
113 vuedraggable: 125 vuedraggable:
114 specifier: ^4.1.0 126 specifier: ^4.1.0
115 version: 4.1.0(vue@3.2.37) 127 version: 4.1.0(vue@3.2.37)
  128 + xgplayer:
  129 + specifier: ^3.0.14
  130 + version: 3.0.14(core-js@3.36.1)
  131 + xgplayer-flv:
  132 + specifier: ^3.0.14
  133 + version: 3.0.14(core-js@3.36.1)(xgplayer@3.0.14)
  134 + xgplayer-hls:
  135 + specifier: ^3.0.14
  136 + version: 3.0.14(core-js@3.36.1)(xgplayer@3.0.14)
  137 + xgplayer-mp4:
  138 + specifier: ^3.0.14
  139 + version: 3.0.14(core-js@3.36.1)(xgplayer@3.0.14)
116 140
117 devDependencies: 141 devDependencies:
118 '@commitlint/cli': 142 '@commitlint/cli':
@@ -1158,6 +1182,12 @@ packages: @@ -1158,6 +1182,12 @@ packages:
1158 - supports-color 1182 - supports-color
1159 dev: true 1183 dev: true
1160 1184
  1185 + /@fingerprintjs/fingerprintjs@3.4.2:
  1186 + resolution: {integrity: sha512-3Ncze6JsJpB7BpYhqIgvBpfvEX1jsEKrad5hQBpyRQxtoAp6hx3+R46zqfsuQG4D9egQZ+xftQ0u4LPFMB7Wmg==}
  1187 + dependencies:
  1188 + tslib: 2.6.2
  1189 + dev: false
  1190 +
1161 /@humanwhocodes/config-array@0.9.5: 1191 /@humanwhocodes/config-array@0.9.5:
1162 resolution: {integrity: sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==} 1192 resolution: {integrity: sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==}
1163 engines: {node: '>=10.10.0'} 1193 engines: {node: '>=10.10.0'}
@@ -2409,6 +2439,10 @@ packages: @@ -2409,6 +2439,10 @@ packages:
2409 engines: {node: '>=6.0'} 2439 engines: {node: '>=6.0'}
2410 dev: true 2440 dev: true
2411 2441
  2442 + /claygl@1.3.0:
  2443 + resolution: {integrity: sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ==}
  2444 + dev: false
  2445 +
2412 /clean-css@5.3.2: 2446 /clean-css@5.3.2:
2413 resolution: {integrity: sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==} 2447 resolution: {integrity: sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==}
2414 engines: {node: '>= 10.0'} 2448 engines: {node: '>= 10.0'}
@@ -2540,6 +2574,12 @@ packages: @@ -2540,6 +2574,12 @@ packages:
2540 resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 2574 resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
2541 dev: true 2575 dev: true
2542 2576
  2577 + /concat-typed-array@1.0.2:
  2578 + resolution: {integrity: sha512-aC878bxeWSlrY6h60cCDwBUXpKwovZrB7+C4+VHNO1CIXW2gBLxbQ757jWtOXUscLGgYI8R84N6uy9fTJPe+0g==}
  2579 + engines: {node: '>=0.10.0'}
  2580 + deprecated: 'WARNING: This package has been renamed to typed-array-concat.'
  2581 + dev: false
  2582 +
2543 /connect-history-api-fallback@1.6.0: 2583 /connect-history-api-fallback@1.6.0:
2544 resolution: {integrity: sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==} 2584 resolution: {integrity: sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==}
2545 engines: {node: '>=0.8'} 2585 engines: {node: '>=0.8'}
@@ -2605,6 +2645,11 @@ packages: @@ -2605,6 +2645,11 @@ packages:
2605 safe-buffer: 5.1.2 2645 safe-buffer: 5.1.2
2606 dev: true 2646 dev: true
2607 2647
  2648 + /core-js@3.36.1:
  2649 + resolution: {integrity: sha512-BTvUrwxVBezj5SZ3f10ImnX2oRByMxql3EimVqMysepbC9EeMUOpLwdy6Eoili2x6E4kf+ZUB5k/+Jv55alPfA==}
  2650 + requiresBuild: true
  2651 + dev: false
  2652 +
2608 /cosmiconfig-typescript-loader@2.0.1(@types/node@17.0.43)(cosmiconfig@7.0.1)(typescript@4.7.3): 2653 /cosmiconfig-typescript-loader@2.0.1(@types/node@17.0.43)(cosmiconfig@7.0.1)(typescript@4.7.3):
2609 resolution: {integrity: sha512-B9s6sX/omXq7I6gC6+YgLmrBFMJhPWew7ty/X5Tuwtd2zOSgWaUdXjkuVwbe3qqcdETo60+1nSVMekq//LIXVA==} 2654 resolution: {integrity: sha512-B9s6sX/omXq7I6gC6+YgLmrBFMJhPWew7ty/X5Tuwtd2zOSgWaUdXjkuVwbe3qqcdETo60+1nSVMekq//LIXVA==}
2610 engines: {node: '>=12', npm: '>=6'} 2655 engines: {node: '>=12', npm: '>=6'}
@@ -2646,6 +2691,10 @@ packages: @@ -2646,6 +2691,10 @@ packages:
2646 which: 2.0.2 2691 which: 2.0.2
2647 dev: true 2692 dev: true
2648 2693
  2694 + /crypto-es@1.2.7:
  2695 + resolution: {integrity: sha512-UUqiVJ2gUuZFmbFsKmud3uuLcNP2+Opt+5ysmljycFCyhA0+T16XJmo1ev/t5kMChMqWh7IEvURNCqsg+SjZGQ==}
  2696 + dev: false
  2697 +
2649 /crypto-js@4.1.1: 2698 /crypto-js@4.1.1:
2650 resolution: {integrity: sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==} 2699 resolution: {integrity: sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==}
2651 dev: false 2700 dev: false
@@ -2692,6 +2741,20 @@ packages: @@ -2692,6 +2741,20 @@ packages:
2692 resolution: {integrity: sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==} 2741 resolution: {integrity: sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==}
2693 dev: false 2742 dev: false
2694 2743
  2744 + /d@1.0.2:
  2745 + resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==}
  2746 + engines: {node: '>=0.12'}
  2747 + dependencies:
  2748 + es5-ext: 0.10.64
  2749 + type: 2.7.2
  2750 + dev: false
  2751 +
  2752 + /danmu.js@1.1.13:
  2753 + resolution: {integrity: sha512-knFd0/cB2HA4FFWiA7eB2suc5vCvoHdqio33FyyCSfP7C+1A+zQcTvnvwfxaZhrxsGj4qaQI2I8XiTqedRaVmg==}
  2754 + dependencies:
  2755 + event-emitter: 0.3.5
  2756 + dev: false
  2757 +
2695 /dargs@7.0.0: 2758 /dargs@7.0.0:
2696 resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==} 2759 resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==}
2697 engines: {node: '>=8'} 2760 engines: {node: '>=8'}
@@ -2765,6 +2828,11 @@ packages: @@ -2765,6 +2828,11 @@ packages:
2765 resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} 2828 resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
2766 dev: true 2829 dev: true
2767 2830
  2831 + /deepmerge@2.2.1:
  2832 + resolution: {integrity: sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==}
  2833 + engines: {node: '>=0.10.0'}
  2834 + dev: false
  2835 +
2768 /deepmerge@4.2.2: 2836 /deepmerge@4.2.2:
2769 resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} 2837 resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==}
2770 engines: {node: '>=0.10.0'} 2838 engines: {node: '>=0.10.0'}
@@ -2807,6 +2875,10 @@ packages: @@ -2807,6 +2875,10 @@ packages:
2807 engines: {node: '>=0.4.0'} 2875 engines: {node: '>=0.4.0'}
2808 dev: false 2876 dev: false
2809 2877
  2878 + /delegate@3.2.0:
  2879 + resolution: {integrity: sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==}
  2880 + dev: false
  2881 +
2810 /detect-file@1.0.0: 2882 /detect-file@1.0.0:
2811 resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==} 2883 resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==}
2812 engines: {node: '>=0.10.0'} 2884 engines: {node: '>=0.10.0'}
@@ -2900,6 +2972,20 @@ packages: @@ -2900,6 +2972,20 @@ packages:
2900 engines: {node: '>=12'} 2972 engines: {node: '>=12'}
2901 dev: true 2973 dev: true
2902 2974
  2975 + /downloadjs@1.4.7:
  2976 + resolution: {integrity: sha512-LN1gO7+u9xjU5oEScGFKvXhYf7Y/empUIIEAGBs1LzUq/rg5duiDrkuH5A2lQGd5jfMOb9X9usDa2oVXwJ0U/Q==}
  2977 + dev: false
  2978 +
  2979 + /echarts-gl@2.0.9(echarts@5.3.3):
  2980 + resolution: {integrity: sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==}
  2981 + peerDependencies:
  2982 + echarts: ^5.1.2
  2983 + dependencies:
  2984 + claygl: 1.3.0
  2985 + echarts: 5.3.3
  2986 + zrender: 5.3.2
  2987 + dev: false
  2988 +
2903 /echarts-liquidfill@3.1.0(echarts@5.3.3): 2989 /echarts-liquidfill@3.1.0(echarts@5.3.3):
2904 resolution: {integrity: sha512-5Dlqs/jTsdTUAsd+K5LPLLTgrbbNORUSBQyk8PSy1Mg2zgHDWm83FmvA4s0ooNepCJojFYRITTQ4GU1UUSKYLw==} 2990 resolution: {integrity: sha512-5Dlqs/jTsdTUAsd+K5LPLLTgrbbNORUSBQyk8PSy1Mg2zgHDWm83FmvA4s0ooNepCJojFYRITTQ4GU1UUSKYLw==}
2905 peerDependencies: 2991 peerDependencies:
@@ -3021,6 +3107,37 @@ packages: @@ -3021,6 +3107,37 @@ packages:
3021 is-symbol: 1.0.4 3107 is-symbol: 1.0.4
3022 dev: true 3108 dev: true
3023 3109
  3110 + /es5-ext@0.10.64:
  3111 + resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==}
  3112 + engines: {node: '>=0.10'}
  3113 + requiresBuild: true
  3114 + dependencies:
  3115 + es6-iterator: 2.0.3
  3116 + es6-symbol: 3.1.4
  3117 + esniff: 2.0.1
  3118 + next-tick: 1.1.0
  3119 + dev: false
  3120 +
  3121 + /es6-iterator@2.0.3:
  3122 + resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==}
  3123 + dependencies:
  3124 + d: 1.0.2
  3125 + es5-ext: 0.10.64
  3126 + es6-symbol: 3.1.4
  3127 + dev: false
  3128 +
  3129 + /es6-promise@4.2.8:
  3130 + resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==}
  3131 + dev: false
  3132 +
  3133 + /es6-symbol@3.1.4:
  3134 + resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==}
  3135 + engines: {node: '>=0.12'}
  3136 + dependencies:
  3137 + d: 1.0.2
  3138 + ext: 1.7.0
  3139 + dev: false
  3140 +
3024 /esbuild@0.11.3: 3141 /esbuild@0.11.3:
3025 resolution: {integrity: sha512-BzVRHcCtFepjS9WcqRjqoIxLqgpK21a8J4Zi4msSGxDxiXVO1IbcqT1KjhdDDnJxKfe7bvzZrvMEX+bVO0Elcw==} 3142 resolution: {integrity: sha512-BzVRHcCtFepjS9WcqRjqoIxLqgpK21a8J4Zi4msSGxDxiXVO1IbcqT1KjhdDDnJxKfe7bvzZrvMEX+bVO0Elcw==}
3026 hasBin: true 3143 hasBin: true
@@ -3265,6 +3382,16 @@ packages: @@ -3265,6 +3382,16 @@ packages:
3265 - supports-color 3382 - supports-color
3266 dev: true 3383 dev: true
3267 3384
  3385 + /esniff@2.0.1:
  3386 + resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==}
  3387 + engines: {node: '>=0.10'}
  3388 + dependencies:
  3389 + d: 1.0.2
  3390 + es5-ext: 0.10.64
  3391 + event-emitter: 0.3.5
  3392 + type: 2.7.2
  3393 + dev: false
  3394 +
3268 /espree@9.3.2: 3395 /espree@9.3.2:
3269 resolution: {integrity: sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==} 3396 resolution: {integrity: sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==}
3270 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 3397 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -3310,6 +3437,17 @@ packages: @@ -3310,6 +3437,17 @@ packages:
3310 engines: {node: '>=0.10.0'} 3437 engines: {node: '>=0.10.0'}
3311 dev: true 3438 dev: true
3312 3439
  3440 + /event-emitter@0.3.5:
  3441 + resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==}
  3442 + dependencies:
  3443 + d: 1.0.2
  3444 + es5-ext: 0.10.64
  3445 + dev: false
  3446 +
  3447 + /eventemitter3@4.0.7:
  3448 + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
  3449 + dev: false
  3450 +
3313 /events@3.3.0: 3451 /events@3.3.0:
3314 resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} 3452 resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
3315 engines: {node: '>=0.8.x'} 3453 engines: {node: '>=0.8.x'}
@@ -3341,6 +3479,12 @@ packages: @@ -3341,6 +3479,12 @@ packages:
3341 homedir-polyfill: 1.0.3 3479 homedir-polyfill: 1.0.3
3342 dev: true 3480 dev: true
3343 3481
  3482 + /ext@1.7.0:
  3483 + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==}
  3484 + dependencies:
  3485 + type: 2.7.2
  3486 + dev: false
  3487 +
3344 /extend@3.0.2: 3488 /extend@3.0.2:
3345 resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} 3489 resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
3346 dev: true 3490 dev: true
@@ -3490,6 +3634,13 @@ packages: @@ -3490,6 +3634,13 @@ packages:
3490 resolution: {integrity: sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==} 3634 resolution: {integrity: sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==}
3491 dev: true 3635 dev: true
3492 3636
  3637 + /flv.js@1.6.2:
  3638 + resolution: {integrity: sha512-xre4gUbX1MPtgQRKj2pxJENp/RnaHaxYvy3YToVVCrSmAWUu85b9mug6pTXF6zakUjNP2lFWZ1rkSX7gxhB/2A==}
  3639 + dependencies:
  3640 + es6-promise: 4.2.8
  3641 + webworkify-webpack: 2.1.5
  3642 + dev: false
  3643 +
3493 /follow-redirects@1.15.1: 3644 /follow-redirects@1.15.1:
3494 resolution: {integrity: sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==} 3645 resolution: {integrity: sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==}
3495 engines: {node: '>=4.0'} 3646 engines: {node: '>=4.0'}
@@ -4590,6 +4741,10 @@ packages: @@ -4590,6 +4741,10 @@ packages:
4590 resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} 4741 resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
4591 dev: true 4742 dev: true
4592 4743
  4744 + /next-tick@1.1.0:
  4745 + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
  4746 + dev: false
  4747 +
4593 /no-case@3.0.4: 4748 /no-case@3.0.4:
4594 resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} 4749 resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
4595 dependencies: 4750 dependencies:
@@ -5732,6 +5887,10 @@ packages: @@ -5732,6 +5887,10 @@ packages:
5732 resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} 5887 resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==}
5733 dev: true 5888 dev: true
5734 5889
  5890 + /tslib@2.6.2:
  5891 + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
  5892 + dev: false
  5893 +
5735 /tsutils@3.21.0(typescript@4.6.3): 5894 /tsutils@3.21.0(typescript@4.6.3):
5736 resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} 5895 resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
5737 engines: {node: '>= 6'} 5896 engines: {node: '>= 6'}
@@ -5774,6 +5933,10 @@ packages: @@ -5774,6 +5933,10 @@ packages:
5774 engines: {node: '>=8'} 5933 engines: {node: '>=8'}
5775 dev: true 5934 dev: true
5776 5935
  5936 + /type@2.7.2:
  5937 + resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==}
  5938 + dev: false
  5939 +
5777 /typescript@4.6.3: 5940 /typescript@4.6.3:
5778 resolution: {integrity: sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==} 5941 resolution: {integrity: sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==}
5779 engines: {node: '>=4.2.0'} 5942 engines: {node: '>=4.2.0'}
@@ -5912,6 +6075,10 @@ packages: @@ -5912,6 +6075,10 @@ packages:
5912 videojs-vtt.js: 0.15.4 6075 videojs-vtt.js: 0.15.4
5913 dev: false 6076 dev: false
5914 6077
  6078 + /videojs-flvjs-es6@1.0.1:
  6079 + resolution: {integrity: sha512-wAI5ff2tZVW+uftTLyPmS38F4SHmMlxqBFOgXEBqMs2X0N4uIVQK0iCCv5XACXH+oc+mP70D23mJmT8KsoHx0g==}
  6080 + dev: false
  6081 +
5915 /videojs-font@3.2.0: 6082 /videojs-font@3.2.0:
5916 resolution: {integrity: sha512-g8vHMKK2/JGorSfqAZQUmYYNnXmfec4MLhwtEFS+mMs2IDY398GLysy6BH6K+aS1KMNu/xWZ8Sue/X/mdQPliA==} 6083 resolution: {integrity: sha512-g8vHMKK2/JGorSfqAZQUmYYNnXmfec4MLhwtEFS+mMs2IDY398GLysy6BH6K+aS1KMNu/xWZ8Sue/X/mdQPliA==}
5917 dev: false 6084 dev: false
@@ -6261,6 +6428,10 @@ packages: @@ -6261,6 +6428,10 @@ packages:
6261 - uglify-js 6428 - uglify-js
6262 dev: true 6429 dev: true
6263 6430
  6431 + /webworkify-webpack@2.1.5:
  6432 + resolution: {integrity: sha512-2akF8FIyUvbiBBdD+RoHpoTbHMQF2HwjcxfDvgztAX5YwbZNyrtfUMgvfgFVsgDhDPVTlkbb5vyasqDHfIDPQw==}
  6433 + dev: false
  6434 +
6264 /which-boxed-primitive@1.0.2: 6435 /which-boxed-primitive@1.0.2:
6265 resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} 6436 resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
6266 dependencies: 6437 dependencies:
@@ -6308,6 +6479,101 @@ packages: @@ -6308,6 +6479,101 @@ packages:
6308 resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} 6479 resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
6309 dev: true 6480 dev: true
6310 6481
  6482 + /xgplayer-flv@3.0.14(core-js@3.36.1)(xgplayer@3.0.14):
  6483 + resolution: {integrity: sha512-9mSBUbbzkJhFjQOaU8SvnEzDj4Y9tkHsW0IaIxCDi/DlDcHzSubdiuuTVNf1A5cgUSTnFHxwDJtzqVm6FhmTeg==}
  6484 + peerDependencies:
  6485 + core-js: '>=3.12.1'
  6486 + xgplayer: '>=3.0.1'
  6487 + dependencies:
  6488 + core-js: 3.36.1
  6489 + eventemitter3: 4.0.7
  6490 + xgplayer: 3.0.14(core-js@3.36.1)
  6491 + xgplayer-streaming-shared: 3.0.14(core-js@3.36.1)
  6492 + xgplayer-transmuxer: 3.0.14(core-js@3.36.1)
  6493 + dev: false
  6494 +
  6495 + /xgplayer-hls@3.0.14(core-js@3.36.1)(xgplayer@3.0.14):
  6496 + resolution: {integrity: sha512-UiVDiDcIzQ6kiDZitbyXqRx5nX/Pi69S9Vfh5lc+6R8SsA2ZyvxWtGOQ6XfpZyXYHfl56JMbyNObOgMT1iRNLw==}
  6497 + peerDependencies:
  6498 + core-js: '>=3.12.1'
  6499 + xgplayer: 3.0.14
  6500 + dependencies:
  6501 + core-js: 3.36.1
  6502 + eventemitter3: 4.0.7
  6503 + xgplayer: 3.0.14(core-js@3.36.1)
  6504 + xgplayer-streaming-shared: 3.0.14(core-js@3.36.1)
  6505 + xgplayer-transmuxer: 3.0.14(core-js@3.36.1)
  6506 + dev: false
  6507 +
  6508 + /xgplayer-mp4-loader@3.0.14(core-js@3.36.1):
  6509 + resolution: {integrity: sha512-cCPH7I/i+saP5i+vivfnu7Bekxm1sS3JlTzaqX2crUL9a4MfWi/r7Yumqsn52lyQGyb+lGKOsvbHeR3wTwUx1w==}
  6510 + peerDependencies:
  6511 + core-js: '>=3.12.1'
  6512 + dependencies:
  6513 + core-js: 3.36.1
  6514 + eventemitter3: 4.0.7
  6515 + xgplayer-streaming-shared: 3.0.14(core-js@3.36.1)
  6516 + xgplayer-transmuxer: 3.0.14(core-js@3.36.1)
  6517 + dev: false
  6518 +
  6519 + /xgplayer-mp4@3.0.14(core-js@3.36.1)(xgplayer@3.0.14):
  6520 + resolution: {integrity: sha512-EEZ3KtG7JbOBL33P+Uv98PaIft9tI9zw2rfyS7Sip57hT/9tJ2az4qz9qNSRcrrlLTo0DTYGR/JzADu7l/IyUQ==}
  6521 + peerDependencies:
  6522 + core-js: '>=3.12.1'
  6523 + xgplayer: '>=3.0.0'
  6524 + dependencies:
  6525 + concat-typed-array: 1.0.2
  6526 + core-js: 3.36.1
  6527 + deepmerge: 2.2.1
  6528 + eventemitter3: 4.0.7
  6529 + xgplayer: 3.0.14(core-js@3.36.1)
  6530 + xgplayer-mp4-loader: 3.0.14(core-js@3.36.1)
  6531 + xgplayer-streaming-shared: 3.0.14(core-js@3.36.1)
  6532 + xgplayer-transmuxer: 3.0.14(core-js@3.36.1)
  6533 + dev: false
  6534 +
  6535 + /xgplayer-streaming-shared@3.0.14(core-js@3.36.1):
  6536 + resolution: {integrity: sha512-XBXNjnMfFDl15kQfXNgmAkaDqRdN0PhxVFBm7+TezillpTdLmqj+HD90F6BargdZVJ4I20/YHYdb9qBg+hUDhg==}
  6537 + peerDependencies:
  6538 + core-js: '>=3.12.1'
  6539 + dependencies:
  6540 + core-js: 3.36.1
  6541 + eventemitter3: 4.0.7
  6542 + dev: false
  6543 +
  6544 + /xgplayer-subtitles@3.0.14(core-js@3.36.1):
  6545 + resolution: {integrity: sha512-w6H1h+g3kOI477kv2QBRMZe3M/1dHLXttHBwq4LwKTPGVQ19fLIDGwkfn+HeKwe1ocGDaaq96bS+l+BadnP9TA==}
  6546 + peerDependencies:
  6547 + core-js: '>=3.12.1'
  6548 + dependencies:
  6549 + core-js: 3.36.1
  6550 + eventemitter3: 4.0.7
  6551 + dev: false
  6552 +
  6553 + /xgplayer-transmuxer@3.0.14(core-js@3.36.1):
  6554 + resolution: {integrity: sha512-lMS2EwuA6tToCC4NuyJ5Ax0UDaaWU/YuaSkI/Bsj+vHkEzO42vgi2EerSPr91Moz05KhG/+4Vt8qMKUVFjhGTw==}
  6555 + peerDependencies:
  6556 + core-js: '>=3.12.1'
  6557 + dependencies:
  6558 + '@babel/runtime': 7.20.6
  6559 + concat-typed-array: 1.0.2
  6560 + core-js: 3.36.1
  6561 + crypto-es: 1.2.7
  6562 + dev: false
  6563 +
  6564 + /xgplayer@3.0.14(core-js@3.36.1):
  6565 + resolution: {integrity: sha512-TPS77hUIcM1zVx6FSpGG+OzfMwjwxxWihd+YxVx82DQ22QK60v8TXWSZlWISbmS1+fAlFneesvXDjwy60tC37w==}
  6566 + peerDependencies:
  6567 + core-js: '>=3.12.1'
  6568 + dependencies:
  6569 + core-js: 3.36.1
  6570 + danmu.js: 1.1.13
  6571 + delegate: 3.2.0
  6572 + downloadjs: 1.4.7
  6573 + eventemitter3: 4.0.7
  6574 + xgplayer-subtitles: 3.0.14(core-js@3.36.1)
  6575 + dev: false
  6576 +
6311 /y18n@5.0.8: 6577 /y18n@5.0.8:
6312 resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} 6578 resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
6313 engines: {node: '>=10'} 6579 engines: {node: '>=10'}
1 import { defHttp } from '@/utils/external/http/axios' 1 import { defHttp } from '@/utils/external/http/axios'
2 -import { ConfigurationItemType, DictItem, OrganizationListItem, UploadResponse } from './model' 2 +import {CameraRecord, ConfigurationItemType, DictItem, OrganizationListItem, UploadResponse} from './model'
3 import { PaginationResult } from '/#/external/axios' 3 import { PaginationResult } from '/#/external/axios'
4 import { isShareMode } from '@/views/share/hook' 4 import { isShareMode } from '@/views/share/hook'
5 5
@@ -88,7 +88,7 @@ export const getOrganizationList = (params?: OrganizationListItem) => @@ -88,7 +88,7 @@ export const getOrganizationList = (params?: OrganizationListItem) =>
88 88
89 //获取视频列表 89 //获取视频列表
90 export const getVideoList = (params?: object) => 90 export const getVideoList = (params?: object) =>
91 - defHttp.get({ 91 + defHttp.get<{data: CameraRecord[]}>({
92 url: Api.VIDEO, 92 url: Api.VIDEO,
93 params 93 params
94 }) 94 })
@@ -54,3 +54,39 @@ export interface ProductAndDevice { @@ -54,3 +54,39 @@ export interface ProductAndDevice {
54 deviceType: string 54 deviceType: string
55 transportType: string 55 transportType: string
56 } 56 }
  57 +
  58 +
  59 +export interface CameraRecord {
  60 + id: string;
  61 + creator: string;
  62 + createTime: string;
  63 + updater: string;
  64 + updateTime: string;
  65 + name: string;
  66 + enabled: boolean;
  67 + tenantId: string;
  68 + videoUrl: string;
  69 + brand: string;
  70 + sn: string;
  71 + organizationId: string;
  72 + organizationName: string;
  73 + status: boolean;
  74 + accessMode: number;
  75 + playProtocol: number;
  76 + deviceId?: string;
  77 + channelId?: string;
  78 + params?: {
  79 + channelNo: string;
  80 + deviceId: string;
  81 + };
  82 + videoPlatformDTO?: VideoPlatformDTO;
  83 +}
  84 +
  85 +interface VideoPlatformDTO {
  86 + enabled: boolean;
  87 + type: number;
  88 + host: string;
  89 + appKey: string;
  90 + appSecret: string;
  91 + ssl: number;
  92 +}
  1 +export {default as XGPlayer} from './src/index.vue'
  1 +<script setup lang="ts">
  2 +import Player, {Events, IError} from 'xgplayer';
  3 +import {FlvPlugin} from 'xgplayer-flv';
  4 +import Mp4Plugin from 'xgplayer-mp4';
  5 +import {HlsPlugin} from 'xgplayer-hls';
  6 +import {onMounted, shallowRef, computed, unref, toRaw, onUnmounted, ref, watch} from 'vue';
  7 +import PresetPlayer from 'xgplayer';
  8 +import {IPlayerOptions} from 'xgplayer/es/player';
  9 +import 'xgplayer/dist/index.min.css';
  10 +import {StreamType, XGPlayerProps} from './types';
  11 +import {isShareMode} from "@/views/share/hook";
  12 +import {getJwtToken, getShareJwtToken} from "@/utils/external/auth";
  13 +
  14 +const props = withDefaults(defineProps<{
  15 + streamType?: StreamType;
  16 + autoPlay?: boolean;
  17 + url?: string;
  18 + withToken?: boolean;
  19 + config?: Omit<IPlayerOptions, 'url'>;
  20 +}>(), {
  21 + streamType: 'auto',
  22 + autoPlay: true,
  23 + config: () => ({}),
  24 +});
  25 +
  26 +const emits = defineEmits<{
  27 + (eventName: 'ready', player: PresetPlayer): void;
  28 + (eventName: 'onUnmounted', player: PresetPlayer): void;
  29 +}>();
  30 +
  31 +function getStreamTypeByUrl(url = ''): StreamType | undefined {
  32 + if (url.endsWith('.m3u8')) return 'hls';
  33 + else if (url.endsWith('.mp4')) return 'mp4';
  34 + else if (url.endsWith('.flv')) {
  35 + return 'flv';
  36 + } else return;
  37 +}
  38 +
  39 +const getPluginByStreamType = (): IPlayerOptions => {
  40 + let {url, withToken} = props;
  41 + let {streamType} = props;
  42 + streamType = streamType === 'auto' ? getStreamTypeByUrl(url)! : streamType;
  43 +
  44 + const liveConfig = {
  45 + targetLatency: 10,
  46 + maxLatency: 20,
  47 + disconnectTime: 0,
  48 + fetchOptions: withToken
  49 + ? {
  50 + headers: {
  51 + 'X-Authorization': `Bearer ${isShareMode() ? getShareJwtToken() : getJwtToken()}`,
  52 + },
  53 + }
  54 + : {},
  55 + };
  56 + const config: IPlayerOptions = {
  57 + flv: liveConfig,
  58 + hls: liveConfig,
  59 + };
  60 + switch (streamType) {
  61 + case 'hls':
  62 + config.plugins = [HlsPlugin];
  63 + break;
  64 + case 'mp4':
  65 + config.plugins = [Mp4Plugin];
  66 + break;
  67 + case 'flv':
  68 + config.plugins = [FlvPlugin];
  69 + break;
  70 + }
  71 + return config;
  72 +};
  73 +
  74 +const videoElRef = shallowRef<Nullable<HTMLDivElement>>();
  75 +
  76 +const playerRef = shallowRef<Nullable<PresetPlayer>>();
  77 +
  78 +const propsRef = ref<XGPlayerProps>({});
  79 +
  80 +const getPlayerConfig = computed<IPlayerOptions>(() => {
  81 + const {url, autoPlay, config} = props;
  82 +
  83 + const basicConfig: IPlayerOptions = {
  84 + ...config,
  85 + ...propsRef,
  86 + url,
  87 + lang: 'zh',
  88 + isLive: true,
  89 + autoplay: autoPlay,
  90 + autoplayMuted: autoPlay,
  91 + ...getPluginByStreamType(),
  92 + };
  93 + return basicConfig;
  94 +});
  95 +
  96 +function onDecodeError() {
  97 + console.warn('player happend decode error');
  98 + playerRef.value?.switchURL(props.url!);
  99 +}
  100 +
  101 +function initializePlayer() {
  102 + if (unref(playerRef)) {
  103 + playerRef.value?.destroy?.();
  104 + playerRef.value = null;
  105 + }
  106 +
  107 + const config = toRaw(unref(getPlayerConfig));
  108 +
  109 + if (!unref(videoElRef)) return;
  110 +
  111 + const player = (playerRef.value = new Player(Object.assign(config, {el: unref(videoElRef)})));
  112 +
  113 + player.on(Events.READY, () => {
  114 + emits('ready', player);
  115 + });
  116 +
  117 + player.setEventsMiddleware({
  118 + error: (event, callback) => {
  119 + const code = (
  120 + event as unknown as {
  121 + error: MediaError;
  122 + }
  123 + ).error.code;
  124 + if (code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED) {
  125 + if (!props.url) {
  126 + return;
  127 + }
  128 + callback();
  129 + return;
  130 + }
  131 +
  132 + if (code === MediaError.MEDIA_ERR_DECODE) {
  133 + // 视频流可以播放 中途解码失败重载
  134 + if (playerRef.value?.isPlaying) {
  135 + onDecodeError();
  136 + }
  137 + return;
  138 + }
  139 +
  140 + callback();
  141 + },
  142 + });
  143 +}
  144 +
  145 +onMounted(() => {
  146 + initializePlayer();
  147 +});
  148 +
  149 +onUnmounted(() => {
  150 + emits('onUnmounted', unref(playerRef)!);
  151 + playerRef.value?.destroy?.();
  152 +});
  153 +
  154 +watch(
  155 + () => props.url,
  156 + () => {
  157 + initializePlayer();
  158 + }
  159 +);
  160 +
  161 +defineExpose({
  162 + getPlayerInstance: () => unref(playerRef),
  163 +});
  164 +</script>
  165 +
  166 +<template>
  167 + <div ref="videoElRef"></div>
  168 +</template>
  1 +import { IPlayerOptions } from 'xgplayer/es/player';
  2 +
  3 +export type StreamType = 'flv' | 'mp4' | 'hls' | 'auto';
  4 +export interface XGPlayerProps {
  5 + streamType?: StreamType;
  6 + autoPlay?: boolean;
  7 + url?: string;
  8 + withToken?: boolean;
  9 + config?: Omit<IPlayerOptions, 'url'>;
  10 +}
1 -<template>  
2 - <n-spin size="medium" :show="showLoading"  
3 - :style="{background: '#000',width: baseSize?.w + 'px', height: baseSize?.h + 'px' }">  
4 - <template #description> 视频正在努力加载中...... </template>  
5 - <div>  
6 - <div ref="playerContainerElRef" class="go-content-box"  
7 - :style="{ width: baseSize?.w + 'px', height: baseSize?.h + 'px', background: '#000' }">  
8 -  
9 - </div>  
10 - </div>  
11 - </n-spin>  
12 -</template>  
13 -<script setup lang="ts" name="VideoPlay">  
14 -import { onMounted, ref, onUnmounted, watch, unref, PropType, shallowRef } from 'vue'  
15 -import videojs from 'video.js'  
16 -import 'videojs-flvjs-es6'  
17 -import type { VideoJsPlayerOptions } from 'video.js'  
18 -import 'video.js/dist/video-js.min.css'  
19 -import { getJwtToken, getShareJwtToken } from '@/utils/external/auth'  
20 -import { isShareMode } from '@/views/share/hook'  
21 -import { getOpenFlvPlayUrl, closeFlvPlay, getVideoControlStart } from '@/api/external/flvPlay'  
22 -import { useFingerprint } from '@/utils/external/useFingerprint'  
23 -import { GetResult } from '@fingerprintjs/fingerprintjs'  
24 -import { AccessModeEnum, VideoPlayerType } from '../config'  
25 -import { getVideoUrl } from '@/api/external/common'  
26 -  
27 -const props = defineProps({  
28 - sourceSrc: {  
29 - type: String  
30 - },  
31 - option: {  
32 - type: Object  
33 - },  
34 -  
35 - baseSize: {  
36 - type: Object as PropType<{ w: number; h: number }>  
37 - },  
38 - autoPlay: {  
39 - type: Boolean  
40 - },  
41 - name: {  
42 - type: String  
43 - },  
44 - avatar: {  
45 - type: String  
46 - },  
47 - w: {  
48 - type: Number,  
49 - default: 300  
50 - },  
51 - h: {  
52 - type: Number,  
53 - default: 300  
54 - },  
55 - index: {  
56 - type: Number  
57 - }  
58 -})  
59 -  
60 -  
61 -const { getResult } = useFingerprint()  
62 -  
63 -const showLoading = ref<boolean>(false)  
64 -  
65 -const sourceSrc = ref<string | null>(props.sourceSrc || '')  
66 -  
67 -  
68 -// video实例对象  
69 -const videoPlayer = shallowRef<videojs.Player | null>(null)  
70 -const playerContainerElRef = shallowRef<HTMLElement | null>(null)  
71 -  
72 -const fingerprintResult = ref<Nullable<GetResult>>(null)  
73 -  
74 -const isRtspProtocol = (url: string) => {  
75 - const reg = /^rtsp:\/\//g  
76 - return reg.test(url)  
77 -}  
78 -  
79 -const getVideoTypeByUrl = (url = '') => {  
80 - if (!url) return;  
81 - try {  
82 - const { protocol, pathname } = new URL(url)  
83 - if (protocol.startsWith('rtsp:')) return VideoPlayerType.flv  
84 - const reg = /[^.]\w*$/  
85 - const mathValue = pathname.match(reg) || []  
86 - const ext = (mathValue[0] as keyof typeof VideoPlayerType) || 'webm'  
87 - const type = VideoPlayerType[ext]  
88 - return type ? type : VideoPlayerType.webm  
89 - } catch (error) {  
90 - console.error(error)  
91 - return VideoPlayerType.webm  
92 - }  
93 -}  
94 -  
95 -  
96 -//options配置  
97 -const options: VideoJsPlayerOptions & Recordable = {  
98 - language: 'zh-CN', // 设置语言  
99 - controls: true, // 是否显示控制条  
100 - preload: 'auto', // 预加载  
101 - autoplay: props.autoPlay ? true : false, // 是否自动播放  
102 - fluid: false, // 自适应宽高  
103 - poster: props?.avatar || '',  
104 - sources: [],  
105 - muted: props.autoPlay ? true : false,  
106 - userActions: {  
107 - hotkeys: true  
108 - },  
109 - techOrder: ['html5', 'flvjs'],  
110 - flvjs: {  
111 - mediaDataSource: {  
112 - isLive: true,  
113 - cors: true,  
114 - withCredentials: false,  
115 - hasAudio: false  
116 - },  
117 - config: {  
118 - autoCleanupSourceBuffer: true  
119 - }  
120 - }  
121 -}  
122 -  
123 -  
124 -async function getSource() {  
125 - fingerprintResult.value = await getResult()  
126 - let src = unref(sourceSrc) || ''  
127 - if (isRtspProtocol(unref(sourceSrc)!)) {  
128 - src = getOpenFlvPlayUrl(src, unref(fingerprintResult)?.visitorId || '')  
129 - }  
130 - return [  
131 - {  
132 - type: getVideoTypeByUrl(src),  
133 - src  
134 - }  
135 - ]  
136 -}  
137 -  
138 -//针对萤石云或者海康威视,根据视频id获取播放流地址  
139 -const getVideoUrlById = async (id: string) => {  
140 - const res = await getVideoUrl(id)  
141 - if (!res) return  
142 - const { url } = res.data  
143 - return url  
144 -}  
145 -  
146 -//针对gbt28181,根据设备id和通道号获取播放流地址  
147 -const getVideoControlList = async (deviceId: string, channelId: string) => {  
148 - const {  
149 - data: { flv }  
150 - } = await getVideoControlStart({  
151 - deviceId,  
152 - channelId  
153 - })  
154 - return flv  
155 -}  
156 -  
157 -//针对自定义地址,直接获取地址  
158 -const getCustomUrl = (url: string) => {  
159 - return url  
160 -}  
161 -  
162 -  
163 -function createVideoElement() {  
164 - if (!unref(playerContainerElRef)) return  
165 - unref(playerContainerElRef)!.innerHTML = ''  
166 - const video = document.createElement('video')  
167 - video.setAttribute('crossOrigin', 'anonymous')  
168 - video.style.setProperty('width', '100%')  
169 - video.style.setProperty('height', '100%')  
170 - video.classList.add('video-js', 'my-video', 'vjs-theme-city', 'vjs-big-play-centered')  
171 - unref(playerContainerElRef)?.appendChild(video)  
172 - return video  
173 -}  
174 -  
175 -  
176 -// 初始化videojs  
177 -const createVideoPlayer = async () => {  
178 - if (unref(videoPlayer)) dispose()  
179 -  
180 - options.sources = await getSource()  
181 -  
182 - if (isRtspProtocol(unref(sourceSrc) || '')) {  
183 - options.flvjs = {  
184 - ...(options.flvjs || {}),  
185 - config: {  
186 - headers: {  
187 - 'X-Authorization': `Bearer ${isShareMode() ? getShareJwtToken() : getJwtToken()}`  
188 - }  
189 - }  
190 - }  
191 - }  
192 - const video = createVideoElement()  
193 - if (!video) return  
194 -  
195 - videoPlayer.value = videojs(video!, options) //fix 修复videojs解决直播延时的问题  
196 -  
197 - videoPlayer.value?.on('timeupdate', function () {  
198 - // 计算表最新推流的时间和现在播放器播放推流的时间  
199 - let differTime =  
200 - (unref(videoPlayer))!.buffered()?.end(0) -  
201 - (unref(videoPlayer))!.currentTime() // 差值小于1.5s时根据1倍速进行播放  
202 - if (differTime < 1.5) {  
203 - videoPlayer.value?.playbackRate(1)  
204 - } // 差值大于1.5s小于10s根据1.2倍速进行播放  
205 - if (differTime < 10 && differTime > 1.5) {  
206 - videoPlayer.value?.playbackRate(1.2)  
207 - } // 差值大于10s时进行重新加载直播流  
208 - if (differTime > 10) {  
209 - createVideoPlayer()  
210 - }  
211 - })  
212 -}  
213 -  
214 -const getVideosUrl = async () => {  
215 - try {  
216 - showLoading.value = true  
217 - videoPlayer.value?.src('');  
218 - const { option } = props || {}  
219 - const { accessMode, id, channelId, deviceId, customUrl } = option || {}  
220 - if (accessMode === AccessModeEnum.Streaming) {  
221 - return await getVideoUrlById(id)  
222 - } else if (accessMode === AccessModeEnum.GBT28181) {  
223 - return await getVideoControlList(deviceId, channelId)  
224 - } else {  
225 - return await getCustomUrl(customUrl)  
226 - }  
227 - } finally {  
228 - showLoading.value = false  
229 - }  
230 -}  
231 -  
232 -  
233 -watch(() => props.option, async () => {  
234 - console.log(props, 'prop')  
235 - dispose()  
236 - sourceSrc.value = await getVideosUrl()  
237 - createVideoPlayer()  
238 -})  
239 -  
240 -  
241 -  
242 -// 销毁  
243 -function dispose() {  
244 - unref(videoPlayer)?.dispose()  
245 - videoPlayer.value = null  
246 -}  
247 -  
248 -watch(  
249 - () => props.autoPlay,  
250 - async (newData: boolean) => {  
251 - if (newData) {  
252 - handleVideoPlay()  
253 - } else {  
254 - videoPlayer.value?.pause()  
255 - }  
256 - }  
257 -)  
258 -  
259 -onMounted(async () => {  
260 - dispose()  
261 - sourceSrc.value = await getVideosUrl()  
262 - createVideoPlayer()  
263 -})  
264 -  
265 -onUnmounted(() => {  
266 - if (props.sourceSrc) {  
267 - closeFlvPlay(props.sourceSrc, unref(fingerprintResult)!.visitorId!)  
268 - }  
269 - handleVideoDispose()  
270 -})  
271 -  
272 -//播放  
273 -const handleVideoPlay = () => videoPlayer.value?.play()  
274 -  
275 -//暂停和销毁  
276 -const handleVideoDispose = () => videoPlayer.value?.dispose() && videoPlayer.value?.pause()  
277 -  
278 -</script>  
279 -  
280 -<style lang="scss" scoped>  
281 -  
282 -.go-content-box {  
283 - display: flex;  
284 - align-items: center;  
285 - justify-content: center;  
286 -  
287 - .my-video {  
288 - width: 100% !important;  
289 - height: 100% !important;  
290 - position: relative;  
291 - }  
292 -}  
293 -</style>  
294 -<style>  
295 -.vjs-poster {  
296 - background-size: 100% !important;  
297 -}  
298 -</style>  
  1 +<script lang="ts" setup>
  2 +import {XGPlayer} from '@/components/Video'
  3 +import {watch, ref} from "vue";
  4 +import {StreamType} from "@/components/Video/src/types";
  5 +import {getPlayUrl} from "@/packages/components/external/Informations/Mores/SingleCamera/config";
  6 +import {CameraRecord} from "@/api/external/common/model";
  7 +
  8 +;
  9 +
  10 +const props = defineProps<{
  11 + dataset?: any
  12 + width: number
  13 + height: number
  14 +}>()
  15 +
  16 +const loading = ref(false)
  17 +const playUrl = ref<string>()
  18 +const playType = ref<StreamType>()
  19 +
  20 +interface Dataset {
  21 + accessMode: number
  22 + autoPlay: boolean
  23 + channelId?: string
  24 + customUrl?: string
  25 + deviceId?: string
  26 + id?: string
  27 + playProtocol?: number
  28 +}
  29 +
  30 +const handleGetPlayUrl = async (dataset: Dataset) => {
  31 + try {
  32 + loading.value = true
  33 + const {id, channelId, deviceId, accessMode, customUrl, playProtocol} = dataset
  34 + if (!accessMode) return
  35 + const {type, url} = await getPlayUrl({
  36 + id: id,
  37 + accessMode: accessMode,
  38 + playProtocol: playProtocol,
  39 + videoUrl: customUrl,
  40 + params: {
  41 + deviceId: deviceId,
  42 + channelNo: channelId,
  43 + },
  44 + } as unknown as CameraRecord)
  45 + playUrl.value = url
  46 + playType.value = type
  47 + } finally {
  48 + loading.value = false
  49 + }
  50 +}
  51 +
  52 +watch(
  53 + () => props.dataset,
  54 + (newData: Dataset) => {
  55 + handleGetPlayUrl(newData)
  56 + },
  57 + {
  58 + immediate: true
  59 + }
  60 +)
  61 +</script>
  62 +
  63 +<template>
  64 + <NSpin :show="loading" class="player-spin">
  65 + <XGPlayer
  66 + :url="playUrl"
  67 + :stream-type="playType"
  68 + :auto-play="dataset.autoPlay"
  69 + :config="{width: width, height: height, poster: dataset.avatar}"
  70 + />
  71 + </NSpin>
  72 +</template>
  73 +
  74 +<style lang="scss" scoped>
  75 +.player-spin {
  76 + width: 100%;
  77 + height: 100%;
  78 + @include deep() {
  79 + .n-spin-content {
  80 + width: 100%;
  81 + height: 100%;
  82 + }
  83 + }
  84 +}
  85 +
  86 +</style>
1 -import VideoPlay from './VideoPlay.vue'  
2 -  
3 -export { VideoPlay as default }  
@@ -53,6 +53,7 @@ export interface videoList { @@ -53,6 +53,7 @@ export interface videoList {
53 deviceId: string 53 deviceId: string
54 customUrl: string 54 customUrl: string
55 params: GBT28181Params 55 params: GBT28181Params
  56 + playProtocol?: number
56 } 57 }
57 58
58 export const option = { 59 export const option = {
@@ -120,15 +120,16 @@ const handleUpdateTreeValue = (e: string) => { @@ -120,15 +120,16 @@ const handleUpdateTreeValue = (e: string) => {
120 const getVideoLists = async (organizationId: string) => { 120 const getVideoLists = async (organizationId: string) => {
121 const res = await getVideoList({ organizationId }) 121 const res = await getVideoList({ organizationId })
122 if (!res) return 122 if (!res) return
123 - videoOptions.value = res?.data?.map((item: videoList) => ({ 123 + videoOptions.value = res?.data?.map((item) => ({
124 label: item.name, 124 label: item.name,
125 value: item.id, 125 value: item.id,
126 id: item.id, 126 id: item.id,
127 accessMode: item.accessMode, 127 accessMode: item.accessMode,
128 customUrl: item.accessMode === AccessModeEnum.ManuallyEnter ? item.videoUrl: '', //参数只给自定义视频流使用 128 customUrl: item.accessMode === AccessModeEnum.ManuallyEnter ? item.videoUrl: '', //参数只给自定义视频流使用
129 channelId: item.accessMode === AccessModeEnum.GBT28181 ? item?.params?.channelNo : '', //参数只给gbt28181使用 129 channelId: item.accessMode === AccessModeEnum.GBT28181 ? item?.params?.channelNo : '', //参数只给gbt28181使用
130 - deviceId: item.accessMode === AccessModeEnum.GBT28181 ? item?.params?.deviceId : '' //参数只给gbt28181使用  
131 - })) 130 + deviceId: item.accessMode === AccessModeEnum.GBT28181 ? item?.params?.deviceId : '', //参数只给gbt28181使用
  131 + playProtocol: item?.playProtocol
  132 + } as any))
132 } 133 }
133 134
134 //针对萤石云或者海康威视,根据视频id获取播放流地址 135 //针对萤石云或者海康威视,根据视频id获取播放流地址
@@ -2,17 +2,7 @@ @@ -2,17 +2,7 @@
2 <div @mouseenter="handleMouseenter" @mouseleave="handleMouseleave" class="banner-box" ref="root"> 2 <div @mouseenter="handleMouseenter" @mouseleave="handleMouseleave" class="banner-box" ref="root">
3 <div class="wrapper"> 3 <div class="wrapper">
4 <div v-for="(item, index) in option.dataset" :key="index" :class="item.className" :style="item.sty"> 4 <div v-for="(item, index) in option.dataset" :key="index" :class="item.className" :style="item.sty">
5 - <VideoPlay  
6 - :autoPlay="item.autoPlay"  
7 - :option="item"  
8 - :name="item.name"  
9 - :avatar="item.avatar"  
10 - :key="item + index"  
11 - :sourceSrc="item.url"  
12 - :baseSize="{w,h}"  
13 - :index="index"  
14 - />  
15 - <span class="video-title">{{ item.name }}</span> 5 + <VideoPlayer :key="item + index" :dataset="item" :width="w" :height="h"/>
16 </div> 6 </div>
17 </div> 7 </div>
18 <a v-show="isShowSvg" href="javascript:;" class="left" @click="changeSlide('left')"></a> 8 <a v-show="isShowSvg" href="javascript:;" class="left" @click="changeSlide('left')"></a>
@@ -20,11 +10,10 @@ @@ -20,11 +10,10 @@
20 </div> 10 </div>
21 </template> 11 </template>
22 <script setup lang="ts" name="index"> 12 <script setup lang="ts" name="index">
23 -import { PropType, watch, toRefs, shallowReactive, onMounted, ref } from 'vue'  
24 -import { CreateComponentType } from '@/packages/index.d'  
25 -import 'video.js/dist/video-js.min.css'  
26 -import { option as typeOption } from './config'  
27 -import VideoPlay from './components' 13 +import {PropType, watch, toRefs, shallowReactive, onMounted, ref} from 'vue'
  14 +import {CreateComponentType} from '@/packages/index.d'
  15 +import {option as typeOption} from './config'
  16 +import VideoPlayer from "@/packages/components/external/Informations/Mores/Camera/components/VideoPlayer.vue";
28 17
29 const props = defineProps({ 18 const props = defineProps({
30 chartConfig: { 19 chartConfig: {
@@ -35,9 +24,9 @@ const props = defineProps({ @@ -35,9 +24,9 @@ const props = defineProps({
35 24
36 const isShowSvg = ref(false) 25 const isShowSvg = ref(false)
37 26
38 -const { w, h } = toRefs(props.chartConfig.attr) 27 +const {w, h} = toRefs(props.chartConfig.attr)
39 28
40 -const { autoSwitch, interval } = toRefs(props.chartConfig.option) 29 +const {autoSwitch, interval} = toRefs(props.chartConfig.option)
41 30
42 //暂定 any类型 31 //暂定 any类型
43 const option = shallowReactive<{ ['dataset']: any }>({ 32 const option = shallowReactive<{ ['dataset']: any }>({
@@ -50,15 +39,15 @@ const computedFunc = (initial: number, source: Recordable[]) => { @@ -50,15 +39,15 @@ const computedFunc = (initial: number, source: Recordable[]) => {
50 if (initial < 0) initial = 0 39 if (initial < 0) initial = 0
51 if (Array.isArray(source)) { 40 if (Array.isArray(source)) {
52 let len = source.length, 41 let len = source.length,
53 - temp1 = initial - 2 < 0 ? initial - 2 + len : initial - 2,  
54 - temp2 = initial - 1 < 0 ? initial - 1 + len : initial - 1,  
55 - temp3 = initial,  
56 - temp4 = initial + 1 >= len ? initial + 1 - len : initial + 1,  
57 - temp5 = initial + 2 >= len ? initial + 2 - len : initial + 2 42 + temp1 = initial - 2 < 0 ? initial - 2 + len : initial - 2,
  43 + temp2 = initial - 1 < 0 ? initial - 1 + len : initial - 1,
  44 + temp3 = initial,
  45 + temp4 = initial + 1 >= len ? initial + 1 - len : initial + 1,
  46 + temp5 = initial + 2 >= len ? initial + 2 - len : initial + 2
58 return source?.map((item: Recordable, index: number) => { 47 return source?.map((item: Recordable, index: number) => {
59 let transform = `translateX(-50%) scale(0.7)`, 48 let transform = `translateX(-50%) scale(0.7)`,
60 - zIndex = 0,  
61 - className = 'slide' 49 + zIndex = 0,
  50 + className = 'slide'
62 switch (index) { 51 switch (index) {
63 case temp3: 52 case temp3:
64 transform = `translateX(-50%) scale(1)` 53 transform = `translateX(-50%) scale(1)`
@@ -93,23 +82,23 @@ const computedFunc = (initial: number, source: Recordable[]) => { @@ -93,23 +82,23 @@ const computedFunc = (initial: number, source: Recordable[]) => {
93 } 82 }
94 83
95 watch( 84 watch(
96 - () => props.chartConfig.option.dataset,  
97 - newData => {  
98 - option.dataset = newData  
99 - },  
100 - {  
101 - immediate: true,  
102 - deep: true  
103 - } 85 + () => props.chartConfig.option.dataset,
  86 + newData => {
  87 + option.dataset = newData
  88 + },
  89 + {
  90 + immediate: true,
  91 + deep: true
  92 + }
104 ) 93 )
105 94
106 option.dataset = computedFunc(initial.value, option.dataset) 95 option.dataset = computedFunc(initial.value, option.dataset)
107 96
108 watch( 97 watch(
109 - () => initial.value,  
110 - newV => {  
111 - option.dataset = computedFunc(newV, option.dataset)  
112 - } 98 + () => initial.value,
  99 + newV => {
  100 + option.dataset = computedFunc(newV, option.dataset)
  101 + }
113 ) 102 )
114 103
115 // 处理自动轮播 104 // 处理自动轮播
@@ -155,17 +144,17 @@ function changeSlide(dir: string) { @@ -155,17 +144,17 @@ function changeSlide(dir: string) {
155 } 144 }
156 145
157 watch( 146 watch(
158 - () => autoSwitch.value,  
159 - (newV: boolean) => {  
160 - if (newV) {  
161 - autoPlay()  
162 - } else {  
163 - clearInterval(timer) 147 + () => autoSwitch.value,
  148 + (newV: boolean) => {
  149 + if (newV) {
  150 + autoPlay()
  151 + } else {
  152 + clearInterval(timer)
  153 + }
  154 + },
  155 + {
  156 + immediate: true
164 } 157 }
165 - },  
166 - {  
167 - immediate: true  
168 - }  
169 ) 158 )
170 159
171 const handleMouseenter = () => { 160 const handleMouseenter = () => {
@@ -181,6 +170,7 @@ const handleMouseleave = () => (isShowSvg.value = false) @@ -181,6 +170,7 @@ const handleMouseleave = () => (isShowSvg.value = false)
181 height: 100%; 170 height: 100%;
182 display: flex; 171 display: flex;
183 overflow: hidden; 172 overflow: hidden;
  173 +
184 .slide { 174 .slide {
185 width: 20%; 175 width: 20%;
186 height: 100%; 176 height: 100%;
@@ -200,6 +190,7 @@ const handleMouseleave = () => (isShowSvg.value = false) @@ -200,6 +190,7 @@ const handleMouseleave = () => (isShowSvg.value = false)
200 } 190 }
201 } 191 }
202 } 192 }
  193 +
203 .arrow { 194 .arrow {
204 position: absolute; 195 position: absolute;
205 top: 50%; 196 top: 50%;
@@ -211,11 +202,13 @@ const handleMouseleave = () => (isShowSvg.value = false) @@ -211,11 +202,13 @@ const handleMouseleave = () => (isShowSvg.value = false)
211 background-color: white; 202 background-color: white;
212 opacity: 0.5; 203 opacity: 0.5;
213 } 204 }
  205 +
214 a.left { 206 a.left {
215 @extend .arrow; 207 @extend .arrow;
216 background-image: url('./static/left.svg'); 208 background-image: url('./static/left.svg');
217 left: 0px; 209 left: 0px;
218 } 210 }
  211 +
219 a.right { 212 a.right {
220 @extend .arrow; 213 @extend .arrow;
221 background-image: url('./static/right.svg'); 214 background-image: url('./static/right.svg');
1 -<template>  
2 - <div ref="playerContainerElRef" class="go-content-box"  
3 - :style="{ width: baseSize?.w + 'px', height: baseSize?.h + 'px', background: '#000' }">  
4 -  
5 - </div>  
6 -</template>  
7 -<script setup lang="ts">  
8 -import { ref, onUnmounted, watch, unref, PropType, shallowRef } from 'vue'  
9 -import videojs from 'video.js'  
10 -import 'videojs-flvjs-es6'  
11 -import type { VideoJsPlayerOptions } from 'video.js'  
12 -import 'video.js/dist/video-js.min.css'  
13 -import { getJwtToken, getShareJwtToken } from '@/utils/external/auth'  
14 -import { isShareMode } from '@/views/share/hook'  
15 -import { getOpenFlvPlayUrl, closeFlvPlay } from '@/api/external/flvPlay'  
16 -import { useFingerprint } from '@/utils/external/useFingerprint'  
17 -import { GetResult } from '@fingerprintjs/fingerprintjs'  
18 -import { VideoPlayerTypeEnum } from '../config'  
19 -  
20 -const props = defineProps({  
21 - sourceSrc: {  
22 - type: String  
23 - },  
24 - autoPlay: {  
25 - type: Boolean  
26 - },  
27 - name: {  
28 - type: String  
29 - },  
30 - avatar: {  
31 - type: String  
32 - },  
33 - baseSize: {  
34 - type: Object as PropType<{ w: number; h: number }>  
35 - }  
36 -})  
37 -  
38 -const isRtspProtocol = (url: string) => {  
39 - const reg = /^rtsp:\/\//g  
40 - return reg.test(url)  
41 -}  
42 -  
43 -const getVideoTypeByUrl = (url = '') => {  
44 - if (!url) return;  
45 - try {  
46 - const { protocol, pathname } = new URL(url)  
47 - if (protocol.startsWith('rtsp:')) return VideoPlayerTypeEnum.flv  
48 - const reg = /[^.]\w*$/  
49 - const mathValue = pathname.match(reg) || []  
50 - const ext = (mathValue[0] as keyof typeof VideoPlayerTypeEnum) || 'webm'  
51 - const type = VideoPlayerTypeEnum[ext]  
52 - return type ? type : VideoPlayerTypeEnum.webm  
53 - } catch (error) {  
54 - console.error(error)  
55 - return VideoPlayerTypeEnum.webm  
56 - }  
57 -}  
58 -  
59 -// video实例对象  
60 -const videoPlayer = shallowRef<videojs.Player | null>(null)  
61 -const playerContainerElRef = shallowRef<HTMLElement | null>(null)  
62 -  
63 -const fingerprintResult = ref<Nullable<GetResult>>(null)  
64 -  
65 -//options配置  
66 -const options: VideoJsPlayerOptions & Recordable = {  
67 - language: 'zh-CN', // 设置语言  
68 - controls: true, // 是否显示控制条  
69 - autoplay: !!props.autoPlay, // 是否自动播放  
70 - fluid: false, // 自适应宽高  
71 - poster: props?.avatar || '',  
72 - sources: [],  
73 - muted: !!props.autoPlay,  
74 - userActions: {  
75 - hotkeys: true  
76 - },  
77 - techOrder: ['html5', 'flvjs'],  
78 - flvjs: {  
79 - mediaDataSource: {  
80 - isLive: true,  
81 - cors: true,  
82 - withCredentials: false,  
83 - hasAudio: false  
84 - },  
85 - config: {  
86 - autoCleanupSourceBuffer: true  
87 - }  
88 - }  
89 -}  
90 -  
91 -const { getResult } = useFingerprint()  
92 -  
93 -async function getSource() {  
94 - fingerprintResult.value = await getResult()  
95 - let src = props.sourceSrc || ''  
96 - if (isRtspProtocol(props.sourceSrc!)) {  
97 - src = getOpenFlvPlayUrl(src, unref(fingerprintResult)?.visitorId || '')  
98 - }  
99 - return [  
100 - {  
101 - type: getVideoTypeByUrl(props.sourceSrc),  
102 - src  
103 - }  
104 - ]  
105 -}  
106 -  
107 -function createVideoElement() {  
108 - if (!unref(playerContainerElRef)) return  
109 - unref(playerContainerElRef)!.innerHTML = ''  
110 - const video = document.createElement('video')  
111 - video.setAttribute('crossOrigin', 'anonymous')  
112 - video.style.setProperty('width', '100%')  
113 - video.style.setProperty('height', '100%')  
114 - video.classList.add('video-js', 'my-video', 'vjs-theme-city', 'vjs-big-play-centered')  
115 - unref(playerContainerElRef)?.appendChild(video)  
116 - return video  
117 -}  
118 -  
119 -  
120 -// 初始化videojs  
121 -const createVideoPlayer = async () => {  
122 - if (unref(videoPlayer)) dispose()  
123 -  
124 - options.sources = await getSource()  
125 -  
126 - if (isRtspProtocol(props.sourceSrc || '')) {  
127 - options.flvjs = {  
128 - ...(options.flvjs || {}),  
129 - config: {  
130 - headers: {  
131 - 'X-Authorization': `Bearer ${isShareMode() ? getShareJwtToken() : getJwtToken()}`  
132 - }  
133 - }  
134 - }  
135 - }  
136 - const video = createVideoElement()  
137 - if (!video) return  
138 -  
139 - videoPlayer.value = videojs(video!, options) //fix 修复videojs解决直播延时的问题  
140 -  
141 - videoPlayer.value?.on('timeupdate', function () {  
142 - // 计算表最新推流的时间和现在播放器播放推流的时间  
143 - let differTime =  
144 - (unref(videoPlayer))!.buffered()?.end(0) -  
145 - (unref(videoPlayer))!.currentTime() // 差值小于1.5s时根据1倍速进行播放  
146 - if (differTime < 1.5) {  
147 - videoPlayer.value?.playbackRate(1)  
148 - } // 差值大于1.5s小于10s根据1.2倍速进行播放  
149 - if (differTime < 10 && differTime > 1.5) {  
150 - videoPlayer.value?.playbackRate(1.2)  
151 - } // 差值大于10s时进行重新加载直播流  
152 - if (differTime > 10) {  
153 - createVideoPlayer()  
154 - }  
155 - })  
156 -}  
157 -  
158 -// watch(  
159 -// () => props.sourceSrc,  
160 -// async () => {  
161 -// (props as any ).sourceSrc = ''  
162 -// if(!props.sourceSrc) return;  
163 -// await nextTick();  
164 -// if(unref(fingerprintResult)!.visitorId!) {  
165 -// closeFlvPlay(props.sourceSrc!, unref(fingerprintResult)!.visitorId!)  
166 -// }  
167 -// videoPlayer.value?.src('');  
168 -// createVideoPlayer()  
169 -  
170 -// if (props.sourceSrc) {  
171 -// reloadSource({ src: props.sourceSrc, type: getVideoTypeByUrl(props.sourceSrc) })  
172 -// }  
173 -// }  
174 -// )  
175 -  
176 -watch(  
177 - () => props.autoPlay,  
178 - async (newData: boolean) => {  
179 - if (newData) {  
180 - handleVideoPlay()  
181 - } else {  
182 - videoPlayer.value?.pause()  
183 - }  
184 - },  
185 -)  
186 -  
187 -// onMounted(() => {  
188 -// createVideoPlayer()  
189 -// })  
190 -  
191 -onUnmounted(() => {  
192 - if (props.sourceSrc) {  
193 - closeFlvPlay(props.sourceSrc, unref(fingerprintResult)!.visitorId!)  
194 - }  
195 - handleVideoDispose()  
196 -})  
197 -  
198 -  
199 -function anewInit() {  
200 - createVideoPlayer()  
201 -}  
202 -  
203 -function reloadSource(sources: string | videojs.Tech.SourceObject | videojs.Tech.SourceObject[]) {  
204 - if (!unref(videoPlayer)) return  
205 - unref(videoPlayer)?.pause()  
206 - unref(videoPlayer)?.src(sources)  
207 - unref(videoPlayer)?.load()  
208 - unref(videoPlayer)?.play()  
209 -}  
210 -  
211 -function dispose() {  
212 - unref(videoPlayer)?.dispose()  
213 - videoPlayer.value = null  
214 -}  
215 -  
216 -//播放  
217 -const handleVideoPlay = () => videoPlayer.value?.play()  
218 -  
219 -//暂停和销毁  
220 -const handleVideoDispose = () => videoPlayer.value?.dispose()  
221 -  
222 -defineExpose({  
223 - dispose,  
224 - reloadSource,  
225 - anewInit,  
226 -})  
227 -  
228 -</script>  
229 -  
230 -<style lang="scss" scoped>  
231 -.go-content-box {  
232 - display: flex;  
233 - align-items: center;  
234 - justify-content: center;  
235 -  
236 - .my-video {  
237 - width: 100% !important;  
238 - height: 100% !important;  
239 - position: relative;  
240 - }  
241 -}  
242 -</style>  
243 -<style>  
244 -.vjs-poster {  
245 - background-size: 100% !important;  
246 -}  
247 -</style>  
1 -import VideoPlay from './VideoPlay.vue'  
2 -  
3 -export { VideoPlay }  
@@ -2,6 +2,11 @@ import { PublicConfigClass } from '@/packages/public' @@ -2,6 +2,11 @@ import { PublicConfigClass } from '@/packages/public'
2 import { CreateComponentType } from '@/packages/index.d' 2 import { CreateComponentType } from '@/packages/index.d'
3 import { SingleCameraConfig } from './index' 3 import { SingleCameraConfig } from './index'
4 import cloneDeep from 'lodash/cloneDeep' 4 import cloneDeep from 'lodash/cloneDeep'
  5 +import {StreamType} from "@/components/Video/src/types";
  6 +import {useFingerprint} from "@/utils/external/useFingerprint";
  7 +import {getOpenFlvPlayUrl, getVideoControlStart} from "@/api/external/flvPlay";
  8 +import {getVideoUrl} from "@/api/external/common";
  9 +import {CameraRecord} from "@/api/external/common/model";
5 10
6 export enum sourceTypeEnum { 11 export enum sourceTypeEnum {
7 CUSTOM = 'custom', 12 CUSTOM = 'custom',
@@ -13,6 +18,11 @@ export enum sourceTypeNameEnum { @@ -13,6 +18,11 @@ export enum sourceTypeNameEnum {
13 PLATFORM = '平台获取' 18 PLATFORM = '平台获取'
14 } 19 }
15 20
  21 +export enum FluoriteMideaProtocolEnum {
  22 + HLS = 2,
  23 + FLV = 4,
  24 +}
  25 +
16 export enum VideoPlayerTypeEnum { 26 export enum VideoPlayerTypeEnum {
17 m3u8 = 'application/x-mpegURL', 27 m3u8 = 'application/x-mpegURL',
18 mp4 = 'video/mp4', 28 mp4 = 'video/mp4',
@@ -32,6 +42,7 @@ export interface videoListInterface { @@ -32,6 +42,7 @@ export interface videoListInterface {
32 deviceId: string 42 deviceId: string
33 customUrl: string 43 customUrl: string
34 params: GBT28181Params 44 params: GBT28181Params
  45 + playProtocol?: number
35 } 46 }
36 47
37 export interface GBT28181Params { 48 export interface GBT28181Params {
@@ -53,6 +64,7 @@ export interface Dataset { @@ -53,6 +64,7 @@ export interface Dataset {
53 deviceId?: string 64 deviceId?: string
54 id?: string 65 id?: string
55 value?: string 66 value?: string
  67 + playProtocol?: number
56 } 68 }
57 69
58 export const option = { 70 export const option = {
@@ -70,3 +82,42 @@ export default class Config extends PublicConfigClass implements CreateComponent @@ -70,3 +82,42 @@ export default class Config extends PublicConfigClass implements CreateComponent
70 public chartConfig = cloneDeep(SingleCameraConfig) 82 public chartConfig = cloneDeep(SingleCameraConfig)
71 public option = cloneDeep(option) 83 public option = cloneDeep(option)
72 } 84 }
  85 +
  86 +export const isRtspProtocol = (url: string) => {
  87 + const reg = /^rtsp:\/\//g;
  88 + return reg.test(url);
  89 +};
  90 +
  91 +export async function getPlayUrl(
  92 + params: CameraRecord
  93 +): Promise<{ url: string; type: StreamType }> {
  94 + const { accessMode } = params;
  95 + if (accessMode === AccessMode.ManuallyEnter) {
  96 + const { videoUrl } = params;
  97 + if (params.videoUrl) {
  98 + const isRTSPPlay = isRtspProtocol(videoUrl);
  99 +
  100 + if (isRTSPPlay) {
  101 + const { getResult } = useFingerprint();
  102 + const fingerprint = await getResult();
  103 + return { url: getOpenFlvPlayUrl(videoUrl, fingerprint.visitorId), type: 'flv' };
  104 + } else {
  105 + return { url: videoUrl, type: 'auto' };
  106 + }
  107 + }
  108 + } else if (accessMode === AccessMode.GBT28181) {
  109 + const { deviceId, channelNo } = params?.params || {};
  110 + const result = await getVideoControlStart({ channelId: channelNo!, deviceId: deviceId! });
  111 + return { url: result.data.flv, type: 'flv' };
  112 + } else {
  113 + const { id, playProtocol } = params;
  114 + const result = await getVideoUrl(id);
  115 + const type: StreamType =
  116 + playProtocol === FluoriteMideaProtocolEnum.FLV
  117 + ? 'flv'
  118 + : playProtocol === FluoriteMideaProtocolEnum.HLS
  119 + ? 'hls'
  120 + : 'auto';
  121 + return { url: result.data.url, type };
  122 + }
  123 +}
@@ -97,7 +97,7 @@ const handleUpdateTreeValue = (value: string) => { @@ -97,7 +97,7 @@ const handleUpdateTreeValue = (value: string) => {
97 const getVideoLists = async (organizationId: string) => { 97 const getVideoLists = async (organizationId: string) => {
98 const res = await getVideoList({ organizationId }) 98 const res = await getVideoList({ organizationId })
99 if (!res) return 99 if (!res) return
100 - videoOptions.value = res?.data?.map((item: videoListInterface) => { 100 + videoOptions.value = res?.data?.map((item) => {
101 return { 101 return {
102 label: item.name, 102 label: item.name,
103 value: item.id, 103 value: item.id,
@@ -105,8 +105,9 @@ const getVideoLists = async (organizationId: string) => { @@ -105,8 +105,9 @@ const getVideoLists = async (organizationId: string) => {
105 id: item.id, 105 id: item.id,
106 accessMode: item.accessMode, 106 accessMode: item.accessMode,
107 channelId: item.accessMode === AccessMode.GBT28181 ? item?.params?.channelNo : '', //参数只给gbt28181使用 107 channelId: item.accessMode === AccessMode.GBT28181 ? item?.params?.channelNo : '', //参数只给gbt28181使用
108 - deviceId: item.accessMode === AccessMode.GBT28181 ? item?.params?.deviceId : '' //参数只给gbt28181使用  
109 - } 108 + deviceId: item.accessMode === AccessMode.GBT28181 ? item?.params?.deviceId : '', //参数只给gbt28181使用
  109 + playProtocol: item?.playProtocol
  110 + } as any
110 }) 111 })
111 } 112 }
112 113
1 <template> 1 <template>
2 <div> 2 <div>
3 - <n-spin size="medium" :show="showLoading" :content-style="{ background: 'red' }">  
4 - <template #description> 视频正在努力加载中...... </template>  
5 - <div>  
6 - <VideoPlay ref="videoPlayerRef" :baseSize="{ w, h }" :sourceSrc="sourceUrl" :autoPlay="option.autoplay"  
7 - :avatar="option.poster" />  
8 - </div> 3 + <n-spin size="medium" :show="showLoading" class="player-spin">
  4 + <template #description> 视频正在努力加载中......</template>
  5 + <XGPlayer :url="sourceUrl" :stream-type="playType"
  6 + :config="{width: '100%', height: '100%', poster: option.poster}"
  7 + :auto-play="option.autoplay"/>
9 </n-spin> 8 </n-spin>
10 </div> 9 </div>
11 </template> 10 </template>
12 <script setup lang="ts"> 11 <script setup lang="ts">
13 -import { PropType, toRefs, shallowReactive, watch, ref } from 'vue'  
14 -import { CreateComponentType } from '@/packages/index.d'  
15 -import { AccessMode, Dataset, option as configOption, sourceTypeEnum } from './config'  
16 -import { VideoPlay } from './components'  
17 -import { getVideoControlStart } from '@/api/external/flvPlay'  
18 -import { getVideoUrl } from '@/api/external/common' 12 +import {PropType, toRefs, shallowReactive, watch, ref} from 'vue'
  13 +import {CreateComponentType} from '@/packages/index.d'
  14 +import {Dataset, getPlayUrl, option as configOption} from './config'
  15 +import {XGPlayer} from '@/components/Video'
  16 +import {StreamType} from "@/components/Video/src/types";
  17 +import {CameraRecord} from "@/api/external/common/model";
19 18
20 const props = defineProps({ 19 const props = defineProps({
21 chartConfig: { 20 chartConfig: {
@@ -26,9 +25,9 @@ const props = defineProps({ @@ -26,9 +25,9 @@ const props = defineProps({
26 25
27 const showLoading = ref(false) 26 const showLoading = ref(false)
28 27
29 -const { w, h } = toRefs(props.chartConfig.attr) 28 +const {w, h} = toRefs(props.chartConfig.attr)
30 29
31 -const { autoplay, dataset, poster, customVideoUrl } = toRefs(props.chartConfig.option as typeof configOption) 30 +const {autoplay, dataset, poster, customVideoUrl} = toRefs(props.chartConfig.option as typeof configOption)
32 31
33 const option = shallowReactive({ 32 const option = shallowReactive({
34 dataset: configOption.dataset, 33 dataset: configOption.dataset,
@@ -37,87 +36,52 @@ const option = shallowReactive({ @@ -37,87 +36,52 @@ const option = shallowReactive({
37 }) 36 })
38 37
39 const sourceUrl = ref('') 38 const sourceUrl = ref('')
40 -const videoPlayerRef = ref<InstanceType<typeof VideoPlay> | null>()  
41 -  
42 -// 针对萤石云或者海康威视,根据视频id获取播放流地址  
43 -const getVideoUrlById = async (id: string) => {  
44 - const res = await getVideoUrl(id)  
45 - if (!res) return  
46 - const { url } = res.data  
47 - return url  
48 -}  
49 -  
50 -// 针对gbt28181,根据设备id和通道号获取播放流地址  
51 -const getVideoControlList = async (deviceId: string, channelId: string) => {  
52 - const {  
53 - data: { flv }  
54 - } = await getVideoControlStart({  
55 - deviceId,  
56 - channelId  
57 - })  
58 - return flv  
59 -}  
60 -  
61 -// 针对自定义地址,直接获取地址  
62 -const getCustomUrl = (url: string) => {  
63 - return url  
64 -} 39 +const playType = ref<StreamType>()
65 40
66 41
67 async function getPlaySource(params: Dataset) { 42 async function getPlaySource(params: Dataset) {
68 try { 43 try {
69 showLoading.value = true 44 showLoading.value = true
70 -  
71 - const { accessMode, id, deviceId, channelId, customUrl } = params  
72 - if (accessMode === AccessMode.Streaming && id) {  
73 - return await getVideoUrlById(id!)  
74 - } else if (accessMode === AccessMode.GBT28181 && deviceId && channelId) {  
75 - return await getVideoControlList(deviceId!, channelId!)  
76 - } else {  
77 - return getCustomUrl(customUrl!)  
78 - } 45 + const {id, channelId, deviceId, accessMode, customUrl, playProtocol} = params
  46 + if (!accessMode) return
  47 + const {type, url} = await getPlayUrl({
  48 + id: id,
  49 + accessMode: accessMode,
  50 + playProtocol: playProtocol,
  51 + videoUrl: customUrl,
  52 + params: {
  53 + deviceId: deviceId,
  54 + channelNo: channelId,
  55 + },
  56 + } as unknown as CameraRecord)
  57 + sourceUrl.value = url
  58 + playType.value = type
79 } finally { 59 } finally {
80 showLoading.value = false 60 showLoading.value = false
81 } 61 }
82 } 62 }
83 63
84 watch( 64 watch(
85 - () => dataset?.value,  
86 - async (newData) => {  
87 - videoPlayerRef.value?.dispose()  
88 - sourceUrl.value = await getPlaySource(newData)  
89 - videoPlayerRef.value?.anewInit()  
90 - },  
91 - {  
92 - immediate: true  
93 - }  
94 -)  
95 -  
96 -watch(  
97 - () => customVideoUrl.value,  
98 - (newData) => {  
99 - if(newData){  
100 - videoPlayerRef.value?.dispose()  
101 - sourceUrl.value = newData;  
102 - videoPlayerRef.value?.anewInit() 65 + () => dataset?.value,
  66 + async (newData) => {
  67 + getPlaySource(newData)
  68 + },
  69 + {
  70 + immediate: true
103 } 71 }
104 - },  
105 - {  
106 - immediate: true  
107 - }  
108 ) 72 )
109 73
110 -watch(  
111 - () => [poster.value, autoplay.value],  
112 - newData => {  
113 - option.poster = newData.at(-2) as string  
114 - option.autoplay = newData.at(-1) as boolean  
115 - },  
116 - {  
117 - immediate: true  
118 - }  
119 -)  
120 </script> 74 </script>
121 75
122 <style lang="scss" scoped> 76 <style lang="scss" scoped>
  77 +.player-spin {
  78 + width: 100%;
  79 + height: 100%;
  80 + @include deep() {
  81 + .n-spin-content {
  82 + width: 100%;
  83 + height: 100%;
  84 + }
  85 + }
  86 +}
123 </style> 87 </style>