阿里播放器Aliplayer遇到的所有坑

1,关于阿里播放器使用过的几种播放方式

url (source)

① 要在创建播放器前要拿到资源否则会报错

② 在有不同清晰度的资源时  直接调用 player.loadByUrl() 方法会报错 官网给出的例子:JSON.stringify({ 'FD': videoUrl.Fd, 'SD': videoUrl.Sd })   (首次可以播,但在切换时资源路径会出错,官网的demo只有单个视频,无法测试切换)

解决方法是 直接抛弃 player.loadByUrl()  ,每次销毁播放器,然后重新创建。

 

vid+playauth:

这种方式清晰度不需要自己设置,但坑比较多。在F5刷新后视频组件会丢失,只剩下封面,并且此类型必须额外写入一些配置,指定清晰度等等。

这种方式切换视频源不用player.loadByUrl() ,要用  player.replayByVidAndPlayAuth(this.vid, this.playauth)  方法

        // vid+playauth播放
        // vid: this.vid,
        // playauth: this.playauth,
        // encryptType: '1', // 播放加密视频
        // qualitySort: "asc", // 指定排序方式,只有使用Vid + PlayAuth播放方式时支持。
        // format: "m3u8", // 指定播放地址格式,只有使用vid的播放方式时支持可选值。
        // definition: "FD,LD,SD,HD,OD,2K,4K", // 此值是vid对应流清晰度的一个子集,仅H5模式支持。
        // defaultDefinition: 'SD', // 默认视频清晰度,此值是vid对应流的一个清晰度,仅H5模式支持。

2,需注意这几种事件的坑,在记录上报,播放时常要在 播放中playing 而不是 播放play 中执行,播放完毕后顺序播放可以用自带组件,这里是自己实现,但是有个bug是 ended播放完毕回调中 事件会执行两次!这里是设置了个开关来限制的。

3,配置中有个 skinLayout 可以设置一些播放组件,其中 setting 里自带倍速,字幕,音轨,清晰度,但是字幕和音轨这里用不到,还没法去掉。但是注释了setting又会导致 倍速和清晰度不见了。

这里官方提供了 components组件

components: [
          { name: 'RateComponent', type: AliPlayerComponent.RateComponent },
          { name: 'MemoryPlayComponent', type: AliPlayerComponent.MemoryPlayComponent },
          {
            name: 'QualityComponent',
            type: AliPlayerComponent.QualityComponent
          }],
 
RateComponent 为倍速
MemoryPlayComponent 历史记录
QualityComponent 是清晰度 ,这里有个坑,就是原本项目中引入的是   https://g.alicdn.com/de/prismplayer/2.8.1/aliplayer-min.js   (2.8.1版本的js和css)导致 一引入 清晰度组件就会报错,一只到不到原因,后来修改为 2.9.23版本的,报错消失。
 
4,倍速功能,因为有之前的坑,每次会销毁播放器重新创建,所以导致播放下一个视频时倍速会重置成1,官网还没有提供切换倍速抛出的事件,所以就捕获不到倍速的改变,也就无法在下一个时去设置倍速。只能手写一个倍速盒子点击时去对接阿里的方法。
 
音乐播放器的代码:
<template>
    <!-- 容器 -->
    <div class="container">
      <!-- 音频播放器 -->
      <transition name="el-zoom-in-top">
        <div v-show="isShowPlayer" class="audioWrapper">
          <div class="header">
            <div class="title">正在播放 {{playingAudioName}}</div>
            <img class="close" src="@/assets/image/close.png" alt="关闭播放器" @click="closePlayer">
          </div>
          <div class="audio-box" v-loading="playerLoading">
            <img :src="play?pauseIcon:playIcon" alt="播放暂停" @click="playHandle()">
            <div class="audio-progress">
              <el-slider v-model="sliderVal" :format-tooltip="formatTooltip" :min="sliderMin" :max="sliderMax" @change="sliderChange"></el-slider>
              <div id="aliPlayer"></div>
              <div class="audio-info">
                <div>
                  <span class="current-time">{{currentTime}}</span>
                  <span class="time-middle">/</span>
                  <em class="time-long">{{duration}}</em>
                </div>
                <div class="util-box">
                  <span @click="isShowRateList = !isShowRateList">倍速{{rateVal}}x</span>
                  <ul v-if="isShowRateList">
                    <li v-for="(t,i) in playBackRateList" :key="i" @click="changeRate(t)">{{t}}</li>
                  </ul>
                </div>
              </div>
            </div>
          </div>
        </div>
      </transition>
    </div>
</template>

<script>
import { courseStore } from "@/store/course.js";
import { tokenStore } from "@/store/user.js";
export default {
  layout: 'detail',
  name: 'CourseCatalogue',

  // 校验动态路由参数的有效性
  validate({ params, query }) {
  
  },
// 获取详情数据
  async asyncData({ $axios, params }) {
return {

    }
  },


  // 处理数据
  created() {
  
  },

  mounted() {
    // 初始化播放器
    this.createPlayer()
  
  },

  // 销毁定时器及阿里播放器
  beforeDestroy() {
    this.stopReportLearning()
    if (this.player) {
      this.player.dispose()
      this.player = null
    }
  },

  //
  computed: {

  },
  data() {
    return {
      store: courseStore(), // 课程商店
      userStore: tokenStore(), // 用户商店
    
      seeked: true, // 是否可续播
     // 播放相关
      canplay: false,// 是否可以播放
      isShowPlayer: false,// 是否显示播放器
      playerLoading: true, // 播放器loading
      playingAudioName: '', // 正在播放的课程名称
      player: null, // 播放器
      timer: null,  // 学习上报定时器
      playDate: '', // 当前视频的时间戳
      opener: true,
      playerUrl: '/a.m3u8', // 播放的资源路径
      sliderVal: 0, // 滑块当前时长。
      sliderMin: 0,
      sliderMax: 0, // 滑块的总时长。
      currentTime: '00:00', // 当前播放时间
      duration: '00:00', // 总时长
      play: false, // 播放ing?暂停?
      // 播放/暂停图标
      playIcon: require('@/assets/image/play.png'),
      pauseIcon: require('@/assets/image/pause.png'),
      isShowRateList: false, // 是否展示倍速列表
      rateVal: 1, // 当前倍速
      playBackRateList: [0.25, 0.5, 0.75, '正常', 1.25, 1.5, 1.75, 2], // 倍速
    }
  },
  methods: {
    // 获取vid+playauth
    getVid() {

    },// 初始化播放器
    createPlayer() {
      let prop = {
        id: "aliPlayer",
        source: this.playerUrl,
        mediaType: "audio",
        width: "100%",
        height: "100%",
        autoplay: false, // 自动播放
        isLive: false, // 直播
        rePlay: false, // 循环播放
        playsinline: true, //H5是否内置播放
        preload: true, //播放器自动加载
        language: "zh-cn", // 语言
        cover: '', //播放器默认封面图片,需要autoplay为’false’时,才生效
        controlBarVisibility: "hover", //控制面板的实现 ‘click’ 点击出现、‘hover’ 浮动出现、‘always’ 一直在
        useH5Prism: true //指定使用H5播放器
      }

      this.player = new Aliplayer(prop, function (player) {
        console.log("The player is created");
      })

      // 播放 (由暂停恢复为播放时触发)
      this.player.on('play', () => {
        this.play = true
      })

      // 暂停
      this.player.on('pause', () => {
        this.play = false
        this.stopReportLearning()
      })

      // 播放ing (播放中,会触发多次)
      this.player.on('playing', () => {
        // console.log('播放中ing')

        // 学习记录上报
        this.learningReport()
      })

      // 可以播放
      this.player.on('canplay', () => {
        this.playerLoading = false
        const courseInfo = this.store.getCourseInfo
        if (this.canplay && this.seeked && courseInfo.duration && courseInfo.newestLessonId === courseInfo.lessonId) {
          this.seeked = false
          this.player.seek(courseInfo.duration)
        }
      })

      // 等待缓冲
      this.player.on('waiting', () => {
        this.playerLoading = true
      })

      // 时长发生变动时
      this.player.on('timeupdate', () => {
        this.updateTime()
      })

      // 播放出错
      this.player.on('error', () => {
        this.$message.error('播放出错')
        this.stopReportLearning()
        this.sliderVal = 0
        this.play = false
        this.playerLoading = true
        this.isShowPlayer = false
        this.store.setIsPlaying(false)
      })

      // 播放完毕
      this.player.on("ended", () => {
        if (this.opener) {
          this.opener = false
          console.log('播放完了!')

          // 停止学习上报
          this.stopReportLearning()
          this.playDate = ''

          // 调用子组件方法,自动播放下一个
          const courseInfo = this.store.getCourseInfo
          this.$refs.CourseCurriculum.createdPlayList(courseInfo.courseId, courseInfo.lessonId)
        }
      });
    },

    // 学习记录上报
    learningReport() {

      // 先清空
      this.stopReportLearning()

      const courseInfo = this.store.getCourseInfo
      const lessonAudioVideo = courseInfo.lessonAudioVideo

      // 播放时长小于1时不进行上报
      // if (curTime <= 1) {
      //   return
      // }

      if (this.playDate === '') {
        const nowDate = new Date
        this.playDate = nowDate.getTime()
      }

      // 学次唯一值
      const userInfo = this.userStore.getUserInfo
      const key = `${userInfo.userId}_${courseInfo.orderId}_${courseInfo.projectId}_${courseInfo.courseId}_${courseInfo.lessonId}_${this.playDate}`;
      const sessionKey = this.$md5(key)

      this.timer = setInterval(() => {
        const curTime = parseInt(this.player.getCurrentTime())
        this.$axios.$post('/hadoop/learning/learningRecordSend', {
          sessionKey: sessionKey,
          orderId: courseInfo.orderId,
          projectId: courseInfo.projectId,
          courseId: courseInfo.courseId,
          courseCatalogId: courseInfo.lessonId,
          resourceType: lessonAudioVideo.type,
          resourceId: lessonAudioVideo.audioId,
          duration: curTime
        }).then(() => { }).catch((err) => {
          if (err.code === 10009) this.player.pause()
        });
      }, 5000)
    },

    // 停止上报学习记录
    stopReportLearning() {
      if (this.timer) {
        clearInterval(this.timer)
        this.timer = null
      }
    },

    // 关闭播放器
    closePlayer() {
      this.isShowPlayer = false
      this.store.setIsPlaying(false)
      this.stopReportLearning()
      this.play = false
      this.playerLoading = true
      this.player.pause()
    },

    // 播放/暂停
    playHandle() {
      this.play = !this.play
      this.play ? this.player.play() : this.player.pause()
    },

    // 更新时间
    updateTime() {
      if (!Number.isFinite(this.player.getDuration())) {
        this.currentTime = Number.MAX_SAFE_INTEGER;
        this.currentTime = '00:00';
      } else {
        const total = this.formatTime(this.player.getDuration())
        const current = this.formatTime(this.player.getCurrentTime())
        this.duration = `${total.min}:${total.sec}`
        this.currentTime = `${current.min}:${current.sec}`
        this.sliderVal = Math.floor(this.player.getCurrentTime())
        this.sliderMax = Math.floor(this.player.getDuration())
      }
    },

    // 格式化毫秒,返回String型分秒对象
    formatTime(time) {
      // 如果没获取到
      if (!time) return { min: '00', sec: '00' }
      return {
        min: Math.floor(time / 60).toString().padStart(2, '0'),
        sec: Math.floor(time % 60).toString().padStart(2, '0')
      }
    },

    // 格式化毫秒数,对应elm滑块组件
    formatTooltip(val) {
      const time = this.formatTime(val)
      return `${time.min}:${time.sec}`
    },

    // 滑块松动后触发。更新当前时长,
    // 时长发生变动会init里的方法进行更新数据
    sliderChange(val) {
      this.player.seek(this.sliderVal)
    },

    // 倍速改变时
    changeRate(t) {
      if (t === '正常') t = 1;
      this.rateVal = t
      this.player.setSpeed(t)
      this.isShowRateList = false
    },
  }
}
</script>

 

视频播放代码:

<template>
  <div class="wrap">
    <!-- 视频区域 -->
    <div class="video" :class="{'w1': sideBarFlag}">

      <!-- 视频 -->
      <div class="player" id="aliVideoPlayer"></div>

    </div>

    <!-- 侧边栏区域 -->

  </div>
</template>

<script>
import { courseStore } from "@/store/course.js";
import { tokenStore } from "@/store/user.js";
export default {
  layout: 'index',
async asyncData({ $axios, query }) {
    const [chapterList] = await Promise.all([
      // 获取视频资源
      // $axios.$get(`/resources/resources/video/${lessonAudioVideo.videoId}`),
      // 获取课程列表
      $axios.$get('/learning/user/project/pc/catalog/v1', {
        params: {
          projectId: query.projectId,
          courseId: query.courseId
        }
      }),
    ])
    return {
      // videoData,
      chapterList
    }
  },
  data() {
    return {
      store: courseStore(), // 课程商店
      userStore: tokenStore(), // 用户商店
      courseInfo: {},// 当前播放的课程相关信息
      player: null, // 播放器
      opener: true,
      playerUrl: '', // 播放的资源url
      vid: '', // vid + playauth
      playauth: '',
      lessonName: '', // 当前播放的课次名称
      seeked: true, // 是否可续播
      speed: 1, // 倍速
      // 侧边栏
      sideBarFlag: true,
      // 目录/笔记
      listFlag: true,
      // 学习上报定时器
      timer: null,
      // 当前视频的时间戳
      playDate: '',
    }
  },

  beforeMount() {
    this.courseInfo = this.store.getCourseInfo
    // 获取视频资源url
    // this.playerUrl = this.courseInfo.lessonAudioVideo.url
  },

  async mounted() {
    // 获取视频vid+playauth
    const videoInfo = await this.getVid()
    this.playerUrl = JSON.stringify({ 'FD': videoInfo.Fd, 'SD': videoInfo.Sd })
    // this.vid = videoInfo.VideoId
    // this.playauth = videoInfo.PlayAuth
    // 初始化播放器
    this.createPlayer()
  },

  // 销毁定时器
  beforeDestroy() {
    this.stopReportLearning()
    if (this.player) {
      this.player.dispose()
      this.player = null
    }
  },

  methods: {
    // 获取vid+playauth
    getVid() {
      return this.$axios.$get(`/file/sys/file/getVideoPlayUrl`, {
        params: {
          videoId: this.courseInfo.lessonAudioVideo.resourcesId
        }
      })
    },
    // 初始化播放器
    createPlayer() {
      let prop = {
        id: "aliVideoPlayer",

        // 资源url播放
        source: this.playerUrl,

        // vid+playauth播放
        // vid: this.vid,
        // playauth: this.playauth,
        // encryptType: '1', // 播放加密视频
        // qualitySort: "asc", // 指定排序方式,只有使用Vid + PlayAuth播放方式时支持。
        // format: "m3u8", // 指定播放地址格式,只有使用vid的播放方式时支持可选值。
        // definition: "FD,LD,SD,HD,OD,2K,4K", // 此值是vid对应流清晰度的一个子集,仅H5模式支持。
        // defaultDefinition: 'SD', // 默认视频清晰度,此值是vid对应流的一个清晰度,仅H5模式支持。

        mediaType: "video",
        width: "100%",
        height: "100%",
        autoplay: true, // 自动播放
        isLive: false, // 直播
        rePlay: false, // 循环播放
        playsinline: true, //H5是否内置播放
        preload: true, //播放器自动加载
        keyShortCuts: true, // 是否启用快捷键
        language: "zh-cn", // 语言
        cover: '', //播放器默认封面图片,需要autoplay为’false’时,才生效
        controlBarVisibility: "hover", //控制面板的实现 ‘click’ 点击出现、‘hover’ 浮动出现、‘always’ 一直在
        useH5Prism: true, //指定使用H5播放器
        components: [
          { name: 'RateComponent', type: AliPlayerComponent.RateComponent },
          { name: 'MemoryPlayComponent', type: AliPlayerComponent.MemoryPlayComponent },
          {
            name: 'QualityComponent',
            type: AliPlayerComponent.QualityComponent
          }],
        skinLayout: [
          { name: "bigPlayButton", align: "blabs", x: 30, y: 80 },
          { name: "H5Loading", align: "cc" },
          { name: "errorDisplay", align: "tlabs", x: 0, y: 0 },
          { name: "infoDisplay" },
          { name: "tooltip", align: "blabs", x: 0, y: 56 },
          { name: "thumbnail" },
          {
            name: "controlBar", align: "blabs", x: 0, y: 0,
            children: [
              { name: "progress", align: "blabs", x: 0, y: 44 },
              { name: "playButton", align: "tl", x: 15, y: 12 },
              { name: "timeDisplay", align: "tl", x: 10, y: 7 },
              { name: "fullScreenButton", align: "tr", x: 10, y: 12 },
              // { name: "subtitle", align: "tr", x: 15, y: 12 },
              // { name: "setting", align: "tr", x: 15, y: 12 },
              { name: "volume", align: "tr", x: 5, y: 10 }
            ]
          }
        ]
      }

      this.player = new Aliplayer(prop, function (player) {
        console.log("The player is created");
        player.on('sourceloaded', function (params) {
          var paramData = params.paramData
          var desc = paramData.desc
          var definition = paramData.definition
          // 获取清晰度组件并调用清晰度组件的 setCurrentQuality 设置清晰度 
          player.getComponent('QualityComponent').setCurrentQuality(desc, definition)
        })

        // 是否有设置过倍速
        if (this.speed) {
          player.setSpeed(this.speed)
        }
      })

      // 播放
      this.player.on('play', () => {
        // console.log('开始播放')
      })

      // 播放ing
      this.player.on('playing', () => {
        // console.log('播放中ing')

        this.speed = this.player.tag.playbackRate

        // 学习记录上报
        this.learningReport()
      })

      // 可以播放
      this.player.on('canplay', () => {
        if (this.seeked && this.courseInfo.duration && this.courseInfo.newestLessonId === this.courseInfo.lessonId) {
          this.seeked = false
          this.player.seek(this.courseInfo.duration)
        }
      })

      // 暂停
      this.player.on('pause', () => {
        // console.log('播放暂停')

        // 停止上报
        this.stopReportLearning()
      })

      // 播放出错
      this.player.on('error', () => {
        this.$message.error('播放出错')
      })

      // 播放完毕
      this.player.on("ended", (e) => {
        if (this.opener) {
          this.opener = false
          console.log('播放完了!!!!!')
          this.stopReportLearning()
          this.playDate = ''

          // 调用子组件方法,自动播放下一个
          this.$refs.videoList.createdPlayList(this.courseInfo.lessonId)
        }
      })
    },

    // 接受到子组件课次变化,播放新的视频
    async lessonChange(doing) {

      this.courseInfo = this.store.getCourseInfo

      // 未告知要播放时,只展示课次名字
      if (doing !== 'play') {
        this.lessonName = this.courseInfo.lessonName
        return
      }

      // 检测播放源是否相同(目前不进行检测,否则导致点击相同资源时->无法重播)
      // if (this.playerUrl !== this.courseInfo.lessonAudioVideo.url) {

      // 更改视频源  

      // TODO: url
      // this.playerUrl = this.courseInfo.lessonAudioVideo.url
      // this.player.loadByUrl(this.playerUrl)

      // TODO: vod
      const videoUrl = await this.getVid()
      // this.playerUrl = videoInfo.Sd
      // this.player.loadByUrl(this.playerUrl)
      this.player.dispose()
      this.playerUrl = JSON.stringify({ 'FD': videoUrl.Fd, 'SD': videoUrl.Sd })
      this.createPlayer()

      // TODO: vid+playauth
      // this.vid = videoInfo.VideoId
      // this.playauth = videoInfo.PlayAuth
      // this.player.replayByVidAndPlayAuth(this.vid, this.playauth)

      // } else {
      //   this.player.play()
      // }

      this.lessonName = this.courseInfo.lessonName
      this.seeked = true
      this.opener = true
    },

    // 停止上报学习记录,清楚定时器
    stopReportLearning() {
      if (this.timer) {
        clearInterval(this.timer)
        this.timer = null
      }
    },

    // 学习记录上报
    learningReport() {
      // 先清空
      this.stopReportLearning()

      const courseInfo = this.courseInfo
      const lessonAudioVideo = this.courseInfo.lessonAudioVideo


      if (this.playDate === '') {
        const nowDate = new Date
        this.playDate = nowDate.getTime()
        // console.log(this.playDate)
      }

      // 学次唯一值
      const userInfo = this.userStore.getUserInfo
      const key = `${userInfo.userId}_${courseInfo.orderId}_${courseInfo.projectId}_${courseInfo.courseId}_${courseInfo.lessonId}_${this.playDate}`;
      const sessionKey = this.$md5(key)

      this.timer = setInterval(() => {
        const curTime = parseInt(this.player.getCurrentTime())
        this.$axios.$post('/hadoop/learning/learningRecordSend', {
          sessionKey: sessionKey,
          orderId: courseInfo.orderId,
          projectId: courseInfo.projectId,
          courseId: courseInfo.courseId,
          courseCatalogId: courseInfo.lessonId,
          resourceType: lessonAudioVideo.type,
          resourceId: lessonAudioVideo.videoId,
          duration: curTime
        }).then(() => { }).catch((err) => {
          if (err.code === 10009) this.player.pause()
        });
      }, 5000)
    }
  }
}
</script>

 

 
posted @ 2022-08-10 17:01  鹿lu  阅读(2284)  评论(0编辑  收藏  举报