Skip to content

video视频播放抓取当前帧并下载为JPG图片

prompt

使用esm + script setup + ts语法编写一个vue页面,用于在浏览器环境执行下列操作:给定一个cloudflare的r2 bucket的url, 例如https://xx.xx.com/foo.mp4,在域名xx.pages.dev下的页面使用video组件播放它,然后添加一个捕捉视频当前帧的按钮,点击后会获得该帧的jpg格式的binary数据

vue
<script setup lang="ts">
import { ref, onMounted } from 'vue'

const props = defineProps<{
  videoUrl: string
}>()

const videoRef = ref<HTMLVideoElement | null>(null)
const canvasRef = ref<HTMLCanvasElement | null>(null)
const errorMessage = ref<string>('')

const captureFrame = async () => {
  const video = videoRef.value
  const canvas = canvasRef.value

  if (video && canvas) {
    try {
      // Attempt to draw current video frame
      canvas.width = video.videoWidth
      canvas.height = video.videoHeight

      const context = canvas.getContext('2d')
      if (context) {
        // Direct frame capture
        context.drawImage(video, 0, 0, canvas.width, canvas.height)

        canvas.toBlob((blob) => {
          if (blob) {
            const url = URL.createObjectURL(blob)
            const a = document.createElement('a')
            a.href = url
            a.download = 'video-frame.jpg'
            a.click()
            URL.revokeObjectURL(url)
          }
        }, 'image/jpeg')
      }
    } catch (error) {
      errorMessage.value = `Capture error: ${error instanceof Error ? error.message : String(error)}`
    }
  }
}

// Debug video loading
onMounted(async () => {
  try {
    const video = videoRef.value
    if (video) {
      video.onerror = (e) => {
        errorMessage.value = `Video load error: ${JSON.stringify(e)}`
      }
    }
  } catch (error) {
    errorMessage.value = `Mounted error: ${error instanceof Error ? error.message : String(error)}`
  }
})
</script>

<template>
  <div class="video-container">
    <video
      ref="videoRef"
      :src="videoUrl"
      crossorigin="anonymous"
      controls
      class="video-player"
    ></video>

    <button
      @click="captureFrame"
      class="capture-button"
    >
      Capture Frame
    </button>

    <canvas
      ref="canvasRef"
      style="display: none"
    ></canvas>

    <div v-if="errorMessage" class="error-message">
      {{ errorMessage }}
    </div>
  </div>
</template>

<style scoped>
.error-message {
  color: red;
  margin-top: 10px;
  padding: 10px;
  background-color: #ffeeee;
  border: 1px solid red;
}
</style>

version 0.2.0