控制HTML5视频元素时导致延迟的原因是什么?



我已经看了很多相关的文章,但是我还没有找到一个明确的原因,为什么视频控制相关的功能在本地执行会导致这样的延迟。还有,因为它是很久以前写的,所以有不同之处。

关键是在<canvas>上绘制2个视频图像。在此过程中,我们创建了控制视频播放、暂停、播放速率和帧间移动的函数。

然而,当我按下Load按钮时,视频不会立即加载,如果我运行视频,做一个运行播放速率改变并再次运行,当视频完成时,它的行为很奇怪。

使用的两个视频都是4秒长。代码需要优化吗?还是我写的代码逻辑出错了?

我很好奇如何最好地解决这个问题。

// FPS
const FPS = 1 / 60;
const leftVideo  = document.querySelector('#left_video');
const rightVideo = document.querySelector('#right_video');
const leftCanvas  = document.querySelector('#left_canvas');
const rightCanvas = document.querySelector('#right_canvas');
leftCanvas.width = 256;
leftCanvas.height = 256;
rightCanvas.width = 256;
rightCanvas.height = 256;
const leftCanvasContext  = leftCanvas.getContext('2d');
const rightCanvasContext = rightCanvas.getContext('2d');
const mediaLoadButton          = document.querySelector('#media_load');
const mediaPlayButton          = document.querySelector('#media_play');
const mediaPauseButton         = document.querySelector('#media_pause');
const mediaPreviousFrameButton = document.querySelector('#media_previous_frame');
const mediaNextFrameButton     = document.querySelector('#media_next_frame');
const mediaPlaybackRateButton  = document.querySelectorAll('.media_playback_rate');
const mediaSeekBar = document.querySelector('#media_seekbar');
const updateVideoTime = () => {
mediaSeekBar.value = leftVideo.currentTime;
mediaSeekBar.style.backgroundSize = (mediaSeekBar.value - mediaSeekBar.min) * 100 / (mediaSeekBar.max - mediaSeekBar.min) + '% 100%';
};
const updateSeekBar = (event) => {
const location = (event.offsetX / mediaSeekBar.offsetWidth) * leftVideo.duration;
leftVideo.currentTime  = location;
rightVideo.currentTime = location;
};
const loadVideoFirstFrame = (direction) => {
switch(direction) {
case 'left':
if(!isNaN(leftVideo.duration)) {
leftVideo.currentTime = 0;
}
break;
case 'right':
if(!isNaN(rightVideo.duration)) {
rightVideo.currentTime = 0;
}
break;
}
};
const drawVideoFrame = (direction) => {
switch(direction) {
case 'left':
leftCanvasContext.drawImage(leftVideo, 0, 0, leftCanvas.width, leftCanvas.height);
requestAnimationFrame(() => { drawVideoFrame('left'); });
break;
case 'right':
rightCanvasContext.drawImage(rightVideo, 0, 0, rightCanvas.width, rightCanvas.height);
requestAnimationFrame(() => { drawVideoFrame('right'); });
break;
}
};
let playbackRate = 1.0;
let videoMousedown = false;
mediaLoadButton.addEventListener('click', () => {
loadVideoFirstFrame('left');
loadVideoFirstFrame('right');
mediaSeekBar.min = 0;
mediaSeekBar.max = leftVideo.duration;
leftCanvasContext.drawImage(leftVideo, 0, 0, leftCanvas.width, leftCanvas.height);
rightCanvasContext.drawImage(rightVideo, 0, 0, rightCanvas.width, rightCanvas.height);
});
leftVideo.addEventListener('play', () => {
drawVideoFrame('left');
});
leftVideo.addEventListener('timeupdate', updateVideoTime, false);
leftVideo.addEventListener('ended', () => {
loadVideoFirstFrame('left');
loadVideoFirstFrame('right');
mediaSeekBar.value = 0;
mediaSeekBar.min   = 0;
mediaSeekBar.max   = leftVideo.duration;
leftCanvasContext.drawImage(leftVideo, 0, 0, leftCanvas.width, leftCanvas.height);
rightCanvasContext.drawImage(rightVideo, 0, 0, rightCanvas.width, rightCanvas.height);
});
rightVideo.addEventListener('play', () => {
drawVideoFrame('right');
});
mediaPlayButton.addEventListener('click', () => {
leftVideo.playbackRate  = 0.2;
rightVideo.playbackRate = 0.2;
leftVideo.play();
rightVideo.play();
console.info(`PLAYBACK RATE VALUE : ${parseFloat(playbackRate).toFixed(1)}`);
});
mediaPauseButton.addEventListener('click', () => {
leftVideo.pause();
rightVideo.pause();
});
mediaPreviousFrameButton.addEventListener('click', () => {
leftVideo.currentTime  = Math.max(0, leftVideo.currentTime - FPS);
rightVideo.currentTime = Math.max(0, rightVideo.currentTime - FPS);
});
mediaNextFrameButton.addEventListener('click', () => {
leftVideo.currentTime  = Math.min(leftVideo.duration, leftVideo.currentTime + FPS);
rightVideo.currentTime = Math.min(rightVideo.duration, rightVideo.currentTime + FPS);
});
mediaSeekBar.addEventListener('click', updateSeekBar);
mediaSeekBar.addEventListener('mousemove', (event) => videoMousedown && updateSeekBar(event));
mediaSeekBar.addEventListener('mousedown', () => videoMousedown = true);
mediaSeekBar.addEventListener('mouseup', () => videoMousedown = false);
mediaPlaybackRateButton.forEach((element) => {
element.addEventListener('click', (event) => {
playbackRate = event.target.innerText;
});
});
<div class="container">
<div class="media-wrapper">
<!-- left video -->
<video id="left_video" src="res/left.mp4"></video>
<!-- right video -->
<video id="right_video" src="res/right.mp4"></video>
<!-- left canvas for left video -->
<canvas id="left_canvas"></canvas>
<!-- right canvas for right video -->
<canvas id="right_canvas"></canvas>
</div>
<div class="media-controller-wrapper">
<input id="media_seekbar" type="range" step="any" value="0" min="0" max="100" onchange="updateVideoTime()"/>
<button id="media_load" type="button">Load</button>
<button id="media_play" type="button">Play</button>
<button id="media_pause" type="button">Pause</button>
<button id="media_previous_frame" type="button">Previous frame</button>
<button id="media_next_frame" type="button">Next frame</button>
<button class="media_playback_rate" type="button">1.0</button>
<button class="media_playback_rate" type="button">0.8</button>
<button class="media_playback_rate" type="button">0.6</button>
<button class="media_playback_rate" type="button">0.4</button>
<button class="media_playback_rate" type="button">0.2</button>
</div>
</div>

我发现了一些问题,想分享一下:

我注意到在你的JS文件中你反复使用

document.querySelector()(forclasses))
而不是
document.getElementById()(id)

例如:

const leftVideo  = document.querySelector('#left_video');
// instead of 
const leftVideo  = document.getElementById('left_video');

我在适当的地方对代码进行了所有修改,现在代码片段运行至少(将视频的SRC替换为公共视频链接进行测试):

编辑
我在视频标签下面添加了:

  1. 添加#t=0.1到源根据我在HTML5视频
  2. 中动态使用第一帧作为海报的帖子,视频标签开始加速视频的海报
  3. 添加type="video/mp4"
  4. 添加preload="auto"
    这有助于加载海报在第一次点击的"Load">

所以我们现在有:

<video id="left_video" src="https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_1MB.mp4#t=0.1" type="video/mp4" preload="auto"></video>

只需按"加载">海报应立即出现:

// FPS
const FPS = 1 / 60;
const leftVideo  = document.getElementById('left_video');
const rightVideo = document.getElementById('right_video');
const leftCanvas  = document.getElementById('left_canvas');
const rightCanvas = document.getElementById('right_canvas');
leftCanvas.width = 256;
leftCanvas.height = 256;
rightCanvas.width = 256;
rightCanvas.height = 256;
const leftCanvasContext  = leftCanvas.getContext('2d');
const rightCanvasContext = rightCanvas.getContext('2d');
const mediaLoadButton          = document.getElementById('media_load');
const mediaPlayButton          = document.getElementById('media_play');
const mediaPauseButton         = document.getElementById('media_pause');
const mediaPreviousFrameButton = document.getElementById('media_previous_frame');
const mediaNextFrameButton     = document.getElementById('media_next_frame');
const mediaPlaybackRateButton  = document.querySelectorAll('.media_playback_rate');
const mediaSeekBar = document.getElementById('media_seekbar');
const updateVideoTime = () => {
mediaSeekBar.value = leftVideo.currentTime;
mediaSeekBar.style.backgroundSize = (mediaSeekBar.value - mediaSeekBar.min) * 100 / (mediaSeekBar.max - mediaSeekBar.min) + '% 100%';
};
const updateSeekBar = (event) => {
const location = (event.offsetX / mediaSeekBar.offsetWidth) * leftVideo.duration;
leftVideo.currentTime  = location;
rightVideo.currentTime = location;
};
const loadVideoFirstFrame = (direction) => {
switch(direction) {
case 'left':
if(!isNaN(leftVideo.duration)) {
leftVideo.currentTime = 0;
}
break;
case 'right':
if(!isNaN(rightVideo.duration)) {
rightVideo.currentTime = 0;
}
break;
}
};
const drawVideoFrame = (direction) => {
switch(direction) {
case 'left':
leftCanvasContext.drawImage(leftVideo, 0, 0, leftCanvas.width, leftCanvas.height);
requestAnimationFrame(() => { drawVideoFrame('left'); });
break;
case 'right':
rightCanvasContext.drawImage(rightVideo, 0, 0, rightCanvas.width, rightCanvas.height);
requestAnimationFrame(() => { drawVideoFrame('right'); });
break;
}
};
let playbackRate = 1.0;
let videoMousedown = false;
mediaLoadButton.addEventListener('click', () => {
loadVideoFirstFrame('left');
loadVideoFirstFrame('right');
mediaSeekBar.min = 0;
mediaSeekBar.max = leftVideo.duration;
leftCanvasContext.drawImage(leftVideo, 0, 0, leftCanvas.width, leftCanvas.height);
rightCanvasContext.drawImage(rightVideo, 0, 0, rightCanvas.width, rightCanvas.height);
});
leftVideo.addEventListener('play', () => {
drawVideoFrame('left');
});
leftVideo.addEventListener('timeupdate', updateVideoTime, false);
leftVideo.addEventListener('ended', () => {
loadVideoFirstFrame('left');
loadVideoFirstFrame('right');
mediaSeekBar.value = 0;
mediaSeekBar.min   = 0;
mediaSeekBar.max   = leftVideo.duration;
leftCanvasContext.drawImage(leftVideo, 0, 0, leftCanvas.width, leftCanvas.height);
rightCanvasContext.drawImage(rightVideo, 0, 0, rightCanvas.width, rightCanvas.height);
});
rightVideo.addEventListener('play', () => {
drawVideoFrame('right');
});
mediaPlayButton.addEventListener('click', () => {
leftVideo.playbackRate  = 0.2;
rightVideo.playbackRate = 0.2;
leftVideo.play();
rightVideo.play();
console.info(`PLAYBACK RATE VALUE : ${parseFloat(playbackRate).toFixed(1)}`);
});
mediaPauseButton.addEventListener('click', () => {
leftVideo.pause();
rightVideo.pause();
});
mediaPreviousFrameButton.addEventListener('click', () => {
leftVideo.currentTime  = Math.max(0, leftVideo.currentTime - FPS);
rightVideo.currentTime = Math.max(0, rightVideo.currentTime - FPS);
});
mediaNextFrameButton.addEventListener('click', () => {
leftVideo.currentTime  = Math.min(leftVideo.duration, leftVideo.currentTime + FPS);
rightVideo.currentTime = Math.min(rightVideo.duration, rightVideo.currentTime + FPS);
});
mediaSeekBar.addEventListener('click', updateSeekBar);
mediaSeekBar.addEventListener('mousemove', (event) => videoMousedown && updateSeekBar(event));
mediaSeekBar.addEventListener('mousedown', () => videoMousedown = true);
mediaSeekBar.addEventListener('mouseup', () => videoMousedown = false);
mediaPlaybackRateButton.forEach((element) => {
element.addEventListener('click', (event) => {
playbackRate = event.target.innerText;
});
});
@charset "UTF-8";
html, body {
background-color: #242424;
}
video {
display: none;
}
canvas {
width: 256px;
height: 256px;
}
input[type="range"] {
-webkit-appearance: none;
width             : 100%;
height            : 1px !important;
background        : #FFFFFF;
border-radius     : 0px;
background-image  : linear-gradient(#FF0000, #FF0000);
background-size   : 0% 100%;
background-repeat : no-repeat;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width             : 8px;
height            : 8px;
border-radius     : 50%;
background-color  : #FFFFFF;
cursor            : pointer;
box-shadow        : 0 0 2px 0 #555555;
transition        : background .3s ease-in-out;
}
input[type="range"]::-moz-range-thumb {
-webkit-appearance: none;
width             : 8px;
height            : 8px;
border-radius     : 50%;
background-color  : #FFFFFF;
cursor            : pointer;
box-shadow        : 0 0 2px 0 #555555;
transition        : background .3s ease-in-out;
}
input[type="range"]::-ms-thumb {
-webkit-appearance: none;
width             : 8px;
height            : 8px;
border-radius     : 50%;
background-color  : #FFFFFF;
cursor            : pointer;
box-shadow        : 0 0 2px 0 #555555;
transition        : background .3s ease-in-out;
}
input[type="range"]::-webkit-slider-thumb:hover {
background        : #FF0000;
}
input[type="range"]::-moz-range-thumb:hover {
background        : #FF0000;
}
input[type="range"]::-ms-thumb:hover {
background        : #FF0000;
}
input[type="range"]::-webkit-slider-runnable-track {
-webkit-appearance: none;
box-shadow        : none;
border            : none;
background        : transparent;
}
input[type="range"]::-moz-range-track {
-webkit-appearance: none;
box-shadow        : none;
border            : none;
background        : transparent;
}
input[type="range"]::-ms-track {
-webkit-appearance: none;
box-shadow        : none;
border            : none;
background        : transparent;
}
#media_seekbar {
width: 512px;
height: auto;
}
<div class="container">
<div class="media-wrapper">
<!-- left video -->
<video id="left_video" src="https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_1MB.mp4#t=0.1" type="video/mp4"></video>
<!-- right video -->
<video id="right_video" src="https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_1MB.mp4#t=0.1" type="video/mp4"></video>
<!-- left canvas for left video -->
<canvas id="left_canvas"></canvas>
<!-- right canvas for right video -->
<canvas id="right_canvas"></canvas>
</div>
<div class="media-controller-wrapper">
<input id="media_seekbar" type="range" step="any" value="0" min="0" max="100" onchange="updateVideoTime()"/>
<button id="media_load" type="button">Load</button>
<button id="media_play" type="button">Play</button>
<button id="media_pause" type="button">Pause</button>
<button id="media_previous_frame" type="button">Previous frame</button>
<button id="media_next_frame" type="button">Next frame</button>
<button class="media_playback_rate" type="button">1.0</button>
<button class="media_playback_rate" type="button">0.8</button>
<button class="media_playback_rate" type="button">0.6</button>
<button class="media_playback_rate" type="button">0.4</button>
<button class="media_playback_rate" type="button">0.2</button>
</div>
</div>

相关内容

  • 没有找到相关文章

最新更新