# Software Internals

## A Tour of &app;

The top level of &app; has several libraries, libgnashbase, libgnashcore, libgnashmedia, libgnashamf and libgnashrender. There are several utility programs included for debug parsing and processing of SWF movie files, and other useful utilities for examining local Shared Objects and sniffing LocalConnections.

### The Libraries

#### libgnashbase

Libgnashbase contains support classes used by the rest of the code.This library has no dependencies on any of the other &app; libraries.

&app; makes heavy use of smart pointers, so memory allocations are freed up automatically by the interpreter. Both STL and Boost smart pointers are used.

#### libgnashcore

Libgnashcore is the guts of the interpreter itself. This is where the main code for the interpreter lives. Included in libcore are the two support libraries for the parser and the core of the virtual machine.

#### libgnashmedia

Libgnashmedia handles &app;'s decoding of video and audio, including both streamed and embedded media. Besides the standard SWF formats FLV, MPEG4, Nellymoser, ADPCM, MP3 and RAW, &app; can decode other formats supports by Gstreamer or FFmpeg, including the free OGG container and free codecs.

#### libgnashsound

This library handles sound output and manages sound objects.

#### libgnashamf

AMF is the data format used internally by SWF files. This is &app;'s support library to handle AMF data. This is used by the ActionScript classes SharedObject and LocalConnection. This is also used by the NetStream class when using thre RTMP streaming network protocol.

#### libgnashbackend

Libgnashbackend is a library containing the rendering code that glues this display to the &app;. Supported rendering backends are OpenGL, Cairo, and AGG.

#### libgnashplugin

Libgnashplugin is the Mozilla/Firefox plugin.

#### libklashpart

Libklashpart is the Konqueror plugin.

### The Applications

There are currently a few standalone programs in &app;, which serve either to assist with &app; development or to play SWF movies.

#### The Standalone Player

This is the standalone OpenGL backend used to play movies. There are several command line options and keyboard control keys used by &app;.

#### Gprocessor

Gprocessor is used to print out the actions (using the -va option) or the parsing (using the -vp option) of a SWF movie. It is also used to produce the .gsc files that &app; uses to cache data, thereby speeding up the loading of files.

#### SOLdumper

SOLDumper is a utility program used to find and dump the content of Local Shared Objects, also called "Flash Cookies" by some.

#### Dumpshm

Dumpshm is a program used to find and dump the contents of the LocalConnection shared memory segment.

### The Plugin

The plugin is designed to work within Mozilla or Firefox, although there is Konqueror support as well. The plugin uses the Mozilla plugin API (NPAPI) to be cross platform, and is portable, as well as being well integrated into Mozilla based browsers.

#### Current Implementation

The plugin works in a fashion similar to MozPlugger: the standalone player is used instead of using a thread. This gets around the issue of having to maintain a separate player to support the plugin. It also gets around the other issues that &app; itself is not thread safe at this time.

#### GUI Support

Any plugin that wants to display in a browser window needs to be tied into the windowing system of the platform being used. On GNU/Linux systems, Firefox is a GTK2+ application. There is also KDE support through the use of the Klash plugin.

&app; can use either several different GUI toolkits to create the window, and to handle events for the standalone player.

The SDL version is more limited, but runs on all platforms, including win32. It has no support for event handling, which means mouse clicks, keyboard presses, and window resizing doesn't work. I personally find the default event handler slow and unresponsive. &app; has support to use fast events, (currently not enabled) which is an SDL hack using a background thread to pump events into the SDL event queue at a much higher rate.

There are a variety of development libraries that build a GUI widget system on top of SDL and OpenGL. The use of these to add menus and dialog boxes to the SDL version is being considered.

The GTK support is currently the most functional, and the best integrated into Firefox. The performance of this version is better than the SDL version because of the more efficient event handling within GTK. For the best end user experience, use the GTK enabled version.

GTK also allows &app; to have menus and dialog boxes. Currently this is only being utilized in a limited fashion for now. There is a right mouse button menu that allows the user to control the movie being player the same way the existing keyboard commands do.

#### Klash

Klash is MozPlugger type support for KDE's Konqueror web browser. Klash makes &app; a kpart, so it's integrated into KDE better than when using MozPlugger. Klash uses the standalone player, utilizing &app;'s "-x" window plugin command line option.

By default, Klash is not built. To enable building Klash, use the --enable-klash option when configuring. Other than installing, there is nothing else that needs to be done to install Klash.

&logging;

## Sound handling in &app;

Sound in &app; is handled by libgnashsound. This library takes care of interfacing with a sound handler.

There are two different settings related to sound support: pluginsound and sound. This was done in order to allow the plugin to be independently configured, for instance to block sound from advertisements.

### Sound types

Sounds can be divided into two groups: event-sounds and soundstreams. Event-sounds are contained in a single SWF frame, but the playtime can span multiple frames. Soundstreams can be (and normally are) divided between the SWF frames the soundstreams spans. This means that if a gotoframe-action jumps to a frame which contains data for a soundstream, playback of the stream can be picked up from there.

### Sound parsing

When &app; parses a SWF-file, it creates a sound handler if possible and hands over the sounds to it. Since the event-sounds are contained in one frame, the entire event-sound is retrieved at once, while a soundstream maybe not be completely retrieved before the entire SWF-file has been parsed. But since the entire soundstream doesn't need to be present when playback starts, it is not necessary to wait.

### Sound playback

When a sound is about to be played &app; calls the sound handler, which then starts to play the sound and return. All the playing is done by threads (in both SDL and Gstreamer), so once started the audio and graphics are not sync'ed with each other, which means that we have to trust both the graphic backend and the audio backend to play at correct speed.

### The SDL sound backend

The current SDL sound backend has replaced the original sound handler, based on SDL_mixer, which by design had some limitations, making it difficult to implement needed features such as support for soundstreams. The SDL sound backend supports both event-sounds and soundstreams, using &app;'s internal ADPCM, and optionally MP3 support, using FFMPEG. When it receives sound data it is stored without being decoded, unless it is ADPCM, which is decoded in the parser. When playing, backend relies on a function callback for retrieving output sound, which is decoded and re-sampled if needed, and all sound output is mixed together. The current SDL sound backend was made since &app; needed a working sound backend as soon as possible, and since the gstreamer backend at the time suffered from bugs and/or lack of features in gstreamer. The result was the most complete and best sound handler so far. The advantages of the SDL sound handler is speed, and ease of use, while its only real disadvantage is that it has to be compiled with MP3 support, which some Linux distributions will probably not like...

### The Gstreamer backend

The Gstreamer backend, though not complete, supports both soundstreams and event-sounds. When receiving sound data it stores it compressed, unless if it's ADPCM event-sounds, which it decodes by the parser. When the playback starts, the backend sets up a Gstreamer bin containing a decoder (and other things needed) and places it in a Gstreamer pipeline, which plays the audio. All the sound data is not passed at once, but in small chunks, and via callbacks the pipeline gets fed. The advantages of the Gstreamer backend is that it supports both kinds of sound, it avoids all the legal MP3-stuff, and it should be relatively easy to add VORBIS support. The drawbacks are that it has longer "reply delay" when starting the playback of a sound, and it suffers under some bugs in Gstreamer that are yet to be fixed.

### Detailed description of the Gstreamer backend

Gstreamer uses pipelines, bins and elements. Pipelines are the main bin, where all other bins or elements are places. Visually the audio pipeline in &app; looks like this:

    	 ___
    	|Bin|_
    	|___| \
    	 ___   \ _____       ____________
    	|Bin|___|Adder|_____|Audio output|
    	|___|   |_____|     |____________|
    	 ___   /
    	|Bin|_/
    	|___|

There is one bin for each sound which is being played. If a sound is played more the once at the same time, multiple bins will be made. The bins contains:

    	|source|---|capsfilter|---|decoder|---|aconverter|---|aresampler|---|volume|

In the source element we place parts of the undecoded sound data, and when playing the pipeline will pull the data from the element. Via callbacks it is refilled if needed. In the capsfilter the data is labeled with the format of the data. The decoder (surprise!) decodes the data. The audioconverter converts the now raw sound data into a format accepted by the adder, all input to the adder must in the same format. The audio re-sampler re-samples the raw sound data into a sample accepted by the adder, all input to the adder must in the same sample rate. The volume element makes it possible to control the volume of each sound.

When a sound is done being played it emits a End-Of-Stream-signal (EOS), which is caught by an event-handler-callback, which then makes sure that the bin in question is removed from the pipeline. When a sound is told by &app; to stop playback before it has ended playback, we do something (not yet finally implemented), which makes the bin emit an EOS, and the event-handler-callback will remove the sound from the pipeline. Unfortunately Gstreamer currently has a bug which causes the entire pipeline to stop playing when unlinking an element from the pipeline; so far no fix is known.

Gstreamer also contains a bug concerning linking multiple elements to the adder in rapid succession, which causes to adder to "die" and stop the playback.

## Testing

Instructions on running tests can be found in the section on building &app;.

### Testing Tools

Currently &app; uses three other tools to help with testing. Two of these are free compilers for the SWF format. This lets us write simple test cases for &app; to test specific features, and to see how the features operate.

The primary compiler used at this time is Ming. Since release 0.3, Ming includes a command-line compiler, makeswf. This allows test case development to be done entirely with free tools.

The other tools are optional. DejaGnu is used to run multiple test cases in an automated manner. DejaGnu is used by many other GNU projects like GCC and Samba.

### Test Cases

ActionScript test cases are located under testsuite/actionscript.all/; these are organized in one file for the ActionScript class. Other Ming-generated tests are under testsuite/ming-misc.all/; these are typically used to test specific tag types. Full movies are located in testsuite/movies.all/ and sample movies are found in testsuite/samples/. Other directories in testsuite/ are (or shall be) used for other kind of tests.

### Writing ActionScript Tests

Writing ActionScript tests is very simple. The makeswf compiler makes use of the C preprocessor, thus allowing the inclusion of definitions for macros and external files. We use these feature to provide common utilities for test units.

Each test unit sets an rcsid variable, includes the check.as file and performs some checks using the provided macros. Here is an example:

    	  // This variable will be used by check.as
    	  // to show testcase info as part of the test runs.
    	  rcsid="Name and version of this testcase, usually the RCS id";

    	  #include "check.as"

    	  // Test object creation
    	  check(new Object() instanceOf Object);

    	  // Test parseInt
    	  check(isNaN(parseInt('none')));

    	  // Test assignment
    	  var a = 1;
    	  check_equals(a, 1);

    	  // .. your tests here ...

The check(expr) macro will trace PASSED or FAILED together with the expression being evaluated and the line number of the check. This is the format expected by DejaGnu.

The check_equals(obtained, expected) macro uses equality operator == to check for equality. When possible, use of the check_equals() macro is preferred over check() because it shows what the actual result was in case of a failure.

Additionally, the check.as file provides a transparent way to send results to a TextField rather then using trace. This is very useful when you use a SWF player without tracing support.

Test units are built by running make TestName-v#.swf. This will use TestName.as as source and the value of # as target version. Allowed target version are from 5 to 8 (inclusive).

Note that if you get a syntax error from the compiler, the line number will refer to the pre-processed file. This file is called TestName.as.pp or TestName-v#.swf.frame#.pp (depending on Ming version) and it's not thrown away by makeswf to make debugging easier.

Sometimes an expression is only supported by a specific SWF version, or it's evaluated differently by different SWF versions. For this purpose the framework provides an OUTPUT_VERSION macro that you can use to switch code based on output version. For example:

    	  #if OUTPUT_VERSION >= 7
    	  check(_root.getSWFVersion == OUTPUT_VERSION);
    	  #endif

### Writing Ming-based self-contained SWF tests

Ming-based test cases are located in testsuite/misc-ming.all and contain a test generator and a test runner. The test generator (usually a C program) is used to produce the SWF file, while the test runner (a C++ program) will run it using a MovieTester class. Note that only the test generator needs Ming, not the test runner, so if Ming isn't installed on the user's host, the test cases can still be run as long as SWF has been distributed.

Producing tests using Ming has the advantage that you can easily see and modify the full source code for the SWF movie, and you can use some facilities provided by the &app; testing framework to easily run tests.

For generic Ming API documentation, see http://www.libming.org.

#### Using Ming-based test generators facilities

Ming-based test generator facilities, which might be moved into a loadable SWF in the future, can be currently used by your test generator by including the ming_utils.h file and calling the appropriate functions.

The most useful facility provided for Ming-based SWF test generators is a Dejagnu-like TestState ActionScript class. In order to use this facility you must call 'add_dejagnu_functions()' right after Movie creation. The function takes an SWFMovie object and some parameters specifying depth and location of the "visual" trace textfield; it instantiates a global 'TestState' ActionScript object to keep track of test's state.

You will not need to directly invoke the TestState object created by the 'add_dejagnu_functions()' routine, rather you will be using C macros hiding its complexity:

    	check(SWFMovie mo, const char* expr)

    		Evaluate an ActionScript expression.

    	xcheck(SWFMovie mo, const char* expr)

    		Evaluate an ActionScript expression.
    		A failure is expected
    		(for cases where the call exposes a known bug).

    	check_equals(SWFMovie mo, const char* obtained, const char* expected)

    		Evaluate an ActionScript expression against an expected output.

    	xcheck_equals(SWFMovie mo, const char* obtained, const char* expected)

    		Evaluate an ActionScript expression against an expected output.
    		A failure is expected (for cases where the call exposes a known bug).

    	print_tests_summary(SWFMovie mo)

                    This will print a summary of tests run, and should be
    		called as the last step in your SWF generator.

Test cases generated using Ming and the provided facilities will be self-contained, which means they can be used as tests by simply running them with whatever Player you might have. Any 'check' or 'check_equals' result will be both traced and printed in a textfield. You can use 'gprocessor -v' to have &app; use them as tests.

See section Writing Test Runners for information about writing SWF test runners.

### Writing self-contained SWF tests with other compilers

If you want/need to use a different compiler for your test cases (there's plenty of open source tools for generating SWF out there), you can still make use of a loadable SWF utility provided as part of the &app; testsuite to let your test consistent with the rest of the suite.

The loadable module is called Dejagnu.swf and is built during make check under testsuite/misc-ming.all. In order to use it you will need to load it into your SWF. We currently load it with an IMPORT tag for our ActionScript based test cases, but you can probably also use loadMovie or whatever works in the target SWF you're generating. Just make sure that the module is initialized before using it. You can check this by inspecting the dejagnu_module_initialized variable, which will be set to 'true' when all initialization actions contained in the Dejagnu.swf file are executed.

Once the module is loaded you will be able to invoke the following functions, all registered against the _root sprite (effects of _lockroot untested):

    	  check(expression, [message]);

    	  Evaluate the expression.
    	  Trace result (PASSED: expression / FAILED: expression).
    	  If fails, *visually* trace the failure.
    	  If second argument is given, it will be used instead of
    	  'expression' for printing results.

    	  check_equals(obtained, expected)

    	  Evaluate an expression against an expected output.
    	  Trace result (PASSED: obtained == expected / FAILED: expected X, obtained Y)
    	  If fails, *visually* trace the failure.

    	  xcheck(expression, [message]);

    	  Evaluate the expression.
    	  Trace result (XPASSED: expression / XFAILED: expression).
    	  If fails, *visually* trace the failure.
    	  If second argument is given, it will be used instead of
    	  'expression' for printing results.

    	  xcheck_equals(obtained, expected)

    	  Evaluate an expression against an expected output.
    	  Trace result (XPASSED: obtained == expected / XFAILED: expected X, obtained Y)
    	  If fails, *visually* trace the failure.

    	  note(string)

    	  Print string, both as debugging and *visual* trace.

    	  totals()

    	  Print a summary of tests run, both as debugging and *visual* traces.

Visual traces are lines of text pushed to a textarea defined by the Dejagnu.swf module. The textarea is initially placed at 0, 50 and is 600x800 in size. You can resize/move the clip after loading it. Also, you can completely make the clip invisible if that bothers you. The important thing is the debugging trace (call to the trace function). The latter will be used by the testing framework.

See section Writing Test Runners for information about writing a test runners for your self-contained tests.

### Writing Test Runners

Test runners are executables that run one or more tests, writing results in Dejagnu form to standard output.

The Dejagnu form uses a standard set of labels when printing test results. These are:

Label | Meaning
--- | ---
PASSED | The test succeeded.
FAILED | The test failed.
XPASSED | The test succeeded, but was expected to fail.
XFAILED | The test failed, and was expected to fail.
UNRESOLVED | The results of the test could not be automatically parsed.
UNTESTED | This test case is not complete.
UNSUPPORTED | The test case relies on a conditional feature which is not present in your environment.

The following labels may also appear:

Label | Meaning
--- | ---
ERROR | There was a serious error in running the test.
WARNING | There may have been a problem with running the test.
NOTE | There was some additional information given about the test.

#### Using the generic test runner for self-contained SWF tests

The simplest test runner is one that simply invokes &app; in verbose mode against a self-contained SWF test movie. Self-contained SWF test movies are the ones that print the PASSED/FAILED etc. lines using ActionScript (traces). By invoking &app; in verbose mode this movie will behave as a compliant "Test Runner".

A generator for simple test runners can be found in testsuite/generic-testrunner.sh. The script can be invoked by passing it $(top_builddir) as the first argument and the name of the SWF file (without the path) as the second argument. This will create a specific runner for your test in the current build directory. A simple Makefile.am rule for doing this follows:

    	    MyTest-Runner: $(srcdir)/../generic-testrunner.sh MyTest.swf
    	    sh $(srcdir)/../generic-testrunner.sh $(top_builddir) MyTest.swf > $@
    	    chmod +x $@

By default, the generated test runner will play the movie up to the last frame. If you want the movie to be played more then once (maybe because you're exactly testing loop features) you can use the -r switch to the generic-testrunner.sh call. The following will create a runner playing the movie twice:

    	    MyTest-Runner: $(srcdir)/../generic-testrunner.sh MyTest.swf
    	    sh $(srcdir)/../generic-testrunner.sh -r2 $(top_builddir) MyTest.swf > $@
    	    chmod +x $@

In case your test movie stops before the last frame, or you want to control the exact number of times to call the frame advancement routine, you can use the -f switch to control that.

    	    MyTest-Runner: $(srcdir)/../generic-testrunner.sh MyTest.swf
    	    sh $(srcdir)/../generic-testrunner.sh -f10 $(top_builddir) MyTest.swf > $@
    	    chmod +x $@

When both -f and -r are given, the first exit condition reached will take effect.

#### Writing Movie testers

There are some parts of &app; that can NOT be tested by only using ActionScript tests. Examples include: frame advancements, actual actions execution, gui events and so on.

In this case you might want to use the MovieTester class to implement a C++ test runner. Be aware that you can mix tests in the MovieTester-based class with self-contained tests in the SWF file as long as you activate verbosity for the debug logfile. This is done, for example, for the DefineEditTextVariableNameTest.swf file. The corresponding test runner (DefineEditTextVariableNameTest-Runner) is a C++ runner based on MovieTester class. If you run the runner you see two kinds of test results: the ones coming from the ActionScript engine, and the ones coming from the test runner. You can distinguish between the two because the former contains an additional timestamp and the latter does not. Also, you'll see two final summaries for the two test sets. The 'make check' rule, which uses the testsuite/simple.exp output parser as its work-horse, will count test results from both test sets.

Movie testers are executables which load an SWF, generate events (both user or system) on it, and check its state using a standard interface.

To help this process a MovieTester class is defined in the testsuite/MovieTester.{h,cpp} files; see Doxygen documentation for more information.

Note that you do NOT need access to the SWF source code in order to implement a Movie tester for it. Some knowledge about the expected behavior suffices.