Taking a look at the HTML5 video use in the Bing video homepage
I've always liked the pictures on the Bing homepage. In late September, they started occasionally replacing the picture with an HTML5 video. The Bing team blogged about it, and included an interesting video from the managing editor for the Bing homepage that talks about how they select photos and why they're going to start adding the occasional video backgrounds.
I've been interested in HTML5 video implementations since I first started playing with them in the Firefox 3.x nightly builds. Since the Bing page was presumably designed to handle "industrial strength" use - many browsers, high performance, bandwidth consideration - I thought I'd peek under the hood and see how things were set up.
Note: While I also work for Microsoft, I don't have any inside info on how or why they set things up as they did - I'm just some random guy on the internet for the duration of this post.
The homepage changes every day, but you can click back to previous homepages using the back arrow in the bottom right:
HTML5 Video as a design element
The obvious use of HTML5 video is in the traditional video player experience, like the YouTube HTML5 video player. It's interesting to see <video> used as a design element here, just like the good old <img> tag. It's not content to be viewed, it's not essential to the page, but it's a nice touch - providing it's used subtly and tastefully, which is definitely the case here. I'm this kind of thinking providing it doesn't adversely affect the user experience in other ways, so I'll look at that more next.
HTML5 Video tag overlaying an image
First, I noticed that the <video> tag is on top of a <div> which has background image set via CSS. I think this is a good idea for a few reasons:
- Nice preload experience for the video
- Nice fallback experience if the video can't be shown
Nice preload experience for the video
Since the image is the first frame of the video, the user experience is really smooth - a nice image is shown immediately, and the when the video loads and plays, it's a very smooth transition.
Nice fallback experience if the video can't be shown
In case the video doesn't load, can't be displayed, Javascript is disabled, etc., the image background is just fine as a fallback.
This wouldn't work well if the video were essential content. In that case, I'd use embedded content in the <video> tag to fallback in the case the video tag can't be rendered. Using embedded content for is a subject for another post, but the the idea is that you can put content inside a tag, and browsers will automatically fall back to the embedded content if they can't display the parent, like this:
<!-- Example - not in the Bing source -->
<video> <source src="video.ogg" type="video/ogg" /> <source src="video.mp4" type="video/mp4" /> <embed src=http://site.com/video/player type="application/x-shockwave-flash" allowfullscreen="true"> </embed> </video>
So in this case, we've got a <div> (background image set via CSS) which contains a <video> tag:
<div style='height: 472px;' id='bgDiv'> <video id='vid' onended='VM.play();' preload='auto' loop='true' autobuffer='true' width='956' height='512'> </video> </div>
The #bgDiv background is set to the background image: http://www.bing.com/az/hprichv/?p=OctLeaves_Artbeat_PF-FH103-79_EN-US56140151.jpg
Video Source
Due to some conflicts in codec licensing, there's no one video codec that will work on all browsers. Cross-browser HTML5 video requires several media source types. There are two ways to handle the multiple source issue:
Using any number of <source> elements, as shown below, the browser will choose automatically which file to download. Alternatively, the javascript canPlay() function can be used to achieve the same. The "type" attribute specifies the MIME type and possibly a list of codecs, which helps the browser to determine whether it can decode the file.
So the declarative way to handle this is to nest multiple source elements in the video, like this:
<video poster="movie.jpg" controls> <source src='movie.webm' type='video/webm; codecs="vp8.0, vorbis"'/> <source src='movie.ogv' type='video/ogg; codecs="theora, vorbis"'/> <source src='movie.mp4' type='video/mp4; codecs="avc1.4D401E, mp4a.40.2"'/> <p>This is fallback content</p> </video>
That's what I'm more used to seeing. The Bing implementation use the other, imperative option. When loading, it uses Javascript to query for supported video types and loads based on the result. First, the three main video types are loaded into an array:
var g_vid = [ ["video\/webm; codecs=\"vp8, vorbis\"", "\/az\/hprichv\/?p=OctLeaves_Artbeat_PF-FH103-79_EN-US.webm"], ["video\/ogg; codecs=\"theora, vorbis\"", "\/az\/hprichv\/?p=OctLeaves_Artbeat_PF-FH103-79_EN-US.ogv"], ["video\/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"", "\/az\/hprichv\/?p=OctLeaves_Artbeat_PF-FH103-79_EN-US.mp4"] ];
Next, an onload function for the video element decides which source to load:
this.sa_vid_ld = function (e) { try { a = _ge("vid"); if (this.Loaded || _w.g_vid == undefined || _w.g_vid == null || a == null) return; var d = _w.g_vid; sj_be(a, "canplaythrough", e ? e : VM.fade); sj_so(a, 0); a.width = VM.Constants.VideoWidth; a.height = VM.Constants.VideoHeight; for (var b = 0; b < d.length; b++) if ( !! (a.canPlayType && a.canPlayType(d[b][0]).match(/^(probably|maybe)$/i))) { a.type = d[b][0]; a.src = d[b][1]; c = sb_gt(); break } } catch (f) { HPV_er(f, "Load") } }
Note the interesting match on canPlayType - since browsers may rely on external codecs, calling canPlayType on a codec type can return probably, maybe, or an empty string.
Browser sniffing
There's definitely some server-side browser sniffing going on here. Normally I frown on that kind of thing, but for high-volume bandwidth-sensitive search engine home pages, all bets are off. View source on http://google.com - it's not pretty either, and I'm certain smarter people than me have looked at both of these home pages in great detail.
I think this is similar to database denormalization (assuming you're into normalized databased) - a general rule applies under general conditions, but the general rules go out the window in special circumstances. Normally I'd call web developers crazy for writing custom (non-jQuery) selector logic, but Bing and Google both eschew jQuery.
I compared the HTML for the Bing homepage when using IE9 and IE9 with a IE6 user-agent.
When browsing with an IE6 user-agent, Bing doesn't send the <video> tag. Additionally, it sets a g_hasVid boolean variable so there's no attempt to determine video capabilities and set the source.
Again, while this would normally get me worked up, I can see the wisdom in minimizing the content written to the browser.
Looping and HTTP 304
The <video> tag is set to automatically loop. Note the loop="true" in the video tag below:
<div style='height: 472px;' id='bgDiv'> <video id='vid' onended='VM.play();' preload='auto' loop='true' autobuffer='true' width='956' height='512'> </video> </div>
Profiling the page, though, I see that there are continued requests for the video. The page is correctly using a HTTP 304 (not modified) response, so that the locally cached video is reused:
URL |
Method |
Result |
Type |
Received |
Taken |
Initiator |
Wait |
Start |
Request |
Response |
---|---|---|---|---|---|---|---|---|---|---|
/fd/ls/GLinkPing.aspx?IG=8226ea3583704baaac8a91619295f16b&ID=SERP,5033.1 |
GET |
200 |
image/gif |
250 B |
171 ms |
<img> |
1981 |
78 |
78 |
15 |
/az/hprichv/?p=OctLeaves_Artbeat_PF-FH103-79_EN-US56140151.jpg |
GET |
200 |
image/jpeg |
80.01 KB |
266 ms |
background-image |
2620 |
0 |
47 |
219 |
/az/hprichv/?p=OctLeaves_Artbeat_PF-FH103-79_EN-US.webm |
GET |
200 |
video/webm |
1.58 MB |
1.03 s |
2636 |
0 |
62 |
968 |
|
/az/hprichv/?p=OctLeaves_Artbeat_PF-FH103-79_EN-US.webm |
GET |
304 |
video/webm |
219 B |
16 ms |
13915 |
0 |
0 |
16 |
|
/az/hprichv/?p=OctLeaves_Artbeat_PF-FH103-79_EN-US.webm |
GET |
304 |
video/webm |
219 B |
< 1 ms |
19016 |
0 |
0 |
0 |
|
/az/hprichv/?p=OctLeaves_Artbeat_PF-FH103-79_EN-US.webm |
GET |
304 |
video/webm |
219 B |
15 ms |
24102 |
0 |
0 |
15 |