Efficiently Capture Screenshots from Video in the Browser with JavaScript
Problem
You have a video and you need to take screenshots from the video at 1-second interval.
Initial Thoughts
This task is typically suited for servers. However, processing on a server involves several steps:
- Upload the video to the server.
- For lengthy videos, store them in a storage facility like AWS S3.
- Extract screenshots using tools like moviepy(python), ffmpeg(Node.js), FFMpeg-PHP(php), etc.
- Save image in S3 or somewhere elese and allow the user to download them.
Why spend so much time and resources on server and storage when it can be done directly in the browser?
Solution
Load the video in a standard video
element. After the video starts playing, create a timer using setInterval
with a 1000ms delay. This timer will run a function to render the video on a canvas and extract the dataURL from the canvas every second.
However, this solution has a major flaw. Users need to wait for the entire video to play to get all the screenshots. For lengthy videos, this approach is too slow and inefficient.
Improved version
Instead of using a timer, utilize the timeupdate
event from the video element. Manually increment the currentTime
property of the video by 1 second after each screenshot is taken. This method advances the video immediately after capturing a screenshot, significantly reducing wait times. Performance may vary depending on the user’s device.
Here are some info from the demo:
- Video: [Sample Video](https://media.istockphoto.com/id/1369711265/video/kathmandu-city-nepal-from-drone-point-of-view.mp4?s=mp4-640x640-is&k=20&c=PJr3YPcOyjGD45JiBqfx9fr5FjSFpDFRfwarPydO83A=)
- Video duration: 12 seconds
- Time taken to execute: 5.73 seconds
Note:
If you are doing this is on a browser extension and you need to perform it in background, then you may encounter the issue of timeupdate
event not firing. It happens when processing is happening on one browser tab and user is working on another tab. For that you will need to switch to that tab where video processing is running and you can immediately switch back to previous one after timeupdate
has fired. For that, you need to keep track of the browser tabs.
// use this code to keep track of active tabs
chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
workingTabId = tabId;
});
// switch to video-processing-tab
await chrome.tabs.update(processingTabId, { active: true, selected: true });
// take screenshots here...
// ......
// ......
// switch back to previous tab
video.addEventListener('timeupdate', async function () {
await chrome.tabs.update(workingTabId, { active: true, selected: true });
// ...
// ...
}
This switching will happen within milliseconds. So, users may not event notice it. timeupdate
fires multiple time. Make sure to add a condition so that switching happens only once.
Conclusion
This article explores the perks of browser-based video processing to save server costs, time, and implementation effort. If it can be implemented on server, it doesn’t mean that it should be. With increasing capacity of desktop and mobile devices a lot of stuffs can be done on end devices without renting expensive servers.