root/testsuite/misc-ming.all/NetStream-SquareTest.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. main

/* 
 *   Copyright (C) 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */ 

/*
 * Plays an external FLV video
 * Should be used with the MovieTester to test if the video decoder works.
 *
 * Expected behaviour:
 *
 *   Shows a 54x54 pixels red square moving from left to right over
 *   a 152x120 yellow background. The whole thing rotated by 45 degrees clockwise.
 */

#include <stdlib.h>
#include <stdio.h>
#include <ming.h>

#include "ming_utils.h"

#define OUTPUT_VERSION 7
#define OUTPUT_FILENAME "NetStream-SquareTest.swf"

const char* mediadir=".";
char filename[256];
char filename2[256];
char filename3[256];


int
main(int argc, char** argv)
{
  SWFMovie mo;
  SWFMovieClip dejagnuclip;
  SWFVideoStream stream;
  SWFDisplayItem item;
  SWFAction a;
  SWFAction b;
  char buffer_a[2024];
  char buffer_b[2024];
  char buffer_c[2024];

  // This is different from the real video width to make sure that
  // Video.width returns the actual width (128).
  int video_width = 130;
  int video_height = 96;

  if ( argc>1 ) mediadir=argv[1];
  else
  {
    fprintf(stderr, "Usage: %s <mediadir>\n", argv[0]);
    return 1;
  }
        
  sprintf(filename, "%s/square.flv", mediadir);
  sprintf(filename2, "%s/square.ogg", mediadir);
  sprintf(filename3, "%s/audio_timewarp.flv", mediadir);

  // Some online examples...
  //
  // 'ffdec_vp6f' 
  //sprintf(filename, "http://www.helpexamples.com/flash/video/water.flv");
  //sprintf(filename, "http://www.helpexamples.com/flash/video/clouds.flv");
  //sprintf(filename, "http://www.helpexamples.com/flash/video/typing_long.flv");
  //
  // This ones work
  //sprintf(filename, "http://www.helpexamples.com/flash/video/caption_video.flv");
  //sprintf(filename, "http://www.helpexamples.com/flash/video/sheep.flv");
  //
  //
  
  sprintf(buffer_a,
        "note(System.capabilities.version);"
        "note('SWF version %d');"
        "nc=new NetConnection();"
        "check(!nc.isConnected, 'newly created NetConnection is not connected');"
        "nc.connect(null);"
        "check(nc.isConnected, 'NetConnection is connected after .connect(null)');"
        "check(!NetStream.prototype.hasOwnProperty('currentFPS'));" // version 7 here
        "xcheck(!NetStream.prototype.hasOwnProperty('currentFps'));"
        "stream = new NetStream();"
        "check_equals ( typeof(stream.bytesTotal), 'undefined' );" // not connected..
        "stream.play('fake');" // just test not to segfault..
        "stream = new NetStream(nc);"
        "check_equals ( typeof(stream.bytesTotal), 'number' );"
        "stream.bytesTotal = 'string';"
        "check_equals ( typeof(stream.bytesTotal), 'number' );"
        "stream2 = new NetStream(nc);"
        "stream3 = new NetStream(nc);"
        , OUTPUT_VERSION);

  sprintf(buffer_b,
        // bytesTotal (read-only)
        "MovieClip.prototype.addBytesLoadedProgress = function(v, s) {"
        "       var nam = 'blprogress_'+v;"
        "       var dep = this.getNextHighestDepth();"
        "       var pc = this.createEmptyMovieClip(nam, dep);"
        "       pc.stream = s;"
        "       pc.video = v;"
        "       var pcp = pc.createEmptyMovieClip('bar', pc.getNextHighestDepth());"
        "       var x = v._x;"
        "       var y = v._y+v._height+10;"
        "       var w = v._width;"
        "       var h = 10;"
        "       with(pcp) {"
        "               _x = x;"
        "               _y = y;"
        "               moveTo(0,0);"
        "               beginFill(0xFF0000,50);"
        "               lineTo(0, h);"
        "               lineTo(w, h);"
        "               lineTo(w, 0);"
        "               lineTo(0, 0);"
        "               endFill();"
        "       };"
        "       pc.onEnterFrame = function() {"
        "               pcp._xscale = 100*(this.stream.bytesLoaded/this.stream.bytesTotal);"
        "       };"
        "};");

    sprintf(buffer_c,
        "MovieClip.prototype.addBufferLoadedProgress = function(v, s) {"
        "       var nam = 'blprogress_'+v;"
        "       var dep = this.getNextHighestDepth();"
        "       var pc = this.createEmptyMovieClip(nam, dep);"
        "       pc.stream = s;"
        "       pc.video = v;"
        "       var pcp = pc.createEmptyMovieClip('bar', pc.getNextHighestDepth());"
        "       var x = v._x;"
        "       var y = v._y+v._height+22;"
        "       var w = v._width;"
        "       var h = 10;"
        "       with(pcp) {"
        "               _x = x;"
        "               _y = y;"
        "               moveTo(0,0);"
        "               beginFill(0x00FF00,50);"
        "               lineTo(0, h);"
        "               lineTo(w, h);"
        "               lineTo(w, 0);"
        "               lineTo(0, 0);"
        "               endFill();"
        "       };"
        "       pc.onEnterFrame = function() {"
        //"             _root.note(this.video._x+': bufferloaded: '+this.stream.bufferLength+'/'+this.stream.bufferTime);"
        "               pcp._xscale = 100*(this.stream.bufferLength/this.stream.bufferTime);"
        //              when bufferLength > bufferTime (common):
        "               if ( pcp._xscale > 100 ) {"
        "                       pcp._xscale = 100;"
        "                       pcp._alpha = 100;"
        "               } else {"
        "                       pcp._alpha = 50;"
        "               }"
        "       };"
        "};"
        "stream.play('%s');"
        "stream2.play('%s');"
        "stream3.play('%s');"
        "stream.pause(true);" 
        "stream.paused=true;"
        "_root.metadataNotified=0;"
        "_root.startNotified=0;"
        "_root.stopNotified=0;"
        "stop();",
        filename, filename2, filename3);

  Ming_init();
  Ming_useSWFVersion (OUTPUT_VERSION);


  mo = newSWFMovie();
  SWFMovie_setDimension(mo, 800, 600);

  // We also want to test that 1FPS of SWF rate doesn't influence
  // rate of video playback, but for now it's more useful to actually
  // have something rendered, so check_pixel can eventually do something,
  // so we run fast...
  SWFMovie_setRate(mo, 32);

  dejagnuclip = get_dejagnu_clip((SWFBlock)get_default_font(mediadir), 10, 0, 0, 800, 600);
  item = SWFMovie_add(mo, (SWFBlock)dejagnuclip);
  SWFDisplayItem_moveTo(item, 0, 250);
  SWFMovie_nextFrame(mo); 
 
  stream = newSWFVideoStream();
  SWFVideoStream_setDimension(stream, video_width, video_height);
  item = SWFMovie_add(mo, (SWFBlock)stream);
  /* SWFDisplayItem_moveTo(item, 0, 200); */
  SWFDisplayItem_setName(item, "video");

  stream = newSWFVideoStream();
  SWFVideoStream_setDimension(stream, video_width, video_height);
  item = SWFMovie_add(mo, (SWFBlock)stream);
  SWFDisplayItem_moveTo(item, 400, 0);
  SWFDisplayItem_setName(item, "video2");

  stream = newSWFVideoStream();
  SWFVideoStream_setDimension(stream, video_width, video_height);
  item = SWFMovie_add(mo, (SWFBlock)stream);
  SWFDisplayItem_setName(item, "video3");

  a = newSWFAction(buffer_a);
  if(a == NULL) return -1;
  SWFMovie_add(mo, (SWFBlock)a);

  /* Check that properties exist here
     See actionscript.all/NetStream.as for checks before
     NetStream is attached to a connection */

  check(mo, "NetStream.prototype.hasOwnProperty('currentFps')"); // currentFps
  check(mo, "NetStream.prototype.hasOwnProperty('bufferLength')"); // check bufferLength
  check(mo, "NetStream.prototype.hasOwnProperty('bufferTime')"); // check bufferTime
  check(mo, "NetStream.prototype.hasOwnProperty('liveDelay')"); // check liveDelay
  check(mo, "NetStream.prototype.hasOwnProperty('time')"); // check time
  check(mo, "NetStream.prototype.hasOwnProperty('bytesLoaded')"); // check bytesLoaded
  check(mo, "NetStream.prototype.hasOwnProperty('bytesTotal')"); // check bytesTotal

  //-----------------------------------------
  // Dynamic volume tests using Sound object
  //-----------------------------------------

  // create a movieclip to use as sound controller
  add_actions(mo, "createEmptyMovieClip('dv1', 1);");

  // attach Sound s1 to DisplayObject dv1
  add_actions(mo, "s1 = new Sound(dv1);");
  // Sound.getVolume fetches volume from dv1 (see s2 below)
  check_equals(mo, "s1.getVolume()", "100");

  // Change volume of Sound s1 (will change volume of attached DisplayObject, see below)
  add_actions(mo, "s1.setVolume(1000);");
  check_equals(mo, "s1.getVolume()", "1000"); 

  // attach Sound s2 to DisplayObject dv2
  add_actions(mo, "s2 = new Sound(dv1);");
  // Sound s2 finds volume of dv1 being 1000
  check_equals(mo, "s2.getVolume()", "1000");

  // This shows that setVolume/getVolume make callbacks
  // to attached DisplayObject as changing volume of one Sound
  // influence the other attached sound...
  add_actions(mo, "s2.setVolume(5);");
  check_equals(mo, "s2.getVolume()", "5"); 
  check_equals(mo, "s1.getVolume()", "5"); 
  add_actions(mo, "s1.setVolume(80);");
  check_equals(mo, "s2.getVolume()", "80"); 
  check_equals(mo, "s1.getVolume()", "80");
  // btw, negative volume is fine..
  add_actions(mo, "s1.setVolume(-20);");
  check_equals(mo, "s2.getVolume()", "-20"); 
  check_equals(mo, "s1.getVolume()", "-20");

  // If the attached-to DisplayObject gets unloaded,
  // getVolume returns undefined...
  add_actions(mo, "dv1.removeMovieClip();");
  check_equals(mo, "typeof(s2.getVolume())", "'undefined'"); 
  check_equals(mo, "typeof(s1.getVolume())", "'undefined'");
  // even if you reset it explicitly
  add_actions(mo, "s2.setVolume(50);");
  check_equals(mo, "typeof(s2.getVolume())", "'undefined'"); 
  check_equals(mo, "typeof(s1.getVolume())", "'undefined'");
  // but you get it back when you create a replacement..
  add_actions(mo, "createEmptyMovieClip('dv1', 2);");
  check_equals(mo, "s2.getVolume()", "100"); 
  check_equals(mo, "s1.getVolume()", "100");
  add_actions(mo, "s1.setVolume(80);");
  check_equals(mo, "s2.getVolume()", "80"); 
  check_equals(mo, "s1.getVolume()", "80");

  // And you can attach a Sound to any DisplayObject, not
  // just MovieClip ones.
  // This is against a video instance
  add_actions(mo, "s1 = new Sound(video); s2 = new Sound(video);");
  check_equals(mo, "s2.getVolume()", "100"); 
  check_equals(mo, "s1.getVolume()", "100");
  add_actions(mo, "s1.setVolume(80);");
  check_equals(mo, "s2.getVolume()", "80"); 
  check_equals(mo, "s1.getVolume()", "80");

  // Here's a 3 level volume controller. Our square.flv doesn't have sound
  // so you can't really see if it's working or not... Anyway, change
  // the code to load a movie with sound and see what happens changing the volumes
  // below
  add_actions(mo, 
                "dv1.createEmptyMovieClip('dv2', 1);"
                "dv1.dv2.createEmptyMovieClip('dv3', 1);"
                "dv1.dv2.dv3.attachAudio(stream);" // attach stream to 3rd level
                "s1 = new Sound(dv1);"
                "s2 = new Sound(dv1.dv2);"
                "s3 = new Sound(dv1.dv2.dv3);"
                "s3.setVolume(50);" // ---------- level1 volume (change me for tests)
                "s1.setVolume(50);" // ---------- level2 volume (change me for tests)
                "s2.setVolume(400);" // --------- level3 volume (change me for tests)
                );

  //------------------------------------------
  // Now attach video to the video DisplayObjects
  //------------------------------------------
  check(mo, "Video.prototype.hasOwnProperty('attachVideo')");
  check(mo, "Video.prototype.hasOwnProperty('smoothing')");
  check(mo, "Video.prototype.hasOwnProperty('deblocking')");
  check(mo, "Video.prototype.hasOwnProperty('clear')");
  check(mo, "Video.prototype.hasOwnProperty('height')");
  check(mo, "Video.prototype.hasOwnProperty('width')");
  check_equals(mo, "video.height", "0");
  check_equals(mo, "video.width", "0");
  check_equals(mo, "video2.height", "0");
  check_equals(mo, "video2.width", "0");

  add_actions(mo, "video.attachVideo(stream);"); 
  add_actions(mo, "video2.attachVideo(stream2);"); 
  add_actions(mo, "video3.attachVideo(stream3);"); 
  
  check_equals(mo, "video.height", "0");
  check_equals(mo, "video.width", "0");
  check_equals(mo, "video2.height", "0");
  check_equals(mo, "video2.width", "0");

  // currentFps (read-only)
  check_equals (mo, "typeof(stream.currentFps)", "'number'" );
  add_actions(mo, "stream.currentFps = 'string';");
  check_equals (mo, "typeof(stream.currentFps)", "'number'" );
  add_actions(mo, "stream.currentFps = false;");
  check_equals (mo, "typeof(stream.currentFps)", "'number'" );

  // bufferLength (read-only)
  check_equals (mo, "typeof(stream.bufferLength)", "'number'" );
  add_actions(mo, "stream.bufferLength = 'string';");
  check_equals (mo, "typeof(stream.bufferLength)", "'number'" );
  add_actions(mo, "stream.bufferLength = false;");
  check_equals (mo, "typeof(stream.bufferLength)", "'number'" );

  // bufferTime
  check_equals (mo, "typeof(stream.bufferTime)", "'number'" );
  add_actions(mo, "stream.setBufferTime(2);");
  check_equals (mo, "stream.bufferTime", "2");
  add_actions(mo,"stream.bufferTime = 20;");
  check_equals (mo, "stream.bufferTime", "2");
  add_actions(mo,"stream.setBufferTime = 30;");
  check_equals (mo, "stream.bufferTime", "2");
  add_actions(mo,"stream.setBufferTime(false);");
  check_equals (mo, "stream.bufferTime", "2");
  add_actions(mo,"stream.setBufferTime('string');");
  check_equals (mo, "stream.bufferTime", "2");
  add_actions(mo,"stream.setBufferTime('5');");
  check_equals (mo, "stream.bufferTime", "2");
  add_actions(mo,"stream.setBufferTime(10);");  // can't change it once set ...
  check_equals (mo, "stream.bufferTime", "2");

  // liveDelay (read-only)
  xcheck_equals (mo, "typeof(stream.liveDelay)", "'number'");
  add_actions(mo, "stream.liveDelay = 'string';");
  xcheck_equals (mo, "typeof(stream.liveDelay)", "'number'");

  // time (read-only)
  check_equals (mo, "typeof(stream.time)", "'number'" );
  add_actions(mo, "stream.time = 'string';");
  check_equals (mo, "typeof(stream.time)", "'number'" );

  // bytesLoaded (read-only)
  check_equals (mo, "typeof(stream.bytesLoaded)", "'number'" );
  add_actions(mo, "stream.bytesLoaded = 'string';");
  check_equals (mo, "typeof(stream.bytesLoaded)", "'number'" ); 

  check_equals (mo, "stream.currentFps", "0" );

  /* Play video */
  b = newSWFAction(buffer_b);
  if(b == NULL) return -1;
  SWFMovie_add(mo, (SWFBlock)b);
  b = newSWFAction(buffer_c);
  if(b == NULL) return -1;
  SWFMovie_add(mo, (SWFBlock)b);
 
  check_equals (mo, "stream.currentFps", "0" );
 
  /* Publisher Methods */

  // These are documented as player methods for connections
  // to a media server.
  // Possibly they are only defined if the connection is to
  // such a server; at this point, they should be undefined in
  // any case.

  check_equals (mo, "typeof(stream.attachAudio())", "'undefined'");
  check_equals (mo, "typeof(stream.attachVideo())", "'undefined'");
  check_equals (mo, "typeof(stream.publish())", "'undefined'");
  check_equals (mo, "typeof(stream.send())", "'undefined'");
  check_equals (mo, "typeof(stream.receiveAudio())", "'undefined'");
  check_equals (mo, "typeof(stream.receiveVideo())", "'undefined'");

  /* Video checks */ 
  
  check_equals(mo, "video._xscale", "100");
  check_equals(mo, "video._yscale", "100");
  check_equals(mo, "video._rotation", "0");
  check_equals(mo, "video._target", "'/video'");
  check_equals(mo, "video.height", "0");
  check_equals(mo, "video.width", "0");
  check_equals(mo, "video2.height", "0");
  check_equals(mo, "video2.width", "0");

  check(mo, "Video.prototype.hasOwnProperty('attachVideo')");
  check(mo, "Video.prototype.hasOwnProperty('smoothing')");
  check(mo, "Video.prototype.hasOwnProperty('deblocking')");
  check(mo, "Video.prototype.hasOwnProperty('clear')");
  check(mo, "Video.prototype.hasOwnProperty('height')");
  check(mo, "Video.prototype.hasOwnProperty('width')");

  check(mo, "!Video.prototype.hasOwnProperty('_alpha')");
  check(mo, "!Video.prototype.hasOwnProperty('_height')");
  check(mo, "!Video.prototype.hasOwnProperty('_name')");
  check(mo, "!Video.prototype.hasOwnProperty('_parent')");
  check(mo, "!Video.prototype.hasOwnProperty('_rotation')");
  check(mo, "!Video.prototype.hasOwnProperty('_visible')");
  check(mo, "!Video.prototype.hasOwnProperty('_width')");
  check(mo, "!Video.prototype.hasOwnProperty('_x')");
  check(mo, "!Video.prototype.hasOwnProperty('_xmouse')");
  check(mo, "!Video.prototype.hasOwnProperty('_xscale')");
  check(mo, "!Video.prototype.hasOwnProperty('_y')");
  check(mo, "!Video.prototype.hasOwnProperty('_ymouse')");
  check(mo, "!Video.prototype.hasOwnProperty('_yscale')");
  check(mo, "!Video.prototype.hasOwnProperty('_xmouse')");

  check(mo, "!video.hasOwnProperty('_alpha')");
  check(mo, "!video.hasOwnProperty('_height')");
  check(mo, "!video.hasOwnProperty('_name')");
  check(mo, "!video.hasOwnProperty('_parent')");
  check(mo, "!video.hasOwnProperty('_rotation')");
  check(mo, "!video.hasOwnProperty('_visible')");
  check(mo, "!video.hasOwnProperty('_width')");
  check(mo, "!video.hasOwnProperty('_x')");
  check(mo, "!video.hasOwnProperty('_xmouse')");
  check(mo, "!video.hasOwnProperty('_xscale')");
  check(mo, "!video.hasOwnProperty('_y')");
  check(mo, "!video.hasOwnProperty('_ymouse')");
  check(mo, "!video.hasOwnProperty('_yscale')");
  check(mo, "!video.hasOwnProperty('_xmouse')");

  add_actions(mo,
                "video._x = 100;"
                "video._xscale = 120;"
                "video._yscale = 120;"
                "video._rotation = 45;"
                "_root.addBytesLoadedProgress(video, stream);"
                "_root.addBytesLoadedProgress(video2, stream2);"
                "_root.addBufferLoadedProgress(video, stream);"
                "_root.addBufferLoadedProgress(video2, stream2);"
        );

  check_equals(mo, "video._x", "100")   ;
  check_equals(mo, "Math.round(video._xscale*100)/100", "120");
  check_equals(mo, "Math.round(video._yscale*100)/100", "120");
  check_equals(mo, "Math.round(video._rotation*100)/100", "45");


  // How can I test props here ?
  check_equals(mo, "typeof(video.hitTest)", "'undefined'");
  check_equals(mo, "typeof(video.getBounds)", "'undefined'");

  SWFMovie_add(mo, (SWFBlock)newSWFAction(
        "note('The video on the right is an OGG theora stream. It will appear "
        "in Gnash, but not the Adobe player');"
                "_root.onKeyDown = function() {"
                "       _root.note(' bufferLength:'+stream.bufferLength+"
                "               ' bytesLoaded:'+stream.bytesLoaded+"
                "               ' currentFps:'+stream.currentFps+' time:'+stream.time);"
                "       var ascii = Key.getAscii();"
                "       trace('Key down: '+ascii);"
                "       if ( ascii == 32 ) {" // ' ' - pause(toggle)
                "               stream.paused = !stream.paused;"
                "               stream.pause();" // stream.paused);"
                "       }"
                "       else if ( ascii == 112 ) {" // 'p' - play()
                "               stream.play();"
                "               _root.note(\"2. Verify video hasn't started, then press space to continue.\");"
                "       }"
                "       else if ( ascii == 99 ) {" // 'c' - play()
                "               _root.check(nc.isConnected, 'NetConnection is connected');"
                "               _root.nc.close();"
                "               _root.note(\"Closed netconnection\");"
                "               _root.check(!nc.isConnected, 'NetConnection is not connected');"
                "       }"
                "};"
                "Key.addListener(_root);"

                "\n"

                "stream.onStatus = function(info) {"

                "  if ( ! _root.enumerableStatusInfoChecked ) {"
                "    _root.check(info.code != undefined);"
                "    _root.check(info.level != undefined);"
                "    var tmp = new Array();"
                "    for (var e in info) tmp.push(e);"
                "    tmp.sort();"
                "    _root.check_equals(tmp.length, 2);"
                "    _root.check_equals(tmp[0], 'code');"
                "    _root.check_equals(tmp[1], 'level');"
                "    _root.enumerableStatusInfoChecked=true;"
                "    var backup = info.code; info.code = 65;"
                "    _root.check_equals(info.code, 65);"
                "    _root.check(delete info.code);"
                "    _root.check_equals(info.code, undefined);"
                "    info.code = backup;"
                "    var backup = info.level; info.level = 66;"
                "    _root.check_equals(info.level, 66);"
                "    _root.check(delete info.level);"
                "    _root.check_equals(info.level, undefined);"
                "    info.level = backup;"
                "  }"

                "\n"

                // Ignore Buffer.Flush for now
                "  if ( info.code == 'NetStream.Buffer.Flush' ) return; "

                // Print some info
                " _root.note('onStatus('+info.code+') called'); "

                "\n"

                " if ( info.code == 'NetStream.Play.Start' )"
                " {"
                "       check(!_root.startNotified, 'No duplicated Play.Start notification');"
                "       _root.startNotified++;"
                " }"

                " else if ( info.code == 'NetStream.Play.Stop' )"
                " {"
                "       if ( ! _root.stopNotified )"
                "       {"
                "               _root.stopNotified++;"
                "               stream.seek(0);"
                "       } else {"
                "               _root.stopNotified++;"
                "               _root.check(this instanceOf NetStream); "
                "               _root.check_equals(this.bufferTime, 2); "
                "               _root.check_equals(this.bytesTotal, 21482); "
                "               _root.nextFrame();"
                "       }"
                " }"
#if 0
                " else if ( info.code == 'NetStream.Buffer.Empty' && this.stopNotified ) "
                " {"
                "       check ( this.stopNotified )"
                "       this.close();"
                "       _root.nextFrame();"
                " }"
#endif

                "};"

                "\n"

                "stream.onCuePoint = function(info) {"
                " _root.note('onCuePoint('+info+') called'); "
                "};"
                "stream.onMetaData = function(info) {"

                // debugging
                " var s='';"
                " for (e in info) { "
                "  s += e+':'+info[e]+' ';"
                " }"
                " _root.note('onMetaData: '+s);"


                " _root.metadataNotified++;"

                // don't run other tests if already done
                // BTW: should be called once, gnash (gst) calls this twice
                //      and would succeed in composition checking in second call.
                " if ( _root.metadataNotified > 1 ) return;"

                " check(_root.startNotified, 'onMetaData should be notified after Play.Start');"
                " check_equals(arguments.length, 1, 'single argument');"
                " check(info instanceof Array, 'onMetaData argument sent from square.flv should be instanceof Array');"
                " check_equals(info.length, 11);" 

                // Test enumeration
                " var enu = new Array;"
                " for (e in info) { "
                "  enu.push(e);"
                " }"
                " check_equals(enu.length, 11);" // this is actual (not virtual) composition

                "\n"


                // Test composision

                " check(info.hasOwnProperty('length'), 'metadata has length');" // it's an array...

                " check(info.hasOwnProperty('filesize'), 'metadata has filesize');"
                " check_equals(typeof(info.filesize), 'number', 'filesize is a number');"
                " check_equals(info.filesize, '21482', 'actual filesize');"
                " info.filesize = 'changed';"
                " check_equals(info.filesize, 'changed');" // can be overridden
                " delete info.filesize;"
                " check(!info.hasOwnProperty('filesize'), 'metadata filesize can be deleted');"

                " check(info.hasOwnProperty('audiocodecid'), 'metadata has audiocodecid');"
                " check_equals(typeof(info.audiocodecid), 'number', 'audiocodecid is a number');"
                " check_equals(info.audiocodecid, 2, 'actual audiocodecid');"
                " info.audiocodecid = 'changed';"
                " check_equals(info.audiocodecid, 'changed');" // can be overridden
                " delete info.audiocodecid;"
                " check(!info.hasOwnProperty('audiocodecid'), 'metadata audiocodecid can be deleted');"

                "\n"

                " check(info.hasOwnProperty('stereo'), 'metadata has stereo');"
                " check_equals(typeof(info.stereo), 'boolean', 'stereo is boolean');" 
                " check_equals(info.stereo, false, 'actual stereo');"
                " info.stereo = 'changed';"
                " check_equals(info.stereo, 'changed');" // can be overridden
                " delete info.stereo;"
                " check(!info.hasOwnProperty('stereo'), 'metadata stereo can be deleted');"

                " check(info.hasOwnProperty('audiosamplesize'), 'metadata has audiosamplesize');"
                " check_equals(typeof(info.audiosamplesize), 'number', 'audiosamplesize is a number');"
                " check_equals(info.audiosamplesize, 16, 'actual audiosamplesize');"
                " info.audiosamplesize = 'changed';"
                " check_equals(info.audiosamplesize, 'changed');" // can be overridden
                " delete info.audiosamplesize;"
                " check(!info.hasOwnProperty('audiosamplesize'), 'metadata audiosamplesize can be deleted');"

                "\n"

                " check(info.hasOwnProperty('audiosamplerate'), 'metadata has audiosamplerate');"
                " check_equals(typeof(info.audiosamplerate), 'number', 'audiosamplerate is a number');"
                " check_equals(info.audiosamplerate, 44100, 'actual audiosamplerate');"
                " info.audiosamplerate = 'changed';"
                " check_equals(info.audiosamplerate, 'changed');" // can be overridden
                " delete info.audiosamplerate;"
                " check(!info.hasOwnProperty('audiosamplerate'), 'metadata audiosamplerate can be deleted');"

                " check(info.hasOwnProperty('videocodecid'), 'metadata has videocodecid');"
                " check_equals(typeof(info.videocodecid), 'number', 'videocodecid is a number');"
                " check_equals(info.videocodecid, 2, 'actual videocodecid');"
                " info.videocodecid = 'changed';"
                " check_equals(info.videocodecid, 'changed');" // can be overridden
                " delete info.videocodecid;"
                " check(!info.hasOwnProperty('videocodecid'), 'metadata videocodecid can be deleted');"

                "\n"

                " check(info.hasOwnProperty('height'), 'metadata has height');"
                " check_equals(typeof(info.height), 'number', 'height is a number');"
                " check_equals(info.height, 96, 'actual height');"
                " info.height = 'changed';"
                " check_equals(info.height, 'changed');" // can be overridden
                " delete info.height;"
                " check(!info.hasOwnProperty('height'), 'metadata height can be deleted');"

                " check(info.hasOwnProperty('width'), 'metadata has width');"
                " check_equals(typeof(info.width), 'number', 'width is a number');"
                " check_equals(info.width, 128, 'actual width');"
                " info.width = 'changed';"
                " check_equals(info.width, 'changed');" // can be overridden
                " delete info.width;"
                " check(!info.hasOwnProperty('width'), 'metadata width can be deleted');"

                "\n"

                " check(info.hasOwnProperty('duration'), 'metadata has duration');"
                " check_equals(typeof(info.duration), 'number', 'duration is a number');"
                " check_equals(info.duration, 2.299, 'actual duration');" // seconds, rounded to milliseconds
                " info.duration = 'changed';"
                " check_equals(info.duration, 'changed');" // can be overridden
                " delete info.duration;"
                " check(!info.hasOwnProperty('duration'), 'metadata duration can be deleted');"
                " _root.note('1. Press \"p\" key');"
                "};"
                ));

  SWFMovie_nextFrame(mo);

  check_equals(mo, "video.height", "96");
  check_equals(mo, "video.width", "128");


  // See https://savannah.gnu.org/bugs/?26687
  SWFMovie_add(mo, (SWFBlock)newSWFAction(
  //"note('VM.time:'+getTimer()+' stream3.time:'+stream3.time);"
  "check(stream3.time > 4150, 'stream3.time '+stream3.time+' > 4150');"
  ));


  //add_actions(mo, "note('This is an OGG stream, so the Adobe player "
  //    "will fail the next two tests.');");
  // gstreamer also fails, so let's not test this.
  //check_equals(mo, "video2.height", "96");
  //check_equals(mo, "video2.width", "128");

  check_equals(mo, "metadataNotified", "1");
  check_equals(mo, "stopNotified", "2");
  check_equals(mo, "startNotified", "1");
  SWFMovie_add(mo, (SWFBlock)newSWFAction("totals(197); stop(); end_of_test=true;"));

  SWFMovie_nextFrame(mo);


  /* Output movie */
  puts("Saving " OUTPUT_FILENAME );
  SWFMovie_save(mo, OUTPUT_FILENAME);

  return 0;
}

/* [<][>][^][v][top][bottom][index][help] */