Html5: 在画布中显示视频

有没有可能显示一个 html5视频作为画布的一部分?

就像在画布上画图一样。

context.drawVideo(vid, 0, 0);

谢谢!

154375 次浏览
var canvas = document.getElementById('canvas');
var ctx    = canvas.getContext('2d');
var video  = document.getElementById('video');


video.addEventListener('play', function () {
var $this = this; //cache
(function loop() {
if (!$this.paused && !$this.ended) {
ctx.drawImage($this, 0, 0);
setTimeout(loop, 1000 / 30); // drawing at 30fps
}
})();
}, 0);

我猜上面的代码是自我解释,如果不删除下面的注释,我将尝试解释上面的几行代码

编辑 :
这里有一个网上的例子,仅供参考:)
演示

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var video = document.getElementById('video');


// set canvas size = video size when known
video.addEventListener('loadedmetadata', function() {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
});


video.addEventListener('play', function() {
var $this = this; //cache
(function loop() {
if (!$this.paused && !$this.ended) {
ctx.drawImage($this, 0, 0);
setTimeout(loop, 1000 / 30); // drawing at 30fps
}
})();
}, 0);
<div id="theater">
<video id="video" src="http://upload.wikimedia.org/wikipedia/commons/7/79/Big_Buck_Bunny_small.ogv" controls></video>
<canvas id="canvas"></canvas>
<label>
<br />Try to play me :)</label>
<br />
</div>

使用画布显示视频

显示视频与显示图像大致相同。微小的差异是与 onload 事件和事实,你需要渲染的视频每帧或你将只看到一帧,而不是动画帧。

下面的演示与示例有一些细微的差别。一个静音功能(在视频下面点击静音/声音来切换声音)和一些错误检查来捕捉 IE9 + 和 Edge,如果他们没有正确的驱动程序。

让答案与时俱进。

User372551之前的答案已经过时了(2010年12月) ,并且在所使用的渲染技术上有缺陷。它使用 setTimeout和速率为33.333。.Ms,setTimeout 将四舍五入到33ms,这将导致帧每两秒丢失一次,如果视频帧速率高于30,可能会丢失更多帧。使用 setTimeout还将引入创建的视频剪切,因为 setTimeout 无法与显示硬件同步。

目前还没有可靠的方法可以确定视频帧速率,除非你事先知道视频帧速率,你应该在浏览器上以最大的显示刷新率显示它。60帧/秒

给出的最佳答案是当时(6年前)的最佳解决方案,因为 requestAnimationFrame没有得到广泛支持(如果有的话) ,但是 requestAnimationFrame现在是主流浏览器的标准,应该用来代替 setTimeout 来减少或删除掉落的帧,并防止剪切。

示例演示。

加载视频并设置为循环。视频不会播放,直到你点击它。再次点击将暂停。视频下面有一个静音/声音按钮。默认情况下视频是静音的。

请注意 IE9 + 和 Edge 的用户。您可能无法播放视频格式的 WebM,因为它需要额外的驱动程序来播放视频。他们可以在 Tools.google.com 下载 IE9 + WebM 支持找到

// This code is from the example document on stackoverflow documentation. See HTML for link to the example.
// This code is almost identical to the example. Mute has been added and a media source. Also added some error handling in case the media load fails and a link to fix IE9+ and Edge support.
// Code by Blindman67.




// Original source has returns 404
// var mediaSource = "http://video.webmfiles.org/big-buck-bunny_trailer.webm";
// New source from wiki commons. Attribution in the leading credits.
var mediaSource = "http://upload.wikimedia.org/wikipedia/commons/7/79/Big_Buck_Bunny_small.ogv"


var muted = true;
var canvas = document.getElementById("myCanvas"); // get the canvas from the page
var ctx = canvas.getContext("2d");
var videoContainer; // object to hold video and associated info
var video = document.createElement("video"); // create a video element
video.src = mediaSource;
// the video will now begin to load.
// As some additional info is needed we will place the video in a
// containing object for convenience
video.autoPlay = false; // ensure that the video does not auto play
video.loop = true; // set the video to loop.
video.muted = muted;
videoContainer = {  // we will add properties as needed
video : video,
ready : false,
};
// To handle errors. This is not part of the example at the moment. Just fixing for Edge that did not like the ogv format video
video.onerror = function(e){
document.body.removeChild(canvas);
document.body.innerHTML += "<h2>There is a problem loading the video</h2><br>";
document.body.innerHTML += "Users of IE9+ , the browser does not support WebM videos used by this demo";
document.body.innerHTML += "<br><a href='https://tools.google.com/dlpage/webmmf/'> Download IE9+ WebM support</a> from tools.google.com<br> this includes Edge and Windows 10";
    

}
video.oncanplay = readyToPlayVideo; // set the event to the play function that
// can be found below
function readyToPlayVideo(event){ // this is a referance to the video
// the video may not match the canvas size so find a scale to fit
videoContainer.scale = Math.min(
canvas.width / this.videoWidth,
canvas.height / this.videoHeight);
videoContainer.ready = true;
// the video can be played so hand it off to the display function
requestAnimationFrame(updateCanvas);
// add instruction
document.getElementById("playPause").textContent = "Click video to play/pause.";
document.querySelector(".mute").textContent = "Mute";
}


function updateCanvas(){
ctx.clearRect(0,0,canvas.width,canvas.height);
// only draw if loaded and ready
if(videoContainer !== undefined && videoContainer.ready){
// find the top left of the video on the canvas
video.muted = muted;
var scale = videoContainer.scale;
var vidH = videoContainer.video.videoHeight;
var vidW = videoContainer.video.videoWidth;
var top = canvas.height / 2 - (vidH /2 ) * scale;
var left = canvas.width / 2 - (vidW /2 ) * scale;
// now just draw the video the correct size
ctx.drawImage(videoContainer.video, left, top, vidW * scale, vidH * scale);
if(videoContainer.video.paused){ // if not playing show the paused screen
drawPayIcon();
}
}
// all done for display
// request the next frame in 1/60th of a second
requestAnimationFrame(updateCanvas);
}


function drawPayIcon(){
ctx.fillStyle = "black";  // darken display
ctx.globalAlpha = 0.5;
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.fillStyle = "#DDD"; // colour of play icon
ctx.globalAlpha = 0.75; // partly transparent
ctx.beginPath(); // create the path for the icon
var size = (canvas.height / 2) * 0.5;  // the size of the icon
ctx.moveTo(canvas.width/2 + size/2, canvas.height / 2); // start at the pointy end
ctx.lineTo(canvas.width/2 - size/2, canvas.height / 2 + size);
ctx.lineTo(canvas.width/2 - size/2, canvas.height / 2 - size);
ctx.closePath();
ctx.fill();
ctx.globalAlpha = 1; // restore alpha
}


function playPauseClick(){
if(videoContainer !== undefined && videoContainer.ready){
if(videoContainer.video.paused){
videoContainer.video.play();
}else{
videoContainer.video.pause();
}
}
}
function videoMute(){
muted = !muted;
if(muted){
document.querySelector(".mute").textContent = "Mute";
}else{
document.querySelector(".mute").textContent= "Sound on";
}




}
// register the event
canvas.addEventListener("click",playPauseClick);
document.querySelector(".mute").addEventListener("click",videoMute)
body {
font :14px  arial;
text-align : center;
background : #36A;
}
h2 {
color : white;
}
canvas {
border : 10px white solid;
cursor : pointer;
}
a {
color : #F93;
}
.mute {
cursor : pointer;
display: initial;
}
<h2>Basic Video & canvas example</h2>
<p>Code example from Stackoverflow Documentation HTML5-Canvas<br>
<a href="https://stackoverflow.com/documentation/html5-canvas/3689/media-types-and-the-canvas/14974/basic-loading-and-playing-a-video-on-the-canvas#t=201607271638099201116">Basic loading and playing a video on the canvas</a></p>
<canvas id="myCanvas" width = "532" height ="300" ></canvas><br>
<h3><div id = "playPause">Loading content.</div></h3>
<div class="mute"></div><br>
<div style="font-size:small">Attribution in the leading credits.</div><br>

画布临时演员

使用画布呈现视频为您提供了在 fx 中显示和混合的其他选项。下面的图像显示了使用画布可以获得的一些 FX。使用2D API 提供了广泛的创造性可能性。

图片与答案相关 < a href = “ https://stackoverflow. com/a/39026987/3877726”> 从灰度到彩色的褪色画布视频 Video filters "Lighten", "Black & white", "Sepia", "Saturate", and "Negative"

请参阅上面演示中的视频标题,了解上图中内容的归属。

您需要更新 currentTime 视频元素,然后在画布中绘制框架。不要在视频中初始化 play ()事件。

您也可以使用 ex.this 插件 https://github.com/tstabla/stVideo

这里有一个解决方案,它使用了更现代的语法,而且没有已经提供的那些语法那么冗长:

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const video = document.querySelector("video");


video.addEventListener("play", () => {
function step() {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
requestAnimationFrame(step);
}
requestAnimationFrame(step);
});

一些有用的连结:

我从 2018年关于 requestAnimationFrame的答案开始,但是它有三个问题。

  • 不必要的复杂
  • 有一种新的方法来处理这项任务(大约从2020年开始)
  • 将事件侦听器附加到 play事件会导致性能问题

首先,事件侦听器可以简单地连接到执行所需处理的函数,并调度对其自身的下一个调用。没有必要将它包装在一个匿名函数中,并调用另一个 requestAnimationFrame

第二,不要使用 requestAnimationFrame。该函数以浏览器能够处理的最快速度(通常为60Hz)调度对回调的下一个调用,这将导致大量处理工作负载。只有当视频进入下一帧时,video.requestVideoFrameCallback才会调用您的回调。这减少了视频运行速度低于60 FPS 时的工作负载,并且在视频不播放时完全不需要进行任何处理,从而显著提高了性能。

第三,与 play事件相连的事件监听器会在你将视频告诉 video.play()时触发,每次你将一个新的视频加载到视频元素并告诉它开始播放时都会触发,也会在使用 video.pause()后恢复播放时触发。因此,每当视频被告知 play()时,视频就被画在画布上(加上你正在做的任何其他处理)一次,play()很快就会积累起来。

如果你确定你只会播放一个你想暂停和恢复的视频,你可以通过改变播放速率来切换播放/暂停,例如 video.playbackRate = !video.playbackRate。如果要在这个元素中加载多个视频,最好完全放弃 play事件侦听器,在加载第一个视频时插入对 step()的手动调用来启动它。注意,这是在 video 元素上激活的,而不是在特定加载的视频上,所以你需要设置和检查一个标志,以确保你只在加载第一个视频时调用 step(),或者取消任何活动的视频帧请求,然后再做一个新的(如下所示)。

let animation_handle;
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const video = document.querySelector("video");
video.addEventListener('loadeddata', video_load_callback, false);


function video_load_callback() {
video.cancelVideoFrameCallback(animation_handle);
step()
}
function step() { // update the canvas when a video proceeds to next frame
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
animation_handle = video.requestVideoFrameCallback(step);
}