Comments
Patrick Collands wrote: collands (AT) gmail com I'd be very grateful for an invitation. Thank you.
Cloud Expo on Google News

SYS-CON.TV

2009 East
PLATINUM SPONSORS:
IBM
Smarter Business Solutions Through Dynamic Infrastructure
IBM
Smarter Insights: How the CIO Becomes a Hero Again
Microsoft
Windows Azure
GOLD SPONSORS:
Appsense
Why VDI?
CA
Maximizing the Business Value of Virtualization in Enterprise and Cloud Computing Environments
ExactTarget
Messaging in the Cloud - Email, SMS and Voice
Freedom OSS
Stairway to the Cloud
Sun
Sun's Incubation Platform: Helping Startups Serve the Enterprise
POWER PANELS:
Click For 2008 West
Event Webcasts
Video
The way your ancestors intended

One of the primary strengths of Macromedia Director is the various media types it can combine in any application. Director can utilize most any standard graphic format, audio format, and almost every popular video technology on the market. If there is not a current media type supported Director can easily extend its feature set with an Xtra, making it infinitely expandable. This allows for amazing flexibility, but all these different formats can create some authoring hassles.

Video formats vary in capabilities, quality, and size. There is no one universal format that seems to outweigh all others for every use. Flash video is wonderful for Web delivery, and for smaller files. If you need a perfect quality video for a kiosk MPEG-2 or MPEG-4 may suit your needs. QuickTime is great for CD delivery, but there is always the hassle of having to install applications on the end user's machines. Each format has its uses but this can create some complexity for developers when trying to author with multiple formats in mind. There is also the inevitable occurrence of a late project video switch that causes some recoding to adapt to a new format. No one wants to be up late the night before a large project is due trying to rebuild everything for the switch, but is there an easier way to handle this?

Well, since we have gone this far, you can probably guess that I am going to say yes, and also explain my approach for handling this type of problem. As with any solution this approach will require some upfront planning, but the benefits can be reaped many times over. You also get the benefit of taking what I have already written and expanding on it, rather than starting completely from scratch. Here is the thought process that I went through to streamline video playback so that I would almost never have to deal with rebuilding the same old video controller again.

My first rule of programming is simply this. "If you have to do it more than once, there is probably an easier way." Any time you write a new script or behavior look for anything that can be reused. If you make a behavior to control a button, write it so that it can be reused for any type of button in the future. The basic concept can apply here as well. When constructing our video controller we need to look at ways to make it as flexible as possible. This means that we don't want to commit some common mistakes like referring to specific sprite numbers, frames, or requiring some exact order for elements to be aligned in the score. The coding should be as flexible as possible so any part of the system can be removed and it can still function.

The next step is to look at the scope of what we are building. We are creating a video controller, but we already know that this will need to support multiple formats. There are three ways we could easily handle this, each with their pros and cons.

1.  Write separate behaviors for each media type.
This can make editing code simple for each type but also creates a lot of unnecessary duplication in the code. If you want to change core functionality then you have to edit each behavior separately. Let's say we decide one day that we want the rewind button to run on mouseUp until we click play instead of while its being held down. If we have six video formats, that's six chunks of code that have to be rewritten. This is not ideal at all.

2.  Write one big behavior.
This is definitely more elegant, as we have one source to work from. In many cases I would recommend this route as it keeps all the necessary code in one place. An early approach to this behavior that I wrote years ago combined the code for the video, buttons, slider, etc., all into one behavior. It's handy to not have to keep up with the separate pieces. But when dealing with so many formats you can quickly end up with long chunks of code in a rat's nest of "if" and "case" statements. It may be nice and compact, but debugging is still a bit harder than it needs to be.

3.  Centralize all common code and split off only the parts that vary.
This approach is what I demonstrate here. The idea is to carefully plan your code so that the core behavior will handle all the general tasks, then you only split up code that is specific to a particular media type. We will accomplish this through the use of "ancestors".

So what is an ancestor? It doesn't mean you have to track your code's genealogy, but the same basic concept applies. Here is a brief example of how an ancestor can be used.

Let's say we have two animals - a cat and a dog. Both are animals, both have legs, but some properties vary. The following is a simple script for the general "animal".


Property pSound, pHasFur

On new me
  PHasFur = 1
  Return me
End

On putSound me
  Put pSound
End

	Now let's create one of each...

Cat = script("cat").new()
Cat.putSound()
--"meow"

	and a dog...

Dog = script("dog").new()
Dog.putSound()
--"arf"

Neither cat nor dog have a property named "pSound" built in and neither have a function called putSound, but both are able to use these as they have defined the "animal" script as their ancestor. Ancestors are similar to parent classes or prototypes. They simply allow one set of code to inherit all the properties, methods, and functions of another.

With this in mind, there are two ways we can approach our new controller.

  1. Create one ancestor for each new controller type.
  2. Create one script that dynamically selects an ancestor.
Either way is perfectly fine, but for my uses I think the second option will be better. With this approach the ancestor is chosen based on the type of media it finds. Because the core behavior is the same, we can swap media on a sprite over and over again without ever having to change the behavior attached to it. This allows us to focus more on the project at hand, and worry less about what type of media we just placed on screen.

The first part of the code we need is the video controller. This will be placed on the video sprite and handles all the interaction. The behavior has a few key tasks.

  1. Determine what type of media we are using
  2. Attach the appropriate ancestor
  3. Handle all video events from buttons (play, pause, seek)
  4. Track base properties of the video
Listing 1 is the beginSprite handler.

The first thing we do is to establish the type of media we are going to utilize. Using that we can check to see if we have an available script for that type and attach it. As long as we name the future scripts in the same manner we can continue to add new media types without ever touching the base code again. This becomes infinitely scalable with no hassles for existing media types.

We then grab the Director movie's tempo (it is used by some media types to calculate rewind and FF intervals) and run a prepVideo() handler. This is the first example of a handler that does not reside anywhere in the base script. PrepVideo becomes a handler we place in any new media type script to initialize and setup calls for that video. For QuickTime it might be for turning on Direct-to-Stage. For Flash we may want to calculate its playback speed/rate. We look for any common places that we may need to interact with the media and create these universal calls to adapt to each new type.

The last item we run is the doVideo() command. This is our base script handler to interpret events from buttons. We run this with the #play property to trigger the video to start up immediately.

Let's take a quick look at the doVideo() handler to see how we handle each event; then we will look at how two specific media types vary.


on doVideo me, vItem, vParam
  if not pActive then exit
  case vItem of
    #play :
      --slow director
      me.slowDirector()
      --play it
      me.playVideo()

If you look at the beginSprite handler you will see that we disable the script if a valid ancestor is not found. This prevents errors on unrecognized types. We check this at the start of the doVideo command and exit if we are not active. Next we look at what command was sent. In this case we focus on the play command. The slowDirector call is an option I have written into my code to allow Director to slow its frame rate and allow the video to claim more of the CPU if necessary. Next we call the playVideo() call to the ancestor to let it start the video. Let's look at the Flash and QuickTime versions of the ancestor scripts to see how they vary for this command.

Flash:


on playVideo me
  sprite(me.spriteNum).fixedRate = me.pInfo[#playRate]
  sprite(me.spriteNum).play()
end

QuickTime:
on playVideo me
  sprite(me.spriteNum).movieRate = 1
end

Both trigger the video to start, but each has a different approach. We now have the ability to play back two media types from the same behavior, and can keep adding more. Let's add another popular type - MPEG. MPEG Advance Xtra is a very popular solution for this, so here is its variant of this handler.


on playVideo me
  sprite(me.spriteNum).rate = 1
  sprite(me.spriteNum).play()
end

If we tried to create one large behavior for this we would have a mass of "if" and "case" statements for every command. This can make debugging difficult and gets more complex as we start finding larger and larger variations in how some media types handle different functions. Here is an example comparing QuickTime and Flash for rewinding.

QuickTime allows rewinding by simply setting the movieRate of the sprite to -2. Flash does not allow reverse playback so we instead have to fake it by calculating the time elapsed each frame and reversing the video by 2 times that amount. Here are the two scripts...

QuickTime:


on rewindVideo me
  sprite(me.spriteNum).movieRate = -2
end

Flash:


on rewindVideo me
  sprite(me.spriteNum).pause()
  --grab the time to fake the rewind
  me.pInfo[#lastTime] = the milliseconds
end

on rewindStepVideo me
  --Fake Rewind since MPEG Advance does not support it
  t = the milliseconds -  me.pInfo[#lastTime]
  --backup 2X this ammount
 sprite(me.spriteNum).seek(sprite(me.spriteNum).currentTIme - (2 * t))
  me.pInfo[#lastTime] = the milliseconds
end

Flash needs to check this every frame, while QT simply runs until we change the rate back. These differences would make a single script very unmanageable, while splitting the pieces into separate ancestors make this a very simple and elegant process.

After we code for play, pause, rewind, seek, etc., we need to make one more behavior for all the buttons. Since these buttons all address our core behavior they do not need any changes for each media type either. You can either communicate with scripts directly, use a sendSprite, or a sendAllSprites call. Here is a short overview of the differences.

  1. object.function(): This approach calls a function on a specific object, or sprite. This can be handy as it runs fast, but if there is any problem it will throw an error and stop your program.
  2. sendSprite: This is a step up from calling the object directly. It sends a message to a specific sprite, but if the handler is not on that sprite it's simply ignored. The down side is you have to know where everything is. There are many ways to handle this up front, but I find for systems where speed is not an issue, there is another approach...
  3. SendAllSprites: This command broadcasts a message out to all sprites. If the sprite has the handler it runs it, otherwise it's ignored. This can be a little slower (a very few milliseconds at most) but for a flexible system like this I find it to be very handy. You can establish buttons that span multiple frames and move media to different channels without the worry of any piece losing sight of the rest of the system.
Here is an example of a portion of the button handler that sends out the play command...


on mouseUp me
  case pWhatItem of
    #play, #pause, #stop :
      --play, pause or stop was clicked
      sendAllSprites(#doVideo, pWhatItem)

When we click the button it simply tells the sprite(s) with a doVideo command to run the specified command. This makes it very fast and easy to deploy solutions that will not break by moving frames or changing sprite orders.

You can download the example movie that demonstrates how to use this approach for QuickTime, Flash, and MPEG Advance. I have also utilized this in various portions for Real Media, Windows Media, LDMs (Linked Director Movies), Audio, or other video Xtras. Creating a new media type is as simple as copying another ancestor and adjusting the few lines of code that handle the video playback. The rest is already done for you and never has to be rewritten again.

Summary
Ancestors are a powerful way to reuse and simplify complex systems in lingo. Give them a try and you will see why it's great to be kind to your ancestors.

About Chuck Neal
Chuck Neal is CEO of MediaMacros, Inc. and owner and operator of www.mediamacros.com, a free Web resources for Macromedia Director developers. He has been involved in multimedia for over 8 years in both traditional multimedia as well as 3D animation and video. Over the years Chuck has worked on projects for a number of major clients, including Coca-Cola, United Parcel Service, and Ford Motor Company. Through MediaMacros, he strives to push the envelope of multimedia taking on full projects as well as offering advanced coding services to over 40 multimedia firms in over a dozen countries around the globe.

In order to post a comment you need to be registered and logged in.

Register | Sign-in

Reader Feedback: Page 1 of 1

Latest Cloud Developer Stories
The Enterprise Cloud Requires a real time infrastructure and a management discipline that understands and can enforce service level discipline.
CloudBench Applications, Inc. announced its financial results for the three months and nine months ending September 30, 2009. All amounts are stated in Canadian dollars unless otherwise noted. Revenues from BasicGov, the Company's cloud computing solution for local government, gr...
The new contract is an industry first, with CSC being the first Microsoft partner to lead and win a cloud computing services agreement of this scale. Under terms of the contract, CSC will provide Royal Mail Group's 30,000 employees with access to new IT services using Microsoft's...
Operates in over 170 countries and is one of the world’s leading providers of communications solutions and services. Richard Tarboton talks for MeettheBoss.TV on his role as Head of Energy & Carbon for BT and what they are doing towards reducing carbon emissions.
CA is going to put its Agile Planner software on salesforce.com’s Force.com platform in the first half to accelerate development time and give users visibility over their development initiatives to reduce time-to-market. Customers are supposed to be able to accelerate the deploym...
Subscribe to the World's Most Powerful Newsletters
Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
Click to Add our RSS Feeds to the Service of Your Choice:
Google Reader or Homepage Add to My Yahoo! Subscribe with Bloglines Subscribe in NewsGator Online
myFeedster Add to My AOL Subscribe in Rojo Add 'Hugg' to Newsburst from CNET News.com Kinja Digest View Additional SYS-CON Feeds
Publish Your Article! Please send it to editorial(at)sys-con.com!

Advertise on this site! Contact advertising(at)sys-con.com! 201 802-3021

SYS-CON Featured Whitepapers
ADS BY GOOGLE

Breaking Cloud Computing News
CloudBench Applications, Inc. announced its financial results for the three months and nine months e...