【腾讯课堂】视频点播上云实践

栏目: 编程工具 · 发布时间: 5年前

内容简介:视频点播分为视频上传和视频播放两个部分,下面的表格整理了上云前后的部分数据对比:可以看出来上传成功率和视频转码速度有了极大的提升,PC 和 H5 侧的播放成功率云和腾讯视频基本持平。

总体介绍

腾讯课堂 是一款通过线上的直播与点播向用户提供在线教育服务的产品,从 2014 年成立至今,已累计存储了 250 万个视频,共 600 TB,累计时长 150 万小时。之前一直采用的是腾讯视频的方案,但使用的是 MP4 格式,用户拿到了播放链接之后很容易盗版,所以趁着上云的潮流,我们将视频点播迁移到了 腾讯云 - 云点播 上,本文主要会讲一讲我们 整体的方案、Web 接入的方法和遇到的一些问题

视频点播分为视频上传和视频播放两个部分,下面的表格整理了上云前后的部分数据对比:

腾讯视频 腾讯云
Web 视频上传成功率 92% 99.5%
视频转码速度(两小时左右的视频) > 60 分钟 < 20 分钟
播放成功率 - PC 99% 98.7%
播放成功率 - H5 97% 97.1%

可以看出来上传成功率和视频转码速度有了极大的提升,PC 和 H5 侧的播放成功率云和腾讯视频基本持平。

整体方案

考虑到存量视频较多,没法短时间内全部从腾讯视频迁移至腾讯云,同时迁移过程中用户可能继续使用老的方式向腾讯视频上传,所以整个点播上云分为两期进行:

  1. 第一期主要工作是接入腾讯云的上传、转码和播放功能,确保用户新上传的视频均走云的流程,同时后台将新上传的视频旁路一份到腾讯视频,这样既可以在用户播放云视频失败时前端降级至腾讯视频播放,也方便出现重大问题时快速切回至老的腾讯视频方案。
  2. 第二期工作则是将存量的腾讯视频全部迁移至腾讯云上,同时接入云的 AI 功能,进行鉴黄、鉴暴和鉴政。待现网数据稳定且达到预期后,即可彻底摒弃老的方案。

【腾讯课堂】视频点播上云实践

视频上传流程

【腾讯课堂】视频点播上云实践

视频上传整体方案如上图所示,主要涉及三块:

  1. 向业务后台获取签名
  2. 调用云SDK 进行视频上传
  3. 云服务器进行视频转码

上面三块中最重要也最容易出问题的是"调用 SDK 上传"这一部分,直接决定了上传成功率,但也很容易受用户网络状况的影响,需要重点关注,建议记录详细的用户日志以便进行问题定位与排查。

另外,其实上述流程图与腾讯云文档给出的 客户端上传指引 略微有点差别,主要在于第 4 步通知业务后台上传完成这里,官方文档中是云后台来通知,我们实际采用的方式是 Web 侧来通知,从而避免出现 Web 侧调后台接口出错提示用户上传失败后,云后台又通知业务后台保存相关数据的情况。

视频播放流程

在以前使用腾讯视频的方案时,出于种种考虑,我们并未对视频做加密处理,导致有些课程被他人恶意盗录。目前上云之后,我们使用的是加密 HLS 的方案,通过云提供的 Key 防盗链DRM(数字版权管理)方案 ,我们对视频做了加密处理,就算被拿到了视频地址,也无法进行盗录,进一步打击了恶意行为,保护了老师的版权。

【腾讯课堂】视频点播上云实践

用户浏览器在播放视频时主要流程如上图所示,其中依靠第 1 步获取 Token 和第 3 步获取 DK 进行版权的保护,他们的作用分别为:

  • Token 用于防盗链,可以 限制视频 URL 的过期时间、最大允许播放 IP 数等,具体的计算方法和验证逻辑由业务方自定义。
  • DK 用于对视频的加密切片进行解密,用户直接获取到的视频分片均通过 AES-128 进行了加密,其值由腾讯云密钥管理服务(KMS)提供。

Web 接入的流程

视频上传

接入方法

视频上传主要依赖云提供的 vod-js-sdk-v6 ,用 TypeScript编写,具有较为完善的的测试用例,代码质量很高 :+1: 其底层依赖的是 cos-js-sdk-v5 ,也是由腾讯云提供的对象存储能力。

接入 SDK 的方法很简单,只涉及两方面:

upload
import TCVod from 'vod-js-sdk-v6';

// 用签名函数触发
const uploader = new TCVod({
  getSignature,
});

// 向业务后台获取签名
function getSignature() {
  return fetch('FAKE_CGI_URL').then((result) => {
    return result.sign;
  })
}

// 调用 SDK 上传
function uploadVideo(videoFile) {
  const upVideo = uploader.upload({ videoFile });
  upVideo.on('video_progress', (info) => {
    // 此处获取上传进度
    // 例如上传百分比、上传速度等
  });

  upVideo.done().then((result) => {
    // 此处获取上传结果
    // 例如 fileId、CDN 源文件地址等
  }).catch((error) => {
    // 上传失败
  });
}

uploadVideo(fileA);
uploadVideo(fileB);

【腾讯课堂】视频点播上云实践

虽然上传的 SDK 用起来很简单,但在我们灰度的过程中,还是遇到了一些问题,因而强烈建议在代码中加入详细的上报日志,例如上面的 DEMO 中可以加入的日志信息包括:获取签名的开始、成功与失败,文件上传的开始、成功与失败等。

遇到的问题

1. 默认只开启了重庆存储区

上线后我们发现视频上传的链接均是 xxx.cos.ap-chongqing.myqcloud.com 的形式,这看起来不太对呀,怎么都往 chongqing(重庆区)上传了呢?难道不支持就近上传的能力吗?后来我们联系云的同事得知,由于视频云的底层依赖的是腾讯云的对象存储(COS),所以具体往哪传,怎么传比较快是由 COS 保证的,需要在云控制台开启相关配置。

【腾讯课堂】视频点播上云实践

2. SDK 上传部分报错

上传初期进行灰度时发现上传成功率为 97%,距离预期的 99% 还存在一定距离,通过双方的合作排查,最终发现主要是由两个问题引起的:

  • 用户本地时间与服务器时间不一致时,依赖的 cos-js-sdk-v5 鉴权报错,导致出现 403;
  • 用户网络抖动时,云视频的 vod-js-sdk-v6 对签名的处理存在问题,导致出现 403。

目前在最新版的 vod-js-sdk-v6 中上述问题均已解决,上传成功率在全量后也在 99.5% 以上。

PC & H5 视频播放

前面已经简单提过了视频播放流程,我们这里再来详细说明一下。

流程简介

点播播放其实很简单,简单来说就是下面这个流程:

【腾讯课堂】视频点播上云实践

第一步: 获取 m3u8 地址

第二步:调用播放器播放

就是这么简单。

这时候我们发现一个问题,有了 m3u8 地址,所有人都能播放了。这个 m3u8 地址可以肆无忌惮的传播,任何人拿到链接都可以播放,就没有付费课的概念了。于是我们开始引入前面提到的第一个技术,我们称之为 Key 防盗链 。防盗链参数是动态变化的,引入之后我们的流程就变成了:

【腾讯课堂】视频点播上云实践

加了防盗链之后,缺少防盗链参数的链接就没法播放了。就算带防盗链参数的 m3u8 地址传播出去,因为有时效性,这个链接过一阵子也会失效。

这时候,聪明的小伙伴应该又发现了另外一个问题,假设在防盗链参数失效之前把 m3u8 文件下载下来,一样是可以拿来传播的。

要解决这个问题,我们可以简单来看下 m3u8 的格式。

【腾讯课堂】视频点播上云实践 【腾讯课堂】视频点播上云实践

简单的说, m3u8 是一个遵循某种格式的文本文件,里面是一些 TS 分片的索引,通过这些索引就可以找到所有的视频分片。

回到我们加密的主题,如果是每一个 TS 分片做加密,是不是就算把 m3u8 下载下来,也没法播放了呢? HLS 的普通 AES 加密技术正是这样做的。引入了 HLS 普通加密之后,整个流程就变成了这样:

【腾讯课堂】视频点播上云实践

为了简单起见,我们忽略了 COS CDN 这一块的图示。解释一下上图:

首先是加密,要加密就要要密钥。这时候就引入了 KMS ,我们暂时不关心 KMS 内部实现,简单认为做了就是提供密钥的工作。腾讯云收到了业务后台发起的视频加密请求之后,就会从 KMS 获取对应的加密密钥,对文件进行加密处理。这就是上图蓝色字的部分。

然后是解密,业务前端在拿到 m3u8 的内容的时候,发现需要解密 TS 的,所以需要解密密钥,于是就会请求业务后台去获得解密密钥。业务后台怎么认为请求是合法的呢?当然是要有用户的身份信息(cookie)。腾讯云提供了两种方式,具体可以看 HLS 普通加密 。上图示例即是第一种方案,用例子来解释一下。我们看一个 m3u8 地址示例:

https://1258712167.vod2.myqcloud.com/fb8e6c92vodtranscq1258712167/c896adc25285890789334843878/drm/voddrm.token.dWluPTt2b2RfdHlwZT0yO2NpZD00MDY4NDQ7dGVybV9pZD0xMDA0ODUxNzc7cHNrZXk9O2V4dD0=.v.f3071.m3u8?t=5d2f1647&exper=0&us=7776585111527298975&sign=195ed8bcbc08bb5e40f4823c49e71696

这里的 dWluPTt2b2RfdHlwZT0yO2NpZD00MDY4NDQ7dGVybV9pZD0xMDA0ODUxNzc7cHNrZXk9O2V4dD0= 即是需要带给业务后台的鉴权 token 。再看看这个文件的内容:

【腾讯课堂】视频点播上云实践

m3u8 格式里用 EXT-X-KEY 值用于解密,上图的 cgi-bin/qcloud/get_dk 即是我们图示里的第 5 步,携带身份信息,向业务后台获取解密密钥。获得解密密钥之后,就可以对 TS 文件解密并且播放啦~

代码实现

了解了流程之后,代码其实就很简单了。

首先:获取 m3u8 地址,并拼接上 token

async getM3U8List(fileId: string) {
  const { termId, onError } = this.props;
  try {
    // 获取防盗链参数,对应流程图里第2步
    const urlParams = await getUrlToken({
      termId,
      fileId,
    });
    // 获取 m3u8 地址,对应流程图里第3步
    const videoInfo = await getPlayInfo(fileId, urlParams);
    // 获取拼接了 token 之后的 m3u8 地址
    const m3u8List = getPlayListWithToken(videoInfo, {
      termId,
    });

    return m3u8List;
  } catch (e) {
    onError(e);
  }
}

其次,调用播放器,这里可以参考 超级播放器 或者 tcplayerlite 。文档比较详细,这里就不赘述了。我们播放完整流程图里的第 4 步则是由播放器发起的,第 5 步由浏览器自己发起的。

播放质量监控

关于监控,播放目前是使用内部 monitor + tdw + badjs 上报做监控的。

monitor 用于告警和数据累积量的查看。

tdw 用于报表、日报、周报的生成。

badjs 则用于出现了播放失败等情况时的排查。

小程序视频播放

小程序端有两个问题需要解决:

  1. 腾讯云并没有提供可用的云播放组件供前端使用,所以需要我们自己封装一个组件,提供云视频播放能力;
  2. 小程序没有cookie,而且m3u8文件获取解密密钥的方法是由video自动完成的,代码无法控制,所以小程序端只能采用 QueryString 传递身份认证信息 的方案去鉴权;

我们先来看一下小程序组件腾讯云视频播放的一个基本流程:

【腾讯课堂】视频点播上云实践

  • 课堂这边是开启了防盗链和HLS加密的,所以上述的判断流程都走绿色的路径;
  • tokenObj 是防盗链的token,里面包括: 播放地址的过期时间戳、试看时长、链接标识、防盗链签名。参考 Key 防盗链 ;
  • drmToken 是m3u8获取解密密钥需要用到的鉴权token,具体规则由前后端在业务层约定加密规则。参考 QueryString 传递身份认证信息
  • <cloud-player-video /> 组件内部的播放还是用的小程序的 <video /> 组件,只是提供了通过参数获取真正播放地址的功能;
  • 目前 <cloud-player-video /\> 是我们自己研发的组件,还在持续迭代优化中,后续会加入倍速切换,清晰度切换等播放器常用功能;
  1. 小程序端通过业务的cgi拿到对应的fileId,然后通过getCloudUrlToken的接口获取对应的 tokenObj
  2. 通过登录接口获取的内容经过加密生成 drmToken 用以解密时的鉴权;
  3. 结合对应腾讯云业务的 appid 以及获取到的 tokenObjdrmTokenfileId 这四个关键参数传递给云播放组件 <cloud-player-video />
  4. 在组件内部利用 appidtokenObjfileId 这三个参数可以到腾讯云拿到加密的m3u8地址(通过getPlayInfo),然后利用 drmToken 信息附加到原始 m3u8 地址上(通过getUrlToken);
  5. 将新的 m3u8 地址传递给小程序的video组件,获取到的 m3u8 文件内部就会将 drmToken 的信息注入到 EXT-X-KEY 字段的URI中,以 QueryString 的方式传递,最终 drmToken 将会注入到 m3u8 文件内,图片上面已经贴过,再贴一遍

【腾讯课堂】视频点播上云实践

  1. video组件会自动读取这个URI去拿到解密的密钥将TS文件解密然后进行播放;

课堂小程序中获取 tokenObjdrmToken ,由于这两个参数的获取方式是业务决定的,内部流程就不赘述了,贴一下的步骤代码:

getCloudUrlToken(params)
.then(tokenObj => {
  const drmToken = getDrmToken({ term_id: termId });
  this.setData({
    fileId,
    appId: '1258712167', // pro
    drmToken,
    tokenObj,
  });
})
.catch(({ err_code, err_msg }) => {
  // 降级播放
  this.init(this.properties.playInfo, null, true);
});

然后将四个关键参数传递给组件,如下:

<cloud-player-video
  player-id="course-video-player{{r}}"
  file-id="{{fileId}}"
  app-id="{{appId}}"
  token-obj="{{tokenObj}}"
  drm-token="{{drmToken}}"
  safety
  poster="{{poster && tools.renderUrl(poster)}}"
  bindplay="onPlay"
  bindpause="onPause"
  binderror="onVideoError"
  bindended="onEnded"
  bindmedianotsup="onMediaNotSup"![](http://imweb-io-1251594266.cos.ap-guangzhou.myqcloud.com/b645c306e5a3695be09104cfdb27183a.png)
></cloud-player-video>

然后是 <cloud-player-video /> 组件内部的一些关键方法,getPlayInfo是根据 appidtokenObjfileId 获取原始 m3u8 播放地址的方法;formatUrlWithToken是为 m3u8 地址附加drmToken的方法:

// 获取视频播放地址的方法
getPlayInfo() {
  const {
    fileId,
    appId,
    safety,
    tokenObj: {
      t,
      us,
      sign,
      exper = 0,
    },
  } = this.properties;
  // 当前版本默认获取playInfo的地址
  let url = `https://playvideo.qcloud.com/getplayinfo/v2/${appId}/${fileId}`;
  // 如果开启了防盗链,将防盗链信息加到querystring里面
  if (safety) {
    url += `?t=${t}&us=${us}&sign=${sign}&exper=${exper}`;
  }

  return request({ url });
}

// 附加drmToken的方法
formatUrlWithToken(m3u8 = '', drmToken) {
  const reg = /(\/drm\/)/g;
  let tokenUrl = m3u8.replace(/http:/, 'https:');
  tokenUrl = tokenUrl.replace(reg, `$1voddrm.token.${drmToken}.`);
  return tokenUrl;
}

写在最后

虽然在上云的过程中遇到了一些问题,但都能顺利地解决,而且最后的产品数据与用户体验都比之前有了提升,希望越来越多业务能积极地拥抱云的时代!


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

C++编程思想

C++编程思想

埃克尔(美) / 刘宗田/等 / 机械工业出版社 / 2000-01 / 39.00

一起来看看 《C++编程思想》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

MD5 加密
MD5 加密

MD5 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具