H5直播系列三 Video

参考
HTML5视频的那些事儿
H5-Video 能做的事&存在的坑
浅谈video标签在移动端的运用
视频播放--踩坑小计
h5 video 移动端填坑记
微信H5 Video 开发小结
H5 video使用总结
移动端 HTML5 <video> 视频播放优化实践

一、基本属性
<video width="658" height="444" 
src="http://www.youname.com/images/first.mp4" 
poster="http://www.youname.com/images/first.png" 
autoplay="autoplay" 
preload="none"
loop="loop"
controls="controls"></video>
1.src属性和poster属性

你能想象src属性是用来干啥的。跟<img>标签的一样,这个属性用于指定视频的地址。而poster属性用于指定一张图片,在当前视频数据无效时显示(预览图)。视频数据无效可能是视频正在加载,可能是视频地址错误等等。

2.preload属性

这个属性也能通过名字了解用处,此属性用于定义视频是否预加载。属性有三个可选择的值:none、metadata、auto。如果不使用此属性,默认为auto。

  • none:不进行预加载。使用此属性值,可能是页面制作者认为用户不期望此视频,或者减少HTTP请求。
  • metadata:部分预加载。使用此属性值,代表页面制作者认为用户不期望此视频,但为用户提供一些元数据(包括尺寸,第一帧,曲目列表,持续时间等等)。
  • auto:全部预加载。
3. autoplay属性

又是一个看名字知道用处的属性。Autoplay属性用于设置视频是否自动播放,是一个布尔属性。当出现时,表示自动播放,去掉是表示不自动播放。但是在ios上无法执行自动播放......(添加muted可以自动播放)需要用户执行play方法。(具体可参考视频播放--踩坑小计

4.loop属性

一目了然,loop属性用于指定视频是否循环播放,同样是一个布尔属性。

5.controls属性

设置或返回音频/视频是否显示控制条(比如播放/暂停等)

6.playsinline webkit-playsinline

视频在移动端播放时会自动全屏,而这个属性就是为了阻止全屏动作的,添加-webkit-前缀增加在ios safari上的兼容性。(只用写上属性名就行,和autoplay,loop之类的类似)

7.muted

如果出现该属性,视频的音频输出为静音。

8.通用的video事件

play:视频开始播放触发的事件(触发此事件,但是视频不一定可以播放)
playing:视频可以播放触发的事件
timeupdate:音频/视频(audio/video)的播放位置发生改变时触发
pause:视频停止播放触发的事件
ended:视频播放结束或中断触发的事件

更多属性和事件,参考总结H5 video 方法 属性 事件

二、兼容性
1.视频格式
image.png

当前,video 元素支持三种视频格式:

  • Ogg = 带有 Theora 视频编码和 Vorbis 音频编码的 Ogg 文件
  • MPEG4 = 带有 H.264 视频编码和 AAC 音频编码的 MPEG 4 文件
  • WebM = 带有 VP8 视频编码和 Vorbis 音频编码的 WebM 文件

浏览器对视频格式的支持各不相同,小一点的浏览器厂商比如firefox和opera不愿支持商业的视频格式(mp4),因为需要支付专利费,而大一点的厂商如微软苹果等,不愿支持开源的格式,因为可能有专利问题。为了解决兼容性的问题,HTML5也给出了解决办法,那就是source标签。

<video controls autoplay>
    <source src="media/butterfly.mp4" type="video/mp4">
    <source src="media/butterfly.webm" type="video/webm">
    <source src="media/butterfly.ogv" type="video/ogg">
    <p>当前环境不支持video标签。</p>
</video>

浏览器会最先尝试播放第一个视频,如果发现不支持会播放第二个,依次类推直到找到一个可以播放的,或者全部能播放。。

2.字幕

字幕也是一个复杂的问题,简单的一个字幕就可能有下面的需求:格式,换行,颜色,卡拉OK等。所以现存的字幕格式就有50多种。使用字幕的方式和使用source的方式类似,同时可指定多个字幕文件,用来指代不同语言的字幕,用户可以自己选择想要的字幕。

<video controls loop autoplay>
    <source src="media/butterfly.mp4" type="video/mp4">
    <source src="media/butterfly.webm" type="video/webm">
    <track src="media/butterfly.vtt" srclang="en" kind="subtitles" label="English" default>
    <track src="media/butterfly_fr.vtt" srclang="fr" kind="subtitles" label="French">
</video>

vtt格式如下所示,标记了每个字幕开始出现的时间和消失的时间。

WEBVTT

00:00:01.000 --> 00:00:03.000
Butterflies are lovely.

00:00:04.000 --> 00:00:08.000
Don't you think?
3.HTMLVideoElement的canPlayType API进行当前环境的格式兼容判断
let videoEl = document.createElement("video");

// 是否支持 MP4
videoEl.canPlayType('video/mp4') !== '';

// 是否支持 MP4 & 特定编码的
videoEl.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"') !== '';
// 是否支持 webm & 特定编码的
videoEl.canPlayType('video/webm; codecs="vp8, vorbis"') !== '';
// 是否支持 ogg & 特定编码的
videoEl.canPlayType('video/ogg; codecs="theora, vorbis"') !== '';

// 是否支持 HLS 的 m3u8
videoEl.canPlayType('application/vnd.apple.mpegURL') !== '';
// 是否支持 HLS 的 TS 切片
videoEl.canPlayType('video/mp2t; codecs="avc1.42E01E,mp4a.40.2"') !== '';

使用canPlayType方法传入要检测的媒体类型或具体编码格式,我们将可能得到'maybe'、'probably'、''三个String值中的一个,当得到空字串的时候,可以确定为不支持。

4.各环境中Video UI不一致

我们拿同一个页面,放一个“大白兔”的示例视频,在不同的浏览器环境下观察截图:


image.png

怎么样,意不意外?惊不惊喜?同一个浏览器不同的版本都可能大相径庭,当你的用户给你反馈播放器上一些奇奇怪怪按钮看不懂事干嘛的、一些奇特的功能用着不爽的时候,你能确定他说的与你看到的是一个东西吗?

我们针对不同的环境,必须做一些符合它的要求的配置,以期望能尽量达成我们想要的目的。但是不得不说的是,标准API里的controls、poster、autoplay也都未必有效(只是想隐藏控制条都得想各种纠结的办法--裁剪出镜、更或者一些宣传短片干脆做成序列帧...),前面说的通过source来设置不同媒体源的方案,部分浏览器也可能是不认识的。

W3C标准和MDN告诉我们可以通过VideoElement.error取得媒体播放异常的参考信息,但是实践告诉我们,很多环境下是压根没有这么个东东,或者参考信息不够详尽靠谱。

通过MediaEvents&API 检测,会发现各环境中播放进度变化、事件触发频率不同、部分事件触发时相应状态值未必可靠、部分场景缺少事件(全屏状态变化、系统播放器劫持)、seek时不一定触发play...

例如ios部分环境监听canplay和canplaythrough(是否已缓冲了足够的数据可以流畅播放),当加载时是不会触发的,即使preload="auto"也没用,但在pc的chrome调试器下和android下,是会在加载阶段就触发,ios需要播放后才会触发。

部分环境中视频从开始播到能展现画面会有短暂的黑屏(处理视频源数据的时间),为了避免这个黑屏,可以在视频上加个浮层或设置容器背景并在播放前隐藏video,然后监听相关事件,开始播放或认为有画面的时候再切换到Video界面。

所以总体来看,并没有可靠统一的规范。

5.状态处理容易存在冲突

假如我们将原生video元素视作一个数据状态储存容器,那么可以看到以上四种需求需要如下权限:

对于有播放状态控制的一些控件,会同时需要状态的读、写;对于展示式需求,例如无交互的弹幕、打赏礼品、特效挂件等,只需要读状态 --- 例如暂停则停止;更高级的,例如片头、片尾、插片广告,我们不但要知道播放状态、时机,甚至要在广告播放期间阻止一切默认行为,直到允许用户跳过等,这就不只是需要完整的读写权限,还要具备阻截别人读写的功能...

当业务逻辑日积月累状态操作的场景越来越繁杂,缺乏合理的状态管理机制,势必会导致状态混乱、冲突的产生。

6.交互与层级管理的矛盾

UI插件的需求在业务中也是纷繁复杂的,比如通常的弹幕插件是不需要交互,只要滚动展示内容,这时候我们点击弹幕跟点击播放器本身一样要触发暂停或播放行为。突然有一天需要夹杂展示点击后跳转详情的广告内容,这时需要交互行为,这也就与前面的需求相矛盾了。

再加上一些动态变更层级的UI组件---比如右键菜单、popup配置浮层... 这也需要有一套可靠灵活的层级管理机制,来支撑这些可能出现的需求。


image.png
7.视频层级

在实际中,经常会有产品同学过来说,我要在视频上加下按钮,加下信息之类。嗯,理想很美好,但是现实很骨感。至今,除了在IOS的微信上可以做到这种效果之外,其余的主流浏览器都不支持。在这些浏览器里面,视频的层级是最高的。

三、Laya中使用video
1.添加原生的video
class DOM_Video {
    constructor() {
        Laya.init(800, 600);
        Laya.stage.bgColor = "#FFFFFF";
        Laya.stage.alignH = Laya.Stage.ALIGN_CENTER;
        Laya.stage.alignV = Laya.Stage.ALIGN_MIDDLE;
        
        // 创建Video元素
        var videoElement:any = Laya.Browser.createElement("video");
        Laya.Browser.document.body.appendChild(videoElement);
        
        // 设置Video元素地样式和属性
        videoElement.style.zInddex = Laya.Render.canvas.style.zIndex + 1;
        videoElement.src = "../../res/av/mov_bbb.mp4";
        videoElement.controls = true;
        // 阻止IOS视频全屏
        videoElement.setAttribute("webkit-playsinline", true);
        videoElement.setAttribute("playsinline", true);
        
        // 设置画布上的对齐参照物
        var reference:Laya.Sprite = new Laya.Sprite();
        Laya.stage.addChild(reference);
        reference.pos(100, 100);
        reference.size(600, 400);
        reference.graphics.drawRect(0, 0, reference.width, reference.height, "#CCCCCC");
        
        // 每次舞台尺寸变更时,都会调用Utils.fitDOMElementInArea
        //设置Video的位置,对齐的位置和refence重合
        Laya.stage.on(Laya.Event.RESIZE, this, Laya.Utils.fitDOMElementInArea,
        [videoElement, reference, 0, 0, reference.width, reference.height]);
    }
}
new DOM_Video();
2.视频(Video)播放问题

LayaAir关于视频的实现和原生不一样,我们的视频是绘制到了canvas上,可以控制显示区域的大小,原生是直接使用的html5标签,如果你的项目不存在像游戏里的交互功能的话,建议你可以直接使用原生video标签来实现。两者都会存在兼容性问题,layaAir的可能在某些浏览器无法播放,原生的无法控制播放区域的大小,关于这个问题还请开发者自行舍取。

参考Video.as:

public class Video extends Sprite
...

public function Video(width:int = 320, height:int = 240)
{
    super();
    
    if (Render.isWebGL)
        htmlVideo = new WebGLVideo();
    else
        htmlVideo = new HtmlVideo();

    videoElement = htmlVideo.getVideo();
    videoElement.layaTarget = this;
    
    internalTexture = new Texture(htmlVideo);
...

/**
 * 开始播放视频。
 */
public function play():void
{
    videoElement.play();
    Laya.timer.frameLoop(1, this, renderCanvas);
}

private function renderCanvas():void
{
    if (readyState === 0)
        return;
    
    if (Render.isWebGL)
        htmlVideo['updateTexture']();
    
    this.graphics.clear();
    this.graphics.drawTexture(internalTexture, 0, 0, this.width, this.height);
}

override public function size(width:Number, height:Number):Sprite
{
    super.size(width, height)
    
    videoElement.width = width / Browser.pixelRatio;
    
    if (paused) renderCanvas();
    return this;
}

从截取的部分代码中,可以看出是在帧事件中使用graphics.drawTexture把原生的video画面绘制到了canvas上。

3.解决fitDOMElementInArea在ios下不能正常显示
    Utils.fitDOMElementInArea=function(dom,coordinateSpace,x,y,width,height){
        if (!dom._fitLayaAirInitialized){
            dom._fitLayaAirInitialized=true;
            dom.style.transformOrigin=dom.style.webKittransformOrigin="left top";
修改为:dom.style.transformOrigin=dom.style.webkitTransformOrigin="left top";
            dom.style.position="absolute"
        };
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,884评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,212评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,351评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,412评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,438评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,127评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,714评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,636评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,173评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,264评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,402评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,073评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,763评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,253评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,382评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,749评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,403评论 2 358

推荐阅读更多精彩内容

  • 硬件编码相关知识(H264,H265) 阅读人群:研究硬件编码器应用于iOS开发中,从0研究关于硬件编解码,码流中...
    小东邪啊阅读 12,772评论 0 18
  • [TOC] 音视频&流媒体 是什么促使我要写这一篇音视频入门文章?那是因为和一妹子打赌码率的概念,结果输了;对一个...
    AllenWu阅读 4,835评论 1 24
  • 为什么进行压缩编码? 视频是由一帧帧的图像组成(见实例)比如一张Gif图片其实就可以被分解成若干张单独的图片1.g...
    Tate_code阅读 975评论 0 3
  • 视频编码介绍 为什么进行压缩编码? 视频是由一帧帧的图像组成(见实例)比如一张Gif图片其实就可以被分解成若干张单...
    coderwhy阅读 2,322评论 3 15
  • 为什么要进行编码 因为未经过编码(压缩)的视频,具有极大的数据量,不利于存储,传输,实时应用. 视频编码的原理 空...
    痴人会说梦阅读 3,277评论 6 3