整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

HTML5 Video(视频)

多站点都会使用到视频. HTML5 提供了展示视频的标准。

检测您的浏览器是否支持 HTML5 视频:

检测

Web站点上的视频

直到现在,仍然不存在一项旨在网页上显示视频的标准。

今天,大多数视频是通过插件(比如 Flash)来显示的。然而,并非所有浏览器都拥有同样的插件。

HTML5 规定了一种通过 video 元素来包含视频的标准方法。

浏览器支持

Internet Explorer 9+, Firefox, Opera, Chrome, 和 Safari 支持 <video> 元素.

注意: Internet Explorer 8 或者更早的IE版本不支持 <video> 元素。

HTML5 (视频)- 如何工作

如需在 HTML5 中显示视频,您所有需要的是:

实例

<video width="320" height="240" controls>

<source src="movie.mp4" type="video/mp4">

<source src="movie.ogg" type="video/ogg">

您的浏览器不支持Video标签。

</video>

<video> 元素提供了 播放、暂停和音量控件来控制视频。

同时<video> 元素元素也提供了 width 和 height 属性控制视频的尺寸.如果设置的高度和宽度,所需的视频空间会在页面加载时保留。。如果没有设置这些属性,浏览器不知道大小的视频,浏览器就不能再加载时保留特定的空间,页面就会根据原始视频的大小而改变。

<video> 与</video> 标签之间插入的内容是提供给不支持 video 元素的浏览器显示的。

<video> 元素支持多个 <source> 元素. <source> 元素可以链接不同的视频文件。浏览器将使用第一个可识别的格式:

视频格式与浏览器的支持

当前, <video> 元素支持三种视频格式: MP4, WebM, 和 Ogg:

浏览器MP4WebMOgg
Internet ExplorerYESNONO
ChromeYESYESYES
FirefoxYESYESYES
SafariYESNONO
OperaYES (从 Opera 25 起)YESYES
  • MP4 = 带有 H.264 视频编码和 AAC 音频编码的 MPEG 4 文件

  • WebM = 带有 VP8 视频编码和 Vorbis 音频编码的 WebM 文件

  • Ogg = 带有 Theora 视频编码和 Vorbis 音频编码的 Ogg 文件

视频格式

格式MIME-type
MP4video/mp4
WebMvideo/webm
Oggvideo/ogg

HTML5 <video> - 使用 DOM 进行控制

HTML5 <video> 和 <audio> 元素同样拥有方法、属性和事件。

<video> 和 <audio>元素的方法、属性和事件可以使用JavaScript进行控制.

其中的方法有用于播放、暂停以及加载等。其中的属性(比如时长、音量等)可以被读取或设置。其中的 DOM 事件能够通知您,比方说,<video> 元素开始播放、已暂停,已停止,等等。

例中简单的方法,向我们演示了如何使用 <video> 元素,读取并设置属性,以及如何调用方法。

实例 1

为视频创建简单的播放/暂停以及调整尺寸控件:

播放/暂停 放大 缩小 普通

上面的例子调用了两个方法:play() 和 pause()。它同时使用了两个属性:paused 和 width。

HTML5 Video 标签

标签描述
<video>定义一个视频
<source>定义多种媒体资源,比如 <video> 和<audio>
<track>定义在媒体播放器文本轨迹

照相机、留声机诞生,解决了人们记录影像、声音的需求以来,人们就不断地追求着在有限的条件下尽可能地提高这些记录的品质,而从模拟时代过渡到数字时代以后,这方面的追求有一部分变成了在尽可能小的空间中提供尽可能好的内容质量,这就催生出了一系列不断演进着的媒体压缩技术。

不过这期课堂的主题尚未来到媒体编码的历史,在讲音视频图像的压缩算法进化之路前,我们先来了解一下这些媒体内容的载体——容器格式的进化历程。

首先需要区分清楚的,就是容器格式与媒体编码格式。

何为容器(Container)

对于数字媒体数据来说,容器就是一个可以将多媒体数据混在一起存放的东西,就像是一个包装箱,它可以对音视频数据进行打包装箱,将原来的两块数据整合到一起,也可以单单只存放一种类型的媒体数据。它就像电影胶片一样,中央是一帧一帧的图像,而两旁则印有对应的音轨。



举个简单的例子,常见的MP4就是一种媒体容器格式而不是编码格式,它里面的视频编码可以是现在最常见的AVC/H.264,也可以是它的前任H.263或者下一任——HEVC,音频编码可以是常见的AAC也可以是AC-3。

另一个生僻点的例子:最常见的图片格式——JPEG,它其实只是一种压缩方式,而它的存放方式其实叫做JFIF(JPEG File Interchange Format),虽然在JPEG标准中定义了一种名为JIF(JPEG Interchange Format)的容器格式,但是因为其缺乏某些关键要素,造成了使用的不便而被后来第三方开发出来的JFIF容器给取代了,今天我们能够看到的JPEG文件几乎都是装在JFIF容器中的。

虽然今天我们能够直接拿到的MP4文件里面装的几乎都是AVC+AAC的组合,但还是不能混淆了两者的概念,容器就是个容器,它没得灵魂。

AVI: 老而弥坚

AVI可能是和笔者年龄相仿的朋友最早接触的一个格式。确实,它推出的时间相对较早,也是许久以前最为常见的一种容器格式。它全称叫做音频视频交错( Audio Video Interleave),顾名思义,它就是简单地将视频与音频交错在一起,几帧视频之后就是对应的音频段,这样重复,直到结束。



AVI文件的结构示意图

它由三部分组成,头部、主体以及位于文件尾部的索引。头部中含有文件的元数据(metadata),比如视频的分辨率、码率总帧数等信息。主体部分是媒体数据的存放区,它使用了块(chunk)的概念,将原本的视频流和音频流分成块状进行交错放置,就是上面讲的一段视频一段音频交错放置,而尾部则是用来放置索引,它用来记录每个数据块在文件中的偏移位置。



红框标注为对轨道的标识

讲到AVI,还可以联动一下WAV和苹果那边的AIFF,这些容器格式其实是同源的,来自于EA(对,就是现在那个做游戏的EA)为了让不同公司开发出来的软件之间进行数据交换而在1985年开发出来的IFF(Interchange File Format)格式。苹果在IFF的基础上开发出了AIFF,而微软与IBM将IFF格式使用的大端序改成小端序就成了RIFF(Resource Interchange File Format),也就是AVI、WAV这两个容器的基本原型。




AVI虽然老,但是因为它以帧为单位把数据切成块来存放的特性,使得它几乎支持市面上几乎所有的音视频编码。而它的缺点也有很多,首先因为索引在文件尾部的关系,所以它并不适合用来流传输;另外在容器中也没有时间戳,只能通过帧数和帧率信息来进行计算,在索引里面并没有写明时间戳—媒体位置的数据,所以要在播放AVI时进行快速跳转还需要额外的技术手段;而媒体数据分块存放也使得它对很多使用运动预测特性的视频编码的支持并不是太好,因为这些帧,比如P帧和B帧,都是通过I帧进行计算得到的,这就需要访问当前帧以外的数据了。

MPEG-PS:VCD、DVD的功臣

用电脑播放过VCD的朋友一定还记得会在目录里寻找那个最大的.DAT文件来播放,而DVD则是找那个最大的.VOB。其实这两个格式都是MPEG-PS容器规范的一种,此PS非彼Photoshop,而是指Program Stream,是MPEG组织在1993年发布的一个容器标准,并且随后写入ISO/IEC国际标准,除了以上两种后缀名之外,还有.mpg也用的是这个标准。

MPEG-PS标准中引入了包的概念,整个文件由一个个包组成,每个包的大小并不相等,包里面含有这个包的时间码以及对应的音视频数据。

MPEG-PS已经随着时代的进步被废弃了,它只能存放MPEG-1、MPEG-2、MPEG-4这些出自同门的视频编码,限制性较大。但因为VCD和DVD的广泛流行,实际上它还是被用的相当多的。

MPEG-TS:专为流传输而生

MPEG组织不仅仅为音视频文件的存储制定了容器标准,还早早地顺应时代潮流,为它们的传输准备了相应的容器。我们的数字电视和IPTV用的就是MPEG组织在1995年制定的Transport Stream,也就是TS容器,当然它也并不限于这两个场景,在现在的低延时直播系统中,MPEG-TS仍然占据了绝对主流的地位,原因就是它的整个结构就是为了流传输而设计的。



从IP层到TS包内部的结构

一个TS文件中可以容纳多个TS流,不同的流上面可以带有不同的音视频数据,这样通过接收一个TS文件,用户方面可以自由地在这个TS文件中的多个子TS流之间进行切换,非常适合用于传输电视节目。而它同时针对复杂的传输环境进行了针对性的优化,TS流的基础单位是一个个大小仅为188字节的包,每个包都有自己的独立时基,并且由于采用了固定大小,所以在传输过程中即使遭遇丢包也很快就可以恢复正常播放。

发展到今天,TS仍然在网络流传输时代中发挥着自己重要的作用,在苹果主导的HLS(Http Live Streaming)协议中使用的就是TS流,它比MPEG-DASH更加通用,因为后者对于MP4文件进行了一定的修改,在老平台上面支持不太好,而HLS使用的TS仍然是规范中的,可以被大多数设备兼容。

不过也因为TS分包较多的特性,会产生一些数据冗余,所以在存储场景中一般不会使用TS作为容器。

M2TS:专为高清时代而生

M2TS多见于Blu-ray光盘和高清录像(AVCHD)中,它由MPEG-TS修改而来,加入了对于高清时代新的音视频编解码支持。



一张Blu-ray视频光盘中的M2TS

并且由于TS文件的特性,每一个小片上都有自己的独立时间戳,这使得文件中一部分数据即使遭到破坏也不会影响到其他部分的正常播放,而且可以从中随意的进行切片操作。

ASF:先进却早夭

见过ASF格式视频的朋友我想应该不会太多,但是见过WMV和WMA这两个微软以前主推的媒体编码格式的朋友肯定有很多。其实WMV和WMA就是存放在ASF容器之中的,它全称高级系统格式(Advanced Systems Format),微软原本计划是用它来作为AVI容器的后继者的,它具有诸多先进的特性,比如说它可以包含视频除了规格以外的元数据,如导演、电影名这些,它也可以提供数字版权管理(DRM),还有非常好的流传输支持——仅需要加载文件的最小部分即可开始播放,这点小编感受过一次。




ASF文件开头会用这32个字节来标识自己是个ASF文件

ASF身上的这些特性在当时还算是比较先进的,但不过这于事无补,微软建立它那套封闭媒体格式体系的做法并没有得到太多厂商和用户的支持,大家仍然更喜欢用其他更为开放一点的标准,比如MP3就是一个很好的例子。ASF容器也随着微软媒体格式的衰亡而渐渐消失了,我们今天已经几乎看不到WMV、WMA这两个曾经还很常见的格式的影子了。

RM:昔日王者,如今不见踪影



与ASF差不多同时代流行的就是RM和RMVB了,在那个AVC尚未开始普及,DivX和XviD应用较少的年代中,RMVB在国内的各大下载站中都占据了绝对主流的地位,很多视频站也大多使用RM来提供“网络视频点播”的服务。



RM容器的内部结构

不过RM容器本身并不出彩,索引仍然位于文件尾部,不过由于数据段里面有加入时间戳,所以在流传输时还是可以应付用户的跳转操作的。因为这个容器本身与RM编码息息相关的原因,它本身也只能容纳RM编码的视频流,所以在RM编码没落之后我们就很少再看到这些昔日王者了,一个容器格式想要长存,要么在设计上有其独到之处,要么就是要开放,在众多平台上面提供支持。而RM两个理由都不占,效果又比不过新兴的AVC,所以它的没落也是必然了。

FLV:前高清时代的宠儿

还记得十年前的土豆网吗?彼时它还被称为“国内的Youtube”,当时视频网站普遍都还在用Flash写播放器实现流视频播放,而自然而然地,Adobe制定的Flash Video格式就成了这些视频网站主要使用的容器格式,也就是我们熟知的FLV。



FLV格式是在Flash Player 6中引入的,当时更多的是被存放在SWF文件的内部,不过后来因为体积越来越大而直接独立了出来,它的结构相对而言比较简单,主要分为两块,位于文件头部的元数据信息和后面的音视频数据。不过在数据的存放上面,FLV是将数据分为多个标签进行存储的,每个标签都带有自己的时间戳,所以这就保证了流传输时的音画同步。

由于FLV结构简单但是功能足够用,并且被Flash Player天然支持,所以在当时的视频网站上面普遍都使用它作为容器,直到今天还有很多网站没有放弃它,虽然其中的视频编码早已升级,它也有衍生出来的F4V作为后继者,虽然后者的血统已经不是Flash家族的了。

F4V:换了血的继任者

小编还记得土豆网在2009、2010年左右在国内率先开始使用H.264编码,当时如果将清晰度切换到“高清”就会播放这些用H.264编码的视频。而用飞速土豆加速会缓存到一些.f4v扩展名的文件,乍一看还以为它就是FLV,但其实不然,F4V其实是MP4所在的ISO标准容器家族的,但也是Adobe搞出来的FLV的后继者。关于后者的详情,请往下看。

MOV:苹果向业界作出的贡献

MOV格式的正式名字叫QuickTime File Format。看到这个QuickTime第一反应肯定是“哦,这是苹果的东西”。确实,QuickTime File Format是由苹果在1998年推出的,它引入了原子(atom)的概念,在QTFF格式中,atom是基本的数据单元,它可以用来容纳实际的音视频数据,也可以放置元数据和字幕等文本信息,atom中所容纳的数据类型和大小在每个atom的头部进行描述,经过一层层的嵌套之后,整个数据文件呈现了一种树状的结构,并且保留了强大的可扩展性。



MOV和MP4这些ISO标准容器格式的开头都有一个ftyp用于标记格式

MOV作为苹果QuickTime编码的成员,在目前仍然被苹果设备广泛使用着,并且对于它的支持非常好。1998年推出QTFF的同时,苹果将这个格式交给了ISO组织,后者将它标准化为国际通用容器格式,而基于这个标准衍生出来的容器,又可以叫做ISO/IEC base media file format,同时被MPEG组织采纳,写入MPEG-4 Part 12标准中。

ISO标准容器格式

ISO标准容器格式是一个规范,它代表符合这个规范的容器类型,而不是特指某个格式。它是由苹果的QuickTime File Format发展而来的,在MPEG-4 Part 12中被最终确定并被ISO/IEC组织写入标准。它虽然没有具体实现,但是它定义了基于时间码的多媒体文件的通用结构,并由此成为了MP4、3GP等格式的基础。



3GP、MP4与ISO标准容器格式的关系

MP4:标准,泛用

MP4肯定是现在最通用最流行的媒体容器,甚至可以说没有之一。但其实现在的MP4和早期的并不是同一个标准,目前常见的MP4标准是在2003年完整的的MPEG-4 Part 14规范中制定的,到今天为止也经过了多次的修订。它其实与MOV之间并没有太大的区别,基本上就是把MOV的atom改了个名字,叫成box,然后加了一点别的佐料。



MP4的内部结构

苹果建立iTunes Store卖数字音乐的时候选择了AAC-LC作为他们的音频编码格式,而容器格式上面他们并没有选择与AAC-LC处于同一时代(MPEG-2)的ADTS,而是选择了比AAC-LC大一辈的MPEG-4标准容器,也就是MP4,不过因为它只含音频所以我们看到的扩展名就是.m4a,iTunes Store还卖一种只有视频没有音频的MV,它用的也是MP4,扩展名为.m4v,本质上它们就是同一种东西。

目前很多视频网站已经从FLV切换到MP4上面了,而且还有一种新的MPEG-DASH格式就是借助于MP4可分割的特性实现的,它将一整段视频切成许多段小块,方便浏览器进行加载,减少HTTP长连接对服务器的压力。

这里说一句题外话,当年某站刚上HTML5播放器的时候,因为他们原来的视频几乎全部都用的是FLV存储的,而HTML5标准并不支持它,所以要进行一个容器转换,某站当时一位非常年轻的程序员写出了一个在浏览器内实时将FLV文件转成MP4并喂给浏览器的媒体播放器的脚本,名为flv.js,这个脚本可能给某站省下了非常大的格式转换成本。不久之后这位程序员因为受不了某站的低薪而离职。这件事曾经引起了很多社区的热烈讨论,因为今天是程序员日所以特地写了这么一段。

3GP:精简小巧,手机最爱

3GP是MP4的同族兄弟,一样是基于ISO标准容器格式,用过3GP的兄弟肯定还记得这格式最多出现的地方是哪里——以诺基亚为代表的前智能手机时代,手机录像出来的文件大多都是3GP格式的。

3GP这个容器格式标准其实不是由以往的MPEG啊这类专注于多媒体编码的组织搞出来的,而是3GPP,对没错就是制定通信行业标准的那个组织制定的。它在容器支持的格式上进行了精简,只面向于手机可以进行的编码,比如MPEG-4 Visual、H.263这些比较老的视频编码和AMR、AAC这两种前智能手机时代使用较多的音频编码。

因为前智能手机时代的手机性能并不强大,一般也不需要支持很多种格式,不用像MP4那么全面,所以3GP最终成为了一种被广泛支持的格式,不过也因为它支持的格式过于有限,最终在智能手机时代被同门大哥MP4给取代了。

MPEG-DASH

面对时下流行的流媒体,MPEG组织对MP4文件进行了魔改,由于MP4天生可以进行无损切割的特性,DASH方式将原本媒体文件中完整的文件头的元数据信息和片段Box中的信息抽取出来单独写在一个文件(MPD)中,同时还包含了片段的URL等信息,播放器可以自适应选择需要的片段进行播放,在自适应程度上面比HLS更强一些。(其实MPEG-DASH也可以用TS作为容器,但用MP4更多一些)



目前MPEG-DASH已经成为了一项国际标准,人们比较熟悉的应用平台就是Youtube和Netflix,在这些平台上面你会发现浏览器在不断地加载一些小的视频文件,但是视频的播放是连续的。

MKV:强大无需多言,免费让它受爱

说到MKV,喜欢收藏高清电影的朋友肯定不会陌生,这种容器格式大概是和高清时代一起发展起来的,但其实它在2002年底就已经完成制定了,不过推广的很缓慢,到了高清时代和UHD时代人们才开始发现这种容器的强大,并用的越来越多,连微软都在Windows 10的初始版本中加入了对它的支持。



MKV的内部结构

MKV全名Matroska,它身上最大的特点就是开放标准、免费使用,而且它可能是目前地球上最强大的数字媒体容器格式,一个文件中可以放音频、视频、字幕、字体还有章节信息等等等等,前面东西都是不限数量任你放多少都可以吃得下的,而且它是目前唯一一个支持封装ASS字幕的格式。

值得一提的是,这玩意儿是俄罗斯组织матрёшка搞出来的,其实本身是用于盗版的,俄罗斯的网络情况跟我们挺像的,网络上盗版横行,而MKV也帮助了文件的传播,目前来看,MKV可能是众多容器格式里面最好用的,无论是编辑元数据还是抽取轨道重新封装都有GUI工具支持,不过可惜的是众多视频编辑剪辑软件还是没有提供对它的支持。

总结

其实读到最后你会发现,这些容器格式内部对于音视频数据的处理都是大同小异的,区别点其实并不大。更多的差距在于它们对于不同编码格式的支持程度、元数据的详细程度以及对于是否能够支持音视频以外的数据。

而发展到至今,MP4仍然够用,在互联网时代扮演着非常重要的角色;MKV在下载党那里被奉为圭臬;而TS格式仍然在数字电视系统中被广泛使用。但在他们之前的格式也不是说非常弱或者不好,只不过可能是他们支持的那些格式没落了顺带着把它们也带没了,真正像AVI那样确实在技术规格上落伍的容器并不多。

而对于各种容器之间无损互转的事情,小编这里推荐mp4box、mkvtoolnix和强大的ffmpeg这些工具。下一篇系列课堂,我们会转向在如今多媒体领域中扮演无可替代角色的视频编码的变迁史,并且会看一看未来的AV1和VVC这两种对于大众来说还很陌生的下一代视频编码。

所周知,16年无疑是直播行业的春天,同时也是H5的一次高潮。

so,到现在用H5技术在移动端做网页直播也是见怪不怪了,但是!!!

今天我们的主角是webApp下播放视频

参考文献:

1)HTML5+CSS3+JQuery打造自定义视频播放器

简介

HTML5的<video>标签已经被目前大多数主流浏览器所支持,包括还未正式发布的IE9也声明将支持<video>标签,利用浏览器原生特性嵌入视频有很多好处,所以很多开发者想尽快用上,但是真正使用前还有些问题要考虑,尤其是 Opera/Firefox 和IE/Safari浏览器所支持的视频编码不同的问题,Google几个月前发布的开源视频编码VP8有望能解决这一问题,另外Google还发布了开放网络媒体项目WebM,旨在帮助开发者为开放网络制作出世界级媒体格式,Opera, Firefox, Chrome和IE9都将支持VP8,而且Flash Player也将可以播放VP8,这就意味着我们很快就可以只制作一个版本的视频然后在所有主流浏览器上播放了。另外一个主要的问题就是如何构建自定义的HTML5<video>播放器,这是目前Flash Player的优势所在,利用Flash的IDE所提供的接口可以很方便的构建一个个性化的视频播放器,那HTML5的<video>标签要怎样才能实现呢?这个问题就是本文所要解决的!我们将开发一个HTML5<video>视频播放器的jQuery插件,并且可以很方便的进行自定义,将分为以下几个部分:

1.视频控制工具条

2.视频控制按钮

3.打包成jQuery插件

4.外观和体验

5.自定义皮肤

视频控制工具条

做为一个专业的web开发人员,我们创建一个视频播放器时一定希望它的外观在各个浏览器中看起来一致(consistent),但是通过下面的图可以看到目前各个浏览器提供的视频控制工具条外观各不相同:

那就没办法了,我们得自己从头来创建这个控制工具条,利用HTML和CSS再加上一些图片实现起来并不算很难,另外通过HTML5多媒体元素提供的API我们可以很方便将创建的任何按钮与播放/暂停等事件进行绑定。

视频控制按钮

基本的视频控制工具条要包含一个播放/暂停按钮,一个进度条,一个计时器和一个音量控制按钮,我们将这些按钮放在<video>元素下面,并用一个div作为父容器:

Java代码

  1. <div class="ghinda-video-controls">

  2. <a class="ghinda-video-play" title="Play/Pause"></a>

  3. <div class="ghinda-video-seek"></div>

  4. <div class="ghinda-video-timer">00:00</div>

  5. <div class="ghinda-volume-box">

  6. <div class="ghinda-volume-slider"></div>

  7. <a class="ghinda-volume-button" title="Mute/Unmute"></a>

  8. </div>

  9. </div>

复制代码

注意,我们使用元素的class属性来代替ID属性是为了方便在一个页面上使用多个播放器。

打包成jQuery插件

创建好控制按钮后我们需要配合多媒体元素的API来实现视频控制的目的,正如前面提到的一样我们将我们的播放器打包成jQuery插件,这样可以很好的实现复用,代码如下:

Java代码

  1. $.fn.gVideo = function(options) {

  2. // build main options before element iteration

  3. var defaults = {

  4. theme: 'simpledark',

  5. childtheme: ''

  6. };

  7. var options = $.extend(defaults, options);

  8. // iterate and reformat each matched element

  9. return this.each(function() {

  10. var $gVideo = $(this);

  11. //create html structure

  12. //main wrapper

  13. var $video_wrap = $('<div></div>').addClass('ghinda-video-player').addClass(options.theme).addClass(options.childtheme);

  14. //controls wraper

  15. var $video_controls = $('<div class="ghinda-video-controls"><a class="ghinda-video-play" title="Play/Pause"></a><div class="ghinda-video-seek"></div><div class="ghinda-video-timer">00:00</div><div class="ghinda-volume-box"><div class="ghinda-volume-slider"></div><a class="ghinda-volume-button" title="Mute/Unmute"></a></div></div>');

  16. $gVideo.wrap($video_wrap);

  17. $gVideo.after($video_controls);

这里先假设您了解jQuery并知道如何创建一个jQuery插件,因为这个不在本文的讨论范围之内,在上面这段脚本中我们使用jQuery动态创建视频控制工具条的元素,接下来为了绑定事件我们需要获取对应的元素:

Java代码

  1. //get newly created elements

  2. var $video_container = $gVideo.parent('.ghinda-video-player');

  3. var $video_controls = $('.ghinda-video-controls', $video_container);

  4. var $ghinda_play_btn = $('.ghinda-video-play', $video_container);

  5. var $ghinda_video_seek = $('.ghinda-video-seek', $video_container);

  6. var $ghinda_video_timer = $('.ghinda-video-timer', $video_container);

  7. var $ghinda_volume = $('.ghinda-volume-slider', $video_container);

  8. var $ghinda_volume_btn = $('.ghinda-volume-button', $video_container);

  9. $video_controls.hide(); // keep the controls hidden

这里我们通过className方式获取,先让工具条隐藏直到所有资源加载完成,现在来实现播放/暂停按钮:

Java代码

  1. var gPlay = function() {

  2. if($gVideo.attr('paused') == false) {

  3. $gVideo[0].pause();

  4. } else {

  5. $gVideo[0].play();

  6. }

  7. };

  8. $ghinda_play_btn.click(gPlay);

  9. $gVideo.click(gPlay);

  10. $gVideo.bind('play', function() {

  11. $ghinda_play_btn.addClass('ghinda-paused-button');

  12. });

  13. $gVideo.bind('pause', function() {

  14. $ghinda_play_btn.removeClass('ghinda-paused-button');

  15. });

  16. $gVideo.bind('ended', function() {

  17. $ghinda_play_btn.removeClass('ghinda-paused-button');

  18. });

  19. 大多数浏览器在右键点击视频时会提供一个独立的菜单,它也提供了视频控制功能,如果用户通过这个右键菜单控制视频那就会跟我们的自定义控件冲突,所以为了避免这一点我们需要绑定视频播放器自身的“播放”,“暂停”和“结束”事件,在事件处理函数中处理播放/暂停按钮,控制按钮的样式。

    为了创建进度条的拖动块,我们使用了jQuery UI的Slider组件:

    Java代码

    1. var createSeek = function() {

    2. if($gVideo.attr('readyState')) {

    3. var video_duration = $gVideo.attr('duration');

    4. $ghinda_video_seek.slider({

    5. value: 0,

    6. step: 0.01,

    7. orientation: "horizontal",

    8. range: "min",

    9. max: video_duration,

    10. animate: true,

    11. slide: function(){

    12. seeksliding = true;

    13. },

    14. stop:function(e,ui){

    15. seeksliding = false;

    16. $gVideo.attr("currentTime",ui.value);

    17. }

    18. });

    19. $video_controls.show();

    20. } else {

    21. setTimeout(createSeek, 150);

    22. }

    23. };

    24. createSeek();

    正如你所看到的,这里我们写了一个递归函数,通过循环比较video的readyState属性来判断视频是否已经准备好,否则我们就不能获得视频的时长也无法创建滑动块,当视频准备好后我们初始化滑动块并显示控制工具条,下一步我们通过绑定video元素的timeupdate事件实现计时器功能:

    Java代码

    1. var gTimeFormat=function(seconds){

    2. var m=Math.floor(seconds/60)<10?"0"+Math.floor(seconds/60):Math.floor(seconds/60);

    3. var s=Math.floor(seconds-(m*60))<10?"0"+Math.floor(seconds-(m*60)):Math.floor(seconds-(m*60));

    4. return m+":"+s;

    5. };

    6. var seekUpdate = function() {

    7. var currenttime = $gVideo.attr('currentTime');

    8. if(!seeksliding) $ghinda_video_seek.slider('value', currenttime);

    9. $ghinda_video_timer.text(gTimeFormat(currenttime));

    10. };

    11. $gVideo.bind('timeupdate', seekUpdate);

    这里我们用seekUpdate函数获取video的currentTime属性值然后调用gTimeFormat函数进行格式化后得到当前播放的时间点。

    至于音量控制控件我们还是利用jQuery UI的Slider组件然后利用自定义函数实现静音和取消静音的功能:

    Java代码

    1. $ghinda_volume.slider({

    2. value: 1,

    3. orientation: "vertical",

    4. range: "min",

    5. max: 1,

    6. step: 0.05,

    7. animate: true,

    8. slide:function(e,ui){

    9. $gVideo.attr('muted',false);

    10. video_volume = ui.value;

    11. $gVideo.attr('volume',ui.value);

    12. }

    13. });

    14. var muteVolume = function() {

    15. if($gVideo.attr('muted')==true) {

    16. $gVideo.attr('muted', false);

    17. $ghinda_volume.slider('value', video_volume);

    18. $ghinda_volume_btn.removeClass('ghinda-volume-mute');

    19. } else {

    20. $gVideo.attr('muted', true);

    21. $ghinda_volume.slider('value', '0');

    22. $ghinda_volume_btn.addClass('ghinda-volume-mute');

    23. };

    24. };

    25. $ghinda_volume_btn.click(muteVolume);

    26. 最后当我们自己的自定义视频控制工具条构造完成后需要移除<video>标签的controls属性,这样浏览器默认的工具条就被去掉了。

      好了,我们的插件功能已经全部完成了,调用方法:

      Java代码

      1. $('video').gVideo();

      这会将我们的插件应用到页面上每一个video元素上。

      外观和体验

      好的,现在到了比较有意思的部分,也就是播放器的外观和体验了。当插件功能已经完成后利用一点CSS就可以很容易地自定义样式了,我们将全部使用CSS3来实现。

      首先,我们给播放器主容器加一些样式:

      Java代码

      1. .ghinda-video-player {

      2. float: left;

      3. padding: 10px;

      4. border: 5px solid #61625d;

      5. -moz-border-radius: 5px; /* FF1+ */

      6. -ms-border-radius: 5px; /* IE future proofing */

      7. -webkit-border-radius: 5px; /* Saf3+, Chrome */

      8. border-radius: 5px; /* Opera 10.5, IE 9 */

      9. background: #000000;

      10. background-image: -moz-linear-gradient(top, #313131, #000000); /* FF3.6 */

      11. background-image: -webkit-gradient(linear,left top,left bottombottom,color-stop(0, #313131),color-stop(1, #000000)); /* Saf4+, Chrome */

      12. box-shadow: inset 0 15px 35px #535353;

      13. }

      14. 下一步,我们设置视频控制工具条左边浮动使它们水平对齐,利用CSS3的opacity和transitions我们给播放/暂停和静音/取消静音按钮添加了非常不错的悬浮效果:

        Java代码

        1. .ghinda-video-play {

        2. display: block;

        3. width: 22px;

        4. height: 22px;

        5. margin-right: 15px;

        6. background: url(../images/play-icon.png) no-repeat;

        7. opacity: 0.7;

        8. -moz-transition: all 0.2s ease-in-out; /* Firefox */

        9. -ms-transition: all 0.2s ease-in-out; /* IE future proofing */

        10. -o-transition: all 0.2s ease-in-out; /* Opera */

        11. -webkit-transition: all 0.2s ease-in-out; /* Safari and Chrome */

        12. transition: all 0.2s ease-in-out;

        13. }

        14. .ghinda-paused-button {

        15. background: url(../images/pause-icon.png) no-repeat;

        16. }

        17. .ghinda-video-play:hover {

        18. opacity: 1;

        19. }

        20. 如果您仔细看了前面那段根据视频播放状态(Playing/Paused)添加和移除播放/暂停按钮样式的JavaScript代码,就会明白为什么.ghinda-paused-button为什么要重写.ghinda-video-play的背景属性了。

          现在轮到滑动块了,我们进度条和音量控制的滑动块的实现都是利用了jQuery UI的Slider组件,这个组件它本身自带了样式,定义在jQuery UI对应的css文件中,但是为了使滑动块和播放器其他控件外观保持一致我们全部重写了它的样式:

          Java代码

          1. .ghinda-video-seek .ui-slider-handle {

          2. width: 15px;

          3. height: 15px;

          4. border: 1px solid #333;

          5. top: -4px;

          6. -moz-border-radius:10px;

          7. -ms-border-radius:10px;

          8. -webkit-border-radius:10px;

          9. border-radius:10px;

          10. background: #e6e6e6;

          11. background-image: -moz-linear-gradient(top, #e6e6e6, #d5d5d5);

          12. background-image: -webkit-gradient(linear,left top,left bottombottom,color-stop(0, #e6e6e6),color-stop(1, #d5d5d5));

          13. box-shadow: inset 0 -3px 3px #d5d5d5;

          14. }

          15. .ghinda-video-seek .ui-slider-handle.ui-state-hover {

          16. background: #fff;

          17. }

          18. .ghinda-video-seek .ui-slider-range {

          19. -moz-border-radius:15px;

          20. -ms-border-radius:15px;

          21. -webkit-border-radius:15px;

          22. border-radius:15px;

          23. background: #4cbae8;

          24. background-image: -moz-linear-gradient(top, #4cbae8, #39a2ce);

          25. background-image: -webkit-gradient(linear,left top,left bottombottom,color-stop(0, #4cbae8),color-stop(1, #39a2ce));

          26. box-shadow: inset 0 -3px 3px #39a2ce;

          27. }

          28. 这时候音量控制的滑动块一直显示在音量按钮旁边,我们需要将它改成默认隐藏,当鼠标悬浮在音量按钮上再动态显示出来,使用transitions来实现这个效果会是个不错的的选择:

            Java代码

            1. .ghinda-volume-box {

            2. height: 30px;

            3. -moz-transition: all 0.1s ease-in-out; /* Firefox */

            4. -ms-transition: all 0.1s ease-in-out; /* IE future proofing */

            5. -o-transition: all 0.2s ease-in-out; /* Opera */

            6. -webkit-transition: all 0.1s ease-in-out; /* Safari and Chrome */

            7. transition: all 0.1s ease-in-out;

            8. }

            9. .ghinda-volume-box:hover {

            10. height: 135px;

            11. padding-top: 5px;

            12. }

            13. .ghinda-volume-slider {

            14. visibility: hidden;

            15. opacity: 0;

            16. -moz-transition: all 0.1s ease-in-out; /* Firefox */

            17. -ms-transition: all 0.1s ease-in-out; /* IE future proofing */

            18. -o-transition: all 0.1s ease-in-out; /* Opera */

            19. -webkit-transition: all 0.1s ease-in-out; /* Safari and Chrome */

            20. transition: all 0.1s ease-in-out;

            21. }

            22. .ghinda-volume-box:hover .ghinda-volume-slider {

            23. position: relative;

            24. visibility: visible;

            25. opacity: 1;

            26. }

            27. 利用一些基础的CSS属性以及CSS3提供的新属性我们打造了一个全新的播放器外观,它看起来是这个样子:

              自定义皮肤

              可能您已经注意到,我们在编写插件的时候已经定义了一些默认选项,它们是theme和childtheme,可以在调用插件的时候根据需要方便的应用自定义皮肤。

              这里解释下theme就是所有控件的一整套样式定义,childtheme就是在theme基础上重写某些样式,我们在调用插件的时候可以同时指定这两个选项或者其中的一个:

              Java代码

              1. $('video').gVideo({

              2. childtheme:'smalldark'

              3. });

              我们写了一个示例的皮肤smalldark,它只重写了部分的样式,显示效果是这样的:

              总结

              利用HTML5 video,JavaScript和CSS3打造自定义的视频播放器真的非常容易,t实现工具条功能用JavaScrip,外观和体验交给CSS3,我们得到了一个功能强大并且易于定制的解决方案!

              enjoy!

              2)mui Html5 Video 实现方案

              前言: 最近项目中需要用到html5 视频播放功能,于是稍微研究了解了下,遇到了很多坑,特此记录下.

              一、 Html5 Video

              参考来源: http://www.xuanfengge.com/html5-video-play.html

              (这篇博文确实帮助很大)

              1.1、 目的

              将Html5 Video功能应用到实际项目中,针对不同的平台和环境,进行个性化处理。

              基本只考虑webkit浏览器兼容问题

              1.2、 Html5 Video支持格式

              只支持: .mp4后缀(.h264编码格式),和.webm后缀(专用web视频格式),以及.ogg后缀(音频文件)

              注意: Html5 Video 可以添加多个source源来进行兼容适配,这样,当第一个源读取出问题时会自动读取下一个源. 比如可以同时在前面加上.webm和.mp4源,这样一个出错时会自动读取另一个可用源(因为不同浏览器,支持的格式是不一样的)

              但是,Hybird模式的 Android 下,有些机型只能读取第一个source来源(测试华为和联想都是),所以也就是说在这种情况下,要确保第一个source源是正确的

              各大浏览器兼容如图所示:

              见图1

              1.3、 不同平台环境和对应实现方案

              说明: 这里分为两大块,普通浏览器环境(pc和手机,主要是移动端,pc不做特别处理)和Hybird模式的APP环境(Android和iOS).

              注: Html5 video可以播放本地视频或者网络视频

              1.3.1、 普通浏览器环境

              *用Html5 Video 自带的播放栏控件

              *用 Video 视频统一处理方法处理后,点击图片手动隐藏图片,设置视频大小,手动播放视频.

              注: 播放效果则由各大浏览器自行实现

              手机端浏览器实现的不同效果,比如:

              QQ浏览器(包括QQ客户端内置的浏览器):播放时会自动进入全屏

              华为自带浏览器: 正常小窗口播放

              1.3.2、 Hybird App环境

              说明: 内联播放是指直接在video标签中播放视频,没有必要进入全屏

              1.3.2.1、 Android内联播放

              *用Html5 Video 自带的播放栏控件

              *用 Video 视频统一处理方法处理后,点击图片手动隐藏图片,设置视频大小,手动播放视频.

              *Android内联播放需要注意,必须开启硬件加速,由于有些Android手机 webview是默认关闭硬件加速的,所以必须在创建这个带视频播放的webview时手动添加 硬件加速属性才行.(详情见plus创建webview的style)

              style.hardwareAccelerated = true;

              1.3.2.2、 iOS内联播放

              *用Html5 Video 自带的播放栏控件

              *用 Video 视频统一处理方法处理后,点击图片手动隐藏图片,设置视频大小,手动播放视频.

              *内联播放注意要点,由于iOS下默认是全屏播放的,所以需要经过设置才能正常内联播放

              第一步:在项目的manifest里面配置允许webview内联播放

              "plus": {
               "splashscreen": {
               "autoclose": true,/*是否自动关闭程序启动界面,true表示应用加载应用入口页面后自动关闭;false则需调plus.navigator.closeSplashscreen()关闭*/
               "waiting": true/*是否在程序启动界面显示等待雪花,true表示显示,false表示不显示。*/
               },
               "allowsInlineMediaPlayback": true,/*设置ios下允许内联播放*/
               "popGesture": "close"

              第二步: 创建video标签时,手动加上内联播放的属性(iOS不支持preload)

              <!--
               让ios支持内联播放,必须添加 webkit-playsinline 标签
               -->
               <video webkit-playsinline id="videoMedia" controls="controls" preload>

              这样iOS手机在播放的时候才会采用内联播放

              1.3.2.3、 Android非内联播放

              *通过NJS使用原生播放器来播放视频,传入的url可以是本地的或网络的地址

              *用 Video 视频统一处理方法处理后,点击图片之后,图片保持不变(所以没有必要隐藏图片),直接获取视频的资源地址,传给原生播放器播放

              注: 这种模式下,性能要比直接html5自带播放器播放高

              1.3.2.4、 iOS非内联播放

              *用Html5 Video 自带的播放栏控件(非内联播放需要去除特定内联属性”webkit-playsinline”,这样才能全屏播放)

              if(!isInlinePlay){
               //如果是非内敛,ios需要去除内联样式
               mediaTarget.removeAttribute('webkit-playsinline');
               }

              *用 Video 视频统一处理方法处理后,点击图片之后,图片保持不变(所以没有必要隐藏图片),直接调用video.play()播放视频(这时候会用一个全屏播放器来播放视频)

              1.3.3、 注意要点

              如果采用NJS通过Android原生播放器播放视频,目前无法监听到视频的一些自定义事件.(如下载中,播放完毕,暂停,播放时间等)

              而如果采用Html5 Video自带播放,这些是可以通过脚本控制的.

              所以选定方案时需要进行衡量

              *另外,在Html5 Video播放时,无法监听到规定的结束事件seeked,只能在timeUpdate里面判断,如果ended为true就代表结束了.

              *在NJS通过Android原生播放器播放时,可以通过document监听resume和pause事件判断是否进入播放和退出播放

              1.4、 Tips

              1.4.1、 关于Video 视频统一处理的方案

              说明: 由于将一个<Video>直接显示在页面中,会有各种五花八门的播放器效果,如图:

              (这里引用了参考来源的图)

              见图2

              显然,体验效果并不好,所以现在的做法是用一张模拟播放的图片来替代<Video>所在的地方,而将Video元素设置为1*1像素大小.然后给图片设置点击监听,监听到点击时,播放视频.

              注意:

              *这里不要用{display: none}或者{width:0;height:0;}的方式,因为这样视频元素会处于未激活的状态,给后续的处理带来麻烦.

              *这里没有考虑ios<6和一些低版本的Android的兼容性问题了(这些版本里,无法直接通过video.play()来播放),因为项目环境基本上要求Android>4.0 iOS 7.0以上的.

              *关于点击图片播放视频后,如果是内联播放模式下(或者是普通浏览器),就应该将图片隐藏,然后将视频大小设置为本来的大小(一般为图片大小);如果是非内联播放模式(全屏模式),就没有必要隐藏图片了,因为iOS下会自动打开一个全屏播放器来播放视频,Android下考虑到Html5 video较卡,所以也会通过NJS使用原生播放器来全屏播放视频.

              1.4.2、 Android NJS播放视屏的实现代码

              说明: 这个是Dcloud论坛中有人分享的

              //非内联模式下的plus下的android才用到
               var Intent = plus.android.importClass("android.content.Intent");
               var Uri = plus.android.importClass("android.net.Uri");
               var main = plus.android.runtimeMainActivity();
               var intent = new Intent(Intent.ACTION_VIEW);
               var uri = Uri.parse(url);
               intent.setDataAndType(uri, "video/*");
               main.startActivity(intent);

              1.4.3、 如何读取元素的宽高

              *在获取视频的宽度时,发现用 video.style.width无法获取到宽度.

              后来查了资料,发现dom.style.width(height)只能获取在stye直接赋予的值.而如果是通过css样式表赋予的值是无法直接获取的,只能通过dom.offsetWidth(offsetHeight)获取.

              *设置元素宽和高是不要直接在style里设置,而是最好通过css样式表赋予

              *读取元素宽和高时,用offsetWidth(offsetHeight)

              1.4.4、 关于全屏播放的问题

              在PC端webkit浏览器下,全屏代码如下:

              进入全屏: videoContainer(对应的dom).webkitRequestFullscreen();

              退出全屏: document.webkitCancelFullScreen();

              *但是经测试,在手机浏览器和Hybird模式下的手机环境中都无法使用,

              应该是手机浏览器中video 播放器的全屏模式和pc端的有区别,已经脱离了webkit的全屏组件,而是用原生自己实现的.

              1.5、 遇到问题及解决方法

              1.5.1、 Video.currentTime 设置值时设置无效,或者变为0

              原因分析:

              与测试的服务器和端口有关,测试环境是放在hbuild本地浏览器的,没有处理好视频快进问题,所以会导致每次快进后,视频都会重置-在某些测试服务器上,则出现快进无效,但不会重置

              解决方法:

              将网页用其它正式服务器打开均可正常,如tomcat,wampserver,甚至直接在本地打开也行.

              1.5.2、 无法通过代码Video.src获取资源路径

              原因分析:

              本实例中,Video是通过source添加src的,无法直接读取video的src

              解决方法:

              可以通过读取到第一个source的标签,再获取source的src

              注:本来这个方法有一个缺点就是有可能第一个source的src不可用.但是由于Android中第一个source必须有用才行.否则无法正常播放.所以在确保第一个source正确的情况下能这样用.

              1.5.3、 部分Android机型无法退出全屏

              描述:

              在使用Html5 Video自带播放器播放时,部分Android机型(如联想K860点击全屏按钮进入全屏后,无法退出全屏-因为进入全屏后,全屏按钮不见了)

              原因分析: 可能是手机厂商擅自劫持了浏览器或者篡改了浏览器实现方式

              解决方法:

              目前无法解决,在这类机型中,建议直接采用非内联模式播放或者是尽量不要手动进入全屏

              3)移动端HTML5<video>视频播放优化实践

              遇到的挑战

              移动端HTML5使用原生<video>标签播放视频,要做到两个基本原则,速度快和体验佳,先来分析一下这两个问题。

              下载速度

              以一个8s短视频为例,wifi环境下提供的高清视频达到1000kbps,文件大小大约1MB;非wifi环境下提供的低码率视频是500kbps左右,文件大小大约500KB;参考QzoneTouch多普勒测速,2g网络的平均速度是14KB/s,那么下载一个低码率视频耗时35s;那么要想流畅播放视频,就需要一个加载等待的过程,这个过程要有明确的反馈,不能让用户有“坏掉了”的感觉。

              多普勒测速数据参考

              #dns(s)conn(s)rtt(s)tran(kb/s)
              2g3.857852.334822.5747814.0374
              3g1.606430.7431090.60804760.1967
              wifi0.9869210.5502080.44433270.8728

              用户体验

              视频是否可以自动播放,是否能循环播放,是否能显示下载进度,播放的时候如何隐藏控制条,暂停的时候又能显示出来呢。这些问题看上去貌似简单,但是由于PC/iOS/Android这些不同平台、不同的浏览器内核、甚至相同内核的不同版本,所实现的<video>属性、方法和事件差异较大,解决兼容性问题又给开发造成了很大困扰。

              分析原因

              事件差异

              下面是播放一个短视频,在不同平台触发事件和获取属性的差异表现。

              PC

              #eventreadyStatecurrentTime (s)buffered (s)duration (s)视频状态
              1loadstartNOTHING0
              2suspendNOTHING0
              3playNOTHING0
              4waitingNOTHING0
              5durationchangeMETADATA05.357.91获取到视频长度
              6loadedmetadataMETADATA00.667.91获取到元数据
              7loadeddataENOUGHDATA00.667.91
              8canplayENOUGH_DATA00.667.91
              9playingENOUGH_DATA00.667.91开始播放
              10canplaythroughENOUGH_DATA00.667.91可以流畅播放
              11progressENOUGH_DATA0.113.687.91持续下载
              12timeupdateENOUGH_DATA0.144.447.91播放进度变化
              23progressENOUGH_DATA1.777.917.91下载完毕
              24suspendENOUGH_DATA1.777.917.91
              25timeupdateENOUGH_DATA1.97.917.91继续播放中
              48timeupdateENOUGH_DATA7.77.917.91
              49timeupdateENOUGH_DATA07.917.91
              50seekingMETADATA07.917.91
              51waitingMETADATA07.917.91
              52timeupdateENOUGH_DATA07.917.91
              53seekedENOUGH_DATA07.917.91播放完毕进度回到起点
              54canplayENOUGH_DATA07.917.91
              55playingENOUGH_DATA07.917.91循环播放
              56canplaythroughENOUGH_DATA07.917.91
              57timeupdateENOUGH_DATA0.197.917.91

              iOS

              #eventreadyStatecurrentTime (s)buffered (s)duration (s)视频状态
              1loadstartNOTHING0
              2playNOTHING0
              3waitingNOTHING0
              4durationchangeMETADATA07.91获取到视频长度
              5loadedmetadataMETADATA07.91获取到元数据
              6loadeddataENOUGHDATA07.91
              7canplayENOUGH_DATA07.917.91
              8canplaythroughENOUGH_DATA07.917.91可以流畅播放
              9playingENOUGH_DATA07.917.91开始播放
              10progressENOUGH_DATA07.917.91下载完毕
              11suspendENOUGH_DATA07.917.91
              12timeupdateENOUGH_DATA0.027.917.91播放进度变化
              43timeupdateENOUGH_DATA7.87.917.91
              44timeupdateENOUGH_DATA07.917.91
              45seekedENOUGH_DATA07.917.91播放完毕进度回到起点
              46timeupdateENOUGH_DATA0.227.917.91循环播放

              Android

              #eventreadyStatecurrentTime (s)buffered (s)duration (s)视频状态
              1loadstartNOTHING0
              2playNOTHING0
              3waitingNOTHING00
              4durationchangeENOUGH_DATA000
              5durationchangeENOUGH_DATA007.91获取到视频长度
              6loadedmetadataENOUGH_DATA007.91获取到元数据
              7loadeddataENOUGHDATA007.91
              8canplayENOUGH_DATA007.91
              9canplaythroughENOUGH_DATA007.91
              10playingENOUGH_DATA007.91
              11timeupdateENOUGH_DATA007.91
              12progressENOUGH_DATA03.577.91下载中
              13timeupdateENOUGH_DATA0.26.897.91开始播放
              14progressENOUGH_DATA07.917.91下载完毕
              49timeupdateENOUGH_DATA7.797.917.91
              50progressENOUGH_DATA7.877.917.91
              51timeupdateENOUGH_DATA07.917.91
              52seekingENOUGH_DATA07.917.91播放完毕进度回到起点
              53timeupdateENOUGH_DATA07.917.91
              54seekedENOUGH_DATA07.917.91循环播放失败卡住了
              55progressENOUGH_DATA07.917.91
              56stalledENOUGH_DATA07.917.91

              一些常用且需要重点关注的<video>事件

              eventiOSAndroid
              ****************************************************************************************************************
              play只是要播放视频,响应的是video.play()方法,并不代表已经开始播放和iOS一样,仅是响应video.play()方法
              durationchange会执行一次,一定会获取到视频的duration可能会执行多次,只有最后一次才能获取到真实的duration,前面的duration都是0;但低版本Android可能获取到的duration是0或1;(本文提到的低版本Android大部分是4.1以下)
              canplay可以认为是视频元素没有问题,可以运行,没有更多含义了,基本用不上同iOS
              canplaythrough会有明确的缓冲,表示可以流畅播放了;没有什么用,视频仍然会卡住,数据可能还没有开始加载;
              playing明确表示播放开始了;依然没有用,视频可能并没有开始播放;
              progress有明确的下载,可以获取到当前的buffer,并且全部下载完毕后不在触发;不一定有明确的数据下载,并且全部下载完毕后依然继续触发;
              timeupdate会有明确的进度变化,可以获取到currentTime;进度不一定变化,currentTime可能总是0,但是第一次有currentTime变化的timeupdate事件一定代表了视频开始播放了;
              erroriOS中会有明确的错误抛出;Android中某些浏览器会莫名其妙的抛出error;
              stalled网络状况不佳,导致视频下载中断;在没有play之前,也可能会抛出该事件。

              属性差异

              attributesiOSandroid
              ****************************************************************************************************************
              poster

              封面图片

              支持,但是加载速度明显比在<img>中要慢;不一定支持(浏览器厂商的实现标准不统一);
              preload

              预加载

              iPhone不支持;可能支持;
              autoplay

              自动播放

              iPhone Safari中不支持,但在webview中可能被开启;iOS开发文档明确说明蜂窝网络下不允许autoplay;可能支持;
              loop

              循环播放

              支持可能支持;
              controls

              控制条

              支持,但是需要开始播放了才显示基本都支持显示或者不显示
              width和height一定给出明确的属性设置,切不能为0;如果不设置,仅仅通过CSS样式去控制视频大小,可能会导致标签失效。

              其他怪异bug和不友好表现

              iOSandroid
              ******************************************************************************************************************
              物理位置覆盖在<video>区域上的元素,click和touch等事件会失效,比如一个<a>链接如果覆盖在<video>上,那么点击后没有任何效果。
              iOS8.0+中,单页面播放视频超过16个,再播放的视频全部MediaError解码异常无法播放。
              iPhone的Safari会弹出一个全屏的播放器来播放视频,iPad则支持内联播放。iOS7+ 如果webview(比如微信)开启了webview.allowsInlineMediaPlayback = YES;,可以通过设置webkit-playsinline属性支持内联播放;支持内联播放,但某些厂商会用自己的播放器劫持原生的视频播放;
              下载视频时,会先发送一个2字节的请求来获取视频元数据(比如时长),然后再不断的发送分包续传(206)请求来下载视频,抓包显示请求数和请求量至少有一倍的冗余(x2),这个严重的bug在iOS8中有明显的修复,但是分包的206请求仍然会有冗余数据的下载,浪费了流量。比iOS的处理方式好,没有第一个2字节请求,没有流量损耗;
              低版本Android(<=4.0.4)中,<video>如果在有相对和决定定位的层中,可能会导致整个页面错位。
              某些浏览器厂商会劫持<video>,用其“自己”的播放器来播放视频,“破坏”了产品本身的播放体验,那么只能case by case的解决了。
              加载视频时没有进度提示,视觉上看不出是播放完了还是卡住了;加载视频时,大都会显示一个自带的loading UI(菊花)。

              最佳实践

              视频初始化

              如果将一个<video>直接显示在页面中,那么就会看到各种五花八门的播放器初始效果;

              这显然不是一个好的视觉体验,那么通常的做法是制作一个模拟的视频播放视图,比如一个封面加一个播放按钮。

              而真实的<video>视频元素要隐藏起来,如何隐藏呢?最好不要用{display: none}或者{width:0;height:0;}的方式,因为这样视频元素会处于未激活的状态,给后续的处理带来麻烦。最佳的方式是将视频设置成1×1像素大小,放在视觉边缘的位置。

              1

              2

              3

              4

              5

              <!--iOS-->

              <video webkit-playsinline width="1"height="1"class="vplayinside notaplink"x-webkit-airplay controls loop="loop"src="<%=src%>"></video>

              <!--Android-->

              <video width="1"height="1"controls loop="loop"src="<%=src%>"></video>

              自动播放

              autoplay的支持依赖内核和网络状况,比如iPhone在蜂窝网络下明确禁用了autoplay;

              经过试验,在没有明确的用户操作的情况下,直接通过video.play()也是无法激活播放的;

              并且在产品设计上,自动播放也不是一个舒服的用户体验,所以产品设计上尽量避免使用自动播放。

              点击播放

              之前提到,视频最好通过1px大小隐藏起来,那么这时如何触发播放呢?

              经过试验,当在明确的用户操作(touch、click)时,通过这些用户行为事件的回调函数,用video.play()是可以触发视频播放的,那么能否在用户操作后,再去同步的创建和播放视频呢?答案是肯定的,这无疑是一个视频元素初始化的最佳实践,但是有些差异需要注意。

              iOS6+

              可以在用户的touch时间中动态创建并播放视频。

              iOS < 6

              可以在用户的touch时间中动态创建视频,但不能播放;要再追加一个click事件来启动播放;也就是说,给伪造的视频播放按钮同时绑定tap和click事件,在tap的时候创建,在之后300毫秒的click中去播放。

              Android

              大部分高版本Android可以像iOS6+那样去处理,但是低版本的不行,必须要通过click事件去传递video.play(),为了保持兼容,最好是用帮tap和click两个事件来分别完成视频的初始化和播放。

              我们还发现,有些低版本Android中,无法通过video.play()来播放视频,必须有真实的用户点击视频元素才能播放;这种情况,有一个技巧就是在tap的时候初始化并放大视频覆盖在播放视图中,让300毫秒后的真实点击行为穿透点击在视频元素上来实现播放。

              循环播放

              如果视频需要循环播放,那么就增加loop属性,是否能循环播放就看浏览器是否支持了,因为还没有找到hack技巧来强制循环播放;

              即使,在不支持循环播放的Android中,通过监听seeked事件知道了播放进度到了终点或起点暂停了,此时也无法通过video.play()来让视频重新播放。

              监控下载进度

              如何获取视频时长和已经下载的时长?

              1

              2

              3

              4

              5

              6

              7

              8

              9

              10

              11

              12

              13

              // 视频时长

              varduration=video.duration

              // 获取视频已经下载的时长

              functiongetEnd(video){

              varend=0

              try{

              end=video.buffered.end(0)||0

              end=parseInt(end*1000+1)/1000

              }catch(e){

              }

              returnend

              }

              progress事件表示视频在加载,但是它的触发频率和时机并不规律,最佳做法是通过一个定时器去实时获取end,当end >= duration时,表示已经下载完毕,再终止定时器。

              1

              2

              3

              4

              5

              6

              7

              8

              9

              10

              vartimer=setInterval(function(){

              varend=getEnd(video),

              duration=video.duration

              if(end<duration){

              return

              }

              clearInterval(timer)

              },1000)

              全部下载后再播放

              假设播放短视频,如果网络不佳,会造成播放断断续续,在iOS中这种停顿还没有一个明确的等待提示,这不是一个好的体验,那么是否可以将视频全部下载完毕再播放呢?

              在iOS中,可以在视频刚开始下载的时候马上暂停,此时下载还将继续,可以做一个loading的菊花告知视频正在加载,然后等到视频全部下载完再开始播放。

              1

              2

              3

              4

              5

              6

              7

              8

              9

              10

              11

              12

              13

              14

              15

              16

              17

              18

              19

              20

              21

              22

              23

              24

              25

              $(video).one('loadeddata',function(){

              // 暂停,但下载还在继续

              video.pause()

              // 启动定时器检测视频下载进度

              vartimer=setInterval(function(){

              varend=getEnd(video),

              duration=video.duration

              if(end<duration){

              return

              }

              varwidth=$(video).parent().width()

              // 下载完了,开始播放吧

              $(video).attr{

              width:width,

              height:width

              }

              video.play()

              clearInterval(timer)

              },1000)

              })

              缓冲播放——边下边播时,选择开始播放的最佳时间点

              当视频越来越长或者网络慢时,等待视频全部下载完再播放也不是好的体验,最好能边下边播,缓冲到流畅状态就开始播放,那什么时候播放才是最佳时间点呢?

              在iOS中,canplaythrough事件就是这个最佳时间点,它是通过动态计算缓冲量和下载速度得出的视频可以流畅播放的状态反馈。

              canplaythrough event: The user agent estimates that if playback were to be started now, the media resource could be rendered at the current playback rate all the way to its end without having to stop for further buffering.

              注意:下载完再播放和缓冲播放只适用于iOS。

              统计播放时间和播放次数

              要统计实际的播放时间,要累加timeupdate事件变化的时间,再减去中间可能暂停的时间。

              1

              2

              3

              4

              5

              6

              7

              8

              9

              10

              11

              12

              13

              14

              15

              16

              17

              18

              19

              20

              21

              22

              23

              24

              25

              26

              27

              28

              29

              30

              $video.on('playing',function(){

              // 开始播放是打点

              $video.attr('data-updateTime',+newDate())

              })

              $video.on('pause',function(){

              // 暂停播放时清除打点

              $video.removeAttr('data-updateTime')

              })

              // 累加播放时间

              $video.on('timeupdate',function(event){

              var$video=$(event.target),

              updateTime=parseInt($video.attr('data-updateTime')||0),

              playingTime=parseInt($video.attr('data-playingTime')||0),

              times=parseInt($video.attr('data-times')||0),

              newtimes=0,

              video=$video.get(0),

              duration=parseFloat($video.attr('data-duration')||0),

              now=+newDate()

              // 播放时间

              playingTime=playingTime+now-updateTime

              // 播放次数

              newtimes=Math.ceil(playingTime/1000/duration)

              $video.attr('data-playingTime',playingTime)

              $video.attr('data-updateTime',now)

              })

              异常处理

              对error事件做详细的上报;

              对stalled事件做统计上报,并提示用户网络慢等。

              参考数据

              微视触屏版iOS视频测速

              网络环境视频码率获取到视频时长

              时间点(s)

              开始流畅播放

              时间点(s)

              全部下载完毕

              时间点(s)

              视频长度(s)
              wifi1000kbps2.863.975.858.69
              非wifi500kbps4.56810.628.67

              搬好凳子看HTML

              首先我们在HB下创建一个新的app项目,名称为 欠债

              新建一个video.html

              webkit-playsinline : 在ios中,加入此属性,可以关闭自动全屏播放

              object-fit:fill : 视频充满video容器的大小

              详细理由请看参考文献2or3

              在此我们向项目里放置一个mp4格式的视频,视频内容不限,可以是小动画,也可以是

              ps:要在meta中加上,否则视频会扩充变形哦

              <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />

              OK,现在布局已经完成,一个视频已经在页面中了

              旁白:尼玛,点了没反应,那这怎么播放?

              楼主:你们这群家伙看别的小视频等个1小时都行。。。

              旁白:一个简单的播放器,至少要有 暂停/播放,进度条,视频时长,全屏等控件吧

              楼主:来来来,不要急,先来个播放按钮写在video标签后面

               <div class="bad-video">
               <video class="" webkit-playsinline style="object-fit:fill;">
               <source src='xx.mp4' type="video/mp4"></source>
               <p>设备不支持</p>
               <video>
               <img src="img/play.png"/>
               </div>

              写好样式、

               .bad-video { position: relative; overflow: hidden; background-color: #CCCCCC;
               } 
               .bad-video .vplay{ position: absolute; width: 15%; z-index: 99; top: 50%; left: 50%; -webkit-transform: translate(-50%, -50%); transform: translate(-50%, -50%);
               }

              楼主:当当当

              再在后面加一个控制条

               <img src="img/play.png" class="vplay" /> <div class="controls">
               <div>
               <div class="progressBar">
               <div class="timeBar"></div>
               </div>
               </div>
               <div><span class="current">00:00</span>/<span class="duration">00:00</span></div>
               <div><span class="fill">全屏</span></div>
               </div>
              .bad-video .controls { width: 100%; height: 2rem; line-height: 2rem; font-size: 0.8rem; color: white; display: block; position: absolute; bottom: 0; background-color: rgba(0, 0, 0, .55); display: -webkit-flex; display: flex;
              }.bad-video .controls>* { flex: 1;
              }.bad-video .controls>*:nth-child(1) { flex: 6;
              }.bad-video .controls>*:nth-child(2) { flex: 2; text-align: center;
              }.bad-video .controls .progressBar { margin: .75rem 5%; position: relative; width: 90%; height: .5rem; background-color: rgba(200, 200, 200, .55); border-radius: 10px;
              }.bad-video .controls .timeBar { position: absolute; top: 0; left: 0; width: 0; height: 100%; background-color: rgba(99, 110, 225, .85); border-radius: 10px;
              }

              总算有个看起来像样的了

              旁白:楼主,可是还是不能播放啊

              楼主:叫你别急,要不你先去撸一把,我写好了文字@你

              旁白:好啊,早说嘛,我先走了,记得@我

              楼主:你走,省的我精神分裂码两个人的字

              好,现在Html元素已经基本上弄好啦,看起来不是那么low了