|
@@ -75,6 +75,8 @@ export default defineComponent({
|
|
|
const videoRef = ref();
|
|
|
const videoItem = ref();
|
|
|
const videoID = ref('video' + Date.now() + Math.floor(Math.random() * 100));
|
|
|
+ const videoSlider =
|
|
|
+ 'videoSlider' + Date.now() + Math.floor(Math.random() * 100);
|
|
|
|
|
|
// 对时间进行格式化
|
|
|
const timeFormat = (num: number) => {
|
|
@@ -223,7 +225,7 @@ export default defineComponent({
|
|
|
let previousBytesLoaded = 0;
|
|
|
let timer: any = null;
|
|
|
let previousTime = Date.now();
|
|
|
-
|
|
|
+ let buffterCatchArray = [] as any; // 缓存数据显示
|
|
|
let isWaiting = false;
|
|
|
|
|
|
// 缓存检测状态
|
|
@@ -240,7 +242,7 @@ export default defineComponent({
|
|
|
}, time);
|
|
|
}
|
|
|
|
|
|
- function buffterCatch() {
|
|
|
+ function buffterCatch(time = 0) {
|
|
|
// 设定一个计时器,检查是否在指定的时间内再次触发了progress事件
|
|
|
bufferTimeout = setTimeout(() => {
|
|
|
if (isBuffering) {
|
|
@@ -249,7 +251,7 @@ export default defineComponent({
|
|
|
isBuffering = false;
|
|
|
videoFroms.speedInKbps = '';
|
|
|
}
|
|
|
- }, BUFFER_CHECK_INTERVAL);
|
|
|
+ }, time || BUFFER_CHECK_INTERVAL);
|
|
|
}
|
|
|
|
|
|
function resetBuffterCatch() {
|
|
@@ -263,14 +265,41 @@ export default defineComponent({
|
|
|
buffterCatch();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 格式化视屏播放有效时间 - 合并区间
|
|
|
+ * @param intervals [[], []]
|
|
|
+ * @example [[4, 8],[0, 4],[10, 30]]
|
|
|
+ * @returns [[0, 8], [10, 30]]
|
|
|
+ */
|
|
|
+ const formatEffectiveTime = (intervals: any[]) => {
|
|
|
+ const res: any = [];
|
|
|
+ intervals.sort((a, b) => a[0] - b[0]);
|
|
|
+ let prev = intervals[0];
|
|
|
+ for (let i = 1; i < intervals.length; i++) {
|
|
|
+ const cur = intervals[i];
|
|
|
+ if (prev[1] >= cur[0]) {
|
|
|
+ // 有重合
|
|
|
+ prev[1] = Math.max(cur[1], prev[1]);
|
|
|
+ } else {
|
|
|
+ // 不重合,prev推入res数组
|
|
|
+ res.push(prev);
|
|
|
+ prev = cur; // 更新 prev
|
|
|
+ }
|
|
|
+ }
|
|
|
+ res.push(prev);
|
|
|
+ return res;
|
|
|
+ };
|
|
|
+
|
|
|
element.addEventListener('loadedmetadata', () => {
|
|
|
// 获取视频总时长
|
|
|
const duration = element.duration;
|
|
|
+ let noData = false;
|
|
|
|
|
|
element.addEventListener('progress', () => {
|
|
|
const currentTime = Date.now();
|
|
|
const buffered = element.buffered;
|
|
|
let currentBytesLoaded = 0;
|
|
|
+ let currentLength = 0;
|
|
|
|
|
|
// 计算视频已缓存的总时长
|
|
|
let cachedDuration = 0;
|
|
@@ -278,18 +307,66 @@ export default defineComponent({
|
|
|
for (let i = 0; i < buffered.length; i++) {
|
|
|
currentBytesLoaded += buffered.end(i) - buffered.start(i);
|
|
|
cachedDuration += buffered.end(i) - buffered.start(i);
|
|
|
+ // console.log(buffered.end(i), buffered.start(i), 'bufferd');
|
|
|
+ buffterCatchArray = formatEffectiveTime([
|
|
|
+ ...buffterCatchArray,
|
|
|
+ [buffered.start(i), buffered.end(i)]
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+ for (let i = 0; i < buffered.length; i++) {
|
|
|
+ // 寻找当前时间之后最近的点
|
|
|
+ if (
|
|
|
+ buffered.start(buffered.length - 1 - i) < element.currentTime
|
|
|
+ ) {
|
|
|
+ cachedDuration += buffered.end(i) - buffered.start(i);
|
|
|
+ currentLength =
|
|
|
+ (buffered.end(buffered.length - 1 - i) / duration) * 100;
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
currentBytesLoaded *= element.duration * element.seekable.end(0); // 更精确地近似字节加载量
|
|
|
}
|
|
|
|
|
|
// 计算未缓存的时间段
|
|
|
const uncachedDuration = duration - cachedDuration;
|
|
|
- console.log(uncachedDuration, duration, cachedDuration, 'duration');
|
|
|
+ let uncachedTime = true; // 没有缓存时间
|
|
|
+ buffterCatchArray.forEach((item: any) => {
|
|
|
+ if (element.currentTime >= item[0] && uncachedTime <= item[1]) {
|
|
|
+ uncachedTime = false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (duration) {
|
|
|
+ const sliderDom: any = document.querySelector(
|
|
|
+ '#' + videoSlider + ' .n-slider'
|
|
|
+ );
|
|
|
+ if (sliderDom) {
|
|
|
+ sliderDom.style.setProperty(
|
|
|
+ '--catch-width',
|
|
|
+ uncachedDuration > 0 ? `${currentLength}%` : 'calc(100% + 17px)'
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(
|
|
|
+ uncachedTime,
|
|
|
+ duration,
|
|
|
+ cachedDuration,
|
|
|
+ 'duration',
|
|
|
+ buffterCatchArray,
|
|
|
+ element.currentTime,
|
|
|
+ currentLength + '%',
|
|
|
+ isWaiting,
|
|
|
+ currentBytesLoaded <= previousBytesLoaded
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
// 如果存在未缓存的时间段,可以根据具体情况做出相应处理
|
|
|
if (uncachedDuration > 0) {
|
|
|
// console.log(
|
|
|
// 'progress',
|
|
|
// currentBytesLoaded > previousBytesLoaded,
|
|
|
+ // currentBytesLoaded,
|
|
|
+ // previousBytesLoaded,
|
|
|
// '------ 加载中'
|
|
|
// );
|
|
|
if (
|
|
@@ -299,8 +376,8 @@ export default defineComponent({
|
|
|
const timeDiff = (currentTime - previousTime) / 1000; // 时间差转换为秒
|
|
|
const bytesDiff = currentBytesLoaded - previousBytesLoaded; // 字节差值
|
|
|
const speed = bytesDiff / timeDiff; // 字节每秒
|
|
|
- console.log(timeDiff, bytesDiff, speed);
|
|
|
- console.log(element.paused, 'element.paused');
|
|
|
+ // console.log(timeDiff, bytesDiff, speed);
|
|
|
+ // console.log(element.paused, 'element.paused');
|
|
|
if (!element.paused) {
|
|
|
const kbps = speed / 1024;
|
|
|
const speedInKbps = kbps.toFixed(2); // 转换为千字节每秒并保留两位小数
|
|
@@ -311,16 +388,37 @@ export default defineComponent({
|
|
|
} else {
|
|
|
videoFroms.speedInKbps = `${Number(speedInKbps)} KB/s`;
|
|
|
}
|
|
|
+
|
|
|
+ // 如果1秒钟没有返回就重置数据
|
|
|
+ clearTimeout(timer);
|
|
|
+ resetDownloadSpeed();
|
|
|
}
|
|
|
|
|
|
previousBytesLoaded = currentBytesLoaded;
|
|
|
previousTime = currentTime;
|
|
|
+ noData = false;
|
|
|
+ console.log('-------');
|
|
|
}
|
|
|
- if (!element.paused && videoFroms.isOnline) {
|
|
|
+ console.log(noData, 'noData');
|
|
|
+ if (currentBytesLoaded <= previousBytesLoaded && !uncachedTime) {
|
|
|
// 如果1秒钟没有返回就重置数据
|
|
|
+
|
|
|
+ if (!noData) {
|
|
|
+ clearTimeout(timer);
|
|
|
+ setTimeout(() => {
|
|
|
+ if (isBuffering) {
|
|
|
+ // 如果计时器到达且isBuffering仍为true,则认为缓存停止
|
|
|
+ console.log('停止缓存数据。');
|
|
|
+ isBuffering = false;
|
|
|
+ videoFroms.speedInKbps = '';
|
|
|
+ }
|
|
|
+ }, 800);
|
|
|
+ }
|
|
|
+
|
|
|
+ noData = true;
|
|
|
+ }
|
|
|
+ if (element.paused || !videoFroms.isOnline) {
|
|
|
clearTimeout(timer);
|
|
|
- resetDownloadSpeed();
|
|
|
- } else {
|
|
|
videoFroms.speedInKbps = '';
|
|
|
}
|
|
|
|
|
@@ -328,13 +426,21 @@ export default defineComponent({
|
|
|
resetBuffterCatch();
|
|
|
}
|
|
|
} else {
|
|
|
- resetBuffterCatch();
|
|
|
+ clearTimeout(timer);
|
|
|
+ buffterCatch(1000);
|
|
|
}
|
|
|
});
|
|
|
element.addEventListener('waiting', () => {
|
|
|
console.log('waiting');
|
|
|
isWaiting = true;
|
|
|
- if (!element.paused && videoFroms.isOnline) {
|
|
|
+ let uncachedTime = true; // 没有缓存时间
|
|
|
+ buffterCatchArray.forEach((item: any) => {
|
|
|
+ if (element.currentTime >= item[0] && uncachedTime <= item[1]) {
|
|
|
+ uncachedTime = false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (!element.paused && videoFroms.isOnline && uncachedTime) {
|
|
|
// 如果1秒钟没有返回就重置数据
|
|
|
clearTimeout(timer);
|
|
|
resetDownloadSpeed();
|
|
@@ -359,87 +465,15 @@ export default defineComponent({
|
|
|
}
|
|
|
videoFroms.speedInKbps = '';
|
|
|
});
|
|
|
- // element.addEventListener('progress', () => {
|
|
|
- // const currentTime = Date.now();
|
|
|
- // const buffered = element.buffered;
|
|
|
- // let currentBytesLoaded = 0;
|
|
|
-
|
|
|
- // // 计算视频已缓存的总时长
|
|
|
- // let cachedDuration = 0;
|
|
|
- // if (buffered.length > 0) {
|
|
|
- // for (let i = 0; i < buffered.length; i++) {
|
|
|
- // currentBytesLoaded += buffered.end(i) - buffered.start(i);
|
|
|
- // cachedDuration += buffered.end(i) - buffered.start(i);
|
|
|
- // }
|
|
|
- // currentBytesLoaded *= element.duration * element.seekable.end(0); // 更精确地近似字节加载量
|
|
|
- // }
|
|
|
-
|
|
|
- // // 输出未缓存的时间段
|
|
|
- // // console.log('未缓存的时间段:', uncachedDuration);
|
|
|
- // // 计算未缓存的时间段
|
|
|
- // const uncachedDuration = duration - cachedDuration;
|
|
|
- // // 如果存在未缓存的时间段,可以根据具体情况做出相应处理
|
|
|
- // if (uncachedDuration > 0) {
|
|
|
- // console.log('视频部分内容尚未缓存完全!');
|
|
|
- // if (currentBytesLoaded > previousBytesLoaded) {
|
|
|
- // const timeDiff = (currentTime - previousTime) / 1000; // 时间差转换为秒
|
|
|
- // const bytesDiff = currentBytesLoaded - previousBytesLoaded; // 字节差值
|
|
|
- // const speed = bytesDiff / timeDiff; // 字节每秒
|
|
|
- // // console.log(timeDiff, bytesDiff, speed);
|
|
|
- // // console.log(element.paused, 'element.paused');
|
|
|
- // if (!element.paused) {
|
|
|
- // const kbps = speed / 1024;
|
|
|
- // const speedInKbps = kbps.toFixed(2); // 转换为千字节每秒并保留两位小数
|
|
|
- // if (kbps > 1024) {
|
|
|
- // videoFroms.speedInKbps = `${Number(
|
|
|
- // (kbps / 1024).toFixed(2)
|
|
|
- // )} M/s`;
|
|
|
- // } else {
|
|
|
- // videoFroms.speedInKbps = `${Number(speedInKbps)} KB/s`;
|
|
|
- // }
|
|
|
- // }
|
|
|
-
|
|
|
- // previousBytesLoaded = currentBytesLoaded;
|
|
|
- // previousTime = currentTime;
|
|
|
- // }
|
|
|
- // if (!element.paused) {
|
|
|
- // // 如果1秒钟没有返回就重置数据
|
|
|
- // clearTimeout(timer);
|
|
|
- // resetDownloadSpeed();
|
|
|
- // }
|
|
|
- // } else {
|
|
|
- // console.log('整个视频已缓存完毕!');
|
|
|
- // // 如果有缓存检测计时器,则清除它
|
|
|
- // if (bufferTimeout) {
|
|
|
- // clearTimeout(bufferTimeout);
|
|
|
- // }
|
|
|
- // // 标记为正在缓存
|
|
|
- // isBuffering = true;
|
|
|
-
|
|
|
- // buffterCatch();
|
|
|
- // }
|
|
|
- // });
|
|
|
- // });
|
|
|
-
|
|
|
- // element.addEventListener('pause', () => {
|
|
|
- // clearTimeout(timer);
|
|
|
- // // 如果有缓存检测计时器,则清除它
|
|
|
- // if (bufferTimeout) {
|
|
|
- // clearTimeout(bufferTimeout);
|
|
|
- // }
|
|
|
- // videoFroms.speedInKbps = '';
|
|
|
- // });
|
|
|
});
|
|
|
-
|
|
|
- // element.addEventListener('error', () => {
|
|
|
- // element.pause();
|
|
|
- // videoItem.value?.pause();
|
|
|
- // });
|
|
|
};
|
|
|
|
|
|
const onChangeOnlineStatus = (val: any) => {
|
|
|
if (val.type === 'online') {
|
|
|
videoFroms.isOnline = true;
|
|
|
+ // const currentTime = videoItem.value.currentTime();
|
|
|
+ // videoItem.value.load();
|
|
|
+ // videoItem.value.currentTime(currentTime);
|
|
|
} else if (val.type === 'offline') {
|
|
|
videoFroms.isOnline = false;
|
|
|
}
|
|
@@ -532,7 +566,7 @@ export default defineComponent({
|
|
|
emit('close');
|
|
|
emit('reset');
|
|
|
}}>
|
|
|
- <div class={styles.slider}>
|
|
|
+ <div class={styles.slider} id={videoSlider}>
|
|
|
<NSlider
|
|
|
value={videoFroms.currentTimeNum}
|
|
|
step={0.01}
|