简体   繁体   English

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

[英]HTML5 Canvas drawImage from video not showing on first draw

I have a video element with a canvas overlay.我有一个带有 canvas 叠加层的视频元素。 Then I have a drawing tool setup to draw over the video and a save button that does a drawImage from both the video and then the canvas to save a comped frame.然后我有一个绘图工具设置来绘制视频和一个保存按钮,它从视频和 canvas 中执行 drawImage 以保存压缩帧。 However, the first time I press Save I only get the result from the canvas drawImage, the video does not show.但是,我第一次按“保存”时,我只从 canvas drawImage 获得结果,视频没有显示。 On subsequent Saves I receive both images properly layered.在随后的保存中,我收到了正确分层的两个图像。 I thought this might be an issue with loading of the video image, but the video is fully loaded before I hit save and can even advance frames and have it work properly on the 2nd Save.我认为这可能是加载视频图像的问题,但在我点击保存之前视频已完全加载,甚至可以推进帧并使其在第二次保存时正常工作。

Here is the code...这是代码...

<div style="width:960px; height:540px; display:inline-block;">
    <video id="video" src="media/_tmp/AA_017_COMP_v37.mov" width="960" height="540" ></video>
</div>
<canvas id="canvas" width="960" height="540" 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+'" width="180" height="90" src="'+dataURL+'" />Frame: '+currentFrame+'</div>');
}

As this is on OSX with Safari there is some bad news: 由于这是在带有Safari的OSX上,因此有一些坏消息:

Note: Video as a source for the canvas drawImage() method is not currently supported on iOS. 注意:iOS当前不支持将视频作为canvas drawImage()方法的源。

Source 资源

If this is outdated information I don't know but in either case the current implementation has issues. 如果这是过时的信息,我不知道,但是无论哪种情况,当前的实现都有问题。 As established (in comments) the problem is not likely related to codecs as this occur with other formats as well. 正如已经确定的那样(在注释中),该问题不太可能与编解码器有关,因为其他格式也可能出现这种情况。 The fiddle eliminates the toDataURL() method as source so what is left is the drawImage() method. 小提琴消除了toDataURL()方法作为源,因此剩下的是drawImage()方法。

As this works on other platform with the provided code, and from the look of it, this is neither the source of the problem so you are here looking at a very possible issue (bug) with the Safari browser on the OSX platform. 由于这可以通过提供的代码在其他平台上使用,并且从外观上看,这也不是问题的根源,因此,您在这里查看的是OSX平台上Safari浏览器的一个非常可能的问题(错误)。

The official documentation quoted above also states this is not supported so this is probably a very early implementation that still has some issues. 上面引用的官方文档还指出不支持此功能,因此这可能是一个非常早期的实现,仍然存在一些问题。

There is not much you can do in that regard but to wait and also report it as an issue (I looked for a reported issue but couldn't find any so I recommend you do this). 在这方面您无能为力,只能等待并报告为问题(我寻找了已报告的问题,但找不到任何问题,因此建议您这样做)。

I recently ran into the same issue myself, and worked around it by drawing the video onto a "dummy" canvas before doing "real" work with it. 最近,我本人也遇到了同样的问题,并通过将视频绘制到“虚拟”画布上来进行处理,然后再进行“实际”处理。 This works well as all of the methods in my code that attempt to parse the video wait until the "canplaythrough" event has been triggered on the video. 这与我代码中尝试解析视频的所有方法等到视频上触发了“ canplaythrough”事件一样,效果很好。 I wrap the "canplaythrough" event handler in a Promise, and only resolve the promise when the "canplaythrough" event has been received AND an attempt has been made to draw the video onto a dummy canvas. 我将“ canplaythrough”事件处理程序包装在Promise中,并且仅在收到“ canplaythrough”事件并且试图将视频绘制到虚拟画布上时解决承诺。 This appears to be an effective workaround for the Safari bug. 这似乎是解决 Safari错误的有效方法

For example: 例如:

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

I met the same issue and tried out the solution in the last comment. 我遇到了同样的问题,并在最后一条评论中尝试了解决方案。 It didn't work for me quite yet. 它对我来说还行不通。

I figured later and found a workaround: 后来我想出了一个解决方法:

in my code I have some other drawing along with drawImage from video like 在我的代码中,我还有其他一些绘图以及视频中的drawImage

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

and with these, the second draw still won't work even when redraw it after 'canplaythrough' event. 有了这些,即使在“ canplaythrough”事件后重新绘制,第二次绘制仍然不起作用。

So I removed the above three lines, and just did a second draw after event 'canplaythrough'. 因此,我删除了上述三行内容,只是在事件“ canplaythrough”之后进行了第二次抽签。 Then it worked. 然后它起作用了。

Simply, the code that worked for me is like this: 简单来说,对我有用的代码是这样的:

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

None of these worked for me.这些都不适合我。 It seems like chrome (atm it's version 97 ) is doing some nasty optimization in background.似乎 chrome(atm 是97版)在后台做了一些讨厌的优化。

  • canplaythrough seems to be lazy, and doesn't guaranty that 1st frame is loaded canplaythrough似乎很懒惰,并且不保证第一帧已加载
  • if video has style display: none it won't be loaded as well, thus drawing won't work either.如果video有 style display: none它也不会被加载,因此绘图也不会工作。
  • video element should be attached to DOM视频元素应附加到 DOM
  • canvas element should not have any css related to its width or size. canvas 元素不应有任何与其宽度或大小相关的 css。

The only thing worked is setTimeout , it's a nasty hack, but it is like it is.唯一有用的是setTimeout ,这是一个令人讨厌的 hack,但它就是这样。

 fileinput.addEventListener('change', function (e) { var ctx = canvas.getContext("2d"); video.addEventListener('loadedmetadata', function () { canvas.width = video.videoWidth; canvas.height = video.videoHeight; setTimeout(() => { ctx.drawImage(video, 0, 0); }, 100) }); video.querySelector("source").setAttribute('src', URL.createObjectURL(fileinput.files[0])); video.load(); });
 video { opacity: 0; width: 0; height: 0; }
 <input type="file" id="fileinput" name="file"> <video id="video" controls> <source type="video/mp4"> </video> <canvas id="canvas"></canvas>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM