本文档适用于 iOS Safari 浏览器的 HLS 播放, 提供 P2P 功能 XP2P 提供了两个 SDK:XP2P_MAIN_SDK和XP2P_SW_SDK
XP2P_MAIN_SDK 用于主线程
XP2P_SW_SDK 用于service worker内部
接入原理
交互示意
safari 播放器 <---http----> service worker(XP2P_SW_SDK) <---http/message通道----> XP2P_MAIN_SDK
交互描述
1. 播放器发出的 m3u8 请求
service worker 拦截播放器发出的 m3u8 请求
service worker 直接请求 m3u8 并返回给播放器 (XP2P_SW_SDK 已实现)
2. 播放器发出的 ts 请求
service worker 拦截播放器的 ts 请求
worker 内部的 XP2P_SW_SDK 向主线程 XP2P_MAIN_SDK 查询这个 ts (XP2P_SW_SDK 已实现)
XP2P_MAIN_SDK 通过 P2P 或者 http 获取到 ts, 返回给 service worker (XP2P_SW_SDK 已实现)
service worker 将 ts 返回给播放器
demo
参见 我们的接入邮件 demo/sw 目录
实际接入
接入前准备
1. 按照接入说明中的接入前准备
2. 忽略 message 事件中 XP2P 的消息
xp2p XP2P_MAIN_SDK 和 XP2P_SW_SDK 之间的通信, 依赖于 service worker 的 message 通信机制,因此如果您使用到了 message 事件或者设置了 onmessage
请忽略 XP2P 传递的消息,忽略方法如下
/*** 主线程内, 如果使用message事件接收service worker消息* @param {MessageEvent} event*/self.navigator.serviceWorker.addEventListener('message', (event) => {// 判断xp2p消息的方法, 如果是XP2P消息, 请务必忽略, 不要处理或修改if (!Object.prototype.hasOwnProperty.call(event.data, 'xp2pType')) {return;}});/***或者 如果您在主线程使用onmessage接收service worker消息* @param {MessageEvent} event*/navigator.serviceWorker.onmessage = (event) => {// 判断xp2p消息的方法, 如果是XP2P消息, 请务必忽略, 不要处理或修改if (!Object.prototype.hasOwnProperty.call(event.data, 'xp2pType')) {return;}};/*** 在service worker内如果使用message事件接收主线程消息* @param {MessageEvent} event*/self.addEventListener('message', (event) => {// 判断xp2p消息的方法, 如果是XP2P消息, 请务必忽略, 不要处理或修改if (!Object.prototype.hasOwnProperty.call(event.data, 'xp2pType')) {return;}});/*** 或者 如果您在service worker内部使用onmessage接受主线程消息* @param {MessageEvent} event*/self.onmessage = (event) => {// 判断xp2p消息的方法, 如果是XP2P消息, 请务必忽略, 不要处理或修改if (!Object.prototype.hasOwnProperty.call(event.data, 'xp2pType')) {return;}};
3. 设置 service worker 文件的拦截作用域
这一步的目的是确保 sw.js 可以拦截到播放器的 ts 和 m3u8 请求
(1) 设置 sw.js 的 http response header
// 增加一个 response header, 设置sw.js的作用域是跟路径Service-Worker-Allowed : "/"
(2) service worker 代码内设置作用域
// 提升sw.js的拦截作用域, 虽然sw.js的路径是在/js/目录下, 但是可以拦截根路径的请求navigator.serviceWorker.register("/js/sw.js", {scope: "/"}).then(() => {console.log("Install succeeded as the max allowed scope was overriden to '/'.");});
SDK 代码接入
说明:
主线程 XP2P 接入代码(XP2P_MAIN_SDK)
1. 创建 sdk 实例
2. 播放器播放
// 判断当前浏览器环境是否支持XP2Pif (!HLSP2P.isSupportedInServiceWorker()) {alert('浏览器不支持');return;}// TODO 配置的详细内容请参考API文档const config = {// 如下为必传参数enableServiceWorker: true, // 仅在service worker环境中开启, 不传默认为false// 如下为必传参数videoId: 'video_id', // 特别注意是<video>的id, 这里需要跟据实际情况修改url: '实际播放的m3u8 url',domain: '这里要根据邮件填写',xp2pAppId: '这里要根据邮件填写',cloudAppId: 您的腾讯云APPID,videoType: 'LIVE', // 重要!! 直播写LIVE, 点播写VOD,};// 创建SDK实例const hlsp2p = HLSP2P.createCommon(config);// 监听SDK播放失败事件hlsp2p.on(HLSP2P.Events.Rollback, ({reason}) => {console.warn(`p2p 回退 destroy, reason: ${reason}`);// 需销毁sdkhlsp2p.destroy();});window.hlsp2p = hlsp2p;/*** 当停止播放时调用销毁SDK*/function destroySDK() {if (window.hlsp2p) {window.hlsp2p.destroy();window.hlsp2p = null;}}
service worker 内部接入代码(XP2P_SW_SDK)
1. 加载 XP2P_SW_SDK
2. 创建 XP2P_SW_SDK 实例
3. 监听 fetch event 并拦截必要的请求: 包括 xp2p 内部请求, m3u8 请求, ts 请求
// 在service worker内部加载XP2P_SW_SDKvar xp2pswUrl = '这里写XP2P_SW_SDK的url'importScripts(xp2pswUrl);// 创建sw内的XP2P SDK实例, 并绑定在self作用域// 此处create函数返回单例self.xp2pIns = XP2PSW.XP2PSWLib.create();/*** TODO 这里您可以自行实现, 请仅拦截视频正片的ts和m3u8* @param url* @return {boolean}*/function isTsOrM3u8Url(url) {return url.indexOf(".ts") > -1 || url.indexOf(".m3u8") > -1;}// 在fetch事件中, 使用XP2P处理必要的请求self.addEventListener('fetch',/*** @param {FetchEvent} event*/(event) => {// TODO XP2P 拦截两种请求: xp2p自身的请求和 HLS请求// TODO 您可以根据自己的灰度策略, 选择只开启部分资源走P2Pif (self.xp2pIns && (self.xp2pIns.detectXP2PRequest(event) || isTsOrM3u8Url(event.request.url))) {return event.respondWith(// 调用XP2P的match方法来处理fetch event, 尝试请求self.xp2pIns.match(event).then(response => {// 通过XP2P SDK请求成功, 返回response给fetch请求// TODO 如果需要对respone进行处理, 可以在这里做return response;}).catch((e) => {// TODO 重要! 当XP2P请求失败或者XP2P关闭了service worker功能的时候会走到这里, 需要在这里兜底请求一次// 注意: safari会增加pragma header,导致发出不必要的cors preflight请求, 会导致请求失败. 这里删除掉const r = new Request(event.request);r.headers.delete('pragma');// TODO 如果需要对respone进行处理, 可以在这里做return fetch(r);}));}// TODO 对于不需要XP2P处理的其他请求, 您可以在此处自行处理});
测试
如何确认 XP2P 已经启动
观察网络面板, XP2P_MAIN_SDK 启动一段时间后, 会发出
https://xw.xp2p/hello
请求, 如果响应如下则表示启动成功statusCode: 200body: {code: 0}
如何确认是否成功收到 P2P 数据
1. XP2P_MAIN_SDK 提供了数据统计接口,包含了 P2P 和 cdn 字节数, 可以参考 API 文档
2. 可以观察浏览器网络面板, 对于一个ts请求例如: 1.ts
如果没有 P2P 成功, 则会在 network 面板上观察到两个 1.ts 请求, 分别由播放器和 XP2P_MAIN_SDK 发出
如果 P2P 成功, 则在 network 面板上指挥看到一个 1.ts 请求, 由播放器发出