需要根据视频地址获取视频任意一帧,就是给一个视频URL,获取任意一帧图,不需要在页面播放视频。利用canvas实现。大概思路
- js 创建一个视频元素
- 设置视频的
currentTime属性,当前播放时间点,就是你想要的那一帧在视频时长的位置,用时间秒表示 - 利用
CanvasRenderingContext2D.drawImage()把带了设置currentTime的视频传进去,就绘制了你想要的视频那一帧 - 利用
HTMLCanvasElement.toDataURL()返回一个图片格式的url,就是你想要的视频那一帧url了
不复杂哈,有一点要注意的是,上面第2步,你想要的那一帧在视频时长的位置,用时间秒表示
这个需要知道视频帧率,frame rate,假如说你想要视频第45帧,而视频帧率是29.7,可以通过45/29.7 表示45帧在视频时长的位置。
根据视频URL获取视频任意帧
1. 创建视频元素
const video = document.createElement('video')
video.crossOrigin = 'anonymous' // Enable CORS
video.src = videoUrl
设置了允许视频跨域video.crossOrigin = 'anonymous',不然后面操作会报错DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported
2.设置视频的currentTime属性
video.addEventListener('loadedmetadata', function () {
const frameRate = 29.7 // Assuming a frame rate of 29.7fps
const targetFrame = 45
const timeToSeek = targetFrame / frameRate
video.currentTime = timeToSeek
// video.currentTime = 0 第一帧
})
3.利用CanvasRenderingContext2D.drawImage() 绘制视频那一帧
video.addEventListener('seeked', function () {
const canvas = document.getElementById('canvas')
const context = canvas.getContext('2d')
canvas.width = video.videoWidth
canvas.height = video.videoHeight
context.drawImage(video, 100, 100, canvas.width, canvas.height)
})
4. canvas.toDataURL('image/png')返回url
try {
const dataURL = canvas.toDataURL('image/png')
console.log('dataUrl', dataURL)
document.getElementById('anyFrame').src = dataURL
} catch (e) {
console.error('Error exporting canvas as data URL:', e)
alert('Failed to export the canvas.')
}
// Remove the video element from memory
video.remove()
注意:页面需要创建一个canvas元素,样式为隐藏
完整代码 一个html + js示例,可以直接跑起来
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Render Any Frame of Video from URL</title>
</head>
<body>
<div style="display: flex; align-items: center">
<div>
<div>请输入视频地址</div>
<input
type="text"
id="videoUrl"
placeholder="Enter video URL"
value="https://www.apple.com/105/media/us/ipad-pro/2024/97d7edf3-aac0-443d-9731-38d40ff50951/anim/m4-chip/large_2x.mp4"
/>
</div>
<div style="margin: 10px">
<div>请输入视频想要的那一帧</div>
<input
type="number"
id="videoFrame"
placeholder="请输入视频想要的那一帧"
value="45"
/>
</div>
<button onclick="renderAnyFrame()">获取帧图</button>
</div>
<br />
<canvas id="canvas" style="display: none"></canvas>
<img
id="anyFrame"
alt="Any Frame of Video"
style="width: 864px; height: 540px"
/>
<script>
async function renderAnyFrame(frameRate = 30) {
const videoUrl = document.getElementById('videoUrl').value.trim()
if (!videoUrl) {
alert('Please enter a valid video URL')
return
}
const targetFrame = Number(
document.getElementById('videoFrame').value.trim()
)
try {
const video = document.createElement('video')
video.crossOrigin = 'anonymous' // Enable CORS
video.src = videoUrl
video.preload = 'auto' // Preload the video
video.addEventListener('loadedmetadata', function () {
const timeToSeek = targetFrame / frameRate
video.currentTime = timeToSeek
//video.currentTime = 0
})
video.addEventListener('seeked', function () {
const canvas = document.getElementById('canvas')
const context = canvas.getContext('2d')
canvas.width = video.videoWidth
canvas.height = video.videoHeight
context.drawImage(video, 10, 10, canvas.width, canvas.height)
try {
const dataURL = canvas.toDataURL('image/png')
console.log('dataUrl', dataURL)
document.getElementById('anyFrame').src = dataURL
} catch (e) {
console.error('Error exporting canvas as data URL:', e)
alert('Failed to export the canvas.')
}
// Remove the video element from memory
video.remove()
})
video.addEventListener('error', function () {
alert(
'Error loading video. Make sure the video URL is correct and supports CORS.'
)
})
// Load the video
video.load()
} catch (error) {
console.error('Error processing video:', error)
}
}
renderAnyFrame()
</script>
</body>
</html>
获取第100帧图
获取第170帧图
封装一个方法renderAnyFrame,方便调用,参数就传视频地址,帧率,要截取的帧,
function renderAnyFrame(videoUrl, frameRate = 30, targetFrame = 0) {
// videoUrl 视频地址 targetFrame 要截取的帧
return new Promise((resolve, reject) => {
if (!videoUrl) {
alert('Please enter a valid video URL')
reject('Invalid video URL')
return
}
const video = document.createElement('video')
video.crossOrigin = 'anonymous' // Enable CORS
video.src = videoUrl
video.preload = 'auto' // Preload the video
video.addEventListener('loadedmetadata', function () {
const timeToSeek = targetFrame / frameRate
video.currentTime = timeToSeek
})
video.addEventListener('seeked', function () {
const canvas = document.getElementById('canvas')
const context = canvas.getContext('2d')
canvas.width = video.videoWidth
canvas.height = video.videoHeight
context.drawImage(video, 0, 0, canvas.width, canvas.height)
try {
const frameSrc = canvas.toDataURL('image/png')
console.log('frameSrc', frameSrc)
document.getElementById('anyFrame').src = frameSrc
// Clean up video element from DOM
video.remove()
resolve(frameSrc)
} catch (e) {
console.error('Error exporting canvas as data URL:', e)
reject(e)
}
})
video.addEventListener('error', function () {
alert(
'Error loading video. Make sure the video URL is correct and supports CORS.'
)
reject('Error loading video')
})
video.load() // Load the video
})
}
const videoFile =
'https://www.apple.com/105/media/us/ipad-pro/2024/97d7edf3-aac0-443d-9731-38d40ff50951/anim/m4-chip/large_2x.mp4'
onMounted(() => {
// 调用获取帧图片地址
renderAnyFrame(videoFile, 30, 10)
.then(frameSrc => {
console.log('Frame source:', frameSrc)
})
.catch(error => {
console.error('Error:', error)
})
})
上传视频获取视频第一帧
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Extract First Frame of Video</title>
</head>
<body>
<input type="file" id="videoInput" accept="video/*" />
<br />
<canvas id="canvas" style="display: none"></canvas>
<img id="firstFrame" alt="First Frame of Video" />
<script>
document
.getElementById('videoInput')
.addEventListener('change', function (event) {
const file = event.target.files[0]
console.log('file---videoUrl', file)
if (file) {
const url = URL.createObjectURL(file)
console.log('url----', url)
// 打印出来的 url blob:http://127.0.0.1:5500/2384e810-2cf2-4d8c-96d7-394f78f724d8
extractFirstFrame(url)
}
})
function extractFirstFrame(
videoUrl = ''
) {
const video = document.createElement('video')
console.log('videoUrl', videoUrl)
video.src = videoUrl
video.addEventListener('loadeddata', function () {
video.currentTime = 0
})
video.addEventListener('seeked', function () {
const canvas = document.getElementById('canvas')
const context = canvas.getContext('2d')
canvas.width = video.videoWidth
canvas.height = video.videoHeight
context.drawImage(video, 0, 0, canvas.width, canvas.height)
const dataURL = canvas.toDataURL('image/png')
console.log('dataURL', dataURL)
document.getElementById('firstFrame').src = dataURL
URL.revokeObjectURL(videoUrl) // Clean up the URL object
})
// Handle error loading video
video.addEventListener('error', function () {
console.error('Error loading video')
URL.revokeObjectURL(videoUrl) // Clean up the URL object
})
}
</script>
</body>
</html>
参考文档:
currentTime
CanvasRenderingContext2D.drawImage()
HTMLCanvasElement.toDataURL()