【Vue】Aliplayer 视音频播放的实践与思考
简介
阿里云Web播放器SDK(Aliplayer SDK)是阿里视频云端到云到端服务的重要一环,结合对象存储(Object Storage Service,简称OSS) 实现深度融合视频云业务,支持视音频的点播和直播等基础播放功能
本文以 Vue 组件化的角度进行全流程解析、并抽离公共业务逻辑,从而实现可维护、高复用的视音频播放组件
引入
官方实践直接在页面引入对应的JS、CSS文件,通过暴露全局 window.Aliplayer
属性自定义实例化对象,传送门 => 集成文档_Web播放器_播放器SDK_视频点播-阿里云
<link rel="stylesheet" href="https://g.alicdn.com/de/prismplayer/2.8.2/skins/default/aliplayer-min.css" />
<script charset="utf-8" type="text/javascript" src="https://g.alicdn.com/de/prismplayer/2.8.2/aliplayer-min.js"></script>
当我们要对其封装成一个独立的组件时,可以实现一个 dynamicLoadScript
函数去解耦全局引用的问题,调用如下 ⬇️
import { dynamicLoadScript } from '@/utils/'
const aliplayerCDN = 'https://g.alicdn.com/de/prismplayer/2.8.1/aliplayer-min.js'export default {...mounted () {dynamicLoadScript(aliplayerCDN, (err) => {if (err) return this.$notify(err.message);window.addEventListener('beforeunload', this.updateLearnerTime)this.initAliplayer() // 初始化视频容器this.$once('hook:beforeDestroy', () => {window.removeEventListener('beforeunload', this.updateLearnerTime)})})}
}<style lang="postcss" scoped>@import 'https://g.alicdn.com/de/prismplayer/2.8.1/skins/default/aliplayer-min.css';
</style>
通过 dynamicLoadScript
,和 postcss
去处理文件引用,Vue hooks 提供组件挂载与销毁的回调,initAliplayer
方法进行初始化
// html => <div id="J_prismPlayer" />export default {...props: {config: {type: Object,required: true,default () {return {url: '', // 视频播放地址cover: '', // 封面图videoId: '', // 点播vidplayTime: 0, // 续播时长chapterId: '' // 章节ID}}}},data: () => ({baseConfig: {}, // Aliplayer 基础配置player: null, // player 全局共享对象loading: false, // 加载状态toggling: false,erroring: false}),methods: {initAliplayer () {const { cover, url: source, videoId: vid } = this.configconst baseConfig = {cover, id: 'J_prismPlayer', width: '100%', height: '100%', autoplay: false, ...this.baseConfig}let $player = null// 通过 vid 区别是否点播 or 直播,loading 进行状态标识if (vid) {this.loading = truethis.$axios.getPlayAuth(vid).then((res) => {if (res.code == 200 && res.data) {this.player = $player = new window.Aliplayer({...baseConfig,vid,playauth: res.data})this.initPlayerEvent($player) // 初始化容器事件} else {return Promise.reject(res.msg)}}).catch(this.error).finally(() => {this.loading = false})} else {this.player = $player = new window.Aliplayer({...baseConfig,source}); this.initPlayerEvent($player)}}}
}
props.config
配置播放器的基础属性,data.baseConfig
对外抛出自定义播放器入口(mixins),如 Aliplayer 音频播放需单独传入 { format: 'mp3', mediaType: 'audio' }
进行区分,传送门 => 属性和接口说明_Web播放器_播放器SDK_视频点播-阿里云
业务逻辑: this.error
交接全局异常处理的句柄,this.initPlayerEvent
初始化容器事件
import { isObject, throttle } from '@/utils'export default {...methods: {initPlayerEvent ($player) {'ready error pause play ended'.split(' ').forEach((item) => {$player.on(item, this[item] || console.log)})// video 拖动$player.on('completeSeek', ({ paramData }) => this.updateLearnerTime(paramData))// audio 时间更新$player.on('timeupdate', throttle(() => {if (this.audio) {this.audio.currentTime = player.getCurrentTime()}}, 1000))},// 异常优先级 API => Aliplayer => http Statuserror (msg = '播放地址未找到~') {this.erroring = trueconst toastMsg = !isObject(msg)? String(msg): msg.paramData ? msg.paramData.display_msg : '404-NotFound'this.$toast(toastMsg)}}
}
timeupdate
高频事件通过节流函数 throttle
函数限制成1000ms 触发一次,Aliplayer 异常信息从 msg.paramData
中获取
业务逻辑: this.updateLearnerTime
记录当前播放时长,触发事件为视音频暂停、拖动、结束
export default {...methods: {/*** 记录播放时长* @param {Number} time 自定义记录时间* @return {Promise}*/updateLearnerTime (time = 0) {const { chapterId } = this.configconst countTime = time || this.player.getCurrentTime()return this.$axios.postUpdateLearnerTime({chapterId,learnerTime: parseInt(countTime)})},// 播放视频setPlay () {this.player.play()},// 暂停视频setPause () {this.player.pause()}}
}
完整代码
/mixins/media.js
,单独处理成 mixins 混入公用业务逻辑
import { dynamicLoadScript, isObject, throttle } from '@/utils/util'
const aliplayerCDN = 'https://g.alicdn.com/de/prismplayer/2.8.1/aliplayer-min.js'export default {props: {config: {type: Object,required: true,default () {return {url: '', // 视频播放地址cover: '', // 封面图videoId: '', // 点播vidplayTime: 0, // 续播时长chapterId: '' // 章节ID}}}},data: () => ({baseConfig: {}, // Aliplayer 基础配置player: null, // player 全局共享对象loading: false,toggling: false,erroring: false}),watch: {// 切换播放器地址config (newVal, oldVal) {if (newVal.url != oldVal.url) {Object.assign(this, { erroring: false, toggling: true })this.player.loadByUrl(newVal.url)this.ready()}}},mounted () {dynamicLoadScript(aliplayerCDN, 'Aliplayer', (err) => {if (err) return this.$notify(err.message);window.addEventListener('beforeunload', this.updateLearnerTime)this.initAliplayer()this.$once('hook:beforeDestroy', () => {window.removeEventListener('beforeunload', this.updateLearnerTime)})})},methods: {initAliplayer () {const { cover, url: source, videoId: vid } = this.configconst baseConfig = {cover, id: 'J_prismPlayer', width: '100%', height: '100%', autoplay: false, ...this.baseConfig}let $player = null// 通过 vid 区别是否点播 or 直播,loading 进行状态标识if (vid) {this.loading = truethis.$axios.getPlayAuth(vid).then((res) => {if (res.code == 200 && res.data) {this.player = $player = new window.Aliplayer({...baseConfig,vid,playauth: res.data})this.initPlayerEvent($player) // 初始化容器事件} else {return Promise.reject(res.msg)}}).catch(this.error).finally(() => {this.loading = false})} else {this.player = $player = new window.Aliplayer({...baseConfig,source}); this.initPlayerEvent($player)}},initPlayerEvent ($player) {'ready error pause play ended'.split(' ').forEach((item) => {$player.on(item, this[item] || console.log)})// video 拖动$player.on('completeSeek', ({ paramData }) => this.updateLearnerTime(paramData))// audio 时间更新$player.on('timeupdate', throttle(() => {if (this.audio) {this.audio.currentTime = $player.getCurrentTime()}}, 1000))},// 异常优先级 API => Aliplayer => http Statuserror (msg = '播放地址未找到~') {this.erroring = trueconst toastMsg = !isObject(msg)? String(msg): msg.paramData ? msg.paramData.display_msg : '404-NotFound'this.$toast(toastMsg)},/*** 记录播放时长* @param {Number} time 自定义记录时间* @return {Promise}*/updateLearnerTime (time = 0) {const { chapterId } = this.configconst countTime = time || this.player.getCurrentTime()return this.$axios.postUpdateLearnerTime({chapterId,learnerTime: parseInt(countTime)})},// 播放视频setPlay () {this.player.play()},// 暂停视频setPause () {this.player.pause()}}
}
视音频组件封装,以视频为例,常见业务场景为视频初始化后进行续播
import media from '@/mixins/media'export default {name: 'BaseVideo',mixins: [media],methods: {// 设置当前视频播放时长,根据最后一次的剩余时长进行重播setPlaySeek (player) {const { playTime } = this.configconst time = player.getDuration() - playTimeconst onClose = () => player.seek(0)if (time <= 8) {this.$toast({ duration: 2000, message: '已为您重新播放...', onClose })} else {player.seek(playTime)}},// 初始化容器成功ready () {this.toggling = falsethis.setPlaySeek(this.player)this.setPlay()},// 播放结束ended () {const countTime = this.player.getCurrentTime() - 5this.updateLearnerTime(countTime).finally(() => {this.$emit('videoEnded', this.config)})},// 视频暂停pause () {if (!this.toggling) { this.updateLearnerTime() }}}
}
已知的一个问题,当视频地址切换的时候,Aliplayer 会重新调用 pause
事件,虽然可以通过 this.player.getStatus()
获取播放器状态,但是设置 toggling
属性更好规避 Aliplayer 带有副作用的事件回调
常见问题
视频播放容器无法被遮挡、自定义遮罩层级显示错误
通过切换容器 display 属性进行图片占位,vue可采用
v-show
指令自定义播放器样式
以播放按钮居中为例,Xpath ClassName: prism-big-play-btn
欢迎补充
【Vue】Aliplayer 视音频播放的实践与思考相关推荐
- 最简单的视音频播放示例7:SDL2播放RGB/YUV
最简单的视音频播放示例系列文章列表: 最简单的视音频播放示例1:总述 最简单的视音频播放示例2:GDI播放YUV, RGB 最简单的视音频播放示例3:Direct3D播放YUV,RGB(通过Surfa ...
- 最简单的视音频播放示例9:SDL2播放PCM
本文记录SDL播放音频的技术.在这里使用的版本是SDL2.实际上SDL本身并不提供视音频播放的功能,它只是封装了视音频播放的底层API.在Windows平台下,SDL封装了Direct3D这类的API ...
- 最简单的视音频播放演示样例5:OpenGL播放RGB/YUV
===================================================== 最简单的视音频播放演示样例系列文章列表: 最简单的视音频播放演示样例1:总述 最简单的视音频 ...
- 最简单的视音频播放示例8:DirectSound播放PCM
===================================================== 最简单的视音频播放示例系列文章列表: 最简单的视音频播放示例1:总述 最简单的视音频播放示例 ...
- 最简单的视音频播放示例6:OpenGL播放YUV420P(通过Texture,使用Shader)
===================================================== 最简单的视音频播放示例系列文章列表: 最简单的视音频播放示例1:总述 最简单的视音频播放示例 ...
- 最简单的视音频播放示例5:OpenGL播放RGB/YUV
===================================================== 最简单的视音频播放示例系列文章列表: 最简单的视音频播放示例1:总述 最简单的视音频播放示例 ...
- 最简单的视音频播放示例2:GDI播放YUV, RGB
===================================================== 最简单的视音频播放示例系列文章列表: 最简单的视音频播放示例1:总述 最简单的视音频播放示例 ...
- 最简单的视音频播放示例1:总述
===================================================== 最简单的视音频播放示例系列文章列表: 最简单的视音频播放示例1:总述 最简单的视音频播放示例 ...
- 移动应用程序设计基础——安卓动画与视音频播放器的实现
<移动应用程序设计基础>实验6 安卓动画与视音频播放器的实现 实验名称: 实验6 安卓动画与视音频播放器的实现 所使用的工具软件及环境: JDK1.8,Android Studio 一.实 ...
- 最简单的视音频播放演示样例4:Direct3D播放RGB(通过Texture)
===================================================== 最简单的视音频播放演示样例系列文章列表: 最简单的视音频播放演示样例1:总述 最简单的视音频 ...
最新文章
- “火柴棍式”程序员面试题
- Shell整数型变量自增自减的实现方式(+1,-1,++,--)
- A way to visualize mip levels
- .ssh文件夹在哪里_【TOOLS】本地利用ssh远程连接服务器并启用远程服务器的jupyter lab并配置好anaconda的环境...
- Android数据存储——SharedPreferences
- [机器学习入门] 经典台大李宏毅机器学习课程从这里开始
- caxa cam数控车2020破解版 v20.0.0.6460附安装教程|caxa数控车2020破解版
- matlab作图有拉盖尔,拉盖尔高斯光束matlab
- vs2015 2017 2019社区版免登录延长许可证
- 用U盘给虚拟机装系统——U深度
- 弘辽科技:抖加投放后会增粉吗?有什么技巧?
- Rust vs. Go:为什么他们在一起更好
- [WPF]Win10便签软件
- 提示:The word is not correctly spelled 解决方法
- 论文查重的内容是哪些?
- 计算机连接电视显示超范围,HDMI连接后电脑操作界面的边框超出电视屏幕,怎么解决...
- 达梦物化视图概念及简单示例
- 动态加载Animator和AnimatorController
- 移动端布局-px转vw、vh
- 一张图看明白电信云解决方案架构
热门文章
- 最简单的视音频播放示例7:SDL2播放RGB/YUV