问题描述
如何在视频中的特定时间以静音方式获取通过< input type =file>
选择的视频文件的快照背景(即没有可见元素,闪烁,声音等)?
How do I grab a snapshot of a video file selected via <input type="file">
at a specific time in the video silently in-the-background (i.e. no visible elements, flickering, sound, etc.)?
推荐答案
有四个主要步骤:
- 创建
< canvas>
和< video>
elements。 - 加载
URL.createObjectURL生成的视频文件的
进入src
< video>
元素并等待它通过侦听被触发的特定事件加载 。 - 将视频时间设置为您想拍摄快照的时间并收听其他活动。
- 使用画布抓住图像。
- Create
<canvas>
and<video>
elements. - Load the
src
of the video file generated byURL.createObjectURL
into the<video>
element and wait for it to load by listening for specific events being fired. - Set the time of the video to the point where you want to take a snapshot and listen for additional events.
- Use the canvas to grab the image.
第1步 - 创建元素
这很简单:只需创建一个< canvas>
和一个< video>
元素并将它们附加到< body>
(或者在任何地方真的,它并不重要):
Step 1 - Create the elements
This is very easy: just create one <canvas>
and one <video>
element and append them to <body>
(or anywhere really, it doesn't really matter):
var canvasElem = $( '<canvas class="snapshot-generator"></canvas>' ).appendTo(document.body)[0];
var $video = $( '<video muted class="snapshot-generator"></video>' ).appendTo(document.body);
请注意,视频元素的属性已静音
。不要放置任何其他属性,如 autoplay
或控件
。另请注意,它们都具有类 snapshot-generator
。这样我们就可以为它们设置样式,使它们不受影响:
Notice that the video element has the attribute muted
. Don't put any other attributes like autoplay
or controls
. Also notice that they both have the class snapshot-generator
. This is so we can set the style for both of them so that they are out of the way:
.snapshot-generator {
display: block;
height: 1px;
left: 0;
object-fit: contain;
position: fixed;
top: 0;
width: 1px;
z-index: -1;
}
有些浏览器使用它们设置为 display:none
,但是除非它们在页面上呈现,否则其他浏览器会出现严重问题,所以我们只是将它们设置得很小,这样它们基本上是不可见的。 (不要将它们移到视口之外,否则你可能会在页面上看到一些丑陋的滚动条。)
Some browsers work with them set to display: none
, but other browsers will have serious problems unless they are rendered on the page, so we just make them minuscule so that they are essentially invisible. (Don't move them outside the viewport though, as otherwise you may see some ugly scrollbars on your page.)
这里的事情开始变得棘手。您需要收听事件以了解何时继续。不同的浏览器会触发不同的事件,不同的时间和不同的顺序,所以我会省你省力。在视频准备好之前,有三个事件必须始终至少触发一次;它们是:
Here's where things start to get tricky. You need to listen to events to know when to continue. Different browsers will fire different events, different times and in different orders, so I'll save you the effort. There are three events that must always fire at least once before the video is ready; they are:
- loadedmetadata
- loadeddata
- 暂停
为这些事件设置事件处理程序并跟踪已触发的事件数。一旦三人全部解雇,你就可以继续了。请记住,由于其中一些事件可能会多次触发,因此您只需要处理触发的每种类型的第一个事件,并放弃后续的触发。我使用了jQuery的 .one
,负责处理此事。
Set up the event handler for these events and keep track how many have fired. Once all three have fired, you are ready to proceed. Keep in mind that, since some of these events may fire more than once, you only want to handle the first event of each type that is fired, and discard subsequent firings. I used jQuery's .one
, which takes care of this.
var step_2_events_fired = 0;
$video.one('loadedmetadata loadeddata suspend', function() {
if (++step_2_events_fired == 3) {
// Ready for next step
}
}).prop('src', insert_source_here);
源应该只是通过 URL.createObjectURL(文件)创建的对象URL )
,其中 file
是文件对象。
The source should just be the object URL created via URL.createObjectURL(file)
, where file
is the file object.
此阶段与上一阶段类似:设置时间然后侦听事件。在上面代码中的 if
块内:
This stage is similar to the previous: set the time and then listen for an event. Inside our if
block from the previous code:
$video.one('seeked', function() {
// Ready for next step
}).prop('currentTime', insert_time_here_in_seconds);
幸运的是这次只有一次活动,所以它非常简洁明了。最后......
Luckily its only one event this time, so it's pretty clear and concise. Finally...
这部分只是使用< canvas>
获取屏幕截图的元素。在我们的搜索
事件处理程序中:
This part is just using the <canvas>
element to grab a screenshot. Inside our seeked
event handler:
canvas_elem.height = this.videoHeight;
canvas_elem.width = this.videoWidth;
canvas_elem.getContext('2d').drawImage(this, 0, 0);
var snapshot = canvas_elem.toDataURL();
// Remove elements as they are no longer needed
$video.remove();
$(canvas_elem).remove();
画布需要与视频尺寸相匹配(不 < video>
元素)以获得正确的图像。另外,我们设置画布的内部 .height
和 .width
属性,不是画布高度/宽度CSS样式值。
The canvas needs to match the dimensions of the video (not the <video>
element) to get a proper image. Also, we are setting the canvas's internal .height
and .width
properties, not the canvas height/width CSS style values.
快照的值是一个数据URI,它基本上只是一个以数据开头的字符串:image / jpeg; base64
然后是base64数据。
The value of snapshot is a data URI, which is basically just a string that starts with data:image/jpeg;base64
and then the base64 data.
我们的最终JS代码应如下所示:
Our final JS code should look like:
var step_2_events_fired = 0;
$video.one('loadedmetadata loadeddata suspend', function() {
if (++step_2_events_fired == 3) {
$video.one('seeked', function() {
canvas_elem.height = this.videoHeight;
canvas_elem.width = this.videoWidth;
canvas_elem.getContext('2d').drawImage(this, 0, 0);
var snapshot = canvas_elem.toDataURL();
// Delete the elements as they are no longer needed
$video.remove();
$(canvas_elem).remove();
}).prop('currentTime', insert_time_here_in_seconds);
}
}).prop('src', insert_source_here);
庆祝!
你有你的形象在base64!发送到您的服务器,将其作为< img>
元素的 src
或其他任何内容。
Celebrate!
You have your image in base64! Send this to your server, put it as the src
of an <img>
element, or whatever.
例如,您可以将其解码为二进制并直接将其写入文件(首先修剪前缀),这将成为JPEG图像文件。
For example, you can decode it into binary and directly write it to a file (trim the prefix first), which will become a JPEG image file.
您还可以使用此功能在上传视频时提供视频预览。如果您将其作为< img>
的 src
,请使用完整数据URI (不要删除前缀。
You could also use this to offer previews of videos while they are uploaded. If you are putting it as the src
of an <img>
, use the full data URI (don't remove the prefix).
这篇关于生成在特定时间由文件输入选择的视频文件的缩略图/快照的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!