简介

阿里云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 带有副作用的事件回调

常见问题

  1. 视频播放容器无法被遮挡、自定义遮罩层级显示错误

    通过切换容器 display 属性进行图片占位,vue可采用 v-show 指令

  2. 自定义播放器样式

    以播放按钮居中为例,Xpath ClassName: prism-big-play-btn

  3. 欢迎补充

    【Vue】Aliplayer 视音频播放的实践与思考相关推荐

    1. 最简单的视音频播放示例7:SDL2播放RGB/YUV

      最简单的视音频播放示例系列文章列表: 最简单的视音频播放示例1:总述 最简单的视音频播放示例2:GDI播放YUV, RGB 最简单的视音频播放示例3:Direct3D播放YUV,RGB(通过Surfa ...

    2. 最简单的视音频播放示例9:SDL2播放PCM

      本文记录SDL播放音频的技术.在这里使用的版本是SDL2.实际上SDL本身并不提供视音频播放的功能,它只是封装了视音频播放的底层API.在Windows平台下,SDL封装了Direct3D这类的API ...

    3. 最简单的视音频播放演示样例5:OpenGL播放RGB/YUV

      ===================================================== 最简单的视音频播放演示样例系列文章列表: 最简单的视音频播放演示样例1:总述 最简单的视音频 ...

    4. 最简单的视音频播放示例8:DirectSound播放PCM

      ===================================================== 最简单的视音频播放示例系列文章列表: 最简单的视音频播放示例1:总述 最简单的视音频播放示例 ...

    5. 最简单的视音频播放示例6:OpenGL播放YUV420P(通过Texture,使用Shader)

      ===================================================== 最简单的视音频播放示例系列文章列表: 最简单的视音频播放示例1:总述 最简单的视音频播放示例 ...

    6. 最简单的视音频播放示例5:OpenGL播放RGB/YUV

      ===================================================== 最简单的视音频播放示例系列文章列表: 最简单的视音频播放示例1:总述 最简单的视音频播放示例 ...

    7. 最简单的视音频播放示例2:GDI播放YUV, RGB

      ===================================================== 最简单的视音频播放示例系列文章列表: 最简单的视音频播放示例1:总述 最简单的视音频播放示例 ...

    8. 最简单的视音频播放示例1:总述

      ===================================================== 最简单的视音频播放示例系列文章列表: 最简单的视音频播放示例1:总述 最简单的视音频播放示例 ...

    9. 移动应用程序设计基础——安卓动画与视音频播放器的实现

      <移动应用程序设计基础>实验6 安卓动画与视音频播放器的实现 实验名称: 实验6 安卓动画与视音频播放器的实现 所使用的工具软件及环境: JDK1.8,Android Studio 一.实 ...

    10. 最简单的视音频播放演示样例4:Direct3D播放RGB(通过Texture)

      ===================================================== 最简单的视音频播放演示样例系列文章列表: 最简单的视音频播放演示样例1:总述 最简单的视音频 ...

    最新文章

    1. “火柴棍式”程序员面试题
    2. Shell整数型变量自增自减的实现方式(+1,-1,++,--)
    3. A way to visualize mip levels
    4. .ssh文件夹在哪里_【TOOLS】本地利用ssh远程连接服务器并启用远程服务器的jupyter lab并配置好anaconda的环境...
    5. Android数据存储——SharedPreferences
    6. [机器学习入门] 经典台大李宏毅机器学习课程从这里开始
    7. caxa cam数控车2020破解版 v20.0.0.6460附安装教程|caxa数控车2020破解版
    8. matlab作图有拉盖尔,拉盖尔高斯光束matlab
    9. vs2015 2017 2019社区版免登录延长许可证
    10. 用U盘给虚拟机装系统——U深度
    11. 弘辽科技:抖加投放后会增粉吗?有什么技巧?
    12. Rust vs. Go:为什么他们在一起更好
    13. [WPF]Win10便签软件
    14. 提示:The word is not correctly spelled 解决方法
    15. 论文查重的内容是哪些?
    16. 计算机连接电视显示超范围,HDMI连接后电脑操作界面的边框超出电视屏幕,怎么解决...
    17. 达梦物化视图概念及简单示例
    18. 动态加载Animator和AnimatorController
    19. 移动端布局-px转vw、vh
    20. 一张图看明白电信云解决方案架构

    热门文章

    1. 中国省市区乡县名称代码对照表
    2. 绘制专利说明书附图的基本要素
    3. 浅谈各种常见的芯片封装技术DIP/SOP/QFP/PGA/BGA
    4. java是面向对象还是面向过程_Java面向对象编程和面向过程编程的区别
    5. Java面向对象编程及其三大特征
    6. 读后感--《魔鬼数学:大数据时代,数学思维的力量》
    7. Win 7扫雷时间基址查找
    8. 信息学奥赛一本通pdf_信息学奥赛冠军的竞赛“秘籍”
    9. vs编译与停止调试时卡顿、无响应的问题
    10. 基于JSP实现的作业管理系统