H.265
以下是百度百科对于H.265的介绍: H.265是ITU-T VCEG继H.264之后所制定的新的视频编码标准。H.265标准围绕着现有的视频编码标准H.264,保留原来的某些技术,同时对一些相关的技术加以改进。新技术使用先进的技术用以改善码流、编码质量、延时和算法复杂度之间的关系,达到最优化设置。具体的研究内容包括:提高压缩效率、提高鲁棒性和错误恢复能力、减少实时的时延、减少信道获取时间和随机接入时延、降低复杂度等。H.264由于算法优化,可以低于1Mbps的速度实现标清(分辨率在1280P720以下)数字图像传送;H.265则可以实现利用1~2Mbps的传输速度传送720P(分辨率1280720)普通高清音视频传送。
相比H.264带来了很多质的提升,相关对比可以访问H.265与H.264的差异详解。总之,H.265, HEVC 作为当前非常火的视频压缩方式,相对于大家熟知的 H.264 ,平均可以带来接近于 50% 的宽度节省。
相关知识
视频播放器架构
一个典型的现代播放器可以分为三个部分:UI、多媒体引擎和解码器,架构模型如下图:
硬解码支持
随着 4K 视频越来越流行,Apple公司的最新的操作系统版本(Mac Hight Sierra和iOS 11)迎来了 HEVC (高效视频编码,也称 H.265) 这一新的行业标准[6]。与现行的 H.264 视频压缩标准相比,它的视频压缩率最高可提升 50% 之多。使用H.265,在保持视频画质不变的情况下,视频流媒体传输效果更好。而在相同码率下,能给质量带来近两倍的提升。下图是两张相同码率相同分辨率(400kpbs 1080p)的图片,左边的采用H.265编码,右边的采用H.264编码。
Web软解方案
除了硬解码方案之外,软件解码也成为一种有效的选择,由于H.265视频的解码是一个对性能要求很高的CPU密集任务,Web端脚本语言实现的解码器的性能很难达到要求。基于此,我们可以通过基于Flash的H.265解码方案,即通过FlasCC[11]编译器把C语言编写的解码器编译成swc库,然后在Flash播放器中用Action Script调用swc库。
另一种方案是基于HTML5的,即通过WebAssembly技术将金山云自研的高性能解码器编译为wasm库,wasm文件是以二进制形式存在的,其中包含平台无关的虚拟指令(类似汇编指令)。这也是很多移动平台采用的方案。
相关HTML5技术
下图是播放器内核主要模块与依赖的背景技术。
Media Source Extensions(简称MSE):
提供了实现无插件且基于 Web 的流媒体的功能。使用MSE API(主要包括:Media Source,Source Buffer等),媒体流能够通过 JavaScript 创建,并且能通过HTMLMediaElement元素(包括:video和audio元素)进行播放。IE11(win8+)及其他现代浏览器都支持。
Streams
标准提供了一套API,来创建和操作流数据,具体地,包括ReadableStream, WritableStream, 以及TransformStream。这允许我们可以增量地处理数据,而不必将所有数据缓存到内存中统一处理。我们可以采用Fetch API获取视频数据,返回的body是一个ReadableStream对象。该对象代表一个数据源,内部维护了一个队列来记录尚未被读取的底层数据源。可以通过ReadableStream的getReader()接口读取内部队列中chunk数据。
Web Workers
让单线程的JavaScript具备了多线程编程的能力,让视频播放器内核可以分离解复用、解码、渲染、UI操作监听等任务到不同的线程中,并行地处理计算密集型任务和界面显示等。worker间通信是通过MessageChannel进行。IE10+及其他现代浏览器都支持。
WebAssembly
是Web端的字节码技术,它定义了一个通用的、体积紧凑、加载迅捷的二进制格式为编译目标,能发挥通用硬件的性能,以更接近原生应用的速度运行。在浏览器中对H.265编码的视频进行软件解码,是一项对性能非常有挑战的任务,JavaScript等脚本语言无法胜任此项工作。因此可以将C/C++语言编写的高性能解码库编译成字节码,再通过JavaScript调用来运行。目前此项技术在Chrome、Firefox、Safari和Edge浏览器的较新版本中都可以使用(如Chrome57+,Firefox 52+)。
H.265 Vs H.264
关于H.265 与 H.264更详细的差异,可以访问H.265与H.264的差异详解。
H.265/HEVC的编码架构大致上和H.264/AVC的架构相似,主要也包含:帧内预测(intra prediction)、帧间预测(inter prediction)、转换(transform)、量化(quantization)、去区块滤波器(deblocking filter)、熵编码(entropy coding)等模块。
在HEVC编码架构中,整体被分为了三个基本单位,分别是编码单位(coding unit, CU)、预测单位(predict unit, PU)和转换单位(transform unit, TU)。比起H.264/AVC,H.265/HEVC提供了更多不同的工具来降低码率,以编码单位来说,H.264中每个宏块(macroblock/MB)大小都是固定的16x16像素,而H.265的编码单位可以选择从最小的8x8到最大的64x64。
同时,H.265的帧内预测模式支持33种方向(H.264只支持8种),并且提供了更好的运动补偿处理和矢量预测方法。 反复的质量比较测试已经表明,在相同的图象质量下,相比于H.264,通过H.265编码的视频大小将减少大约39-44%。由于质量控制的测定方法不同,这个数据也会有相应的变化。
支持情况
ios
目前,根据苹果官网的资料,对于HEVC的支持情况可以用下面的一句话来说明: iOS 11 and macOS High Sierra introduced support for these new, industry-standard media formats。 也即是说,下面的设备都是支持的。
- iPhone 7 or iPhone 7 Plus or later
- iPad (6th generation)
- iPad Pro (10.5 inch)
- iPad Pro 12.9-inch (2nd generation)
Android
浏览器
目前,浏览器对于H.265支持的并不是很友好:
实战
目前,HEVC 的普及速度还没有那么快,不过我们还是可以尝试在 Web 中优雅的播放 H265 视频。
判断是否支持播放
要判断平台是否支持H.265格式的视频,可以通过H265 的 mimetype 值来判断:type="video/mp4; codecs=hevc"。例如:
var supportHEVC = function(video) {
if (typeof video.canPlayType == ‘ function’) {
var playable = elem.canPlayType('video/mp4; codecs="hevc"');
if ((playable.toLowerCase() == 'maybe') || (playable.toLowerCase() == 'probably')) {
return true;
}
}
return false;
};
复制代码
如果,使用H.265无法播放视频,则使用H.264进行播放,所以我们可以通过 source 设置多种格式:
<video controls autoplay>
<source src="your_video.mp4" type="video/mp4; codecs=hevc">
<source src="your_video.webm" type="video/webm; codecs=vp9">
<source src="your_video.mp4" type="video/mp4; codecs=avc1">
</video>
复制代码
对于web平台来说,我们用到的是libde265.js,它是一个通过 JS 来解码 H.265 视频的库,它通过将 视频的 frame data 转化为 rgba 像素,然后绘制到 Canvas 上。下面是使用示例:
<canvas id="canvas"></canvas>
<script src="./libde265.min.j"></script>
<script>
var VIDEO_URL = 'h265-test-640.mp4'
var video = document.getElementById('canvas')
// var fpsWrap = document.querySelector('.hevc-fps')
var status = document.querySelector('.hevc-status')
var playback = function (event) {
// event.preventDefault()
if (player) {
player.stop()
}
player = new libde265.RawPlayer(video)
player.set_status_callback(function (msg, fps) {
player.disable_filters(true)
console.log(msg);
switch (msg) {
case 'loading':
status.innerHTML = 'Loading movie...'
break
case 'initializing':
status.innerHTML = 'Initializing...'
break
case 'playing':
status.innerHTML = 'Playing...'
break
case 'stopped':
status.innerHTML = 'stopped'
break
case 'fps':
// fpsWrap.innerHTML = Number(fps).toFixed(2) + ' fps'
break
default:
status.innerHTML = msg
}
})
player.playback(VIDEO_URL)
}
playback()
</script>
复制代码