如何獲取影片地址的某一幀?

热饭班长發表於2024-11-07

場景

需要用影片的某一幀作為預覽圖

思路

建立video物件,載入影片後設資料,然後用canvas繪製video的畫面。

實現細節

1 建立video物件,載入後設資料,然後監聽必要事件

const getVideoFirstFrame = (videoUrl: string) => {
  const video = document.createElement("video")
  video.preload = "metadata"
  video.src = videoUrl

  video.addEventListener("loadedmetadata", () => {
    // 設定影片時間到第1秒(取決於你想獲取第幾秒的畫面)
    video.currentTime = 1
  })

  // currentTime修改後,會觸發seeked事件
  video.addEventListener("seeked", () => {

  }, { once: true }) // 只監聽一次
}

2 建立canvas物件,然後繪製video畫面

const getVideoFirstFrame = (videoUrl: string) => {
  const video = document.createElement("video")
  video.preload = "metadata"
  video.src = videoUrl

  video.addEventListener("loadedmetadata", () => {
    // 設定影片時間到第1秒(取決於你想獲取第幾秒的畫面)
    video.currentTime = 1
  })

  // currentTime修改後,會觸發seeked事件
  video.addEventListener("seeked", () => {
    const canvas = document.createElement("canvas")
    canvas.width = video.videoWidth
    canvas.height = video.videoHeight

    const ctx = canvas.getContext("2d")
    ctx?.drawImage(video, 0, 0)

    // 轉換為base64(這裡已經獲取到影片畫面了)
    const dataUrl = canvas.toDataURL("image/jpeg")

  }, { once: true }) // 只監聽一次
}

3 繪製操作是非同步的,修改函式返回promise

const getVideoFirstFrame = (videoUrl: string) => {
  return new Promise((resolve, reject) => {
    const video = document.createElement("video")
    video.preload = "metadata"
    video.src = videoUrl

    video.addEventListener("loadedmetadata", () => {
      // 設定影片時間到第1秒(取決於你想獲取第幾秒的畫面)
      video.currentTime = 1
    })

    // currentTime修改後,會觸發seeked事件
    video.addEventListener("seeked", () => {
      try {
        const canvas = document.createElement("canvas")
        canvas.width = video.videoWidth
        canvas.height = video.videoHeight

        const ctx = canvas.getContext("2d")
        ctx?.drawImage(video, 0, 0)

        // 轉換為base64(這裡已經獲取到影片畫面了)
        const dataUrl = canvas.toDataURL("image/jpeg")

        // 清理
        video.remove()
        canvas.remove()

        resolve(dataUrl)
      } catch (err) {
        reject(err)
      }
    }, { once: true }) // 只監聽一次

    // 錯誤處理
    video.addEventListener("error", (err) => {
      reject(new Error(`影片載入失敗: ${err}`))
    })
  })
}

4 呼叫

<script setup lang="ts">
onMounted(async () => {
  const videoUrl = "https://vjs.zencdn.net/v/oceans.mp4"
  const frame = await getVideoFirstFrame(videoUrl)
  img.value = frame
})
</script>

<template>
  <img v-if="img" :src="img" />
</template>

5 驗證結果
圖片

注意事項

如果你的影片地址是跨域的,canvas將無法繪製影片內容
你需要做兩件事:
1 讓後臺設定允許跨域的響應頭
Access-Control-Allow-Origin: * 或指定具體域名

2 設定video.crossOrigin = "anonymous"
否則瀏覽器會報SecurityError錯誤:
SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

相關文章