关于 video 播放的新探索
前端同学要使用 HTML5 播放器视频,必然会使用 video 标签,不过大多数同学只是使用了较简单的功能,其实它本身拥有不凡之力有待我们发现。
首先,我们先来看下 video 最基础的用法:
使用 src 属性
<video src="http://v2v.cc/~j/theora_testsuite/320x240.ogg" controls>
你的浏览器不支持 <code>video</code> 标签。
</video>使用 source 标签
<video controls>
<source src="foo.ogg" type="video/ogg">
<source src="foo.mp4" type="video/mp4">
Your browser does not support the <code>video</code> element.
</video>
这是 MDN 关于 video 给出的基本用例。在这里我们简单介绍下两种方法的不同,src 只能赋予 video 一个播放地址,当浏览器不支持这种视频格式的解码时就会出现错误,导致视频播放失败。为了解决这个问题才有了 source 标签,利用多个 source 标签引入不同格式的视频,从上到下解析直到遇到看上述代码当浏览器不支持 ogg 格式时,浏览器会自动播放 foo.mp4。
实用技巧
我们会发现使用 video:src 属性播放视频的时候会经常出现播放失败,我们该怎么做能提升视频播放的质量?
这种情况一般都使用 cdn,为了更保险一般还会将不同的 cdn 厂商分为主用 cdn 和备用 cdn。那么问题来了,我们怎么利用 video 本身的特性并结合 cdn 保证我们的视频播放质量?
<video controls>
<source src="//a.com/main.mp4" type="video/mp4">
<source src="//b.com/backup.mp4" type="video/mp4">
Your browser does not support the <code>video</code> element.
</video>
不难看出,source 标签不仅支持不同视频格式的自动切换,也适用于相同视频格式的失败切换,即 main.mp4 因网络问题无法获取时浏览器会自动切换到 backup.mp4 。
高阶技能
如果大家浏览视频的时候,可以发现很多网站的 video 是这个样子的:
<video src="blob:http://abc.com/d0823f0f-2b2a-4fd6-a93a-e4c82173c107">
</video>
如果直接访问 blob 这个地址发现并不存在。
这个地址就是映射 Blob 对象的 DOMString,其实 video 属性 src 支持 Blob 的,不过新的标准是使用 srcObject 属性来替代这一个功能,目前代码可以这样写:
const mediaSource = new MediaSource();
const video = document.createElement('video');
try {
video.srcObject = mediaSource;
} catch (error) {
video.src = URL.createObjectURL(mediaSource);
}
在这段代码中除了 Blob 对象,还有 MediaSource 对象,让 video 拥有不凡之力的主要因素是浏览器对 MediaSource 对象的支持,才让 JavaScript 对 video 有了更大的操纵空间。关于如何使用 MediaSource 不在本文讲述,大家可以自行查阅,我们要说的是 video 结合 MediaSource 可以做哪些事情?
清晰度无缝切换
点播领域里 mp4 是最普遍、兼容性最好的视频容器,不过 mp4 也有它的局限性,比如常见的清晰度切换,我们是无法像youtube那样做到无缝切换的。我们可以看下普通的mp4播放的网络请求和youtube视频播放的网络请求的区别。
图1.1 普通mp4的下载请求过程
图1.2 Youtube视频下载请求过程
这两张图不难看出,在默认情况下 mp4 使用一次 http 请求所有的视频数据,Youtube 则分次请求。当然这个描述很不专业,但确实形象。造成这种差异的是 video 不支持流式的视频数据,Youtube 采用的是流式的视频容器 webm,而 mp4 是非流式的。那如何解释清楚流式的视频数据呢,从专业的角度三言两语很难说清楚,但用大白话翻译过来就是流式的视频数据支持分段独立播放,非流式的不可以。换句话说一个10M的视频文件,流式的视频可以把0~1M的数据请求回来单独播放,但是非流式的不可以。
上面我们描述了视频格式的不同,接下来我们要说的是第一张图中的视频加载是浏览器来控制的,通过给 video 的 src 属性配置视频地址,触发播放之后浏览器就会开始下载了,JS干涉不了。而 Youtube 的视频加载是通过JS来控制的,各位可以再次看下第二张图的网络请求类型:xhr,足以证明这一点。
上面两点搞清楚之后我们就该说下清晰度切换的事情了。这个需求大家都不陌生,但是直接使用 mp4 格式做无缝清晰度切换,难度还挺大的。先解释下“无缝清晰度切换”的概念:从播放一个分辨率的视频到另一个分辨率且保证画面、声音不停顿的平滑切换过程。了解了这个概念,大家应该知道了用 video 无缝切换 mp4 有多难。一方面,video 是不支持流式的视频格式的,一方面,video 的加载是不受JS控制的。通过切换 video 的 src 属性,必然会导致画面中断、重新请求视频数据等。有的同学想到说利用两个 video 再结合 z-index 来搞,但是当你生成另一个video去加载视频的时候,无法保证两个画面是严格一致的,即使将原来的画面暂停到一个时刻,用另一个视频通过 currentTime 属性与之同步,切换仍然看到画面闪烁,基本无法和 Youtube 无缝切换的体验匹敌。而且还会造成更多流量的浪费,背后的原因大家可以研究下 mp4 容器和 webm 容器的异同,也可以看下视频解码相关的文章。
还有一种方法就是将 mp4 格式统统转码到流式的视频格式比如 hls、webm 等。不过这种看上去可行的方式实际上会带来很大的成本开销,如将大量视频做转码会消耗高昂的机器资源、双倍存储的费用、CDN的双倍费用等等。其实我们也是在这种背景下研究出来新的技术问题解决清晰度无缝切换的。
首先,我们改变对 mp4 视频的播放流程,不再直接使用 video 的 src 来播放,因为我们没有任何可以操作的空间。video不仅支持 src 属性还支持 Blob 对象,我们就是利用后者。播放的流程如下:
图1.3 mp4 视频新播放流程
- 来请求 mp4 视频数据,这样可以结合视频 Range 服务,做到精确加载。
- 编写解析器将加载回来的部分 mp4 视频数据进行解复用
- 将解复用的视频数据转成 fmp4 格式并传递给 MediaSource
- 使用 video 进行解码完成播放
然后在做清晰度切换的时候流程如下:
图1.4 mp4视频清晰度切换原理示意图
- 来请求 mp4 视频数据,这样可以结合视频 Range 服务,做到精确加载。
- 编写解析器将加载回来的部分 mp4 视频数据进行解复用
- 将解复用的视频数据转成 fmp4 格式并传递给 MediaSource
使用 video 进行解码完成播放
然后在做清晰度切换的时候流程如下:
图1.5 mp4视频清晰度切换流程示意图这个过程看上去比较繁琐,但是所有的操作都是在浏览器端完成,也就是说都是JS来实现的。这样之前说的所有成本问题都不存在,还能做到youtube相同体验的无缝切换。如果大家也想使用这个功能不需要自己再去实现一遍上述流程,可以使用如下代码:
import Player from 'xgplayer';
import 'xgplayer-mp4'; let player = new Player({
el:document.querySelector('#mse'),
url: [{src:'/mp4/',type:"video/mp4"},{src:'/mp5/',type:'video/mp4'}]
}); player.emit('resourceReady', [{name: '高清', url: '/mp4/',cname:'高清'}, {name: '超清', url: '/mp5/',cname:'超清'}]);
如果对这段代码有什么疑惑或者想深入了解下它背后是如何实现的可以参考 文档 或者 Github。
节省视频流量
我们平时直接使用video加载视频,大概是这样的:
图2.1 video默认下载截图
我随便找了个视频,大家看下视频总长度是 02:08,在播放到 00:05 的时候,浏览器已经下载到 01:30 了,如果用户终止观看,下载的视频就这样被浪费掉了。当然,如果不断的 seek 也会造成较多的流量浪费。按照我们之前的统计在短视频领域,用户 seek 的频率在 80%,所以这部分流量是可以节省掉的。具体原理如下:
图2.2 播放器加载视频原理
- 设置每次加载的数据包大小
- 设置预加载时长
- 开启加载队列,完成第一次数据包下载,判断缓冲时间和预加载时长是否满足,不满足请求下一个数据包
具体实现代码如下:
import Player from 'xgplayer';
import 'xgplayer-mp4';
const player = new Player({
id:'vs',
url:'//abc.com/a/mp4',
preloadTime:10
});
这样就实现了视频在播放过程中永远只预加载10秒的数据,进而保证节省流量。
本文转载于:关于 video 播放的新探索
关于 video 播放的新探索的更多相关文章
- IOS(苹果手机)使用video播放HLS流,实现在内部播放及全屏播放(即非全屏和全屏播放)。
需求: 实现PC及移动端播放HLS流,并且可以自动播放,在页面内部播放及全屏播放功能. 初步:PC及安卓机使用hls.js实现hls流自动播放及全屏非全屏播放 首先使用了hls.js插件,可以实现在P ...
- 网页前端video播放m3u8(HLS)
网页前端video播放m3u8(HLS) HLS (HTTP Live Streaming)是Apple公司研发的流媒体传输技术,包括一个m3u8的索引文件.多个ts分片文件和key加密串文件.这项技 ...
- jquery和css自定义video播放控件
下面介绍一下通过jquery和css自定义video播放控件. Html5 Video是现在html5最流行的功能之一,得到了大多数最新版本的浏览器支持.包括IE9,也是如此.不同的浏览器提供了不同的 ...
- jQ效果:jQuery和css自定义video播放控件
下面介绍一下通过jquery和css自定义video播放控件. Html5 Video是现在html5最流行的功能之一,得到了大多数最新版本的浏览器支持.包括IE9,也是如此.不同的浏览器提供了不同的 ...
- 关于大视频video播放的问题以及解决方案(m3u8的播放)
在HTML5里,提供了<video>标签,可以直接播放视频,video的使用很简单: <video width="320" height="240&qu ...
- video播放视频以及相关事件
<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Conten ...
- offset新探索:双管齐下,加速大数据量查询
摘要:随着offset的增加,查询的时长也会越来越长.当offset达到百万级别的时候查询时长通常是业务所不能容忍的. 本文分享自华为云社区<offset新探索:双管齐下,加速大数据量查询> ...
- Vuejs---《Vue.js + Node.js-构建音乐播放器新玩法-video》
官方文档:http://vuejs.org/v2/guide/syntax.html 中文文档: https://cn.vuejs.org/v2/guide/syntax.html https://g ...
- android surfaView surfaHolder video 播放
主文件 package cn.com.sxp;import android.app.Activity;import android.media.AudioManager;import android. ...
随机推荐
- ElementUI Tree树形控件renderContent return时报错
问题描述: 使用Tree树形控件使用render-content渲染时return后报错或npm run dev时候报错,报错信息相同,如下: 问题分析: renderContent函数中需要使用js ...
- ELKB-ElasticSearch-Logstash-Kibana-beats 个人理解
先说一下ELK,E是ElasticSearch,L是Logstash,K是Kibana,还有一个Beats.按照从采集到展示的顺序介绍下各个组件的作用. 1.Beats Beats 是一个免费且开放的 ...
- laravel 解决跨域问题并封装到中间件
larav 官方手册有详细的步骤 https://learnku.com/articles/6504/laravel-cross-domain-solution 我们在用 laravel 进行开发的时 ...
- laravel7 搜索关键字标红及手机号,身份证号隐藏
控制器代码 public function index(Request $request) { //接受搜索关键字 $word = $request->get('name'); $start = ...
- tp5 商城商品模型删除
1:控制器代码 public function delete($id) { //验证id是否正确 $id if (!intval($id)) { return getJsonData(10010, ' ...
- chap8-fluent python
浅拷贝 VS 深拷贝 # In[] # list 生成一个新的引用对象,只是用alst完成初始化 alst = [1,2,3,4,5] blst=list(alst) alst.append(6) p ...
- Golang 数据结构
每种语言在实现数据结构有些许不同.go 是如何实现的呢? 1. 数组 Array go 中数组是相同的元素组成的集合,计算机会为数组分配一段连续的内存来保存元素,可以利用索引快速访问元素. go 中数 ...
- 详解数据库三种删除方法: delete drop truncate
Delete Delete :删除数据表中的行(可以删除某一行,也可以在不删除数据表的情况下删除所有行). 删除某一行:Delete from 数据表名称 where 列名称=值: 删除所有行:Del ...
- python练习册 每天一个小程序 第0004题
1 #-*-coding:utf-8-*- 2 __author__ = 'Deen' 3 ''' 4 题目描述:任一个英文的纯文本文件,统计其中的单词出现的个数. 5 参考学习链接: 6 re ht ...
- 百度Paddle速查_CPU和GPU的mnist预测训练_模型导出_模型导入再预测_导出onnx并预测
需要做点什么 方便广大烟酒生研究生.人工智障炼丹师算法工程师快速使用百度PaddelPaddle,所以特写此文章,默认使用者已有基本的深度学习概念.数据集概念. 系统环境 python 3.7.4 p ...