Flash is capable of many tasks by only using assets that have been created or imported into the authoring environment. However, one of its greatest strengths is its ability to work with external assets at runtime. Flash Player can load several kinds of assets including plain text, XML, HTML, CSS, URL-encoded variables, images, sound, video, and other SWF files, to name some examples. It can even load raw binary data.
This chapter provides a brief overview of loading and unloading a few of these external asset types. You can do very different things with each asset type, depending on the asset, security restrictions, and the version of Flash Player you’re targeting. However, the recipes here should give you some idea of what’s possible, as well as warn you about some possible pitfalls, to help you along the way.
To improve your memory management efforts, and to save some possible debugging time, you should know a little about how Flash Player 9 and later flushes objects from RAM. In short, you can’t immediately remove something from memory. Instead, Flash Player uses a process called garbage collection. When an object is no longer in use, Flash Player marks it for collection. Subsequently, during an optimal low in processor demand, the garbage collector sweeps through and collects all objects previously marked for removal.
Unlike your neighborhood trash removal service, however, you can’t predict when garbage collection will occur, and you can’t reliably force the process. As long as you’re aware of how this system works, you should be able to maintain your code that improves your chances of efficient memory management and more effective garbage collection.
You want to apply the same text styling to one or more text fields, particularly those containing HTML-based content from internal or external sources.
Use the URLLoader
class to load
an external Cascading Style Sheet (CSS) into an instance of the StyleSheet
class, and apply the latter
instance to text fields.
The URLLoader
class can load
plain text, name/value pair variables (such as those in a URL), or
binary data. As seen in the first block of the following script, this
example uses two such loaders—one for an external HTML file and one for
an external CSS document—and a StyleSheet
instance to contain the data from
the CSS file.
The following two functions are very similar, loading first the
CSS document, then the HTML document. In both cases, event listeners for
load complete and input/output (IO) errors are added, and the content’s
loaded. If either process generates an IO error, the onIOErr()
function at the end of the script is
called.
When the CSS document is loaded, the onCSSLoaded()
listener function creates a
StyleSheet
instance, parses the
loaded CSS document to create the requisite styles, and then repeats the
loading process for the HTML file.
When the HTML file is loaded, the onLoadHTML()
listener function stores the
incoming HTML in a local variable, creates a text field, and then sets
several basic text field properties (discussed in Chapter 14). It also applies the style
sheet, populates the htmlText
property with the loaded HTML data, and adds the field to the display
list. Finally, the function removes all listeners you don’t need any
more.
var htmlFile:URLLoader; var cssStyles:StyleSheet; var cssFile:URLLoader; function loadCSS() { cssFile = new URLLoader(); cssFile.addEventListener(Event.COMPLETE, onCSSLoaded, false, 0, true); cssFile.addEventListener(IOErrorEvent.IO_ERROR, onIOErr, false, 0, true); cssFile.load(new URLRequest("demo.css")); } loadCSS(); function onCSSLoaded (evt:Event):void { cssStyles = new StyleSheet(); cssStyles.parseCSS(evt.target.data); htmlFile = new URLLoader(); htmlFile.addEventListener(Event.COMPLETE, onHTMLLoaded, false, 0, true); htmlFile.addEventListener(IOErrorEvent.IO_ERROR, onIOErr, false, 0, true); htmlFile.load(new URLRequest("demo.html")); } function onHTMLLoaded(evt:Event):void { var htmlData:String = evt.target.data; var txtFld = new TextField(); txtFld.width = 550; txtFld.multiline = true; txtFld.wordWrap = true; txtFld.autoSize = TextFieldAutoSize.LEFT; txtFld.selectable = false; txtFld.styleSheet = cssStyles; txtFld.htmlText = htmlData; addChild(txtFld); cssFile.removeEventListener(Event.COMPLETE, onCSSLoaded); cssFile.removeEventListener(IOErrorEvent.IO_ERROR, onIOErr); htmlFile.removeEventListener(Event.COMPLETE, onHTMLLoaded); htmlFile.removeEventListener(IOErrorEvent.IO_ERROR, onIOErr); } function onIOErr(evt:IOErrorEvent):void { trace("A loading error occurred:", evt.text); }
You must apply a StyleSheet
instance to a text field before populating it with text.
Samples of HTML and CSS files required for this exercise to work, follow:
<body> <span class='heading'>Use CSS to Style Text</span><br/> <span class='subheading'>A Simple Example</span><br/> <p>Lorem ipsum dolor sit amet,sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p> </body>
15.1 Creating a Text Field for creating a text field, 15.5 Populating a Text Field for populating a text field, 15.6 Automatically Sizing a Text Field for auto-sizing a text field, 15.10 Formatting Text Using HTML for supported HTML, 15.11 Formatting Text Using CSS for supported CSS.
Use the Loader
class to load an
external JPG, GIF, PNG, or SWF file, and then add it to the display
list.
You can fairly simply load a display object, such as an image or
SWF file. You need to start with an instance of the Loader
class, and then use its load()
method to load the content. Consistency
is a hallmark of ActionScript 3.0. You must use a URLRequest
instance for every URL.
Then you add the image or SWF file to the display list after the
load is complete. You add it by using the addChild()
method inside an event listener
function that triggers when the Event.COMPLETE
event is received. This event’s
not available to the Loader
instance,
but rather to the related LoaderInfo
class. You can immediately access an instance of this class by using the
contentLoaderInfo
property of the
Loader
you created, so you don’t have
to instantiate another class.
After the load complete event is received, the image is added to
the display list. Since the event listener was added to the contentLoaderInfo
property, rather than the
Loader itself, you must add the evt.target.content
to the display list, rather
than the more typical evt.target
. For
more information, see Chapter 14.
var ldr:Loader = new Loader(); ldr.load(new URLRequest("loadMe.jpg")); ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, ¬ onLoaded, false, 0, true); function onLoaded(evt:Event):void { addChild(evt.target.content); evt.target.removeEventListener(Event.COMPLETE, onLoaded); }
Although this example loads an image, loading a SWF file uses the same syntax. You need only change the path inside the URLRequest to resolve to a SWF file. However, you may wish to consider other additional features. For example, when loading a SWF file, you may want to delay display or further action until the SWF file has fully initialized. The next recipe demonstrates this approach.
17.3 Communicating with an ActionScript 3.0 Loaded SWF for information about working with loaded content after it is loaded or initialized.
Having loaded a SWF file that was coded using ActionScript 3.0, you want to communicate between the parent and the loaded SWF file.
Cast the content of a Loader
instance as a movie clip and get or set properties, or invoke methods,
of that movie clip.
When you load a SWF file into a Loader
instance, the content of that instance
contains the SWF file and all data therein. Essentially, the main
timeline of the loaded SWF file is a movie clip, so you can access the
methods or properties of the loaded SWF file the same way you access
similar attributes of a movie clip. Hereafter, the host, or parent, SWF
file is called “Loader” and the loaded SWF is called “Loadee.”
Starting with the loaded content, you need a SWF file named loadee.swf. To demonstrate a variety of communication tasks, the file should include a precreated animated movie clip called anim on the stage and the following script.
The first line stops the movie clip from playing so that another SWF file may start it later. The first function traces a hard-coded string to the Output panel. The second function accepts a string argument from a function call, and traces a new string that includes that argument value. The last function also accepts an argument value, but this time it’s a number, and the function returns a new value (adding 1 to the incoming value) to the script from which it was called. None of the functions are called within the loadee.
anim.stop(); function sayHello():void { trace("Hello, from your loadee!"); } function showMsg(msg:String=""):void { trace("Loadee here again. You said, '" + msg + "'"); } function returnSum(num:Number=0):Number { return (num + 1); }
The loadee is then loaded into a host, or parent, SWF file. To
do so, you must create an instance of the Loader
class, add it to the display list,
and then load the desired content, as the first block of the following
script shows.
In order to communicate with the new SWF file, it must be fully
loaded. Listen for an Event.COMPLETE
event before proceeding, to
make sure the content is loaded.
var loader:Loader = new Loader(); addChild(loader); loader.load(new URLRequest("loadee.swf")); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, callLoadee, ¬ false, 0, true); function callLoadee(evt:Event):void { var loadee:MovieClip = MovieClip(loader.content); loadee.anim.play(); loadee.sayHello(); loadee.showMsg("Hi, from Loader!"); trace("Loader sent 1 to Loadee and got back:", loadee.returnSum(1)); evt.target.removeEventListener(Event.INIT, callLoadee); }
The first line of the callLoadee()
function stores a reference to
the loaded content, rather than the existing Loader
instance. The casting to MovieClip
is required because a Loader
instance can also load images, for which such communication attempts
don’t apply. As such, the compiler must know your property and method
access is legal.
The second line targets the animated movie clip, and tells it to play. Because the clip was initially stopped, if it plays when loaded, then you know this method did its job.
The third line calls a function in the loaded SWF file. The result is the tracing of the string, “Hello, from your loadee!” The fourth line accomplishes a similar task but passes a value into the loadee to vary the outcome of the function. The result is the tracing of the modified string, “Loadee here again. You said, ‘Hi, from Loader!’”
Finally, the last line of callLoadee()
also calls a function and
passes in a value to affect its outcome. However, in this case, a
value is returned to the parent SWF file. Passing in 1 returns a value
of 2, at which point the parent traces, “Loader sent 1 to Loadee and
got back: 2” to the Output panel. This demonstrates round-trip
communication and getting data from a loaded SWF file.
You can add a Loader
instance without content to the display list without generating an
error. However, if you prefer, you can add the instance to the
display list with an event listener after the loader issues an
Event.COMPLETE
event (indicating
the content has been loaded).
17.2 Loading and Displaying an Image or SWF File for loading a SWF.
Having loaded a SWF file that was coded using ActionScript 2.0, you want to communicate between the parent and the loaded SWF file.
Although ActionScript 3.0 can easily load SWF files created using ActionScript 2.0, the two versions of the language can’t coexist in the same file. ActionScript 3.0 was written from the ground up and exists in its own virtual machine—a player within a player, if you will—in Flash Player.
As such, an ActionScript 3.0 SWF file can’t communicate directly
with an ActionScript 2.0 SWF file.
One way you can get around this limitation is to use a LocalConnection
object. Just as you can use
local connections to communicate between multiple SWF files in a browser
window, or between SWF file and projector, you can use the technique to
communicate between ActionScript Virtual Machine 2 (AVM2, used for
ActionScript 3.0) and AVM1 (ActionScript 1.0/2.0). Note that an
ActionScript 3.0 SWF file can load an ActionScript 2.0 SWF file, but the
reverse isn’t possible.
This example shows how to control an animation and trigger a function, while passing data into the AVM1 SWF file. To test all functionality, you need an animated movie clip in the loaded SWF file.
Starting with the host, or parent SWF file, the first step in this process is to load the SWF file. In this case, the loaded SWF file is named as2.swf, and the parent SWF file, although not referenced by filename in the script, is as3.swf.
In this example, the communication is triggered by a button click,
but it’s still a good idea to enable this functionality only after the
loaded content is ready. Otherwise, you may initiate communication
prematurely and encounter an unresponsive button or even errors. Here,
the local connection, button, and event listener are all created only
after the receipt of the init
event,
as you see in the previous recipe. The listener’s contents are explained
after the script.
Hereafter, the host, or parent, SWF file is called “Loader” and the loaded SWF file is called “Loadee.”
var loader:Loader = new Loader(); loader.load(new URLRequest("as2.swf")); addChild(loader); loader.contentLoaderInfo.addEventListener(Event.INIT, onInit, false, 0, true); function onInit(evt:Event):void { var as3as2LC:LocalConnection = new LocalConnection(); var sendBtn:Sprite = new Sprite(); sendBtn.buttonMode = true; sendBtn.graphics.beginFill(0x000099); sendBtn.graphics.drawCircle(0, 0, 15); sendBtn.graphics.endFill(); sendBtn.x = sendBtn.y = 30; addChild(sendBtn); sendBtn.addEventListener(MouseEvent.CLICK, onSendBtn); function onSendBtn(evt:MouseEvent):void { as3as2LC.send("crossVM","playClip"); as3as2LC.send("crossVM","showMsg", "Hello, from AS3!"); } }
The first line of the event listener function initializes the local connection. The second block of the event listener function creates the button used to trigger the communication, and adds it to the display list.
The final block of the event listener function creates the
button event listener. When you click the button, two messages go
through the local connection. This step uses the send()
method of the LocalConnection
instance, as3as2LC
. The first argument value is the
name of the local connection. This connection is like the telephone
number or radio channel over which the connected objects communicate,
increasing security. Any participating parties must send or receive
along this “channel” to successfully communicate. In this case, the
host sends over the “crossVM” connection, and the loaded SWF file must
connect to this same channel to receive instructions and
respond.
The first message sent triggers a function called playClip()
, and the second message triggers
a function called showMsg()
but
also passes along the string argument “Hello, from AS3!”
In the loaded SWF file, the first block of code stops the animated movie clip. The clip is played by instruction issued across the local connection. The variable name used to store the connection in the loadee doesn’t have to be the same as the variable used in the parent SWF file. Instead, the correct connection is established because the loadee connects to the same channel created by the loader, “crossVM”.
The last two blocks of code are simple functions, but one thing
is atypical. Each block is a method of the LocalConnection
instance. You can confine
access from the connection to only those functions you wish to be
executed from a connecting remote SWF file.
anim.stop(); var as3as2LC:LocalConnection = new LocalConnection(); as3as2LC.connect("crossVM"); as3as2LC.playClip = function():Void { anim.play(); }; as3as2LC.showMsg = function(msg:String):Void { if (msg == undefined) { msg = ""; } trace("Loadee here. You said, '" + msg + "'"); };
17.2 Loading and Displaying an Image or SWF File for loading a SWF file.
Use the unload()
method of the
Loader
class, but be sure to clean up
first by stopping timers, closing streams, removing listeners, and
more!
The first part of this recipe is simple. A single method unloads a
loaded image or SWF file, and the optional additional steps of removing
the Loader
instance from the display
list and nullifying the variable reference are also demonstrated.
Hereafter, the host, or parent, SWF file is called “Loader” and the
loaded SWF file is called “Loadee.”
Beginning with the loadee, the first line of the SWF files frame script sets its frame rate to 1 frame per second. This step is only helpful from a tutorial standpoint because it reduces the number of times text will be traced to the Output panel later on during testing.
The next three lines simply draw a maroon circle into the main timeline at point (500, 40) to provide visual feedback to see when the file’s loaded and unloaded.
stage.frameRate = 1; this.graphics.beginFill(0x990000); this.graphics.drawCircle(500, 40, 20); this.graphics.endFill();
Now look at this recipe’s host, or parent, SWF file. The first block of the following script loads and displays a SWF file as discussed in 17.2 Loading and Displaying an Image or SWF File. The second block creates and displays a sprite that serves as a button. The last block adds an event listener that calls its function when the button sprite is clicked.
The first line of the listener function unloads the Loader
instance. The second line removes the
instance from the display list, and the last line nullifies the
instance so the garbage collector can collect it from memory.
var loader:Loader = new Loader(); addChild(loader); loader.load(new URLRequest("loadee.swf")); var unloadBtn:Sprite = new Sprite(); unloadBtn.buttonMode = true; unloadBtn.graphics.beginFill(0x000099); unloadBtn.graphics.drawCircle(0, 0, 15); unloadBtn.graphics.endFill(); unloadBtn.x = unloadBtn.y = 30; addChild(unloadBtn); unloadBtn.addEventListener(MouseEvent.CLICK, onClick, false, 0, true); function onClick(evt:MouseEvent):void { if (loader != null) { loader.unload(); removeChild(loader); loader = null; } }
This process appears to be straightforward and, as described, works with simple content such as loaded images in almost every case. However, when it comes to the average loaded SWF file, many problems arise. Put simply, many features, when used, prevent a SWF file from unloading. This recipe covers some of the most common examples of this problem.
This issue’s widely discussed, however, so if you run into a situation in which your content isn’t unloading, you may be able to find additional information on the web. Grant Skinner, for example, has written several posts about memory management and related topics in his blog. One in particular, covers many of the concerns discussed here, and links to other related topics in his archive: http://www.gskinner.com/blog/archives/2008/04/failure_to_unlo.html.
One of the easiest problems to run into is also one of the easiest problems to miss. If a loaded SWF file contains an enter frame event listener that hasn’t been removed, the SWF file can’t be unloaded. You can see this in action by adding the following to the loadee script, republishing, and testing the loader/loadee relationship again. This code adds an event listener where none existed before, and then traces a simple string to the Output panel every time an enter frame event occurs.
addEventListener(Event.ENTER_FRAME, onLoop, false, 0, true); function onLoop(evt:Event):void { trace("loaded enter frame"); }
When you try to unload the SWF file, although the visual feedback appears to show that the content has unloaded, you notice the trace continues forever. You can’t unload the SWF file.
The workaround is to be certain you remove all enter frame event listeners before trying to unload the applicable SWF file. One approach to this problem is to add a “clean-up” function to your SWF files. Inside this routine, place any maintenance instructions, such as the removal of event listeners, and call the function before unloading. Here’s an example of the function that should be inserted into the loadee.
function cleanUp():void { removeEventListener(Event.ENTER_FRAME, onLoop); }
The following snippet is an example of code you can add to the
parent SWF file, in the existing event listener responsible for
triggering the unloading process. The entire attempt is wrapped in a
try..catch
statement so that any
resulting errors can be suppressed from end-user view. The clean-up
process is invoked in the first two lines of the try
segment.
First a local variable is created to reference the content of
loader
, as described in 17.3 Communicating with an ActionScript 3.0 Loaded SWF. Then the cleanUp()
function you added to the loaded
SWF file is called prior to unloading.
function onClick(evt:MouseEvent):void { try { var loadee:MovieClip = MovieClip(loader.content); loadee.cleanUp(); loader.unload(); removeChild(loader); loader = null; } catch (err:Error) { trace("Error unloading:", err.message); } }
If you test this improved setup, you find that removing the enter frame event listener before unloading lets the SWF file fully unload. Not only do the visual elements disappear from view, but the tracing caused by the loaded SWF file ceases.
You can see the same scenario in action with a running timer. If you substitute the enter frame addition to the original loaded SWF file, loadee.swf, with this timer addition, then you witness the same behavior. This new code triggers a trace every second.
var tmr:Timer = new Timer(1000); tmr.addEventListener(TimerEvent.TIMER, onTimer, false, 0, true); tmr.start(); function onTimer(evt:TimerEvent):void { trace("loaded timer"); }
The solution is similar, but the timer must be stopped prior to removing the listener. The garbage collector can’t ever collect a running timer. The timer reference should also be nullified after the listener’s removed.
function cleanUp():void { tmr.stop(); tmr.removeEventListener(TimerEvent.TIMER, onTimer); tmr = null; }
Since the host SWF file was set to call the cleanUp()
function after its button was
clicked, no further change to the host is required. Upon adding the
preceding code to the loaded SWF file, you find that the tracing
ceases and this version can also be collected from memory.
Unfortunately, the list of steps to remove a SWF file from memory doesn’t stop at enter frame and timer listeners. Several other causes may prevent a SWF file from being unloaded, and here are a few of the most common solutions.
Pause and close all NetStream
instances, remove all
related event listeners, and then nullify the NetStream
instances. Then close all
NetConnection
instances,
remove all related event listeners, and nullify the NetConnection
instances.
Stop all sounds from playing, close all streams, remove
all related listeners, and nullify any Sound
, and SoundChannel
instances.
Close all LocalConnection
instances, remove all
related listeners, and nullify the LocalConnection
instances.
Although stated at the outset of this chapter, this section bears repeating. This list of memory management issues and workarounds is by no means complete. However, this peek into the complex world of unloading content in ActionScript 3.0 may give you a head start when it comes to debugging your own projects.
17.2 Loading and Displaying an Image or SWF File for loading a SWF file.
Create an instance of the Sound
class to load and play the sound, and store the sound in an instance of
the SoundChannel
class for discrete
control.
ActionScript 3.0 introduces a much more granular level of control
over sound playback. Previously, the Sound
class did most of the heavy lifting, but
ActionScript 3.0 introduces a few new classes to both add and distribute
functionality.
You still begin working with an external MP3 file by creating an
instance of the Sound
class, loading
a file, and playing the sound. People also often wait for the sound to
load before playing the file. You do this step in ActionScript 3.0 with
an event listener added to the Sound
instance, as you see in the following script.
However, a new class is introduced into the equation before the
sound is played. The SoundChannel
class creates a discrete sound channel that you can control separately
from other sound channels (up to 32). When the sound is played, it’s
played into the new sound channel the way a single musical instrument is
assigned to a specific channel in an audio mixing console.
var snd:Sound = new Sound(); snd.addEventListener(Event.COMPLETE, onComplete, false, 0, true); snd.load(new URLRequest("music.mp3")); var channel:SoundChannel = new SoundChannel(); function onComplete(evt:Event):void { channel = snd.play(); snd.removeEventListener(Event.COMPLETE, onComplete); }
Finally, after the sound is played into a discrete channel, you no longer need its load complete listener, so it’s removed. You can also manipulate (17.7 Setting the Volume and Pan of a Sound) and visualize the sound (17.8 Visualizing the Amplitude of a Sound) without affecting, or being affected by, other sounds in the SWF file.
17.6 Loading and Playing a Sound through 17.9 Unloading a Sound can be combined into a single script to demonstrate in one file all the sound features discussed in these recipes.
You want to change the volume and/or pan (degree of the sound in each of the left and right stereo channels) of a sound.
Start with the SoundTransform
property of the SoundChannel
class,
and set the values of the volume
and
pan
properties.
Building on 17.6 Loading and Playing a Sound, this
recipe adds volume and pan control. The bolded lines in this recipe’s
code can be added to the onComplete()
function from
17.6 Loading and Playing a Sound to randomly assign a
volume and pan level to the sound when it’s initially played.
The first bold line creates trans
, an instance of another new sound class,
SoundTransform
, by querying the
soundTransform
property of the sound
channel. Continuing the real-world metaphor started in 17.6 Loading and Playing a Sound, using the SoundTransform
class (either directly or
through a sound channel’s soundTransform
property) is similar to
adjusting the volume slider and/or pan knob on a single channel of an
audio mixing console.
These values were formerly in the ActionScript 2.0 Sound
class and have been moved to make
transforming sounds more consistent with other such alterations, like
color transformations, in ActionScript 3.0. As with color
transformations, when altering a sound’s volume or pan setting you
must first edit a transformation instance (either newly created or
retrieved from an existing channel, as in this example), and then
apply (or reapply) the edited transformation to the sound
channel.
function onComplete(evt:Event):void { channel = snd.play(); snd.removeEventListener(Event.COMPLETE, onComplete); var trans:SoundTransform = channel.soundTransform; trans.volume = Math.random(); trans.pan = Math.random() * 2 - 1; channel.soundTransform = trans; }
The second bold line automatically sets the volume
of trans
to a random number between 0 and 1. The
third bold line sets the pan
of
trans
to a random number between −1
and 1.
New to ActionScript 3.0, ranges similar to percentage values (0–100) are now 0 to 1. Values of the volume property range from 0 to 1, and values of the pan property range from −1 to 1. (−1 is far-left, 1 is far-right, and 0 is dead center).
Finally, trans
is reapplied to
the soundTransform
property of
channel, resulting in a random volume between mute and full, and a
random pan between far-left and far-right each time the SWF file
runs.
17.6 Loading and Playing a Sound for loading and playing a sound.
Use the leftPeak
and rightPeak
properties of a sound’s channel to
control the visual appearance of one or more display objects.
This recipe builds on 17.6 Loading and Playing a Sound and 17.7 Setting the Volume and Pan of a Sound, and visualizes sound during playback. You can easily do this if you create traditional peak meters that increase in size with a sound’s amplitude. The first two blocks of this recipe’s script create these peak meters by drawing blue and green sprites (for the left and right stereo channels, respectively), 20 × 100 pixels in size, with a bottom-center registration point.
The last code block contains an event listener that sets the
height of these sprites to the value of an ActionScript sound channel’s
leftPeak
and rightPeak
properties. These properties contain
the left and right stereo amplitudes, respectively, of any sound channel
at query time. These values are always between 0 and 1, so multiplying
them by 100 yields a maximum height of 100 pixels at full amplitude, and
a minimum height of zero during silence.
var leftPeakSP:Sprite = createBar(0x0000FF); leftPeakSP.x = 20; leftPeakSP.y = 120; addChild(leftPeakSP); var rightPeakSP:Sprite = createBar(0x00FF00); rightPeakSP.x = 50; rightPeakSP.y = 120; addChild(rightPeakSP); function createBar(col:uint):Sprite { var sp:Sprite = new Sprite(); var g:Graphics = sp.graphics; g.beginFill(col); g.drawRect(0, 0, 20, -100); g.endFill(); return sp; } addEventListener(Event.ENTER_FRAME, onLoop, false, 0, true); function onLoop(evt:Event):void { leftPeakSP.height = channel.leftPeak * 100; rightPeakSP.height = channel.rightPeak * 100; }
17.6 Loading and Playing a Sound for loading and playing a sound and 17.7 Setting the Volume and Pan of a Sound for setting the volume and pan of a sound.
To minimize impact on performance and available memory, you want to unload a sound after it has served its purpose.
Building on 17.6 Loading and Playing a Sound through 17.8 Visualizing the Amplitude of a Sound, this recipe adds the ability to unload a sound. The first block of code creates a clickable sprite used to unload a sound, and the second block adds an event listener to the sprite that responds when the user clicks on the sprite.
The first step in the listener function is to stop sound playback
using the stop()
method of the
SoundChannel
instance. Next, the
sound stream is closed. All sound files, whether they’re streaming from
a server or from a local file, have a stream. This stream essentially
refers to the background downloading of file content while the sound is
playing.
Closing the stream means you can halt the download process even if
data remains to be downloaded. This process is attempted in a try..catch
statement because the stream may be
fully loaded by the time this instruction is issued. A try..catch
statement tries the requested
commands, and catches any errors thrown so they can be suppressed from
end-user view. For debugging purposes, the statement will trace the
error to the Output panel in authoring mode only.
After the channel playback is stopped and the sound stream is closed, you can choose to nullify either or both variables, and remove the enter frame listener (created in the previous recipe) to allow everything to be collected. The remaining four lines are explained after the script passage.
var unloadBtn:Sprite = new Sprite(); unloadBtn.buttonMode = true; unloadBtn.graphics.beginFill(0x990000); unloadBtn.graphics.drawRect(0, 0, 20, 20); unloadBtn.graphics.endFill(); unloadBtn.x = 520; unloadBtn.y = 10; addChild(unloadBtn); unloadBtn.addEventListener(MouseEvent.CLICK, onUnloadBtn, false, 0, true); function onUnloadBtn(evt:MouseEvent):void { channel.stop(); try { snd.close(); } catch (err:IOError) { trace("Close stream error:", err.message); } var trans:SoundTransform = channel.soundTransform; trans.volume = 0; channel.soundTransform = trans; SoundMixer.stopAll(); channel = null; snd = null; removeEventListener(Event.ENTER_FRAME, onLoop); }
Depending on how you write your code, you may sometimes find that a sound continues to play even after its stream has been closed, because the portion of the external file that’s already been streamed, up to the point of closing the stream, is still eligible for playback.
Stopping playback in the channel may be enough to prevent the
sound from continuing to play. If not, you may want to try one or two
additional steps. You may also want to mute the sound channel
immediately after stopping it, so that if content continues to play it
won’t be heard, and/or stop all sounds playing through the global
SoundMixer
class—the last of the new
sound classes discussed in this chapter. The SoundMixer
class is analogous to the master
mixer on an audio mixing console. All discrete sound channels flow
through the SoundMixer
and,
therefore, can be stopped all at once.
17.6 Loading and Playing a Sound for loading and playing a sound and 17.7 Setting the Volume and Pan of a Sound for setting the volume and pan of a sound, and 17.8 Visualizing the Amplitude of a Sound for visualizing the amplitude of a sound.
Create NetConnection
and
NetStream
instances, as well as a
Video object for display, and then play the NetStream
instance.
You have a few ways to play videos in Flash, the simplest of which is to use one of the provided components designed for this purpose. However, in some situations you don’t want to commit to the memory/file size to use the components, or you may even be dissatisfied with a component’s functionality and want to create your own player.
In these cases, the functionality of a player you create can range from simple to robust, depending on how much time you want to put into coding its features. This recipe covers the bare essentials, adding a few features in descriptive segments. When you’ve completed this recipe, be sure to look at 17.11 Unloading a Video for information about unloading the video content.
The first step in creating your own ActionScript video player is
to create an instance of the Video
class, and add it to the display list, to provide you with a display
object on which to watch your video. You see this in the first two lines
of the following script segment. Describing a real-world parallel, this
is like getting a television.
The second step is to instantiate a NetConnection
object, as seen in the second
two lines of the script. This part lets you connect to a streaming video
server, but passing a value of null
to the connect()
method also lets you
work with progressive download video sources. Using a NetConnection
is loosely analogous to
selecting which video on demand (VOD) service you wish to
watch.
The next step is to create a NetStream
instance, specifying the NetConnection
instance as its argument, and
attaching the stream to the Video instance, as seen in lines 5 and 6 of
the following script. This step is somewhat akin to selecting which
video category offered by the previously selected VOD service to watch
(comedy, drama, and so on).
Finally, playing the video of your choice is like choosing and playing the video from the previously selected genre that you want to view.
var vid:Video = new Video(); addChild(vid); var vidConnection:NetConnection = new NetConnection(); vidConnection.connect(null); var vidStream:NetStream = new NetStream(vidConnection); vid.attachNetStream(vidStream); vidStream.play("<your_video_here>.flv");
Although the previous bare-bones example works, you may also want to handle automatically triggered events associated with the video. For instance, you may receive notifications or errors associated with metadata embedded or injected into the video, or even data from cue points added when encoding the video.
The following script segment creates an object designed to trap
this information to prevent errors from appearing at runtime. This
segment creates an object with functions attributed to each of the
onMetaData
and onCuePoint
event handlers. This object is then
passed to the client
property of the
NetStream
instance.
As a result, the cited functions respond to incoming metadata and/or cue points. In this example, the duration metadata entry is traced to the Output panel, as well as any cue point text data that may be encoded into the video.
var infoClient:Object = new Object(); infoClient.onMetaData = onMetaData; infoClient.onCuePoint = onCuePoint; vidStream.client = infoClient; function onMetaData(info:Object):void { trace("duration:", info.duration); } function onCuePoint(info:Object):void { trace("cuepoint:", info.parameters.text); }
In addition to listening for metadata and cue point information, you may also want to react to any asynchronous errors that may occur when attempting to play a video. This error usually occurs when a server calls a method that the client hasn’t defined, so it’s handy to have active when scripting a no-frills video player.
The following segment adds event listeners for asynchronous event
errors to both the NetConnection
and
NetStream
objects.
vidConnection.addEventListener(AsyncErrorEvent.ASYNC_ERROR, ¬ onAsyncError, false, 0, true); vidStream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, ¬ onAsyncError, false, 0, true); function onAsyncError(evt:AsyncErrorEvent):void { trace(evt.text); }
Finally, if you want your video to do more than just play, then you want to script an accompanying controller. This recipe provides one example of controller functionality by creating a button to toggle the pause state of the video.
var pauseBtn:Sprite = new Sprite(); pauseBtn.buttonMode = true; pauseBtn.graphics.beginFill(0x000099); pauseBtn.graphics.drawRect(0, 0, 20, 20); pauseBtn.graphics.endFill(); pauseBtn.y = vid.y + vid.height + 5; addChild(pauseBtn); pauseBtn.addEventListener(MouseEvent.CLICK, onPauseToggle, false, ¬ 0, true); function onPauseToggle(evt:MouseEvent):void { vidStream.togglePause(); }
17.11 Unloading a Video for information about unloading a video.
Pause and close NetStream
instances. Close NetConnection
instances. Remove all listeners and nullify all stream and connection
references.
Building on 17.10 Loading and Playing a Video, this recipe provides a mechanism for unloading the video. The first block of code creates a button to click for unloading, and adds it to the display list. The second block of code creates the button’s event listener.
To unload a video, you must first pause and then close the
NetStream
instance (vidStream
). You must then remove any event
listeners added to this instance (such as the asynchronous error
listener used in this example) and nullify the instance to let it be
collected. You must then close the NetConnection
instance (vidConnection
), remove any listeners attached
thereto, and nullify that instance to allow it, too, to be
collected.
var unloadBtn:Sprite = new Sprite(); unloadBtn.buttonMode = true; unloadBtn.graphics.beginFill(0x990000); unloadBtn.graphics.drawRect(0, 0, 20, 20); unloadBtn.graphics.endFill(); unloadBtn.x = vid.width - 20; unloadBtn.y = vid.y + vid.height + 5; addChild(unloadBtn); unloadBtn.addEventListener(MouseEvent.CLICK, onUnloadBtn, false, 0, true); function onUnloadBtn(evt:MouseEvent):void { vidStream.pause(); vidStream.close(); vidStream.removeEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError); vidStream = null; vidConnection.close(); vidConnection.removeEventListener(AsyncErrorEvent.ASYNC_ERROR, ¬ onAsyncError); vidConnection = null; // removeChild(vid); vid = null; }
Only after all of these steps can the garbage collector remove the video. Finally, if you’re also finished with the video object used to display the video, then you can remove it from the display list, remove any relevant event listeners, and nullify that instance, as well.
17.10 Loading and Playing a Video for information regarding loading and playing a video.