乐趣区

关于前端:NPlayer-支持任何流媒体和-B-站弹幕体验的视频播放器

NPlayer 是由 Typescript 加 Sass 编写,无任何第三方运行时依赖,Gzip 大小只有 21KB,兼容 IE11,反对 SSR。该播放器高度可定制,所有图标、按钮、色调等都能够替换,并且提供了 内置组件 不便二次开发。它还领有插件零碎,弹幕性能 就是应用插件模式提供。该播放器能够接入任何 流媒体,如 hls、dash 和 flv 等。

  • 官网:https://nplayer.js.org
  • 源码:https://github.com/woopen/npl…
  • 在线编辑 & 预览:https://codesandbox.io/s/anci…

装置

应用如下命令疾速装置 NPlayer。

npm i -S nplayer

更多请查看 装置。

开始应用

import Player from 'nplayer'

const player = new NPlayer({src: 'https://v-cdn.zjol.com.cn/280443.mp4'})

// player.mount('#app')
player.mount(document.body)

下面是创立一个播放器最简略的办法,创立一个 player 对象,设置视频元素的 src,而后将它挂载到 document.body 中。

当然你也能够本人提供 video 元素。

import Player from 'nplayer'

const video = document.createElement('video')
video.src = 'https://v-cdn.zjol.com.cn/280443.mp4'
const player = new Player({video, videoAttrs: { autoplay: 'true'} })

player.mount(document.body)

你还能够应用 videoAttrs 参数,将视频元素的属性增加到这个 video 元素上,videoAttrs 有一些默认值,它会和你传入的合并再设置到视频元素上,详情请查看 参数章节。

player.mount 办法能够将播放器挂载到页面上,它接管一个参数,能够是一个字符串或一个 dom 元素。当是字符串时,将会主动查找该 dom 元素。

预览缩略图

当鼠标放到进度条上时就会呈现,一个小缩略图来预览这个工夫点的截图,当初很多视频网站都有这个性能。NPlayer 也提供了这个性能。

NPlayer 的缩略图有 thumbnail 参数设置,它是一个缩略图配置对象,具体接口如下:

interface ThumbnailOptions {
  startSecond?: number;
  gapSecond?: number;
  row?: number;
  col?: number;
  width?: number;
  height?: number;
  images?: string[];}

它个各个属性默认值如下:

{
  startSecond: 0,
  gapSecond: 10,
  col: 5,
  row: 5,
  width: 160,
  height: 90,
  images: []}

这个预览缩略图其实是由一堆分辨率较小的截图组成的一张图片,如下所示。

咱们能够看到这个雪碧图由 5 x 5 的小缩略图组成,当然一个视频可能有一堆下面这种雪碧图,这就是下面 images 是一个数组字符串的起因。

理解了雪碧图,上面来具体理解各个参数别离是什么意思吧。

参数 形容
startSecond 缩略图制作的开始工夫,比方缩略图是视频的第一秒开始制作的那么,这里就是 1
gapSecond 一张小缩略图时间跨度,如果小缩略图是每 5 秒截一张,那么这里就填 5
col 雪碧图的列数
row 雪碧图的行数
width 小缩略图的宽
height 小缩略图的高
images 雪碧图的链接地址数组

缩略图制作

有很多形式能够制作视频的预览缩略图,比方用 NodeJS node-fluent-ffmpeg 库中的 thumbnails 办法。当然大家能够去网上寻找更多办法。

这里介绍如何间接用 ffmpeg 命令行生成视频缩略图。

ffmpeg 是十分弱小音视频工具,很多播放器都是它作为内核,更多详情请查看 官网文档。

首先须要去 ffmpeg 官网下载并装置好 ffmpeg。

装置好后能够在命令行执行上面命令。

ffmpeg -i ./test.webm -vf 'fps=1/10:round=zero:start_time=-9,scale=160x90,tile=5x5' M%d.jpg

通过下面这个命令生成一堆 5 x 5 的雪碧图,每个雪碧图中小缩略图的尺寸是 160 x 90。雪碧图的文件名是 M1.jpg、M2.jp、M3.jpg… 这样递增。

  • -i 参数前面是视频文件。
  • -vf 参数前面跟着过滤器,多个过滤器用 , 离开,一个过滤器多个参数应用 : 离开。
  • fps=1/10 示意每 10 秒输入一张图片,round=zero 为工夫戳向 0 取整。start_time=-9 是让它从第 1 秒开始截取,疏忽掉 0 秒的黑屏帧,这里是 -9,而不是 1 的起因是,fps 咱们设置的是 10 秒一张,所以想要从第 1 秒开始时,就用 1 - 10 等于 -9
  • scale=160x90 设置输入图像分辨率大小,tile=5x5 将小图用 5x5 的形式组合在一起。
  • 最初面的 M%d.jpg 就是文件名,%d 示意按数字递增。

那么用下面命令生成的缩略图,能够设置如下参数。

new Player({
  thumbnail: {
    startSecond: 1,
    images: ['M1.jpg', 'M2.jpg', 'M3.jpg']
  }
})

因为其余参数都能够应用默认值,所以这里就不填了。

流媒体

当初大家看的网络视频个别不会间接用 .mp4 文件了,而是应用 HLS,DASH 这些流媒体协定。NPlayer 反对接入任何流媒体协定。

import Hls from 'hls'
import Player from 'player'

const hls = new Hls()
const player = new Player()
hls.attachMedia(player.video)

hls.on(Hls.Events.MEDIA_ATTACHED, function () {hls.loadSource('https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8')
})

player.mount(document.body)

因为这些流媒体库其实只须要一个 video 元素,咱们间接传入 player.video 属性就行,对于其余任何流媒体库都是这个套路。更多信息请查看 流媒体章节。

清晰度切换

网上这些支流的视频网站应该都能够调节视频清晰度,有高清晰度还须要开明会员能力观看。而且默认状况下还会依据以后用户网速主动抉择最佳清晰度。

其实应用下面提到的流媒体协定能够十分轻松的实现这些性能,上面就用 NPlayer 来实现清晰度切换吧。(如果代码有艰难,最好先浏览 管制条章节)。

import Player from 'nplayer'
import Hls from 'hls'

// 1. 首先创立一个管制条项
const Quantity = {element: document.createElement('div'),
  init(player) {this.btn = document.createElement('div')
    this.btn.textContent = '画质'
    this.element.appendChild(this.btn)
    this.popover = new player.Player.components.Popover(this.element)
    this.btn.addEventListener('click', () =>  this.popover.show())
    // 点击按钮的时候展现 popover
    this.element.style.display = 'none'
    // 默认暗藏
    this.element.classList.add('quantity')
  }
}

// 2. 咱们把它放到 spacer 前面
const player = new Player({controls: ['play', 'volume', 'time', 'spacer', Quantity, 'airplay', 'settings', 'web-fullscreen', 'fullscreen'],
})

// 3. 创立 HLS 实例
const hls = new Hls();
hls.on(Hls.Events.MEDIA_ATTACHED, function () {hls.on(Hls.Events.MANIFEST_PARSED, function () {
    // 4. 给清晰度排序,清晰度越高的排在最后面
    hls.levels.sort((a, b) => b.height - a.height)
    const frag = document.createDocumentFragment()
    // 5. 给与清晰度对应的元素增加,点击切换清晰度性能
    const listener = (i) => (init) => {const last = Quantity.itemElements[Quantity.itemElements.length - 1]
      const prev = Quantity.itemElements[Quantity.value] || last
      const el = Quantity.itemElements[i] || last
      prev.classList.remove('quantity_item-active')
      el.classList.add('quantity_item-active')
      Quantity.btn.textContent = el.textContent
      if (init !== true && !player.paused) setTimeout(() => player.play())
      // 因为 HLS 切换清晰度会使正在播放的视频暂停,咱们这里让它再主动复原播放
      Quantity.value = hls.currentLevel = hls.loadLevel = i;
      Quantity.popover.hide();}
    // 6. 增加清晰度对应元素
    Quantity.itemElements = hls.levels.map((l, i) => {const el = document.createElement('div')
      el.textContent = l.name + 'P'
      if (l.height === 1080) el.textContent += '超清'
      if (l.height === 720) el.textContent += '高清'
      if (l.height === 480) el.textContent += '清晰'
      el.classList.add('quantity_item')
      el.addEventListener('click', listener(i))
      frag.appendChild(el)
      return el;
    })

    const el = document.createElement('div')
    el.textContent = '主动'
    el.addEventListener('click', listener(-1))
    el.classList.add(styles.QuantityItem)
    frag.appendChild(el)
    Quantity.itemElements.push(el)
    // 这里再增加一个 ` 主动 ` 选项,HLS 默认是依据网速主动切换清晰度

    Quantity.popover.panelElement.appendChild(frag);
    Quantity.element.style.display = 'block';

    listener(hls.currentLevel)(true)
    // 初始化以后清晰度
  })

  // 绑定 video 元素胜利的时候,去加载视频
  hls.loadSource('https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8')
})

hls.attachMedia(player.video)
player.mount(container.current);

const dispose = () => {hls.destroy()
  player.dispose()}
// 调用 dispose 将销毁视频 

这里是应用 HLS 的多码率来实现多清晰度视频切换。只需略微改下代码,你能够应用 NPlayer 对任何流媒体或一般 mp4 视频增加清晰度切换性能。

前面会公布如何制作多码率视频的文章,欢送关注。

弹幕

NPlayer 的弹幕性能能够放弃大量弹幕而不卡顿,弹幕零碎体验和性能与 B 站弹幕十分相似,反对十分多的设置,弹幕防碰撞、弹幕速度、字体、速度、透明度、显示区域、有限弹幕等。

装置

执行上面命令应用 npm 包的模式装置。

npm i -S @nplayer/danmaku

应用

import Player from "nplayer";
import Danmaku from "@nplayer/danmaku";
import items from "./items";

const danmaku = new Danmaku({items});

const player = new Player({
  src: "https://v-cdn.zjol.com.cn/280443.mp4",
  plugins: [danmaku]
});

player.mount(document.body);

点击这个链接进行预览和编辑:https://codesandbox.io/s/npla…

更多弹幕插件信息,请查看 弹幕插件章节。

弹幕实现

NPlayer 的弹幕零碎尝试了多种实现计划,最终抉择了 CSS3 中的 transformtransition 形式,它也是 B 站弹幕默认抉择的计划,当然 B 站还反对 canvas 的形式渲染,NPlayer 也有尝试,然而在 firefox 上测试大量弹幕时,会有一点小卡顿,所以最终抉择了更优一点的 CSS3 的计划。

除了渲染形式,弹幕实现还有很多其余的难点,比方弹幕如何防碰撞,当视频倍速播放时弹幕的速度也如何扭转,视频的播放暂停事件会有一个很小的提早,即便很小的提早也会让弹幕在暂停视频时有个卡顿地位跳跃问题。当然在用户自定义调节弹幕速度和视频播放速度同时疯狂的播放暂停,怎么保障弹幕地位不产生忽然跳动等问题?NPlayer 解决了全副这些问题,和 B 站的弹幕体验十分类似。

这里篇幅无限,我打算后续再写一篇超具体的实现弹幕文章,欢送关注。

总结

除了上述性能外 NPlayer 还有十分多的性能,欢送点击上面链接查看详情。

如果有问题或者是想要新性能,欢送提交 issue。也欢送提交 PR。

  • 官网:https://nplayer.js.org
  • 源码:https://github.com/woopen/npl…
  • 在线编辑 & 预览:https://codesandbox.io/s/anci…
退出移动版