可以使用页面底部的测试资源查看效果。

URL:
KEY:
KE4KEY:
KeyType:
keyList:
渲染项:
MaxStreamCacheSize:
minDecoderBufferSize:
delayTimeLimit:
控制台日志:
       

本播放器说明文档:https://zyun.360.cn/developer/doc?did=QHWWPlayer

你可能用的到:FLV H264/H265 编码类型探测

相关文章分享:Web前端HEVC播放器QHWW实践剖析

测试资源(直播资源随时可能失效)


水滴m3u8点播测试地址:

不加密的:http://vcloud.demo.zyun.360.cn/vod-system-bj/360E0T5060000061_01_01_1581474758.m3u8

加密短视频:http://vcloud.demo.zyun.360.cn/vod-system-bj/360E0T5060000061_01_01_1581479878.m3u8
KEY: 8557a880e8080b805505a2e53fa2ebfc5803d45e50b4ef6c102e3400450b6a6f

加密长视频:http://vcloud.demo.zyun.360.cn/vod-system-bj/360E0T5060000061_01_01_1581496215.m3u8
KEY:8557a880e8080b805505a2e53fa2ebfc5803d45e50b4ef6c102e3400450b6a6f

非加密长的: http://vcloud.demo.zyun.360.cn/vod-system-bj/360E0T5060000061_01_01_1581504512.m3u8
第三方M3U8测试源:

点播:https://vod.cdn.huajiao.com/live_huajiao_v2/vod-bucket-bj/_LC_AL1_non_19259160715819632591515301_OX_1581973506.m3u8
直播:http://ivi.bupt.edu.cn/hls/cctv1hd.m3u8

FLV 直播 KeyMap 测试资源:

http://flvipc.demo.zyun.360.cn/live_demo/360E0T5060000065_01_01.flv

KeyList: [{"index":1,"key":"010e5d786b6209fb637afc89ae01d724"},{"index":2,"key":"ee75e150815aa311b73382f3b163909a"},{"index":3,"key":"6a7f4741c8ede0a2926afc0993fef706"},{"index":4,"key":"dc5e74b8811aca7a187430afe0c40ca7"},{"index":5,"key":"066b8a6dd1ff80371270dbff1cc58be9"},{"index":6,"key":"9f88797401ac983d6bd9c6836da1e2bc"},{"index":7,"key":"2248769fe1b6052f2d8f8e5fa161f1b4"},{"index":8,"key":"d09cd1f620ea1155120cf5211cc6c4f2"},{"index":9,"key":"57ca3d5c8287dcc655124dd0dbbda81a"}]

点播试验资源:

《屌丝男士》: http://vod.pyzy.net/vod-hzj-vod-test-100274-beijing/221133259_3_mp4-1567559796-252c06d6-a7d7-7026.mp4

《憨豆先生》:
http://vod.pyzy.net/vod-hzj-vod-test-100274-beijing/221205948_3_mp4-1567658625-13f118e5-62a9-891c.mp4

《你看起来好美味》:
http://vod.pyzy.net/vod-hzj-vod-test-100274-beijing/221205933_3_mp4-1567658611-99c4cedd-b013-4d4e.mp4

《悬崖上的金鱼姬》:
http://vod.pyzy.net/vod-hzj-vod-test-100274-beijing/221205347_3_mp4-1567657946-4c9d2fce-ad97-589b.mp4

加密点播:
http://vod.pyzy.net/vod-hzj-vod-test-100274-beijing/TL7f1338f022d3fdc7f504c365b356952d.mp4
76a6c65c5ea762046bd749a2e632ccbb

短视频:
http://vod.pyzy.net/vod-hzj-vod-test-100274-beijing/221501274_3_mp4-1568013805-33adda6e-73ad-ac1f.mp4

    
## QHWWPlayer 简单介绍

QHWWPlayer 是奇虎360视频云为解决HEVC(H265)在Web端播放、H265+H264视频解密([Vcodec 检测](https://github.com/huzunjie/Flv-Vcodec-Detection)),实现的一套基于WebAssembly、WebWorker的模块化WEB端播放器。

> QHWWPlayer 强依赖的浏览器API有:WebAssembly、WebWorker、fetch、ArrayBuffer、AudioContext、WebGL、Blob等,建议使用者基于这些API结合自身业务覆盖浏览器类型决定是否启用本播放器。
> 如果你是前端从业者,想从技术方向对这个播放器加深了解,可以[参考这里的文章介绍](http://blog.pyzy.net/post/qhww.html)。

**注意:本播放器基于WASM软解,帧率码率越高CPU消耗依赖也越高,如果非必要情况不建议使用。**

## QHWWPlayer 媒体播放能力

| 场景 | 协议 | 编码  | 解密    |  有音无画  | 有画无音 | 备注  |
| -------- | -------- | -------- | -------- | ------------ |
| 直播 | HTTP(S)-FLV| H264/H265 | 支持 | 支持 | 支持|  延迟追帧可通过delayTimeLimit 控制 |
| 点播 | HTTP(S)-MP4 | H264/H265 | 支持 | 支持 | 支持 | [服务端需要支持Content-Length的ResponseHeader读取](https://zyun.360.cn/developer/doc?did=kyqqmtzysb) |
| 直播<br />点播 | HTTP(S)-HLS| H264/H265 | 支持 | 支持 | 支持 |  -  |

> 注意:有画无音的情况,如果是流中没有音轨,则需要通过 renderType  明确设定不渲染声音。

## QHWWPlayer API 文档

### 示例 - DEMO

本示例实现中包含了播放器整体逻辑流程中各环节的数据生产、消费状况的实时统计报告,业务应用中除非有异常控制策略的开发需求,未必有相应数据展示的必要,请自行斟酌选用,以减少代码量、提高执行效率。


[示例DEMO请点击这里移步查阅](http://lab.pyzy.net/qhww/ "示例DEMO请点击这里移步查阅")。

各参数、API方法、事件的使用说明,请参考本文档后续相关内容。

### 获取及引用方式
如上文示例,在自己的业务页面中引入`https://s.ssl.qhimg.com/pkg/serviceplatform/QHWW_PLAYER_v0.3.10.js`后,可以在window域下得到`QhwwPlayer`类,设置一个DOM元素为播放器的容器,便可以实例化播放器了:

>** 360集团内部开发者**使用,可以基于公司私有npm库安装引用: `qnpm install @q/QhwwPlayer -S`

```html
<div id="container"></div>
<script src="https://s.ssl.qhimg.com/pkg/serviceplatform/QHWW_PLAYER_v0.3.10.js" type="text/javascript"></script>
<script type="text/javascript">
      window.qhwwPlayer = new window.QhwwPlayer({
        isLive: true,
        container: '#container',
        src: '要播放的媒体URL',
      });
</script>
```
### QhwwPlayer 类 - 静态属性

| 属性名 | 值类型  | 备注  |
| -------- | -------- | ------------ |
| isSupport | Boolean | 环境兼容判断;可用于在实例化之前判断当前浏览器环境是否兼容。 |

也就是可以通过 `QhwwPlayer.isSupport` 得到当前环境是否兼容该播放器的Boolean结果。

### QhwwPlayer 类 - 参数说明

前文我们提到可以`new QhwwPlayer(options)`得到一个播放器实例,那么其中的`options`具体可以设置哪些内容,可以参考下文:

| 属性名 | 值类型 | 是否必填  | 备注  |
| -------- | -------- | -------- | ------------ |
| autoplay | Boolean | 否 | 是否在实例化播放器后自动播放;默认flase。<br />注意:部分浏览器环境会限制自动播放媒体资源,除非用户手动触发,<br />为了保证兼容性,建议播放器实例在用户操作行为中进行。 |
| isLive | Boolean | 是 | 是否直播;直播点播内部逻辑不同,需要业务指定。 |
| isHLS | Boolean | 否 | 是否HLS资源;若不设置将自动按URL后缀是否`.m3u8`区分。 |
| src | String | 是 | 播放地址(相对或绝对路径,如果是跨域需要设置允许跨域头)。 |
| container | String | 是 | DOM Selector;要放置UI的DOM容器的Selector或DOM对象。 |
| key | String | 否 | 解密key,明文或加密后的字串(具体加密方式请联系360视频云工作人员获取)。 |
| keyForKey | String | 否 | 当key为加密字串时用于解密key的秘钥。 |
| keyType | Number | 否 | 解密方式:DK_TYPE_SHUIDI = 0, DK_TYPE_FFMPEG = 1, DK_TYPE_DISHI = 2。 |
| keyList | Array | 否 | 用于跟随帧内index使用动态解密的 KeyMap,格式如: [{ index: 0, key: "aaa"}]。 |
| logLevel | Number | 否 | 输出日志级别;可选 0 ~ 2,默认 0 - 不输出任何调试日志。 |
| volume | Number | 否 | 默认音量,默认1。 |
| maxVolume | Number | 否 | 用户可操作设置的最大音量,默认5。 |
| playbackRate | Number | 否 | 默认倍速,默认1。 |
| maxPlaybackRate | Number | 否 | 用户可操作设置的最大倍速,默认2。 |
| minDecoderBufferSize | Number | 否 | 要启动解码器时必须准备就绪的数据量,默认 512 \* 1024;<br />数值越大播放越稳定,但等待时长也就更长。<br /><br />**注意:在FLV直播的场景中,这里可以设置值为1来保证最佳体验;<br />播放器内部会自动判定FLV已加载的数据是否够解码使用。** |
| chunkSize | Number | 否 | 单次加载的buffer块大小(对于直播,相当于内存传递的最大块体积,<br />对于点播相当于单次下载的range长度),默认 512 \* 1024。 |
| maxLoaderCacheSize | Number | 否 | 下载缓存可以使用的内存上限,默认 2 \* 1024 \* 1024。 |
| maxDecoderVCacheLength | Number | 否 | 解码数据队列中视频缓存最大帧数,默认 300。 |
| maxDecoderACacheLength | Number | 否 | 解码数据队列中音频缓存最大帧数,默认 500。 |
| decoderMemorySize | Number | 否 | 解码器内存上限设置,默认值 5 \* 1024 \* 1024。 |
| yuvRenderDelay | Number | 否 | 画面渲染延迟时长(毫秒),默认值 -20;可用于控制音画同步。 |
| pcmRenderDuration | Number | 否 | 音频渲染器最大缓冲时长(毫秒),即允许预先将最多多长时间的音频推入硬件队列,<br />这里时间过长会影响动态调整倍速的响应速度,默认值 1000。<br />*注意:这个值不可以小于waitingPcmDur。* |
| waitingPcmDur | Number | 否 | 当音频资源不足时,等待至少准备多少毫秒的数据再重启播放,默认值 500。<br />*注意:这个值不可以大于pcmRenderDuration。*|
| waitingYuvNum | Number | 否 | 无音频的媒体播放触发waiting后,至少等到准备几帧画面数据再恢复播放。默认值10。|
| waitingProbation | Number | 否 | 渲染数据不足时候,waiting缓期毫秒数,<br />如果这个时长内数据就绪,那么不触发waiting事件。默认值100。|
| *sei* | Number | 否 | 是否解码SEI数据;0 不需要;1需要,默认 0  |
| renderType | String | 否 | 渲染模式:yuv 仅渲染画面;pcm 仅渲染声音;all 全部渲染;默认 all  |
| resample | Number | 否 | 是否需要对音频重新采样 - 0 不需要 1 需要,默认0。<br />部分浏览器不支持底采样率音频,要兼容需要开启该配置。 |
| delayTimeLimit | Number | 否 | **想要控制的直播延迟的最长毫秒值。**<br />即缓存区允许暂存的待渲染帧时长毫秒值;<br />当缓存时长大于该值则触发倍速播放来提高消费效率、降低延时。<br />注意:太小将增加缓冲概率。建议结合GOP时长设置。<br /><br />**默认值0**;不传值或传值为0,则不启用动态倍速渲染追帧达成低延时模式。 |
| rtwPlaybackRate | Number | 否 | 大于1的浮点数(默认值2.23);<br />参数 delayTimeLimit 开启追帧播放时,该参数用于控制追帧播放的最高倍速值。 |
| lowpassFrequencyVal | Number | 否 | 低通滤波频率值,可用于降噪;默认值 4500。<br />播放过程中频率大于该参数值的声波将被过滤掉。 |

`以上参数可以结合自己的应用场景和需求,进行适当配置,达到最佳体验效果。`

**比如在Flv直播中,要保证“首屏效率”及“低延时”可以参考如下设置:**

| 属性名 |  含义  |  建议值  | 备注  |
| -------- | --------  | -------- | ------------ |
| minDecoderBufferSize | 解码器启动前,必须加载到的数据量字节长度  | 1 |  设置1,将交给QHWW内部判断逻辑自动识别控制 | 
| waitingYuvNum  | 渲染帧不足时画面帧至少多少帧再重启播放   | 1 |  设置尽量小的值,有数据立即渲染,减少延迟 |
| waitingPcmDur  | 渲染帧不足时音频帧至少多少毫秒重启播放   | 50 |  设置尽量小的值,有数据立即渲染,减少延迟 |
| waitingProbation    | 渲染帧不足时缓刑多少毫秒再触发waiting   | 500 |  设置偏大一点的值,若数据及时恢复则直接播放,减少零碎的等待 |
| delayTimeLimit   | 直播中内存中允许存放最多多少毫秒待渲染帧   | 1000 |  可以结合GOP和希望兼容的网络抖动带来的延迟偏差设置 |
 

### QhwwPlayer 实例 - 属性说明

前文我们通过`const qhwwPlayer = new QhwwPlayer(options)`得到一个播放器实例后,通过 `qhwwPlayer.属性名`可以直接读写的播放器属性,可参考下面表格内容:

| 属性名 | 值类型 | 是否只读  | 备注  |
| -------- | -------- | -------- | ------------ |
| ready | Boolean | 是 | 播放器实例是否就绪;依赖异步载入的WebWorker\WASM等,所以实例化完毕,未必就是ready状态了。 |
| autoplay | Boolean | 是 | 是否在实例化播放器后自动播放。 |
| isLive | Boolean | 是 | 是否直播。 |
| isHLS | Boolean | 是 | 是否HLS。 |
| currentSrc | String | 是 | 格式化后的当前媒体路径字串。 |
| src | String | 否 | 当前媒体路径字串;写操作会同步影响currentSrc的值。 |
| key | String | 否 | 解密key,明文或按360视频云规则加密后的字串(具体加密方式需要联系360工作人员获取)。 |
| keyForKey | String | 否 | 当key为加密字串时用于解密key的秘钥。 |
| keyType | Number | 否 | 解密方式:DK_TYPE_SHUIDI = 0, DK_TYPE_FFMPEG = 1, DK_TYPE_DISHI = 2。 |
| keyList | Array | 否 | 用于跟随帧内index使用动态解密的 KeyMap,格式如: [{ index: 0, key: "aaa"}]。 |
| duration | Number | 是 | 媒体时长(秒)。 |
| currentTime | Number | 是 | 播放进度时间(秒)。 |
| paused | Boolean | 是 | 是否暂停状态。 |
| ended | Boolean | 是 | 是否播放结束状态。 |
| seeking | Boolean | 是 | 是否正在seek。 |
| error | Any | 是 | 异常信息。 |
| videoWidth | Number | 是 | 视频画面宽度。 |
| videoHeight | Number | 是 | 视频画面高度。 |
| poster | String | 否 | 封面图。 |
| controls | Boolean | 否 | 是否显示控制条。 |
| volume | Number | 否  | 音量。 |
| playbackRate | Number | 否 | 播放倍速。 |
| fullscreen | Boolean | 否 | 是否全屏模式。 |
| viewMode | String | 否 | 设置视窗宽高填充方式:  <br>1. cover 按宽高中小的值 100% 展示, 默认值。<br>2. contain 按宽高中大的值 100% 展示。<br>3. none 不设置样式(可以通过外部CSS控制)。 |
| decodeMode | Number | 否 | 解码模式: 0~3,值越大丢帧越狠,0保持原状;该值通常不需要手动设置,播放器内部有动态适配逻辑。 |
| waitingPcmDur | Number | 否 | 当音频资源不足时,等待至少准备多少毫秒的数据再重启播放,默认值 500。<br />*注意:这个值不可以大于pcmRenderDuration。*|
| waitingYuvNum | Number | 否 | 无音频的媒体播放触发waiting后,至少等到准备几帧画面数据再恢复播放。默认值10。|
| waitingProbation | Number | 否 | 渲染数据不足时候,waiting试用期毫秒数,如果这个时长内数据就绪,那么不触发waiting事件。默认值100。|
| delayTimeLimit | Number | 否 | **想要控制的直播延迟的最长毫秒值。**<br />即缓存区允许暂存的待渲染帧时长毫秒值;大于该值则自动触发动态倍速播放来提高缓存数据的消费效率。注意:太小将增加缓冲概率。<br /><br />**动态倍速规则为:**缓存总时长大于delayTimeLimit设置值的3倍则启用`100%*rtwPlaybackRate`的速度追帧,缓存总时长等于delayTimeLimit则按正常1倍速度播放。<br />**默认值0**;不传值或传值为0,则不启用动态倍速渲染追帧达成低延时模式。 |
| rtwPlaybackRate | Number | 否 | 大于1的浮点数;<br />参数 delayTimeLimit 开启追帧播放时,该参数用于控制追帧播放的最高倍速值。 |
| lowpassFrequencyVal | Number | 否 | 低通滤波频率值,可用于降噪;默认值 4500。<br />播放过程中频率大于该参数值的声波将被过滤掉。 |


### QhwwPlayer 实例 - 方法说明
前文我们通过`const qhwwPlayer = new QhwwPlayer(options)`得到一个播放器实例后,通过 `qhwwPlayer.方法名()`可以执行的操作方法,可参考下面表格内容:

| 方法名 | 返回值 | 接受参数  | 备注  |
| -------- | -------- | -------- | ------------ |
| play | Promise | 否 | 开始播放。 |
| pause | Promise | 否 | 暂停播放。 |
| stop | Promise | 否 | 停止播放;相对pause将重置掉内部状态。 |
| seek | Promise | time: Number 毫秒 | seek到指定的位置。 |
| destroy | Promise | 否 | 销毁播放器实例;释放资源。 |

### QhwwPlayer 实例 - 事件说明

前文我们通过`const qhwwPlayer = new QhwwPlayer(options)`得到一个播放器实例后,首先假设有个监听函数:

```
function eventHandler(event) { 
    const 返回值 = event.data;
    ...
} 
```
那么我们可以通过 `qhwwPlayer.on('eventType', eventHandler)`监听播放器事件。
可以通过`qhwwPlayer.off('eventType', eventHandler)`解除监听播放器事件。

另外,事件绑定时候有两个特殊的特性:

1、事件绑定支持`before`、`after`修饰符。
> 可以用类似`qhwwPlayer.on('eventType@before', eventHandler)`的方式绑定监听,来控制`eventHandler`是放在其他监听器前面还是后面执行;

2、事件绑定支持设置eventType为`*`,来做泛事件绑定。
> 这样绑定的监听函数在任意事件触发时都会执行,*所以必须要慎重使用*。

具体支持的事件类型可参考下面表格内容:

| 事件类型 | 返回值类型 | 返回值内容  | 备注  |
| -------- | -------- | -------- | ------------ |
| ready | Array | 各组件的安装状态 | 播放器就绪(实例化完毕后可能需要等待异步载入webWorker\WASM等)。 |
| play | Undefined | -  | 触发了播放操作。 |
| pause | Undefined | -  | 触发了暂停操作。 |
| stop | Undefined | - | 触发了停止操作。 |
| ended | Undefined | -  | 播放结束了。 |
| playing | Undefined | - | 在媒体开始播放时触发(可能发生在:初次播放、暂停后恢复、结束后重新开始、资源不足导致的卡顿)。 |
| waiting | Undefined | - | 触发播放后帧数据资源不足,需要等待时触发。 |
| error | Any | 错误信息 | 在发生错误时触发。 |
| loadstart | Object | {src, isLive} | 在媒体开始加载时触发。 |
| progress | Object | 单数据包体积&处于流中的range位置 | 告知媒体相关部分的下载进度时周期性地触发。 |
| loadedmetadata | Object | 媒体源的长度、音视频格式、时长等基本信息  | 媒体的元数据已经加载完毕;已经解码拿到基本媒体信息了。 |
| seeking | Nubmer | 要seek到的位置(秒) | 切换播放位置时触发。 |
| seeked | Nubmer | 接下来解码依赖的数据游标位置 | 切换播放位置完毕后触发。 |
| timeupdate | Object | 当前播放器的渲染进展,包含队列数据帧数、时长等状态信息 | 元素的currentTime属性表示的时间已经改变。 |
| durationchange | Number | 当前媒体时长值  | 媒体的时间长度发生了改变。 |
| ratechange | Number | 当前变化后的倍速值 | 倍速发生了变化。 |
| volumechange | Number | 当前变化后的音量值 | 音量值发生了变化。 |
| streamQueueChange | Object | 变化后的队列长度和数据体积 | 下载器数据队列发生变化。 |
| pcmQueueChange | Number | 变化后的队列长度 | 待渲染的音频帧数据队列发生变化。 |
| pcmQueueDurChange | Number | 变化后的队列时长毫秒值 | 待渲染的音频帧数据队列发生变化。 |
| yuvQueueChange | Number | 变化后的队列长度 | 待渲染的画面帧数据队列发生变化。 |
| yuvQueueDurChange | Number | 变化后的队列时长毫秒值 | 待渲染的画面帧数据队列发生变化。 |
| pcmFrameData | Object | 音频时间戳、时长、字节体积 | 解码器输出了一个音频帧数据;只要解码不停会不停的触发。 |
| yuvFrameData | Object | 视频时间戳、字节体积、估算帧率 | 解码器输出了一个画面帧数据;只要解码不停会不停的触发。 |
| seiUpdate | Object | SEI数据及时间戳 | 当实例化播放器时设置了sei参数为1,播放过程中会触发这个事件,并在事件返回数据中返回相应数据。<br />这里事件对象 evt.data.sei 拿到的将是SEI的内容buffer,可能需要根据规则约定截取或转字符串使用。<br /> ArrayBuffer 转字符串可参考:`String.fromCharCode.apply(null, new Uint16Array(data.sei ))` 或  `new TextDecoder('utf8').decode(new Uint8Array( data.sei ))`  |

## 写在后面

根据你的需求或应用场景不同,可能有未能达成满足、或实现并非最优的地方,欢迎随时反馈 。
  

看网络直播

Loading...
Loading...