拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

uniapp接入阿里云点播:播放凭证与跨端播放器集成实战

uniapp接入阿里云点播:播放凭证与跨端播放器集成实战

这两周我一直在折腾一个uniapp项目接入阿里云点播的事,项目本身是一个视频课程App,要求一套代码同时跑Android、iOS、微信小程序和H5四个端。视频这块最早是用nginx静态目录直接放MP4的,开发阶段倒是爽,上线后问题全来了:用户一多带宽扛不住,高清视频没有多码率适配,视频地址被随手转发出去,想看个完播率都不知道从哪统计。后来决定整体迁移到阿里云点播,两周踩坑踩下来,从播放凭证、SDK集成到四端兼容,该遇到的问题基本都遇到了。这篇文章就把完整的接入过程、关键代码和排查思路整理出来,给准备做同样事情的朋友一个参考。

1. 项目背景与方案选型

1.1 为什么最终选了阿里云点播

先说说原来的方案,这也是很多创业团队的第一版做法:视频文件往服务器一扔,nginx开个静态目录,前端拿MP4地址直接播放。好处是简单,坏处是随着用户量上来,问题像连环雷一样被踩响。

第一个是带宽和存储成本。一个720P的MP4,时长45分钟,大概500MB到1GB。假设100个用户同时在线观看,峰值带宽直接冲到几十Gbps,云服务器按流量计费的话,一个月下来账单吓人。第二个问题是转码。用户手机屏幕大小不一,网速不同,你只有一路MP4,低网速用户只能卡着等,高网速用户也享受不到1080P。第三个问题更致命——防盗链。视频地址一旦泄露,可以被任意盗用,你辛辛苦苦做的内容,别人拿去当资源站素材。我当时甚至想自己写一套鉴权,后来想想还是算了,这不是一个能用简单签名解决的问题,涉及CDN、存储、转码、播放器、数据统计一大堆东西,自研成本太高。

阿里云点播本质上把这些事全打包了:视频上传后自动转码,输出多种清晰度和多种编码格式;文件存储在对象存储上,配合CDN分发,带宽成本比直接跑在自己服务器上可控得多;播放层面支持URL鉴权、播放凭证、私有加密、数字水印等安全能力;控制台还自带数据统计,能看播放次数、流量走势、热门视频排行。对团队来说,只需要关注业务本身,视频底层交给云服务。

对比项自建nginx+MP4阿里云点播
多码率转码没有,只能存多份上传后自动转码,可选多种清晰度
CDN分发自己配置,成本高内置CDN,按量计费
防盗链自己写签名逻辑URL鉴权/播放凭证/私有加密
播放数据没有,需自研埋点控制台自带统计
上线周期长,运维成本高短,运维基本为零

这里有个经验:如果你的视频量很小(比如几十个)、用户量也不大,确实没必要上点播,nginx方案能省不少钱。但一旦视频数量上百、用户分布在不同网络环境、内容有版权要求,点播的性价比就开始显现了。

1.2 整体架构与调用链路

项目整体架构大概是这样的:前端是uniapp,一套代码编译到App(Android/iOS)、微信小程序和H5;后端是一个标准Web服务,负责业务逻辑;视频相关能力全部走阿里云点播OpenAPI。

核心调用链路五步:

  1. 前端进入播放页面,先请求后端业务接口,带上视频ID或课程ID。
  2. 后端根据业务逻辑判断用户是否有观看权限,有权限则调用阿里云点播的OpenAPI获取播放凭证(PlayAuth)。
  3. 后端把播放凭证和视频元信息返回给前端。
  4. 前端拿着播放凭证初始化阿里云播放器,播放器向阿里云点播服务请求实际的播放地址并播放。
  5. 播放过程中,播放器上报事件,业务侧记录观看数据。

这里最关键的一点是:前端永远不直接接触阿里云的AccessKey。AccessKey是账号级别的密钥,一旦泄露,别人可以调用你账号下所有API,后果非常严重。播放凭证是临时的、单视频维度的授权,默认有效期100秒,过期要重新获取,这样即使被截获,影响范围也很小。

为什么不推荐用STS临时凭证?STS适用于需要细粒度控制多个云产品权限的场景,但点播播放场景下,播放凭证的语义更清晰、接入更简单,而且阿里云播放器SDK对PlayAuth的支持也更完善。所以我的建议是:常规视频播放,用PlayAuth就够了。

2. 项目初始化与基础配置,别小看manifest这一步

2.1 manifest.json 里的关键配置

uniapp项目里,manifest.json是每个平台的入口配置,很多人忽略它,结果到了原生端才发现一堆问题。这次接入阿里云点播SDK,主要涉及三块配置。

第一块是App模块配置。如果你用的是插件市场里的阿里云点播播放器插件,需要在manifest.json的App模块配置里勾选对应的原生插件。以我的项目为例,我用的是基于原生播放器SDK封装的uni-app插件,在manifest里配置了"视频播放器"相关模块,同时确认了包名和AppID与原生SDK要求的保持一致。这个配置在HBuilderX的可视化界面里能找到,也可以直接改源码:

{ "app-plus": { "modules": { "AliyunVideoPlayer": {} }, "distribute": { "android": { "packagename": "com.example.app", "permissions": [ "<uses-permission android:name=\"android.permission.INTERNET\"/>", "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>" ] }, "ios": { "id": "com.example.app", "privacyDescription": { "NSAppTransportSecurity": true } } } } }

注意,这只是示意,不同插件的模块名和配置方式都不一样,一定要以插件文档为准。视频播放本身只需要网络权限,不需要麦克风、摄像头之类的敏感权限,这点在应用市场上架审核时反而是加分项。

第二块是H5端配置。如果你的H5页面要部署在自己的域名下,需要在阿里云点播控制台将域名加入CDN加速域名,并在iOS的ATS(App Transport Security)里允许对应的HTTPS请求。H5端播放器加载的是阿里云CDN上的播放器JS和CSS,如果项目里面有CSP(Content Security Policy)限制,记得放行相关域名。

第三块是微信小程序配置。小程序是所有端里限制最多的。需要在小程序管理后台配置服务器域名,把阿里云点播的域名加到request、downloadFile的合法域名列表里。注意,小程序里的request合法域名要求HTTPS,并且不能带路径、不能是IP。另外,如果播放器组件本身发起了网络请求,也要确保对应域名在白名单内。很多时候小程序端播放失败,不是代码问题,就是域名白名单忘了配。

2.2 播放凭证的获取与失效处理,搞懂流程再动手

播放凭证是阿里云点播播放的核心凭证,流程上分为服务端获取和前端使用两步。

服务端获取很简单,调用OpenAPI的GetVideoPlayAuth接口即可,传入视频ID(即VideoId),返回结果里包含PlayAuth和VideoMeta。不同的后端语言有不同的SDK,以Java为例,大概是:

// 服务端代码示例(Java) DefaultAcsClient client = new DefaultAcsClient( DefaultProfile.getProfile("cn-shanghai", accessKeyId, accessKeySecret)); GetVideoPlayAuthRequest request = new GetVideoPlayAuthRequest(); request.setVideoId(videoId); GetVideoPlayAuthResponse response = client.getAcsResponse(request); String playAuth = response.getPlayAuth();

前端拿到PlayAuth后,传给播放器SDK进行初始化。播放凭证默认有效期100秒,这个时间是从服务端生成开始算的,不是从前端拿到开始算的,所以后端生成后要尽快返回,前端拿到后要尽快使用。如果用户停在播放页超过100秒还没开始播,凭证就过期了,播放器初始化会报错。

这时候的兜底方案是:监听播放器的错误回调,识别到凭证过期类型的错误后,重新向后端请求一次新的播放凭证,再重新初始化播放器。这里有个细节——不要每次都重新创建播放器实例,有些SDK支持直接更新凭证后重新加载,这样用户体验会好很多。另外,播放凭证是一次性的吗?不是,同一个凭证可以多次使用,但每次播放器初始化后凭证就会被消费,所以实际开发中,每次进入播放页、每次重新播放,最好都重新获取一次凭证,避免各种脏状态。

还有一个常见的困惑:为什么不用视频播放URL直接播放?阿里云点播确实可以生成带鉴权的播放URL,你也可以拿到URL后用video标签播。但这种方式的局限在于:URL鉴权一般只管IP或时间,没法做到"某个用户只能看自己买的课"这种业务级控制。播放凭证是跟视频绑定的,后端可以自由决定"给谁凭证、不给谁凭证",天然适合付费视频的场景。所以强烈建议,只要涉及用户权限判断,就走播放凭证这条路。

3. 播放器接入:两种方案,按场景来选

3.1 用原生video组件打底

如果你的需求特别简单:不需要多清晰度、不需要加密、用户量不大,那uniapp内置的video组件就够了。用法就像写HTML一样:

<template> <view class="player-wrap"> <video class="video-player" :src="videoUrl" :poster="posterUrl" controls @play="onPlay" @error="onError" /> </view> </template> <script> export default { data() { return { videoUrl: '', posterUrl: '' } }, onLoad(options) { // 通过业务接口拿播放地址 this.videoUrl = options.url } } </script> <style> .video-player { width: 100%; height: 220px; } </style>

video组件在四个端的表现不一样:App端和H5端用的是浏览器/系统播放器内核,小程序端则是小程序的原生video组件。最省事的地方是它不需要额外引入SDK,不需要处理播放凭证,直接给地址就播。

但它的限制也摆在那:第一,它只能播一个固定地址,没有清晰度切换的UI,你得自己写清晰度切换按钮;第二,它不做防盗链,只要你把地址给前端,地址就有可能被扒走;第三,在部分安卓机型上,系统播放器的解码能力有限,遇到HEVC编码的视频可能直接黑屏;第四,H5端在浏览器里播放还需要考虑自动播放限制和不同浏览器的兼容性。

我的建议是:视频只是辅助内容、播放量不大、对安全没有要求的项目,直接用video组件,别浪费时间接SDK。但如果你已经在做付费视频、教学内容,早晚要面对安全和体验问题,不如一步到位用播放器SDK。

3.2 阿里云播放器SDK集成实战

接入阿里云播放器SDK,在不同端上有不同的做法。App端推荐用插件市场里的原生封装插件,底层调的是阿里云官方Android/iOS播放器SDK,播放性能和解码能力都更强,还支持硬解、双声道这些原生能力。H5端则引入Aliplayer的JS SDK,本质是一个浏览器播放器。小程序端的情况比较特殊,早期官方SDK对小程序支持不好,很多人是用web-view嵌H5页面来放的,后来随着基础库升级和同层渲染普及,情况好了不少,但仍然要仔细测。

我这里以App端的插件接入为例,展示代码骨架。假设插件暴露的是一个组件:

<template> <aliyun-player ref="player" :vid="videoId" :play-auth="playAuth" @ready="onReady" @play="onPlay" @pause="onPause" @error="onError" /> </template> <script> export default { data() { return { videoId: '', playAuth: '' } }, onLoad(options) { this.videoId = options.videoId this.getPlayAuth() }, methods: { async getPlayAuth() { const res = await uni.request({ url: 'https://api.example.com/video/playAuth', data: { videoId: this.videoId } }) this.playAuth = res.data.playAuth }, onReady(e) { // 播放器准备完成,可以取清晰度列表 console.log('ready, duration:', e.detail.duration) }, onError(e) { // 根据错误码做相应的处理 console.error('play error:', e.detail) } } } </script>

有些插件支持另一种接入方式——通过uni.requireNativePlugin获取原生模块,然后手动控制播放器的创建和销毁。这种方式自由度更高,但代码更复杂,需要自己管理播放器视图挂在哪个节点上,不推荐新手一上来就这么干。

播放器生命周期一般是:初始化(init) -> 准备完成(prepared) -> 播放(playing) -> 暂停/继续 -> 销毁(destroy)。其中最重要的是destroy这一步。在页面卸载时一定要调用播放器的销毁方法,否则安卓端会出现播放器对象泄漏,表现为退出页面后声音还在响、再次进入页面黑屏等诡异问题。我在onUnload生命周期里写了一句清理代码,这个问题就再没出现过。

3.3 清晰度、倍速、进度记忆这些常用功能怎么写

视频播放器接入只是起点,实际业务里通常还要这几个功能。

清晰度切换。播放器拿到播放凭证后,会返回视频元信息,里面包含可用的清晰度列表(比如流畅、标清、高清、超清)。在onReady里把列表取出来渲染成按钮,用户点击后调用播放器的切换清晰度接口即可。注意,切换清晰度时,播放器内部会重新加载视频流,此时最好记录当前的播放进度,切换完成后seek回去,不然用户切个清晰度从头看,体验会很差。贴一段示意逻辑:

onQualityChange(q) { const currentTime = this.currentTime this.$refs.player.setQuality(q) this.$refs.player.seekTo(currentTime) }

倍速播放。点播场景里很常用,比如课程视频用户经常0.5倍看细节、2倍速快速过。播放器SDK一般都有setSpeed方法,直接传倍速值就行。需要注意,倍速播放时声音会变调,有些播放器内置了音调校正,有些没有,如果产品对音质要求高,选型时问一句插件是否支持音调校正。

进度记忆。这是付费视频的标配需求。做法是在播放器的timeupdate事件里,节流保存当前播放位置到服务端;用户再次进入播放页时,初始化完成后判断是否存在历史进度,有就seek到那个位置。保存频率不要太高,5秒一次足够了,否则服务端接口压力大,还容易被刷。

还有一个小功能容易被忽略:断网恢复。移动端网络切换时播放器会抛网络错误,这时候播放器一般是停止状态,需要监听网络变化后自动重连。App端可以用uni.onNetworkStatusChange,小程序也类似,网络恢复正常后重新调用播放器播放即可。

4. 跨端适配:说好的一套代码,结果每端都有自己的脾气

4.1 Android与iOS原生端的差异处理

如果只开发一个端,很多问题根本不会暴露。uniapp最折磨人的地方就在这里:逻辑代码可以复用,但播放器的表现各端都有自己的脾气。

先拿Android来说。第一个坑是返回键。Android系统返回键默认会直接退出当前页面,但视频全屏状态下,用户按返回键期望的是退出全屏,而不是退出页面。这就需要监听Android的物理返回键事件,判断当前是否处于全屏状态,是全屏就调用退出全屏接口,否则才走默认的页面返回逻辑。具体实现上,插件一般会提供一个isFullScreen的属性,配合uni的onBackPress生命周期就能处理。

第二个坑是生命周期管理。Android上页面切到后台后,播放器默认会继续缓冲甚至继续播放,这会导致两个问题:浪费流量、回来之后音画不同步。我的做法是在页面的onHide里暂停播放,onShow里恢复并seek到暂停前的位置。iOS端也有类似问题,但表现不一样,iOS如果在页面隐藏时没停播,系统会直接杀掉播放器,回到页面时就黑屏了。所以不管哪个端,onHide暂停、onShow恢复这个习惯一定要养成。

第三个坑是旋转和全屏。iOS的横屏方向一般跟系统自动旋转绑定,安卓各机型对屏幕方向的处理差异更大。我的经验是:进入全屏时,先锁死当前页面的方向为横屏,退出全屏时再恢复为竖屏。这个在uniapp里可以通过plus.screen.lockOrientation来控制,不同插件可能封装了更便捷的API。如果忽略这步,你会发现有些安卓机全屏后画面是横的但UI按钮还是竖的,非常尴尬。

4.2 H5与微信小程序的特殊坑

H5端适配的核心问题是浏览器策略。先说自动播放,桌面Chrome和iOS Safari对带声音的视频自动播放限制非常严格,用户不点击页面就播放,很可能直接被浏览器拦截。解决方案是:进入页面时不立即调用播放,而是显示封面图,等用户点击播放按钮后再初始化播放器。这不仅是浏览器要求,对用户来说也是合理的交互。

再说H5的域名白名单。如果你把Aliplayer的JS/CSS资源下载到本地部署,这倒是省了跨域问题,但版本更新你得自己跟;如果用官方CDN,又需要处理跨域和CSP的问题。我的建议是生产环境优先使用官方CDN,把相关域名加到CSP白名单,同时给播放器容器设置一个固定宽高比例,不然在部分浏览器里会出现播放器高度塌陷的样式问题。

微信小程序端,最大的是原生组件层级问题。video在小程序里是原生组件,层级天然最高,会盖在页面上面的弹窗、导航栏组件之上。如果你要在播放器上面浮出一层遮罩(比如试看结束弹登录框),就会发现普通的view根本盖不住video。现在虽然有了同层渲染,但兼容性依然不是100%,尤其是一些低版本基础库。稳妥的做法是:需要遮罩时先暂停播放并隐藏播放器,或者用cover-view这类同层组件来做浮层。另外,在小程序里播放加密视频、或者需要动态切换播放源时,一定要在开发者工具里真机调试,模拟器和真机行为差异很大。

4.3 常见问题速查表

把我在这次接入过程中实际遇到的问题整理成一张表,大家可以按图索骥。这里要说明一下,表格里的"解决方案"是我在项目里的实际做法,不一定适用于所有版本,但排查思路是通用的,照着这个思路定位问题,基本八九不离十。

问题现象可能原因解决办法
播放黑屏但音频正常视频编码不被当前解码器支持转码时选择H.264编码,避免使用HEVC;或启用播放器的软解开关
初始化播放器报"InvalidParam"PlayAuth缺失或格式错误确认后端返回字段名,PlayAuth是字符串不是对象
播放凭证过期报错凭证默认有效期100秒在错误回调里识别过期错误,重新请求凭证并再初始化
小程序端点击播放无反应合法域名未配置在小程序后台配置request和downloadFile合法域名,包括CDN域名
H5端无法自动播放浏览器自动播放策略用户主动点击后再调用播放,或用静音+用户手势解锁的方式
Android全屏后退出黑屏播放器未销毁或页面生命周期未处理onHide暂停、onShow恢复,退出页面时调用destroy
视频播放卡顿严重CDN未预热或网络差控制台配置CDN预热,播放器开启首帧优化,必要时降低默认清晰度
切清晰度后从头播切换时未保存进度切换前记录currentTime,切换后seekTo
退出页面仍有声音播放器未销毁在onUnload里调用播放器销毁接口

这张表是我踩坑之后一点一点积累的,大家如果遇到其他问题,也建议养成随手记录的习惯,排查效率会高很多。

5. 性能优化与体验细节

5.1 首帧速度与预加载策略

用户点开视频,最不能忍的就是一直转圈。首帧速度是视频体验的第一道关,我从三个方面做了优化。

第一,封面图。等待视频加载的时候先给用户看一张高质量的封面图,视觉上就不那么焦虑。封面图建议用视频首帧截图,或者设计一张统一风格的课程封面,尺寸要和播放器宽高比一致,避免拉伸变形。阿里云点播可以在控制台为视频设置封面,也可以通过OpenAPI上传封面,前端在初始化播放器前先展示封面,收到prepared事件后再隐藏。

第二,播放地址优化。阿里云点播默认的分发域名可能不是最优节点,建议配置自己的CDN加速域名,边缘节点会更近。另外,优先播放与用户网络环境匹配的清晰度,比如Wi-Fi下默认高清,4G下默认标清,这比让用户手动切省事得多。点播的播放器SDK有自动码率切换功能,在开启的情况下播放器会根据实时网速平滑切换清晰度,体验比固定一种清晰度好很多,就是要注意流量消耗。

第三,列表页预加载。很多产品会在视频列表页给用户预览图或者3秒试看,不要为了做这个效果就在列表页初始化一堆播放器实例,内存直接爆。更合理的做法是:列表页只加载封面图,用户点击进入详情页后再初始化播放器和预加载视频的第一帧。预加载的时机也不要在onLoad里立刻做,而是等页面出现、用户即将点击播放的时候再预加载,这时候预加载命中率最高。

5.2 视频安全:鉴权、加密与防盗链

视频安全是个不能回避的问题,尤其做付费内容。阿里云点播提供了几层安全能力,从弱到强分别是URL鉴权、播放凭证、私有加密、数字水印。

URL鉴权是最基础的,用在播放地址上,通过签名参数控制URL的有效期和访问IP。适合防止其他人直接把视频地址分发出去。播放凭证就是我们前面一直在说的机制,它能做到用户维度的访问控制,后端不给凭证就看不了。私有加密则更进一步,视频内容在存储和传输时都是加密的,播放器拿到解密密钥才能播放,这在安卓端能有效防止别人从本地缓存里把视频文件扒走。

我在项目里实际用到了两层:播放凭证做业务权限控制,私有加密做内容保护。注意,加密功能的开启是在转码模板里配置的,已经转码完成的视频如果要开启加密,需要重新转码一次,而且加密视频只能由支持解密的播放器播放,用浏览器默认的video标签或普通第三方播放器是播放不了的,这点要在需求评审时跟产品讲清楚,不然会以为是bug。

数字水印适合版权溯源场景,当你的视频被翻录分享到其他平台,水印里包含用户ID或订单信息,就能追到泄露源头。它不影响正常播放,但会增加视频处理的时间和成本,量小的话可以不care。

5.3 播放数据埋点与统计

播放数据对内容型产品来说太重要了。完播率直接决定课程质量评估,异常流量能帮你发现盗录行为。uniapp接入点播后,埋点有两种方式:一是用播放器SDK自带的事件回调自己上报,二是用阿里云的视频播放数据上报服务。自己上报更灵活,能跟业务数据结合。

我上报的事件主要有:播放器准备完成(prepared)、开始播放(play)、暂停(pause)、播放结束(ended)、退出页面(离开)、错误(error)。每次上报至少带这些字段:视频ID、用户ID(如果有登录)、当前进度、网络类型、端类型、播放器版本。上报频率需要控制,timeupdate是变化很快的事件,不可能每次都请求接口,我会在前端做聚合,5秒保存一次进度,页面退出时再做一次最终上报。

数据用起来之后,你会发现很多有意思的结论:比如某节课的完播率极低,可能是内容质量不行,也可能是清晰度切换体验太差;某个时间点用户大量退出,可能是视频卡顿太严重。有了这些数据,后续做内容优化和播放器调优才有依据,而不是拍脑袋。

最后分享一个我自己印象最深的坑:第一次联调的时候,安卓端一切正常,结果iOS上一点播放就直接崩溃,查了半天发现是播放器插件和另一个原生SDK的版本冲突,最后只能升级插件的原生依赖并重新打自定义基座才解决。从那以后我就学乖了,凡是接原生SDK相关的插件,第一件事就是看它的原生版本说明,尽量不要在一个项目里混用同一套底层库的不同版本。接入第三方服务就是这样,文档读一百遍不如实际踩一遍坑,但踩完了把经验沉淀下来,后面就能少走很多弯路。这篇文章里的代码和配置都是基于我们项目的实践整理出来的,不同插件、不同版本可能略有差异,大家还是要以对应文档为准。如果你也正在做uniapp + 阿里云点播的接入,希望这篇分享能帮到你。

返回列表