Create YouTube Screenshots with HTML5 and Canvas
Have you ever wanted to easily grab an image from a youtube video and save the screenshot to your computer? After reading a blog post on drawing video frames into the canvas element I wondered if this concept could be extended for YouTube videos. If I save a YouTube video using a chrome extension and then load the video locally it works perfectly, but the whole idea is to be able to do this without downloading the video first.
So, first of all, we need to somehow load YouTube’s actual video files into the HTML5 video tag. At first, I tried using a call to YouTube’s get_video_info script (http://www.youtube.com/get_video_info?video_id=VIDEOID) via a simple php proxy and this did give me lots of different video urls based on quality and formats for a particular YouTube video. Unfortunately, this only worked when I was testing on my own machine. The video urls failed to work on my web server.
The reason for this is because requesting get_video_info script server-side returns video url information for your IP. So, when I do this locally, my browser is also grabbing that video url from the same IP (my machine) as the server-side call. But, when the server-side call happens on my web server, the video url in the response is only available to the IP of my server and not my local browser. The problem is doing the server-side request, but when I tested this out initially, any ajax call to the get_video_info script was failing due to security reasons.
As a result, I decided to hack around this problem and actually load the video, initialize it, grab the video urls, and then insert them into the HTML5 video tag. I looked at the YouTube reference for iframe embeds page and it has an example of how to load a video into an iframe. Once you play the video, which can be done via a function, you have access to the data with video urls for different formats (the data can be found in event.target.g.videoData.url_encoded_fmt_stream_map). It needs to be decoded a couple of times and then I give the end user an option to select what quality video they want to play to make screenshots.
Here is the demo and you can view source to see the code. That demo no longer works as the API has changed and is no longer giving us all the videoData.
Recently, however, I noticed that ajax calls to get_video_info script are returning all the data successfully and the security errors I have seen in the past have disappeared. So, the original implementation is simpler and less intrusive because I don’t need to actually load any video in an iframe. Here is the demo and you can view source to see the code.
An obvious next step is to convert that canvas screenshot into an actual image, perhaps using something like Canvas2Image but if you try to retrieve the data from that CANVAS element (using the toDataURL() function), it throws the following error: Error: SECURITY_ERR: DOM Exception 18. This is because the CANVAS is dirty, it’s filled with a remote image because the source of the video file lives on YouTube’s servers. To prevent information leakage, this type of a CANVAS element cannot be converted to an image which kind of kills the whole idea of doing youtube screenshots. Well, we are making screenshots, but they are CANVAS elements, not IMAGE elements…
Update (Feb 26, 2013):
The demo and chrome extension is no longer working. YouTube has changed the way it now delivers video. Videos seem to be downloaded in pieces, but you can still retrieve the entire video. If you start watching a YouTube video and open the Network tab in Chrome’s Web Developer Toolbar, you will see that video fragments are downloaded instead of the complete youtube video. The URLs have a range parameter and if you remove that query param (“&range=