Flash Video Fallback for HTML5 Video Solutions

Flash Video: how to convert with ffmpeg, how to embed with the video-tag.


There are many alledged and real solutions to extend HTML5 documents with Flash Video capabilities. In this workshop we will present on how you can make the HTML5 video tag work on browser installations only supporting Flash Video. Besides this we will discuss alternative solutions with Java and the no-more-free Flowplayer. Simply reading out all video tags and then to insert embed tags for Flash Video by DOM/ Javascript is an alluringly simple and efficient solution attempt; sole disadvantage: We will have to provide a third format in addition to .webm and .mp4: .swf or Shockwave Flash (The next chapter shows you how to convert into .swf.). The mwEmbed library on the other hand allows direct playback of .ogg videos which is another HTML5 format.

How to Convert Flash Videos with ffmpeg

In order to convert videos into the Shockwave Flash format with ffmpeg you have to give an audio sample rate (one out of 44100, 22050, 11025) rather than an audio bitrate. The sampling rate describes on how many times per second and audio signal is measured. 44 kHz provides full CD quality. Look what your input stream supports and then choose an appropriate rate.

> ffmpeg -i Reiher.MOV -vb 536k -ar 11025 -r 25 Reiher-848x480-600kb.swf Guessed Channel Layout for Input Stream #0.1 : mono Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'Reiher.MOV': Metadata: creation_time : 2008-07-13 16:50:08 Duration: 00:00:16.50, start: 0.000000, bitrate: 12786 kb/s Stream #0:0(eng): Video: mjpeg (jpeg / 0x6765706A), yuvj420p, 848x480, 12720 kb/s, 30 fps, 30 tbr, 30 tbn, 30 tbc Metadata: creation_time : 2008-07-13 16:50:08 Stream #0:1(eng): Audio: pcm_u8 (raw / 0x20776172), 8000 Hz, mono, u8, 64 kb/s Metadata: creation_time : 2008-07-13 16:50:08 [buffer @ 0xe53140] w:848 h:480 pixfmt:yuvj420p tb:1/30 sar:0/1 sws_param:flags=2 [buffersink @ 0xe32fc0] No opaque field provided [format @ 0xe51140] auto-inserting filter 'auto-inserted scaler 0' between the filter 'src' and the filter 'format' [scale @ 0xe33e40] w:848 h:480 fmt:yuvj420p sar:0/1 -> w:848 h:480 fmt:yuv420p sar:0/1 flags:0x4 [aformat @ 0xea02c0] auto-inserting filter 'auto-inserted resampler 0' between the filter 'src' and the filter 'aformat' [aresample @ 0xea13e0] chl:mono fmt:u8 r:8000Hz -> chl:mono fmt:s16 r:11025Hz Output #0, swf, to 'Reiher-848x480-600kb.swf': Metadata: creation_time : 2008-07-13 16:50:08 encoder : Lavf54.6.100 Stream #0:0(eng): Video: flv1, yuv420p, 848x480, q=2-31, 536 kb/s, 90k tbn, 25 tbc Metadata: creation_time : 2008-07-13 16:50:08 Stream #0:1(eng): Audio: mp3, 11025 Hz, mono, s16 Metadata: creation_time : 2008-07-13 16:50:08 Stream mapping: Stream #0:0 -> #0:0 (mjpeg -> flv) Stream #0:1 -> #0:1 (pcm_u8 -> libmp3lame) Press [q] to stop, [?] for help frame= 415 fps=120 q=31.0 Lsize= 3024kB time=00:00:16.51 bitrate=1500.3kbits/s dup=0 drop=80 video:2981kB audio:32kB global headers:0kB muxing overhead 0.360153%

Embedding Videos

The only thing we need is a little bit of Javascript code and an additional .swf source tag for our video tag. You can simply copy and paste the Javascript code into your HTML document or as part of an externally linked Javascript file.

<div><video controls preload=metadata width=848 height=480 poster='data/frame049.jpg'> <source src='data/Reiher-848x480-600kb.mp4' type='video/mp4'> <source src='data/Reiher-848x480-600kb.webm' type='video/webm'> <source src='data/Reiher-848x480-600kb.swf' type='application/x-shockwave-flash'> <source src='data/Reiher-848x480-600kb.ogg' type='video/ogg'> <img src='...'> - Kann das HTML5-Video leider nicht abspielen; bitte Javascript einschalten einen anderen Browser verwenden. </video></div>

Nifty; isn`t it?

Please note that our video tag has been put into an enclosing div tag. This is necessary as no document structure has been defined for our video tag in non HTML5 compliant browsers. Only this way we can ascertain our solutin to work on all browsers.

Please do also note that we have stated the width and height of the video explicitly. This will be necessary to determine the width and height of the Flash Video to be embedded.

The solution we will present is neat and tidy for being studied in a short time and for being adapted by yourself rather than presenting an incomprehensible full blown apply everywhere solution. Thus we will not install any advanced navigation handles like for fast forwarding or rewinding the video. You can still control the playback of your Flash Video by a right click on the video.

The solution we will present was tested and developed on IE8, IE6 and Firefox v 3.04.

Our Approach

Before we will show the sources in the next chapter which can simply be incorporated by copy and paste into your HTML or Javascript document we will present our approach to Flash Support for HTML5 Videos.

We will start right away by discovering our <video> tags which can simply be done by a call to document.getElementsByTagName("VIDEO"). Afterwards things start to get more tricky. As we have not stated finalizing </source> tags the structure in which the source tags will be arranged is not defined. While every new source tag happens to get a child of the preceding source tag in Firefox 3.04 the Internet Explorer will simply enumerate one tag after the other as siblings of the video tag which is just the first tag in a flat list. Things happen to get worse as we do not have anything to close the video tag at least not in Firefox as succeeding elements still happen to be childs of the video tag (</video> is simply ignored). MSIE gives you another opening video tag at the end of the list. We will solve this issue by enclosing our video tag in a dedicated div tag which only contains our video tag as sole element so that we can always spot the end. Otherwise if the website develoepr has forgotten about the div tag we will simply break at the next video or script tag because none of these tags can be encountered within a video tag. Reading beyond the video tag is not a failure either as we do solely look for source tags which can themselves just be encountered withing video tags.

Having successfully read the video tags and their associated source tags we can go on an create embed tags with DOM right before each video tag. Let us have a look on how to embed Flash Videos with the embed tag by plain HTML.

. <embed src="data/Reiher-848x480-600kb.swf" width="848" height="480" play="true" loop="false" quality="high" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash"> </embed>

The figure above shows on how to embed Flash Videos with the embed tag. You may find solutions with the newer object tag on the internet; they may look very obscure as they still embed an embed tag within the object tag to ensure backward compatibility and to avoid getting the video displayed two times. We do not want to use the object tag in order to embed videos here. Newer browsers support HTML5. All other browsers will still support the embed tag for backward compatibility.

The Javascript insertion of embed tags should of course only be done for non HTML5 compliant browsers because we would otherwise duplicate our embedded videos: one time as Flash Video and another time as HTML5 video. We will decide whether HTML5 is supported by querying for the 'videoWidth' property of the video object. If it is present our browser supports HTML5; otherwise we just have an invisible leftover of what the browser has parsed out of the video tag (We do not delete the video tag because it may have visible elements succeeding the </video> tag as childs).

Now let us go on with the alternative content which should be displayed if no video engine, neither Flash Video nor HTML5 video is present. Now the fact that a finalizing </video> tag is missing starts to be very annoying. There should at least be an error message if Javascript is disabled as well. If we would for sure have enclosed everything by a div tag we could now simply delete our enclosing div tag. Howevere we will be much more cautios because we do not want any content in our HTML document to disappear unintentionally. That way we only delete the first text node after or inside the video tag which contains the word "HTML5". The same way we could delete the first encountered img tag if Javascript was enabled. At last we will have to deal with the case that Javascript is enabled but neither HTML5 video nor Flash Video are at disposition. For this case simply insert your alternative content (a text node or an img tag) into the just via DOM created embed tag which can be done by an ebd.appendChild(ourAlternativeContent).

The Sources

Now let us get through to the sources which do basically not need more explanation than given in the last chapter. traverseNodesIE reads for the Internet Explorer all tags in sequence up to the enclosing video tag while traverseNodes descends into the deep for Firefox where every new element happens to be a child of the preceding element. The only thing to do here is to get out the source tags (getFlashSource) in order to store the source URL with Shockwave Flash type; besides this we also mark alternative content for deletion here.

As far as HTML5 is supported we escape the loop for creating Flash content right before it can start to create embed tags for each video tag with a simple break;. At last we delete the alternative content which is only here for the case that both Javascript and Flash have been disabled.

function traverseNodes(parentnode,applyProc,level) { result = true; //document.writeln(level+":"+parentnode.nodeType+""+parentnode.tagName+parentnode.childNodes.length); if(!parentnode.hasChildNodes()) return true; for(cidx=0;cidx<parentnode.childNodes.length;cidx++) { node=parentnode.childNodes[cidx]; if(node.tagName=='VIDEO'||node.tagName=='SCRIPT') return false; applyProc(parentnode,node,level); // mozilla 3.0.4 bug: infinite recursion through script-Tag result = result && traverseNodes(node,applyProc,level+1); if(!result) break; } return result; } function traverseNodesIE(startnode,applyProc,level) { if(startnode.hasChildNodes()) return traverseNodes( startnode, applyProc, level ); else { node = startnode; parentnode = startnode.parentNode; while( node.nextSibling ) { node = node.nextSibling; if(node.tagName=='VIDEO'||node.tagName=='SCRIPT') break; applyProc( parentnode, node, level ); traverseNodes( node, applyProc, level ); } }} var toremove = null; var removeparent = null; var flashsource = '', swftype = "application/x-shockwave-flash"; function getFlashSource(parentnode,node,level) { //document.writeln(level+":"+node.nodeType+""+node.tagName); if( node.nodeType==3 && node.data.indexOf("HTML5")>-1 && !toremove ) { toremove=node; removeparent=parentnode; } if(node.tagName=='SOURCE') { //document.writeln("&nbsp;&nbsp;"+node.getAttribute("src")+"<br>"); if( node.getAttribute("TYPE") == swftype ) flashsource = node.getAttribute("SRC"); //else document.writeln(node.value+node.type); }} videos = document.getElementsByTagName("VIDEO"); for(vidx=0;vidx<videos.length;vidx++) { video = videos[vidx]; if( "videoWidth" in video ) break; // HTML 5 supported //document.writeln("video no "+vidx+"<br>"); flashsource='' traverseNodesIE(video,getFlashSource,0); //document.writeln('source:'+flashsource+'<br>'); ebd = document.createElement("EMBED"); ebd.setAttribute("src",flashsource); ebd.setAttribute("type",swftype); ebd.setAttribute("width",video.getAttribute("WIDTH")); ebd.setAttribute("height",video.getAttribute("HEIGHT")); ebd.setAttribute("loop",video.getAttributeNode("LOOP")?"true":"false"); ebd.setAttribute("play",video.getAttributeNode("AUTOPLAY")?"true":"false"); ebd.setAttribute("quality","high"); ebd.appendChild(document.createTextNode("Neither HTML5 nor Flash Video are supported.")); video.parentNode.insertBefore(ebd,video); if(toremove) removeparent.removeChild(toremove); }

Other Compatibility Solutions

An interesting alternative solution is made up by the usage of .ogg videos which can be redirected with mwEmbed to the Java applet Cortado. The disadvantage about it: Compression rates of VP3.2 used in ogg are not as good as those obtained by VP8/H.264. It should not matter for short videos which can be preloaded entirely but may be a disadvantage for longer videos as you would have to downgrade their quality in order to maintain the same bitrates.

<script type="text/javascript" src="libs/html5media.min.js"></script>

A simple <script>-Tag in the header of your HTML document suffices to load the open source Flash Video player Flowplayer in case of demand. The only disadvantage of the html5media library in 2011 was that the video could not be embedded with <source> elements like shown above. That way the video can not be given in different formats like mp4 and webm at the same time because you have to user the src attribute of the video tag. An error which should however not be that hard to patch. Unfortunately Flowplayer is no more free.

<head> <title> … </title> <script type="text/javascript" src="libs/html5media.min.js"></script> … <video controls preload=metadata src='xy.mp4'> </video>

Enjoye the Results

Test it yourself. The presented solution works on almost any browser.

Reiher 848x480, 600 kbit/s