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 Paths. 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:

I ended up using a pair of GeometryCompositions 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:

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.

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.

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

I’m going to combine my earlier Degrafaskinningefforts with my more recent video work to create a Degrafa-skinned video player. In this post, I’ll build out all the components required for a video player control bar. And in Part 2, I’ll weld the control bar to an Open Video Player OvpNetStream backend.

Button

To get started, I built out a simple button skin with a slight shine on the top half. I also added rounded corners to give the classic web 2.0 look-and-feel. There’s really not much to look at:

Play-Pause Button

I’m using a toggleable Button component as the base of my Play-Pause button. By leveraging Degrafa’s stateful skins, I simply change the visibility of the Play and Pause geometry depending on the skin’s state. Here’s a snippet from the states block of the skin:

Scrubber

Getting total control over a Slider skin is not easy in Flex 3. There are two issue that I find quite annoying: the thumb size is hard-coded into the class and the highlight skin is offset from the track. Fortunately, the web is full of workarounds for the thumb size issue, but for this skin I only needed to hard-code an offset (+6,+6) to bring the thumb’s origin to (0,0) and make all my geometry line up correctly. Similarly, I added a 1 pixel offset to the highlight skin to bring its origin to (0,0) to match the track. The result:

Volume Slider

For consistency, the Volume slider re-uses the Scrubber’s thumb skin, but the track and highlight are wedges (Polygon‘s in Degrafa). The only clever piece of code is the highlight skin’s overridden updateDisplayList() function:

In order to correctly draw the highlight wedge, we first need to know the dimensions of the track. For simplicity, the Volume track height is hard-coded in both the track skin and highlight skin, but the width of the track is variable. In this case, the width of the track is the same as the width of the HSlider component itself. So in the updateDisplayList() code above, the track width is found and used to compute the ratio of widths. This ratio is later used to draw the highlight geometry. The end result:

Files

Recently, I’ve been doing quite a bit of video work in Flex and AIR. My main gripe (that I’ll try to rectify below) is that there sure is a lot of info around the web about streaming, FMS, and friends, but almost none about playing local video. Even the Adobe Media Player, which is a really nice AIR app, is all about streaming content from the web.

And like those before me, I’ve come to the same conclusion about video in Flash: it’s pretty cool, but I sure wish it was better. Enter the Open Video Player as the answer to some of NetStream‘s woes. Alas, all of the OVP docs and samples are, once again, all about streaming, Akamai, bandwidth, etc.

The elephant in the room for an AIR video player: What if I’m offline?

The Easy Way: VideoDisplay

I gonna get started with the easiest possible local video player, which in Flex means using VideoDisplay. Here’s a simple AIR app that allows you to open a FLV file and play it:

The only real magic is the vd.source = 'file://' + f.nativePath line. Apparently, source wants a URL even for a local file, so just prepend file:// and everything works fine.

Getting Fancy with NetStream

As far as I can tell, no one actually uses VideoDisplay at the core of a video player component in a production system. The many benefits of NetStream are not really clear to me, but it is the darling of production systems. Thankfully, despite its name, NetStream is perfectly happy playing local video. Rebuilding the above example yields:

NetStream is a wonder of engineering. Basic usage is understandable, just feed in null to the NetConnection to switch to progressive download mode, and use the same file:// trick above to play a local video. But after that, things get weird fast. First, the metadata handling to totally backwards: you have to create a dynamic object with a onMetaData property that maps to a callback function. My guess would be that there is some legacy bullshit going on that breaks addEventListener. Second, you can’t tell if a video is playing or not. Third, the lack of progress event means you need to use a timer to track playhead time. Add it up – LAME.

Reaching for the Grail: Open Video Player

According to their website, it’s Open Video Player to the rescue. And I’ll have to agree that it fixes many of NetStream issues. Swaping in OvpNetStream yields:

It’s not a revolution because OvpNetStream just extends NetStream, but we instantly get standard events thrown for progress ticks and metadata, and also some pretty time formatting as a bonus. The OVP also does about a hundred other things related to streaming, streaming servers, and Akamai. That’s about all I know about video right now, so before I can post again I’ll need to learn something new…