hookehuyr

feat(video-player): 添加视频多码率切换功能支持

添加 videojs-contrib-quality-levels 和 videojs-hls-quality-selector 依赖
在视频播放器中集成多码率切换功能,支持七牛云多码率 .m3u8 地址
自动在控制条显示清晰度切换菜单(PC/Android端)
......@@ -48,6 +48,8 @@
"vant": "^4.9.22",
"vconsole": "^3.15.1",
"video.js": "^7.21.7",
"videojs-contrib-quality-levels": "^2.2.1",
"videojs-hls-quality-selector": "1.1.4",
"vue": "^3.5.13",
"vue-demi": "0.14.6",
"vue-router": "^4.5.0",
......
......@@ -83,6 +83,12 @@ importers:
video.js:
specifier: ^7.21.7
version: 7.21.7
videojs-contrib-quality-levels:
specifier: ^2.2.1
version: 2.2.1(video.js@7.21.7)
videojs-hls-quality-selector:
specifier: 1.1.4
version: 1.1.4
vue:
specifier: ^3.5.13
version: 3.5.25
......@@ -1172,6 +1178,9 @@ packages:
fraction.js@5.3.4:
resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==}
fs-extra@0.26.7:
resolution: {integrity: sha512-waKu+1KumRhYv8D8gMRCKJGAMI9pRnPuEb1mvgYD0f7wBscg+h6bW4FDTmEZhB9VKxvoTtxW+Y7bnIlB7zja6Q==}
fs-extra@11.3.2:
resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==}
engines: {node: '>=14.14'}
......@@ -1180,6 +1189,10 @@ packages:
resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
engines: {node: '>= 8'}
fs-promise@0.5.0:
resolution: {integrity: sha512-Y+4F4ujhEcayCJt6JmzcOun9MYGQwz+bVUiuBmTkJImhBHKpBvmVPZR9wtfiF7k3ffwAOAuurygQe+cPLSFQhw==}
deprecated: Use mz or fs-extra^3.0 with Promise Support
fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
......@@ -1336,12 +1349,22 @@ packages:
engines: {node: '>=6'}
hasBin: true
jsonfile@2.4.0:
resolution: {integrity: sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==}
jsonfile@6.2.0:
resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==}
karma-safaritechpreview-launcher@0.0.6:
resolution: {integrity: sha512-2QMxAGXPQ37H3KoR9SCdh0OoktQZ5MyrxkvBiZ+VVOQfYVrcyOQXGrPea0/DKvf8qoQvrvP2FHcP/BxsuxuyHw==}
engines: {node: '>=4'}
keycode@2.2.1:
resolution: {integrity: sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==}
klaw@1.3.1:
resolution: {integrity: sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==}
less@4.4.2:
resolution: {integrity: sha512-j1n1IuTX1VQjIy3tT7cyGbX7nvQOsFLoIqobZv4ttI5axP923gA44zUj6miiA6R5Aoms4sEGVIIcucXUbRI14g==}
engines: {node: '>=14'}
......@@ -1392,6 +1415,10 @@ packages:
resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
engines: {node: '>=8'}
marcosc-async@3.0.5:
resolution: {integrity: sha512-CCDofAatyNlbCCE1aIERplqT8r4S3LLc72pg3Bg/RilWPFqdXYO/FiDfzZ9ijf7K1P0j5mUZYCQ6eWXNuAxNBg==}
engines: {'0': '>', '1': '=', '2': '4'}
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
......@@ -1708,6 +1735,11 @@ packages:
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
rimraf@2.7.1:
resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==}
deprecated: Rimraf versions prior to v4 are no longer supported
hasBin: true
rimraf@3.0.2:
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
deprecated: Rimraf versions prior to v4 are no longer supported
......@@ -1974,9 +2006,17 @@ packages:
video.js@7.21.7:
resolution: {integrity: sha512-T2s3WFAht7Zjr2OSJamND9x9Dn2O+Z5WuHGdh8jI5SYh5mkMdVTQ7vSRmA5PYpjXJ2ycch6jpMjkJEIEU2xxqw==}
videojs-contrib-quality-levels@2.2.1:
resolution: {integrity: sha512-cnF6OGGgoC/2nUrbdz54nzPm3BpEZQzMTpyekiX6AXs8imATX2sHbrUz97xXVSHITldk/+d7ZAUrdQYJJTyuug==}
peerDependencies:
video.js: ^6 || ^7 || ^8
videojs-font@3.2.0:
resolution: {integrity: sha512-g8vHMKK2/JGorSfqAZQUmYYNnXmfec4MLhwtEFS+mMs2IDY398GLysy6BH6K+aS1KMNu/xWZ8Sue/X/mdQPliA==}
videojs-hls-quality-selector@1.1.4:
resolution: {integrity: sha512-wWAjlLQui02gp//t9KHGd3XnbYO7wdOptskh3ZYCrbl/5Lbkveqb9yBVjH4e0zIQBPvGdWPMcOeDukf8iuYeBw==}
videojs-vtt.js@0.15.5:
resolution: {integrity: sha512-yZbBxvA7QMYn15Lr/ZfhhLPrNpI/RmCSCqgIff57GC2gIrV5YfyzLfLyZMj0NnZSAz8syB4N0nHXpZg9MyrMOQ==}
......@@ -2894,8 +2934,7 @@ snapshots:
transitivePeerDependencies:
- debug
balanced-match@1.0.2:
optional: true
balanced-match@1.0.2: {}
base64-arraybuffer@1.0.2: {}
......@@ -2907,7 +2946,6 @@ snapshots:
dependencies:
balanced-match: 1.0.2
concat-map: 0.0.1
optional: true
braces@3.0.3:
dependencies:
......@@ -3001,8 +3039,7 @@ snapshots:
compute-scroll-into-view@1.0.20: {}
concat-map@0.0.1:
optional: true
concat-map@0.0.1: {}
confbox@0.1.8: {}
......@@ -3178,6 +3215,14 @@ snapshots:
fraction.js@5.3.4: {}
fs-extra@0.26.7:
dependencies:
graceful-fs: 4.2.11
jsonfile: 2.4.0
klaw: 1.3.1
path-is-absolute: 1.0.1
rimraf: 2.7.1
fs-extra@11.3.2:
dependencies:
graceful-fs: 4.2.11
......@@ -3189,8 +3234,14 @@ snapshots:
minipass: 3.3.6
optional: true
fs.realpath@1.0.0:
optional: true
fs-promise@0.5.0:
dependencies:
any-promise: 1.3.0
fs-extra: 0.26.7
mz: 2.7.0
thenify-all: 1.6.0
fs.realpath@1.0.0: {}
fsevents@2.3.3:
optional: true
......@@ -3248,7 +3299,6 @@ snapshots:
minimatch: 3.1.2
once: 1.4.0
path-is-absolute: 1.0.1
optional: true
global@4.4.0:
dependencies:
......@@ -3303,10 +3353,8 @@ snapshots:
dependencies:
once: 1.4.0
wrappy: 1.0.2
optional: true
inherits@2.0.4:
optional: true
inherits@2.0.4: {}
is-binary-path@2.1.0:
dependencies:
......@@ -3342,14 +3390,27 @@ snapshots:
json5@2.2.3: {}
jsonfile@2.4.0:
optionalDependencies:
graceful-fs: 4.2.11
jsonfile@6.2.0:
dependencies:
universalify: 2.0.1
optionalDependencies:
graceful-fs: 4.2.11
karma-safaritechpreview-launcher@0.0.6:
dependencies:
fs-promise: 0.5.0
marcosc-async: 3.0.5
keycode@2.2.1: {}
klaw@1.3.1:
optionalDependencies:
graceful-fs: 4.2.11
less@4.4.2:
dependencies:
copy-anything: 2.0.6
......@@ -3413,6 +3474,8 @@ snapshots:
semver: 6.3.1
optional: true
marcosc-async@3.0.5: {}
math-intrinsics@1.1.0: {}
merge2@1.4.1: {}
......@@ -3441,7 +3504,6 @@ snapshots:
minimatch@3.1.2:
dependencies:
brace-expansion: 1.1.12
optional: true
minipass@3.3.6:
dependencies:
......@@ -3535,7 +3597,6 @@ snapshots:
once@1.4.0:
dependencies:
wrappy: 1.0.2
optional: true
p-limit@2.3.0:
dependencies:
......@@ -3551,8 +3612,7 @@ snapshots:
path-exists@4.0.0: {}
path-is-absolute@1.0.1:
optional: true
path-is-absolute@1.0.1: {}
path-parse@1.0.7: {}
......@@ -3692,6 +3752,10 @@ snapshots:
reusify@1.1.0: {}
rimraf@2.7.1:
dependencies:
glob: 7.2.3
rimraf@3.0.2:
dependencies:
glob: 7.2.3
......@@ -4041,8 +4105,20 @@ snapshots:
videojs-font: 3.2.0
videojs-vtt.js: 0.15.5
videojs-contrib-quality-levels@2.2.1(video.js@7.21.7):
dependencies:
global: 4.4.0
video.js: 7.21.7
videojs-font@3.2.0: {}
videojs-hls-quality-selector@1.1.4:
dependencies:
global: 4.4.0
karma-safaritechpreview-launcher: 0.0.6
video.js: 7.21.7
videojs-contrib-quality-levels: 2.2.1(video.js@7.21.7)
videojs-vtt.js@0.15.5:
dependencies:
global: 4.4.0
......@@ -4190,8 +4266,7 @@ snapshots:
string-width: 4.2.3
strip-ansi: 6.0.1
wrappy@1.0.2:
optional: true
wrappy@1.0.2: {}
y18n@4.0.3: {}
......
......@@ -4,6 +4,12 @@ import Hls from 'hls.js';
import videojs from "video.js";
/**
* - 使用方法 :您无需修改业务代码。只要传入的视频 URL 是七牛云生成的多码率 .m3u8 地址,播放器控制条右下角会自动出现“齿轮”图标,用户点击即可切换清晰度(或选择 Auto 自动切换)。
* - iOS 注意事项 :在 iOS 移动端(尤其是微信),通常使用系统原生播放器,系统会根据网速自动切换码率(ABR),但通常无法显示手动切换菜单,这是 iOS H5 的系统限制。
* - PC 和 Android 端将正常显示切换菜单。
*/
/**
* 视频播放核心逻辑 Hook
* 处理不同环境下的播放器选择、HLS支持、自动播放策略等
*/
......@@ -291,6 +297,13 @@ export function useVideoPlayer(props, emit, videoRef, nativeVideoRef) {
player.value = payload.player;
if (player.value) {
// 初始化多码率切换插件 (七牛云多码率支持)
if (player.value.hlsQualitySelector) {
player.value.hlsQualitySelector({
displayCurrentQuality: true,
});
}
player.value.on('error', () => {
const err = player.value.error();
handleError(err?.code, err?.message);
......