SVG at the movies - take two!
Wednesday, November 21, 2007 6:13:03 PM
This article will show you how to present video on the web using open standards and SVG (Scalable Vector Graphics.)
As you may have already guessed one of the major new features introduced in the SVG Tiny 1.2 specification is the
videoelement. It should be noted that while this is a major feature and a great leap forward, SVG Tiny 1.2 itself doesn't mandate any particular audio or video codecs. This topic is explored in the call for video on the web article on dev.opera.com. Of particular interest in that article are the svg video demos at the bottom.
First, for maximum enjoyment you need a way of viewing the examples in this article. I suggest the experimental Opera 9.5 video build, which can be downloaded here. At the moment there is only a Windows build, but *nix and OSX builds are in the works. Because of the beauty of open standards you can choose any SVG-supporting browser you like to view the content, such as Firefox, although at the time of writing declarative animations are not supported in Firefox, so the demos below may not work as intended.
The experimental builds of Opera and Firefox both implement Ogg Theora and Ogg Vorbis as the baseline audio/video format, which means that it's implemented natively in the browsers and that it will work the same across all platforms and different browsers.
Lights, camera, ACTION!
Examples first, explanations later. Note that this example requires support for declarative animation in SVG, and some other SVG 1.1 features, like styling with CSS and patterns.
View live example.
Click the white rectangle - a short animation will play, and then a video will start playing.
Animation in SVG
<g transform="matrix(0 -10 10 0 50 50)" style="fill: white"> <g transform="translate(0 -2)"> <path transform="matrix(1 0 0 1 -5 -5)" d="M10,10H4L0,6V0h10V10z"> <animate id="ref" attributeName="d" to="M10,10H0L0,10V0h10V10z" begin="click" dur="1s" fill="freeze" /> </path> <animateTransform attributeName="transform" type="translate" begin="ref.begin" dur="1s" fill="freeze" values="0 -2; 0 0" /> </g> ...
The above code does the animated morphing of the white rect shape in the above example. By animating the
dattribute from one value to the next you don't have to worry about interpolating the values yourself, however, due to the design of path animation the paths must be compatible, which means they must have the same number of path commands and the path command types must be of the same type after normalization has taken place. This is illustrated by this testcase; to morph between any two shapes the best bet is to make all segments into Bezier curves. Kevin Lindsey made some tools for doing just that, and I recommend reading his introduction to Bezier curves article and looking at the path morphing script API. Now let's look at some more code:
... <animateTransform attributeName="transform" type="translate" begin="ref.end" dur="0.5s" fill="freeze" values="50 50;160 120" /> <animateTransform attributeName="transform" type="rotate" begin="ref.end" dur="0.5s" fill="freeze" additive="sum" values="-90;0" /> <animateTransform attributeName="transform" type="scale" begin="ref.end" dur="0.5s" fill="freeze" additive="sum" values="10 10;32 24" /> <set attributeName="display" to="none" begin="ref.end+0.5s" fill="freeze" /> </g>
Here we see what gives us the rotating and scaling rectangle. By using the
additiveattribute it's easy to use multiple transformations and animate them separately. First in the stack of animated transforms is a translation (a move) between the coordinates (50,50) and (160,120). We'll let the browser interpolate values between those two coordinates over the course of half a second. At the same time a rotation animation takes place, between -90 and 0 degrees. This
additive="sum", which means that it will not replace the previous transform, but be appended to it by doing a post-multiplication. Then we add a scaling transform - perhaps you only expected to see two values instead of four in the
valuesattribute? That is of course possible, but sometimes you want to apply different scale factors to the x- and y-axis, and that is the case here. Finally the shape is hidden by animating the
How to do cheap video clipping
You might have been impressed with the non-rectangular clipping of the video (the smooth smooth curved shape that looks something like a tv-set.) The neat thing is that this is not really clipping - it's just drawing on top of the video, which can provide the faded edges due to SVG supporting opacity. The overlay has been optimized to use only fill (since stroking operations are usually more expensive in vector graphic engines), as we can see in the source below.
<g transform="translate(-15, -10)" style="display: none; fill: #fa9bce"> <path style="fill-opacity: 0.4" d="M0,0v260h350V0H0z ..." /> <path d="M0.5,0.5v260h350V0.5H0.5z M330.073,235.451 ..."/> <path style="fill:none;" d="M0.5,0.5v260h350V0.5H0.5z ..."/> </g>
There are drawbacks to this "poor man's" clipping. Say if you want an advanced background around the video frame - eg animated gradients - or something that is not a simple solid color? You can get around this by simply using the clipping features of SVG 1.1 to do arbitrary shape clipping, then you can place your clipped video or graphics anywhere. For an example of using clipping and video together, see here.
Action? I mean interaction
View live example.
Tweaking the examples
To get these examples to work in Opera, I had to do a bit of debugging, but this really was a small effort, basically consisting of applying the debugging techniques presented in the debugging SVG with Opera article. First, since the experimental video build doesn't support MPEG-4 (the format of the original videoclip) there is a choice of re-encoding the video or simply using another video file, for example one of the growing selection of Theora videos available at wikimedia. Next, as is often the case with content authored mainly for the Adobe SVG plugin, some minor details needed to be changed in the scripts. What I did in order to keep the content mostly as-is was to include a wrapper script for the DOM methods getURL and parseXML. These two methods are sometimes used in old svg content, and are also included in the SVG Tiny 1.2 specification, though there are some differences in how it is specified there versus what was implemented in the Adobe SVG plugin.
The rather simple wrapper script uses XMLHttpRequest and DOMParser to provide the same functionality offered by the getURL and parseXML methods.
Many thanks to Antoine Quint and xml.com for letting me publish modified examples from the original SVG at the movies article.