来自视频的 HTML5 Canvas drawImage 在第一次绘制时未显示

Posted

技术标签:

【中文标题】来自视频的 HTML5 Canvas drawImage 在第一次绘制时未显示【英文标题】:HTML5 Canvas drawImage from video not showing on first draw 【发布时间】:2013-11-28 02:09:55 【问题描述】:

我有一个带有画布覆盖的视频元素。然后我有一个绘图工具设置来绘制视频和一个保存按钮,该按钮从视频和画布中执行 drawImage 以保存复合帧。但是,第一次按保存时,我只从画布 drawImage 中得到结果,视频不显示。在随后的保存中,我收到了正确分层的两个图像。我认为这可能是加载视频图像的问题,但在我点击保存之前视频已完全加载,甚至可以推进帧并使其在第二次保存时正常工作。

这里是代码...

<div style="width:960px; height:540px; display:inline-block;">
    <video id="video" src="media/_tmp/AA_017_COMP_v37.mov"   ></video>
</div>
<canvas id="canvas"   style="position:absolute; top:40px; left:9px; z-index:100;"></canvas>
<input type="button" value="save" id="btn" size="30" onclick="save()" style="float:left; padding:4px; margin-right:4px;" >
<div id="saved" style="border:1px solid #000; position:absolute; top:626px; left:10px; bottom:40px; width:958px; overflow:auto;">SAVED:</div>


function save() 

    //COMP CANVAS OVER VIDEOFRAME
    var video = document.getElementById("video");
    var currentFrame = Math.floor((<?php echo $mov_frames ?> / video.duration) * video.currentTime);

    var compCanvas = document.createElement('canvas');
    compCanvas.width = video.width;
    compCanvas.height = video.height;
    compContext = compCanvas.getContext('2d');
    compContext.drawImage(video, 0, 0);
    compContext.drawImage(canvas, 0, 0);
    var dataURL = compCanvas.toDataURL();

    $("#saved").append('<div style="width:954px; border-bottom:1px solid #000; padding:2px 2px 0 2px;"><img id="compFrame_'+currentFrame+'"   src="'+dataURL+'" />Frame: '+currentFrame+'</div>');

【问题讨论】:

你能提供一个显示问题的jsfiddle吗? 这是一个jsFiddle link jsFiddle 我不知道的唯一问题是跨浏览器视频链接不会绘制到画布上......但代码永远不会少。 你的代码是在 DOM 加载后才启动的吗? 保存函数存在于 $(document).ready 之外,但只有在页面完全加载后才会调用该函数。 1) 由于某些缓冲区交换问题,可能是一个错误的错误。你能确定“打印”的图像是看到的那个,而不是以前的吗?而且,你已经有了一个想法:2)在画布上播放视频怎么样?这样您就可以 100% 确定手头的图像。 【参考方案1】:

因为这是在带有 Safari 的 OSX 上,所以有一些坏消息:

注意:画布 drawImage() 方法不能作为视频源 目前在 ios 上受支持。

Source

如果这是我不知道的过时信息,但无论哪种情况,当前的实现都有问题。正如已确定的(在 cmets 中),该问题不太可能与编解码器有关,因为这也发生在其他格式中。 fiddle 消除了 toDataURL() 方法作为源,所以剩下的是 drawImage() 方法。

因为这可以在其他平台上使用提供的代码,从外观上看,这不是问题的根源,因此您在这里查看 OSX 平台上 Safari 浏览器的一个非常可能的问题(错误) .

上面引用的官方文档也声明这不受支持,因此这可能是一个非常早期的实现,仍然存在一些问题。

在这方面您无能为力,只能等待并将其报告为问题(我查找了已报告的问题,但找不到任何问题,因此我建议您这样做)。

【讨论】:

我相信你是对的。在 OSX 上的 firefox 以及 Win7 上的 chrome 和 firefox 上尝试这个似乎都产生了正确的行为。 实际上回过头来阅读您提供的源链接,我意识到不支持 iOS。桌面系统上的 Mavericks 不是 iOS。但是,它似乎确实与 Safari 直接相关,因此它可能仍然是一个错误。【参考方案2】:

我自己最近遇到了同样的问题,并通过将视频绘制到“虚拟”画布上,然后再进行“真实”工作来解决这个问题。这与我的代码中尝试解析视频的所有方法一样,都等到视频上触发了“canplaythrough”事件。我将“canplaythrough”事件处理程序包装在 Promise 中,并且仅在收到“canplaythrough”事件并尝试将视频绘制到虚拟画布上时才解决该承诺。 This appears to be an effective workaround 用于 Safari 错误。

例如:

var readyPromise = new Promise(function(resolve) 
    video.addEventListener("canplaythrough", function() 
        var canvas = document.createElement("canvas"),
            context = canvas.getContext("2d");

        context.drawImage(video, 0, 0);

        resolve(video);
    );
);

readyPromise.then(function() 
    // NOW manipulate the video, draw it onto a canvas, etc
);

【讨论】:

【参考方案3】:

我遇到了同样的问题,并在最后一条评论中尝试了解决方案。 它对我来说还不太奏效。

后来想通了,找到了解决办法:

在我的代码中,我还有一些其他绘图以及来自视频的 drawImage,例如

context.rect(0, 0, 50, 50);
context.fillStyle = 'black';
context.fill();

有了这些,即使在 'canplaythrough' 事件之后重绘,第二次抽奖仍然无法进行。

所以我删除了上述三行,并在事件“canplaythrough”之后进行了第二次绘制。然后就成功了。

简单地说,对我有用的代码是这样的:

context.drawImage(video, 0, 0);
video.addEventListener('canplaythrough', function () 
    context.drawImage(video, 0, 0);
);

【讨论】:

以上是关于来自视频的 HTML5 Canvas drawImage 在第一次绘制时未显示的主要内容,如果未能解决你的问题,请参考以下文章

更改正在动画的精灵表时 HTML5 Canvas 挂起(逐帧播放视频)

如何创建带有 alpha 通道的 h264 视频以用于 HTML5 Canvas?

HTML5 视频截图

案例 HTML5 Canvas流动线条动画特效

HTML5:使用Canvas实时处理Video

html5:在画布内显示视频