element, placing references to them in the variables we just created: function eventWindowLoaded() { videoElement = document.createElement("video"); videoDiv = document.createElement('div'); document.body.appendChild(videoDiv);
Next, we add videoElement as a child of videoDiv, essentially putting it inside of that
on the HTML page. We then set the style attribute of
to display:none;, which will make it invisible on the HTML page. We do this because although we want the video to display on the canvas, we don’t want to show it on the HTML page: videoDiv.appendChild(videoElement); videoDiv.setAttribute("style", "display:none;");
We then create another new variable named videoType that holds the results of a new function we will create, supportedVideoFormat(). This function returns the file extension of the supported video format for the browser; otherwise, it returns “” (an empty string), which means we alert the user that there is no video support in the app for his browser:
276 | Chapter 6: Mixing HTML5 Video and Canvas
var videoType = supportedVideoFormat(videoElement); if (videoType == "") { alert("no video support"); return; }
Finally, we set the src property of the video element using the file extension we just received from supportedVideoFormat(), and create the event handler for the canplay through event: videoElement.setAttribute("src", "muirbeach." + videoType); videoElement.addEventListener("canplaythrough",videoLoaded,false); }
When the video has finished loading, the videoLoaded event handler is called, which in turn calls the canvasApp() function: function videoLoaded(event) { canvasApp(); }
Before the code in the last section will work, we need to define the supportedVideo Format() function. The reason for this function is simple: since we are adding video objects dynamically to the HTML page, we do not have a way to define multiple <source> tags. Instead, we are going to use the canPlayType() method of the video object to tell us which type of audio file to load. The canPlayType() method takes a single parameter, a MIME type. It returns a text string of maybe, probably, or nothing (an empty string). “” (nothing)
This is returned if the browser knows the type cannot be rendered. maybe
This is returned if the browser does not confidently know that the type can be displayed. probably
This is returned if the browser knows the type can be displayed using an audio or video element.
We are going to use these values to determine which media type to load and play. For the sake of this exercise, we will assume that both maybe and probably equate to yes. If we encounter either result with any of our three MIME types (video/webm, video/ mp4, video/ogg), we will return the extension associated with that MIME type so the sound file can be loaded. In the function below, video represents the instance of HTMLVideoElement that we are going to test. The returnExtension variable represents that valid extension for the first
Video and the Canvas | 277
MIME type found that has the value of maybe or probably returned from the call to canPlayType(): function supportedVideoFormat(video) { var returnExtension = ""; if (video.canPlayType("video/webm") =="probably" || video.canPlayType("video/webm") == "maybe") { returnExtension = "webm"; } else if(video.canPlayType("video/mp4") == "probably" || video.canPlayType("video/mp4") == "maybe") { returnExtension = "mp4"; } else if(video.canPlayType("video/ogg") =="probably" || video.canPlayType("video/ogg") == "maybe") { returnExtension = "ogg"; } return returnExtension; }
We do not check for a condition when no valid video format is found and the return value is “”. If that is the case, the code that has called this function might need to be written in a way to catch that condition and alter the program execution. We did that with the test of the return value and alert(), which we described previously.
Video is displayed like an image When you write code to display a video on the canvas, you use the context.draw Image() function, as though you are displaying a static image. Don’t go looking for a drawVideo() function in the HTML5 Canvas spec because you won’t find it. The following code will display a video stored in a variable named videoElement, displayed at the x,y position of 85,30: context.drawImage(videoElement , 85, 30);
However, when you draw a video for the first time, you will notice that it will not move—it stays on the first frame. At first you might think you have done something wrong, but you have not. You just need to add one more thing to make it work.
Set an interval to update the display Just like when we discussed animation in the previous chapters, a video placed on HTML5 Canvas using drawImage() will not update itself. You need to call draw Image() in some sort of loop to continually update the image with new data from the playing video in the HTML page (hidden or not). To do this, we call the video’s play() method, and then use setInterval() to call the drawScreen() function every 33 milliseconds. This will give you about 30 frames per second (FPS). We put this code in our canvasApp() function, which is called after we know the video has loaded: videoElement.play(); setInterval(drawScreen, 33);
278 | Chapter 6: Mixing HTML5 Video and Canvas
In drawScreen(), we will call drawImage() to display the video, but since it will be called every 33 milliseconds, the video will be updated and play on the canvas: function
drawScreen () {
context.drawImage(videoElement , 85, 30); }
Example 6-6 gives the full code for displaying a video on the canvas and updating it using setInterval(). Figure 6-6 shows this code in the browser. Example 6-6. Basic HTML5 loading video onto the canvas <meta charset="UTF-8">
CH6EX6: Basic HTML5 Load Video Onto The Canvas <script src="modernizr-1.6.min.js"> <script type="text/javascript"> window.addEventListener('load', eventWindowLoaded, false); var videoElement; var videoDiv; function eventWindowLoaded() { videoElement = document.createElement("video"); videoDiv = document.createElement('div'); document.body.appendChild(videoDiv); videoDiv.appendChild(videoElement); videoDiv.setAttribute("style", "display:none;"); var videoType = supportedVideoFormat(videoElement); if (videoType == "") { alert("no video support"); return; } videoElement.setAttribute("src", "muirbeach." + videoType); videoElement.addEventListener("canplaythrough",videoLoaded,false); } function supportedVideoFormat(video) { var returnExtension = ""; if (video.canPlayType("video/webm") =="probably" || video.canPlayType("video/webm") == "maybe") { returnExtension = "webm"; } else if(video.canPl