【问题标题】:html5: display video inside canvashtml5:在画布内显示视频
【发布时间】:2023-03-18 13:06:01
【问题描述】:

是否可以将 html5 视频显示为画布的一部分?

基本上与在画布中绘制图像的方式相同。

context.drawVideo(vid, 0, 0);

谢谢!

【问题讨论】:

    标签: video html canvas


    【解决方案1】:
    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);
    

    我猜上面的代码是自我解释的,如果没有在下面发表评论,我会尝试解释上面的几行代码

    编辑
    这是一个在线示例,仅供您参考:)
    Demo

    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="false"></video>
      <canvas id="canvas"></canvas>
      <label>
        <br />Try to play me :)</label>
      <br />
    </div>

    【讨论】:

    • 有人可以解释这一点。做这种表现实习生有什么好处吗??
    • 1) 可以操作画布,这是第一个好处。您可以想象的任何事情都是可能的 - 实时应用过滤器,使画布爆炸,无论您想要什么。 2) 您可以在移动设备上播放视频,而无需打开新视频或显示本机暂停/播放控件。一个例子是 macbook pro 网站,它利用视频 + 画布来支持移动:apple.com/mac-pro
    • 好的,但是假设您想在画布中显示视频,而不是将视频直接添加到页面。以上内容可以修改为,还是需要不同的方法?
    • 应该使用requestAnimationFrame
    • 不使用 $this,您也可以只使用箭头函数,常规 this 将按您的预期工作。
    【解决方案2】:

    使用画布显示视频

    显示视频与显示图像大致相同。细微差别与 onload 事件有关,而且您需要每帧渲染视频,否则您只会看到一帧而不是动画帧。

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

    使答案保持最新。

    user372551 之前的答案已过时(2010 年 12 月),并且使用的渲染技术存在缺陷。它使用setTimeout 和 33.333..ms 的速率,setTimeout 将舍入到 33ms,这将导致帧每两秒丢弃一次,如果视频帧速率高于 30,可能会丢弃更多帧。使用 @ 987654326@ 还将引入由于 setTimeout 无法同步到显示硬件而创建的视频剪切。

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

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

    示例演示。

    加载视频并将其设置为循环播放。在您单击视频之前,视频不会播放。再次单击将暂停。视频下方有一个静音/声音按钮。视频默认静音。

    注意 IE9+ 和 Edge 的用户。您可能无法播放视频格式 WebM,因为它需要额外的驱动程序才能播放视频。可以在 tools.google.com Download IE9+ WebM support

    找到它们

    // 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 提供了广泛的创意可能性。

    与答案相关的图片Fade canvas video from greyscale to color

    查看上述演示中的视频标题,了解上述图片中内容的归属。

    【讨论】:

    • 只是几个注释:video.autoPlay 都是小写字母(由于某种原因不遵循典型的驼峰写法),例如:video.autoplay。它并不是真正需要的,因为它默认为 false ,除非在视频标签中定义。大多数可用的视频是 30 fps (ntsc/hd) 或 25 fps (pal),因此您通常可以安全地在循环内切换绘图,因此您只能绘制 30 fps(每秒节省 30 个绘图操作)。
    • 附加提示:除非视频包含 alpha 通道,否则您不需要每帧清除画布(在消费者端非常罕见,目前仅支持 webm 格式+blink)。
    • 我尝试将自动播放设置为 true,并在 playPauseClick 函数中添加了一个 onload 事件。但是它似乎没有自动播放。有没有办法让视频自动播放?
    • @user3605780 这很奇怪。没有一点帮助,我无法让它自动播放。最简单的解决方案是将行 video,play(); 添加为函数 readyToPlayVideo 的最后一行,它是媒体事件的侦听器 canplay 但是,如果视频不在当前活动的浏览器选项卡中,则可能无法播放。
    • @Blindman67 感谢 Windows 和 Android 上的自动播放。但是在带有 Safari 的 iOS 上,整个画布视频不起作用。有没有办法让它在 iOS 上运行?
    【解决方案3】:

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

    您也可以使用 for ex。这个插件https://github.com/tstabla/stVideo

    【讨论】:

      【解决方案4】:

      这是一个使用更现代的语法并且比已经提供的更简洁的解决方案:

      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);
      })
      

      一些有用的链接:

      【讨论】:

      • 更短、更简单、易于理解的答案,没有噪音或臃肿的代码,请接受我的投票!
      • 此解决方案是在 html 中渲染视频容器,然后将该信息复制到画布中,如何将视频直接播放到画布中?
      • @RyanStone 无法将视频直接播放到画布上。但是这个解决方案效果很好。
      • 嗨,你能举个例子吗?在画布视频中播放和暂停?,我尝试在暂停后播放视频,但我认为它仍在后台播放。
      • @RomanPodlinov 如果无法直接在画布上播放视频,这是否意味着无法构建允许用户上传 mp4/mov/avi 视频和覆盖文本动画的画布视频编辑器,其中输出是 webm 视频?谢谢你的时间。
      猜你喜欢
      • 2012-06-02
      • 2021-08-19
      • 2013-05-10
      • 2016-09-24
      • 1970-01-01
      • 2014-12-03
      • 2016-02-23
      相关资源
      最近更新 更多