|
Comments
Did you read today's front page stories & breaking news?
SYS-CON.TV
|
Director Video
The way your ancestors intended
By: Chuck Neal
Oct. 18, 2004 12:00 AM
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. 2. Write one big behavior. 3. Centralize all common code and split off only the parts that vary. 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.
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.
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.
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 Reader Feedback: Page 1 of 1
Latest Cloud Developer Stories
Subscribe to the World's Most Powerful Newsletters
Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
|
SYS-CON Featured Whitepapers
Most Read This Week
Breaking Cloud Computing News
|
|||||||||||||||||||||||||||||||||||||||||||||||||