关注HTML5开发、HTML5资讯,欢迎关注和订阅

HTML5 中文网站

HTML5中使用video元素播放视频

我们已经讨论了很多简单的应用。后面的示例中,我们将尝试提高一些复杂度。HTML5 video元素同audio元素非常类似,只是比audio元素多了一些特性,关于audio元素的使用,可以参考上一篇文章video元素的特性见下表:

 特性  
 poster  在视频加载完成之前,代表视频内容的图片的URL地址,可以想象一下“电影海报”。该特性不仅可读,而且可以修改,以便更换图片
 width, height  读取或设置显示尺寸。如果设置的宽度与视频本身大小不匹配,可能导致居中显示,上下或左右可能出现黑色条状区域
 videoWidth,videoHeight  返回视频的固有或自适应的宽度和高度。只读

video元素还有一个audio元素不支持的关键特性:可被HTML5 Canvas函数调用。

1、创建视频时序查看器

在下面这个稍显复杂的示例中,我们将演示如何抓取video元素中的帧并显示在动态Canvas上。为了演示这个功能,我们将创建一个简单的视频时序查看器。当视频播放时,定期从视频中抓取图像帧并绘制到旁边的canvas上,当用户单击canvas上显示的任何一帧时,所播放的视频会跳转到相应的时间点。只需寥寥几行代码即可创建时序查看器,它支持用户在较长的视频中随意跳转。

示例中选用的视频是20世纪中期电影院的优惠广告,一起来感受一下吧。

2.添加video元素和canvas元素

先用一段简单的代码来显示视频:

<video id="movies" autoplay oncanplay="startVideo()" onended="stopTimeline()"
autobuffer="true" width="400px" height="300px">
<source src="Intermission-Walk-in.ogv" type='video/ogg; codecs="theora, vorbis"'>
<source src="Intermission-Walk-in_512kb.mp4"
type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"'>
</video>
其中大多数标记与audio元素的示例代码中类似,不再赞述。我们着重讨论不同的地方。很明显<audio>元素替换成了<video>元素,并且<source>元素分别指向Ogg和MPEG的视频以供浏览器选择。

video元素声明了autoplay特性,这样一来,页面加载完成后,视频马上会被自动播放。此外还增加了两个事件处理函数。当视频加载完毕,准备开始播放的时候,会触发oncanplay函数来执行我们预设的动作。类似地,当视颇播放完后,会触发onended函数以停止帧的创建。

接下来我们将创建id为timeline的canvas,之后会以固定的时间间隔在上面绘制视频帧。

<canvas id="timeline" width="400px" height="300px">
3、添加变量
接下来我们要为示例编写脚本代码,在脚本中声明一些有助于调整示例的变量,同时增强代码可读性。

// # of milliseconds between timeline frame updates
var updateInterval = 5000;
// size of the timeline frames
var frameWidth = 100;
var frameHeight = 75;
// number of timeline frames
var frameRows = 4;
var frameColumns = 4;
var frameGrid = frameRows * frameColumns;
updateInterval用来控制抓取帧的频率——代码中是每5s一次。frameWidth和frameHeight两个参数用来指定在canvas中展示的视频帧的大小。类似地,frameRows、frameColumns以及frameGrid三个参数决定了在时序中总共显示多少帧。

// current frame
var frameCount = 0;
// to cancel the timer at end of play
var intervalId;
var videoStarted = false;
为了跟踪当前播放的是哪一帧,我们引入了frameCount变量。frameCount变是可被示例中的所有函数调用。(出于演示目的,示例视频每隔5s会取出一帧)intervalId用来停止控制抓取帧的计时器。最后,我们还添加了videoStarted标志以确保每个示例只创建一个计时器。

4,添加updateFrame函数
整个示例的核心功能是抓取视频帧并绘制到canvas上,它是视频与canvas相结合的部分,其代码如下:

// 把帧绘制到画布上
function updateFrame() {
    var video = document.getElementById("movies");
    var timeline = document.getElementById("timeline");
    var ctx = timeline.getContext("2d");
    //根据帧数计算出当前播放位置
    //然后以视频为输入参数绘制图像
    var framePosition = frameCount % frameGrid;
    var frameX = (framePosition % frameColumns) * frameWidth;
    var frameY = (Math.floor(framePosition / frameRows)) * frameHeight;
    ctx.drawImage(video, 0, 0, 400, 300, frameX, frameY, frameWidth, frameHeight);
    frameCount++;
}
我们知道,在操作canvas前,首先需要做的就是获取canvas的二维上下文对象:

var ctx = timeline.getContext("2d");

因为我们希望按从左到右、从上到下的顺序填充canvas网格,所以需要精确计算从视频中截取的每帧应该对应到哪个canvas网格中。根据每帧的宽度和高度,可以计算出它们的起始绘制坐标(X,Y)。

var framePosition = frameCount % frameGrid;
var frameX = (framePosition % frameColumns) * frameWidth;
var frameY = (Math.floor(framePosition / frameRows)) * frameHeight;
最后是将图像绘制到canvas上的关键函数调用。之前,我们已经使用过位置和缩放参数,但这里我们向drawImage()函数中传入的不是图像,而是视频对象:

ctx.drawImage(video, 0, 0, 400, 300, frameX, frameY, frameWidth, frameHeight);

canvas的绘图程序可以将视频源当做图像或者图案进行处理。这样开发入员就可以方便地修改视频并将其重新显示在其他位置。

5.添加startVideo函数
最后,更新frameCount,这表示我们开始在时序查看器上绘制新的视频截图。现在示例只剩下一个功能要实现,那就是定时更新时序查看器上的帧:

function startVideo() {
// 只在视频第一次播放时设置计时器
if (videoStarted)
return;
videoStarted = true;
// 计算初始帧,然后以规定时间间隔创建其他帧
updateFrame();
intervalId = setInterval(updateFrame, updateInterval);

别忘了,一旦视频加载并可以播放就会触发startVideo()函数。因此,我们首先要保证每次页面加载都仅触发一次startVideo(),除非视颇重新播放。

//只在视频第一次播放时设置计时器
if (videoStarted)
return;
videoStarted = true;

视频开始播放后,我们将抓取第一帧,接着会启用间隔计时器来定期调用updateFrame()函数。所谓间隔计时器是以指定时间间隔不断重复的计时器。示例中的结果是每5s抓取一个新截图:

// 计算初始帧,然后定期创建其他帧
updateFrame();
intervalId = setInterval(updateFrame, updateInterval);
6、处理用户输入
用户单击时序查看器上的某一帧时,系统该怎么处理?下面我们就来回答这个问题:

// 创建事件处理函数,用来在用户单击某帧后定位视频
var timeline = document.getElementById("timeline");
timeline.onclick = function(evt) {
var offX = evt.layerX - timeline.offsetLeft;
var offY = evt.layerY - timeline.offsetTop;
// 计算以零为基准索引的网格中哪帧被单击
var clickedFrame = Math.floor(offY / frameHeight) * frameRows;
clickedFrame += Math.floor(offX / frameWidth);
// 视频开始后已经播放到多少帧
var seekedFrame = (((Math.floor(frameCount / frameGrid)) *
frameGrid) + clickedFrame);
// 如果用户单击的帧在当前帧之前,则假定是上一轮的帧
if (clickedFrame > (frameCount % 16))
seekedFrame -= frameGrid;
// 不允许跳出当前视频
if (seekedFrame < 0)
return;
代码稍显复杂。我们获取了id为timeline的canvas,并对其设置了用于处理用户单击的函数。该函数通过单击事件来确定用户单击位置的x和y坐标:

var timeline = document.getElementById("timeline");
timeline.onclick = function(evt) {
var offX = evt.layerX - timeline.offsetLeft;
var offY = evt.layerY - timeline.offsetTop;
然后,我们利用帧的尺寸计算出用户单击的是16个帧中的哪一个:

var clickedFrame = Math.floor(offY / frameHeight) * frameRows;
clickedFrame += Math.floor(offX / frameWidth);
用户单击的帧一定是刚刚播放的视频帧中的一个,所以通过对应的网格索引能够计算出最近播放的帧:

var seekedFrame = (((Math.floor(frameCount / frameGrid)) *
frameGrid) + clickedFrame);
如果用户单击的帧在当前帧之前,需要向前跳跃一个完整的网格周期,以确定实际播放时间:

if (clickedFrame > (frameCount % 16))
seekedFrame -= frameGrid;
最后,我们必须添加安全控制代码,以免用户单击的帧由于某种原因比视频初始帧还早:

if (seekedFrame < 0)
return;
现在我们已经知道了用户想要跳转到的时间点,接下来是实现跳转播放。虽然这是示例的核心函数,但是代码却很简单:

// 计算出这一帧对应的视频(以s为单位)
var video = document.getElementById("movies");
video.currentTime = seekedFrame * updateInterval / 1000;
// 设置目标帧
frameCount = seekedFrame;
通过设置video元素的currentTime特性,可以让视频自动跳转到指定时间,井将当前帧数设置为新选择的帧。

7、添加stopTimeline函数
整个视频时序查看器示例中,最后要做的工作是在视频播放完毕时,停止帧的抓取。虽然不是必须的,但是如果不这么做,示例会在现有代码基础上不停地抓取帧,过段时间就会让时序查看器变成一片空白:

// 停止绘制时序的帧
function stopTimeline() {
clearInterval(intervalId);
}
视频播放完毕时会触发onended函数,stopTimeline函数会在此时被调用。

我们的视频时序查看器的功能可能还不够强大,无法满足高端用户的要求,不过别忘了它仅用了很少的代码啊。好,继续吧。

我也来参与讨论

你还可以输入600/600个字符 发表评论
称呼: (必填) 登录 | 开通博客
邮箱: (选填) 你的邮箱地址不会被公开
网站: (选填)
验证码: (必填)
看不清换一张 看不清楚换一张