The plan is simple, take the nice Degrafa-skinned components from Part 1 and assemble them into a video player powered by the OvpNetStream
class from the Open Video Player project.
Design
I knew right away that the design was not going to have any right angles, but I also didn’t want to go with rounded rectangles everywhere. Modern TVs tend to have a lot of soft rounded edges, so I decided to go with a more vintage look. So I fired up Inkscape and got to work:

Implementing the video player design above in Degrafa, the cabinet mapped to a RoundedRectangle
, and the screen & antenna became Path
s. You can read more about about translating SVG to Degrafa in my Inkscape SVG to Degrafa Path article. But for now, let’s focus on the resulting Degrafa code:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:Degrafa="http://www.degrafa.com/2007" layout="absolute"> <Degrafa:GeometryComposition graphicsTarget="{[box]}"> <!-- Antenna --> <Degrafa:Path data="...path data..."> <Degrafa:transform> <Degrafa:TranslateTransform x="15" /> </Degrafa:transform> </Degrafa:Path> <!-- TV cabinet --> <Degrafa:RoundedRectangle y="144" width="350" height="300" cornerRadius="20" /> <Degrafa:fill> <Degrafa:SolidFill color="#333333" /> </Degrafa:fill> <Degrafa:stroke> <Degrafa:SolidStroke color="#FF00FF" weight="4" alpha="0.4" /> </Degrafa:stroke> </Degrafa:GeometryComposition> <!-- TV Screen --> <Degrafa:GeometryComposition graphicsTarget="{[tvscreen]}"> <Degrafa:Path data="...path data..."> <Degrafa:fill> <Degrafa:SolidFill color="#FF99FF" /> </Degrafa:fill> <Degrafa:stroke> <Degrafa:SolidStroke color="#FF00FF" weight="2" alpha="0.4" /> </Degrafa:stroke> <Degrafa:filters> <mx:GlowFilter color="#EEEEEE" alpha="0.2" blurX="16" blurY="16" /> </Degrafa:filters> </Degrafa:Path> </Degrafa:GeometryComposition> <mx:Canvas width="350" height="444" horizontalCenter="0" verticalCenter="0"> <mx:Canvas id="box" /> <mx:Canvas x="50" y="174" id="tvscreen" /> </mx:Canvas> </mx:Application>
I ended up using a pair of GeometryComposition
s to wrap my three shapes to help keep my fills, strokes, and filters organized. It made sense to do it this way, but I won’t claim it’s the best way. Throw the control bar on below the TV screen, and the design is done.
Backend
The backend is build on the OvpNetStream
class provided by the Open Video Player project. OvpNetStream
extends NetStream
and smooths out some of the rough edges as I discussed previously. Basically, it provides a sane interface (no need to construct a dynamic object with function callbacks) and useful events (like metadata and progress events).
For this demo, we simply instantiate OvpNetStream
on creationComplete
and wire up all the event handlers:
private function complete():void { nc = new OvpConnection(); nc.addEventListener(OvpEvent.ERROR, errorHandler); nc.addEventListener(NetStatusEvent.NET_STATUS, connStatusHandler); nc.connect(null); ns = new OvpNetStream(nc); ns.addEventListener(OvpEvent.ERROR, errorHandler); ns.addEventListener(NetStatusEvent.NET_STATUS, streamStatusHandler); ns.addEventListener(OvpEvent.NETSTREAM_METADATA, streamMetadataHandler); ns.addEventListener(OvpEvent.PROGRESS, streamProgressHandler); ns.addEventListener(OvpEvent.COMPLETE, streamCompleteHandler); vid = new Video(); vid.attachNetStream(ns); vidContainer.addChild(vid); }
The most interesting events are the metadata and progress events. The metadata event delivers the duration of the video and its size. The progress event arrives periodically (theoretically every 100ms by default, but in reality I see them come in just a couple of times per second) and delivers the current video time.
Control Bar
The control bar consists of three components: a play-pause button, a scrubber, and a volume slider. They were skinned using Degrafa in Part 1. In order to control video playback, we need to wire the control bar components to the instance of OvpNetStream
created above.
Here are the event handlers for the three control bar components:
// PlayPause event handler private function playPauseClick():void { if (first) { first = false; ns.play(filename); } else { ns.togglePause(); } } // Scrub event handlers private function scrubPress():void { ns.pause(); playPause.selected = false; } private function scrubDrag():void { ns.seek(scrub.value); } private function scrubRelease():void { ns.togglePause(); playPause.selected = true; } // Volume event handler private function volumeChange():void { ns.volume = volume.value; }
In the playPauseHandler()
, the initial click calls play()
which actually loads the video (and then starts playback), all subsequent clicks just toggle between play or pause. For the scrubber handlers, I chose to break them up into three separate steps: on mouse down pause the video, on mouse up restart playback, and on drag attempt to seek to the to the new time.
That’s it. Here is the resulting Degrafa-skinned video player (view source enabled):
Click Play to start playing Elephants Dream (which is the “world’s first open movie,” and pretty cool too). Right away you’ll notice some visual issues because the dimensions of the video are unknown until the metadata arrives. Also, scrubbing has some problems which I believe are related to cue points in progressive downloads. Lastly, I didn’t implement any indicators for buffering or download progress, so you’ll need to be patient. Since this is just a demo, I’ll have to leave fixing those bugs as an exercise for the reader.
Files
Comments are closed