點燈坊

失くすものさえない今が強くなるチャンスよ

使用 Vue-video-player 對 HLS 擷取 Image

Sam Xiao's Avatar 2020-06-02

Vue-video-player 與 Video.js 雖然能顯示 HLS,但卻沒提供 Capture Image 功能,我們必須使用 HTML 5 的 Canvas 才能達成需求。

Version

macOS Catalina 10.15.4
WebStorm 2020.1.1
Vue 2.6.11
Vue-video-player 5.0.2

Vue

<template>
  <div>
    <video-player class="video-player" ref="videoPlayer" :options="playerOptions"></video-player>
    <button @click="captureImage">Capture</button>
  </div>
</template>

<script>
import 'video.js/dist/video-js.css'

let captureImage = function() {
  let id = this.$refs.videoPlayer.player.player().id()
  let video = document.getElementById(`${ id }_html5_api`)

  let canvas = document.createElement('canvas')
  canvas.width = video.videoWidth
  canvas.height = video.videoHeight
  canvas.getContext('2d').drawImage(video, 0, 0)

  let img = canvas.toDataURL()
  console.log(img)
}

export default {
  name: 'App',
  data: () => ({
    playerOptions: {
      sources: [{
        type: 'application/x-mpegURL',
        src: 'https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8'
      }],
    }
  }),
  methods: {
    captureImage,
  },
}
</script>

第 3 行

<video-player class="video-player" ref="videoPlayer" :options="playerOptions"></video-player>

<video-player> 加上 ref,稍後將透過 ref 取得 <video>id

因為 <video>id 是動態的,因此必須透過 ref 動態取得

第 4 行

<button @click="captureImage">Capture</button>

加上 <button> 執行 captureImage()

12 行

let id = this.$refs.videoPlayer.player.player().id()
let video = document.getElementById(`${id}_html5_api`)

目的要透過 getElementById() 取得 <video-player><video> tag,但其 id 為動態並不固定,因此只能透過 $ref() 一層一層取得 <video> tag 的動態 id

15 行

let canvas = document.createElement('canvas')
canvas.width = video.videoWidth
canvas.height = video.videoHeight
canvas.getContext('2d').drawImage(video, 0, 0)

為了擷取 image,我們會將 <video> 寫入 <canvas>

因為 <canvas> 目的只是產生 Base64 String 而不是用在顯示,因此不必在 HTML template 使用 <canvas>,使用 document.createElement() 動態建立 <canvas> 即可。

建立 <canvas> 必須指定其 widthheight,因此由 videovideoWithvideoHeight 建立 canvas。

若要將 <video> 的 image 寫入 <canvas>,首先進入 2d context,再使用 drawImage() 寫入 image,傳入 video element 與 top left 的 x 軸與 y 軸,因此為 0, 0

21 行

let img = canvas.toDataURL()
console.log(img)

呼叫 <canvas>toDataUrl() 產生 Base64 String,如此就能傳入 API 做相關應用。

capture000

Conclusion

  • 若覺得要抓 Vue-video-player 的 <video> id 很 tricky,也可參考 Video.js 官網的 Video.js and Vue Integration 自行包裝 component,如此 id 即可固定不變
  • HTML 5 的 Canvas 是關鍵,透過 drawImage() 就能將 <video> 寫入 <canvas> 達成擷取 image

Reference

Manu Bhardwaj, JavaScript: How to Capture Image from Video ?
MDN, CanvasRenderingContext2D.drawImage()
Video.js, Video.js and Vue Integration