Last active
August 16, 2023 10:08
-
-
Save atwong/49b9e7d911dca0663e23c50c60f28784 to your computer and use it in GitHub Desktop.
Extract Multiple Screenshots Video
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Extract multiple screenshots/frames from a video URL | |
* Params | |
* - vidURL : video URL | |
* - frOffsets : array of timestamps | |
* - frameWidth : screenshot width (default: video width) | |
* Returns | |
* - frames: object with offset => {imgUrl, blob} | |
* | |
* An multiple-frame extension of http://cwestblog.com/2017/05/03/javascript-snippet-get-video-frame-as-an-image/ | |
* In this version, video & canvas elements are reused to extract multiple screenshots. Note, video seeks must | |
* occur serially, hence use of async/await. | |
*/ | |
function extractVideoFrames(vidUrl, frOffsets, frameWidth) { | |
function extractFrame(video, canvas, offset) { | |
return new Promise((resolve, reject) => { | |
video.onseeked = event => { | |
var ctx = canvas.getContext('2d'); | |
ctx.drawImage(video, 0, 0, canvas.width, canvas.height); | |
canvas.toBlob(blob => { | |
resolve({offset: offset, imgUrl: canvas.toDataURL() , blob: blob}); | |
}, "image/png"); | |
}; | |
video.currentTime = offset; | |
}); | |
}; | |
async function serialExtractFrames(video, canvas, offsets) { | |
var frames = {}; | |
var lastP = null; | |
for (var offset of offsets) { | |
if (offset < video.duration) { | |
if (lastP) { | |
var f = await lastP | |
frames[f.offset] = f; | |
} | |
lastP = extractFrame(video, canvas, offset); | |
} | |
} | |
if (lastP) { | |
var f = await lastP; | |
frames[f.offset] = f; | |
lastP = null; | |
} | |
return frames; | |
}; | |
return new Promise((resolve, reject) => { | |
var vvid = document.createElement("video"); | |
var vcnv = document.createElement("canvas"); | |
vvid.onloadedmetadata = event => { | |
var aspect_ratio = vvid.videoWidth/vvid.videoHeight; | |
vcnv.width = frameWidth !== undefined ? frameWidth : vvid.videoWidth; | |
vcnv.height = vcnv.width/aspect_ratio; | |
if (vvid.duration) { | |
serialExtractFrames(vvid, vcnv, frOffsets).then(resp => { | |
resolve(resp); | |
}) | |
} | |
} | |
vvid.src = vidUrl; | |
}); | |
}; | |
export default extractVideoFrames; | |
I will do my rounds, to check following:
- Switch to OffscreenCanvas because i would need the videoframe content, but will not interfere with a video player onthe same page
- My goal is a bit different, because i need the frames for analysing, which makes saving them unnecessary, so i will get the frame as data with getImageData
lets see
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
what if the video is not on the same domain as the web app? How to make this work then?