文档中心>X-P2P>接入指引>H5 SDK>HLS>接入 service worker(iOS)

接入 service worker(iOS)

最近更新时间:2024-08-23 15:04:32

我的收藏
本文档适用于 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 代码接入

说明:
相关的 SDK API 请参考 API 文档

主线程 XP2P 接入代码(XP2P_MAIN_SDK)

1. 创建 sdk 实例
2. 播放器播放

// 判断当前浏览器环境是否支持XP2P
if (!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}`);
// 需销毁sdk
hlsp2p.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_SDK
var 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 您可以根据自己的灰度策略, 选择只开启部分资源走P2P
if (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: 200
body: {
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 请求, 由播放器发出