We need to display the framerate of a video that user is watching with JW Player. We have already tried tried everything with JW Player 7 API but it doesn't give us the client side frame rate.
We have built a JW Player plugin which is supposed to calculate the framerate on every ENTER_FRAME event while the video is playing.
We are listening to ENTER_FRAME event on line 61.
api.addEventListener(Event.ENTER_FRAME, calculateFPS);
Everytime a new frame is entered the plugin executes calculateFPS function. The framerate is displayed in a textbox.
For some reason the calculated framerate is around 30 fps. This cannot be possible because the original framerate of the video is 15 fps.
I think that the problem might be on line 61 where the eventlistener is listening to the player instead of the video. Is it possible to link the eventlistener straight to the video?
Here is the whole plugin actionscript code. It is based on a JW Player plugin example APITester.as.
package {
import com.longtailvideo.jwplayer.events.*;
import com.longtailvideo.jwplayer.player.*;
import com.longtailvideo.jwplayer.plugins.*;
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.events.Event;
import flash.utils.getTimer;
/** An example plugin that tests various player integrations. **/
public class APITester extends Sprite implements IPlugin6 {
[Embed(source="mute.png")]
private const MuteIcon:Class;
private var api:IPlayer;
private var field:TextField;
private var clickButton:Sprite;
public var startTime:Number;
public var framesNumber:Number = 0;
public var fps:TextField = new TextField();
/** Let the player know what the name of your plugin is. **/
public function get id():String {
return "apitester";
};
public function get target():String {
return "6.0";
}
/** Constructor **/
public function APITester() {
clickButton = new Sprite();
clickButton.addChild(new MuteIcon());
clickButton.y = 10;
clickButton.buttonMode = true;
addChild(clickButton);
startTime = getTimer();
addChild(fps);
};
/** Called by the player after the plugin has been created. **/
public function initPlugin(player:IPlayer, config:PluginConfig):void {
api = player;
api.addEventListener(Event.ENTER_FRAME, calculateFPS);
api.play();
};
private function calculateFPS(evt:Event):void {
var currentTime:Number = (getTimer() - startTime) / 1000;
framesNumber++;
if (currentTime > 1)
{
fps.text = "FPS: " + (Math.floor((framesNumber/currentTime)*10.0)/10.0);
startTime = getTimer();
framesNumber = 0;
}
};
/** If the player resizes itself, this gets called (including on setup). **/
public function resize(wid:Number, hei:Number):void {
clickButton.x = wid - 50;
}
}
}
Related
I was analyzing an unexpected memory leak in our game project and found some strange results. I am profiling using Adobe Scout and eliminated all other factors like starling, texture or our loading library. I reduced the code to simply load a png and immediately allocate an empty inline function on its complete event.
Loading a png allocates image on default and if you do nothing after loading gc clears that image. But creating an inline function seems to prevent that image to be garbage collected somehow. My test code is;
public class Main extends Sprite
{
private var _callbacks:Array = new Array();
public function Main()
{
load("map.png", onPngLoaded);
}
private function onPngLoaded(bitmap:Bitmap):void
{
_callbacks.push(function():void { });
}
public function load(url:String, onLoaded:Function):void
{
var loader:Loader = new Loader;
var completeHandler:Function = function(e:Event):void {
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, completeHandler);
onLoaded(loader.content);
}
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
loader.load(new URLRequest(url));
}
}
If you remove the code which creates an inline function;
private function onPngLoaded(bitmap:Bitmap):void
{
// removed the code here!
}
gc works and clears the image from memory.
Since having no logical explanation for this, I suspect of a flash / as3 bug. I will be glad to hear any comments who tests my code and gets the same results.
Note: To test, replace the main class of an empty as3 project with my code and import packages. You can load any png. I am using flashdevelop, flex-sdk 4.6.0 and flash player 14.
When you create an inline function, all local variables get stored with it in the global scope. So in this case, that would include the bitmap parameter.
For more information, see this:
http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f54.html
Here is the relevant part:
Any time a function begins execution, a number of objects and properties are created. First, a special object called an activation object is created that stores the parameters and any local variables or functions declared in the function body....Second, a scope chain is created that contains an ordered list of objects that Flash Player or Adobe AIR checks for identifier declarations. Every function that executes has a scope chain that is stored in an internal property. For a nested function, the scope chain starts with its own activation object, followed by its parent function’s activation object. The chain continues in this manner until it reaches the global object.
This is another reason why inline/anonymous functions are best avoided in most situations.
So using asc2, Flash/Air 19 : Yes I get the same results that you are seeing, but due to the anonymous function holding global references I expected that (like my original comment stated).
I rewrote it in my style based upon Adobe's GC technical articles and bulletins and no leaks are seen as all the global references are removed.
A cut/paste AIR example:
package {
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.display.Loader;
import flash.events.Event;
import flash.net.URLRequest;
import flash.system.System;
import flash.utils.Timer;
import flash.events.TimerEvent;
public class Main extends Sprite {
var timer:Timer;
var button:CustomSimpleButton;
var currentMemory:TextField;
var highMemory:TextField;
var hi:Number;
var _callbacks:Array = new Array();
public function Main() {
button = new CustomSimpleButton();
button.addEventListener(MouseEvent.CLICK, onClickButton);
addChild(button);
currentMemory = new TextField();
hi = System.privateMemory;
currentMemory.text = "c: " + hi.toString();
currentMemory.x = 100;
addChild(currentMemory);
highMemory = new TextField();
highMemory.text = "h: " + hi.toString();
highMemory.x = 200;
addChild(highMemory);
timer = new Timer(100, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, timerHandler);
timer.start();
}
function timerHandler(e:TimerEvent):void{
System.pauseForGCIfCollectionImminent(.25);
currentMemory.text = "c: " + System.privateMemory.toString();
hi = System.privateMemory > hi ? System.privateMemory : hi;
highMemory.text = "h: " + hi.toString();
timer.start();
}
function onClickButton(event:MouseEvent):void {
for (var i:uint = 0; i<100; i++) {
//load("foobar.png", onPngLoaded);
load2("foobar.png");
}
}
private function onPngLoaded2(bitmap:Bitmap):void {
var foobarBitMap:Bitmap = bitmap; // assuming you are doing something
foobarBitMap.smoothing = false; // with the bitmap...
callBacks(); // not sure what you are actually doing with this
}
private function callBacks():void {
_callbacks.push(function ():void {
});
}
public function completeHandler2(e:Event):void {
var target:Loader = e.currentTarget.loader as Loader;
// create a new bitmap based what is in the loader so the loader has not refs after method exits
var localBitmap:Bitmap = new Bitmap((target.content as Bitmap).bitmapData);
onPngLoaded2(localBitmap);
}
public function load2(url:String):void {
var loader2:Loader = new Loader;
loader2.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler2, false, 0, true);
loader2.load(new URLRequest(url));
}
}
}
import flash.display.Shape;
import flash.display.SimpleButton;
class CustomSimpleButton extends SimpleButton {
private var upColor:uint = 0xFFCC00;
private var overColor:uint = 0xCCFF00;
private var downColor:uint = 0x00CCFF;
private var size:uint = 80;
public function CustomSimpleButton() {
downState = new ButtonDisplayState(downColor, size);
overState = new ButtonDisplayState(overColor, size);
upState = new ButtonDisplayState(upColor, size);
hitTestState = new ButtonDisplayState(upColor, size * 2);
hitTestState.x = -(size / 4);
hitTestState.y = hitTestState.x;
useHandCursor = true;
}
}
class ButtonDisplayState extends Shape {
private var bgColor:uint;
private var size:uint;
public function ButtonDisplayState(bgColor:uint, size:uint) {
this.bgColor = bgColor;
this.size = size;
draw();
}
private function draw():void {
graphics.beginFill(bgColor);
graphics.drawRect(0, 0, size, size);
graphics.endFill();
}
}
Okay so here's the dealio.
The code below is what I use to spawn a single enemy periodically in my horror game. He spawns at the window every 10 seconds, then every 6 seconds later, he enters the room and you're essentially dead already, because 5 seconds after that you get a lovely jumpscare. The moment the enemy shifts from the window to the bedroom is when I want the window enemy to stop spawning.
EnemyShipTimer is the timer that spawns the enemy.
enemyTimer is the timer that counts how long the enemy has been at
the window, and then in the room.
In the main file, I've attempted to get rid of the enemy a number of ways, to no effect.
So function gtfo is my latest attempt at removing the enemy, by trying to stop the spawning from the class file itself. It still hasn't worked. Maybe I need to somehow remove the class file from my game, but I have no idea how. Please help! ;A;
PS, countClick counts how many clicks you've done to make the enemy go away, although when I make him go away, room enemy still spawns :c
package
{
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.display.Sprite;
import flash.utils.Timer;
import flash.utils.getTimer;
import flash.events.TimerEvent;
import flash.text.TextField;
public class EnemyShip extends MovieClip
{
private var clickCount:int = 0;
private var enemyShipTimer:Timer;
private var enemyTimer:Timer;
public function EnemyShip()
{
this.x = 900;
this.y = 214;
addEventListener(MouseEvent.CLICK, addClick);
addEventListener(MouseEvent.CLICK, countclick);
addEventListener(TimerEvent.TIMER, gtfo);
}
function gtfo(e:TimerEvent):void{
if (enemyTimer.currentCount==5)
enemyShipTimer.stop();
stage.removeChild(this);
}
function addClick(event:MouseEvent):void
{
clickCount = clickCount + 1;
trace("Clickage : " + clickCount);
}
function countclick(event:MouseEvent):void
{
if (clickCount ==4)
{
stage.removeChild(this);
}
}
}
}
The following is the relevant main timeline code that may be clashing with the code (I have updated the package class as per #Karma 's helpful instructions c: though the enemy still spawns; I think what the problem is now is that the timer functions clash with timer functions I've put in my main timeline, and I attempted to sort of move them into the package class but I don't know how to add the necessary movieclips so that I won't get access of undefined property errors so I separated the two once more
enemy is the window spawning enemy.
mosnta is the room spawning enemy.
sod is the screen of death (game over screen) which does not play at all.
var enemyShipTimer:Timer;
function sendEnemy(e:Event)
{
var enemy = new EnemyShip();
stage.addChild(enemy);
var enemytimer = new Timer (1000, count);
enemytimer.start();
enemyShipTimer = new Timer(10000);
enemyShipTimer.addEventListener("timer", sendEnemy);
enemyShipTimer.start();
enemytimer.addEventListener(TimerEvent.TIMER, countdowndesu);
function countdowndesu(e:Event):void{
if (enemytimer.currentCount>5){
trace("ur ded");
enemytimer.removeEventListener(TimerEvent.TIMER, countdowndesu);
mosnta.visible=true;
enemy.visible=false;
if (enemytimer.currentCount==8){
sod.visible=true;
sod.gotoAndPlay(2);
}
}
}
}
Not really understanding the dynamics of how your game works, but here's an altered version of your code with working Timers. Maybe this will help you figure out how to fix your issues
package
{
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.utils.Timer;
import flash.events.TimerEvent;
public class EnemyShip extends MovieClip
{
private var clickCount:int = 0;
private var enemyShipTimer:Timer;
private var enemyTimer:Timer;
public function EnemyShip()
{
this.x = 900;
this.y = 214;
//only need one click handler
addEventListener(MouseEvent.CLICK, addClick);
//Instantiate Timers
enemyShipTimer = new Timer(10000); //10 seconds
enemyTimer = new Timer(6000); //6 seconds
//add TimerEvent listeners onto Timer Objects
enemyShipTimer.addEventListener(TimerEvent.TIMER, gtfo);
enemyTimer.addEventListener(TimerEvent.TIMER, enemyTimerHandler);
//Start the timers
enemyShipTimer.start();
enemyTimer.start();
}
//This gets called every 10 seconds
private function gtfo(e:TimerEvent):void
{
trace("enemy spawns at window");
if (enemyTimer.currentCount == 5) {
enemyShipTimer.stop();
stage.removeChild(this);
}
}
//This gets called every 6 seconds
private function enemyTimerHandler(e:TimerEvent):void
{
trace("enemy moves to room");
}
private function addClick(event:MouseEvent):void
{
clickCount ++;
trace("Clickage : " + clickCount);
if (clickCount == 4)
{
stage.removeChild(this);
}
}
}
}
I'm developing a piano app in as3; however, I get too much latency.
I am using short WAV files from my flash library (not streaming) and loading the files in the constructor. I tried also working with MP3 files but I get the same latency.
I understand that developers know about that and there are a lot of threads regarding it but no one really solves the issue.
Does anyone know a workaround in order to reduce the latency?
There is a solution that says to play a silent sound all the time to prevent the sound mixer going to sleep. I tried it and it didn't work (or maybe I did it wrong).
Here is the code, it's just a simple code to test the latency
package
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.media.Sound;
import flash.media.SoundChannel;
public class SoundLatencyMain extends Sprite
{
public var MainSoundChannel:SoundChannel;
public var playbutton:MovieClip;
public var TheSound:Sound;
public var bgmusic:Sound;
public var bgsoundboolean:Boolean;
public function PlayBGMusic(me:MouseEvent):void
{
MainSoundChannel = bgmusic.play();
}
public function PlaySound(me:MouseEvent):void
{
MainSoundChannel = TheSound.play();
}
public function SoundLatencyMain()
{
MainSoundChannel = new SoundChannel();
playbutton = new PlaybgMusic_MC();
playbutton.x = stage.stageWidth / 2;
playbutton.y = stage.stageHeight / 2;
addChild(playbutton);
playbutton.addEventListener(MouseEvent.CLICK, PlayBGMusic);
bgmusic = new BGMusic();
TheSound = new TestSound();
stage.addEventListener(MouseEvent.MOUSE_DOWN, PlaySound);
}
}
few weeks ago i asked a question about a similair issue, but within the Starling Framework (Starling TouchEvent on Sprite)
Now, i am working on another mobile app/game with Away3d. I seem to have the same problem touching/tapping a moving sphere or box. When the sphere is not moving, i can just tap it exactly at its position, but when it rotates, the click-event doesnt fire very accurate.
this only happens on Mobile (tested on Android 4.2.2: Nexus7/Galaxy S2 and iOS: iPad2/iPad Retina)
package {
import away3d.containers.ObjectContainer3D;
import away3d.containers.View3D;
import away3d.core.pick.PickingColliderType;
import away3d.core.pick.PickingType;
import away3d.debug.AwayStats;
import away3d.entities.Mesh;
import away3d.events.MouseEvent3D;
import away3d.primitives.SphereGeometry;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.geom.Vector3D;
[SWF(frameRate="60")]
public class Main extends Sprite {
private var _container:ObjectContainer3D;
private var _view:View3D;
private var _sphere:Mesh;
public function Main()
{
super();
addEventListener(Event.ADDED_TO_STAGE, onAdded_toStage);
}
private function onAdded_toStage(e:Event):void {
removeEventListener(Event.ADDED_TO_STAGE, onAdded_toStage);
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.addEventListener(Event.RESIZE, onResize_stage);
init3d();
}
private function onResize_stage(e:Event):void {
_view.width = stage.stageWidth;
_view.height = stage.stageHeight;
}
private function onEnter_frame(e:Event):void {
_container.rotationY += 1;
_view.render();
}
private function onClick_sphere(e:MouseEvent3D):void {
//reposition the sphere
var deg:Number = Math.random() * 360;
_sphere.x = 250 * Math.cos(deg);
_sphere.z = 250 * Math.sin(deg);
_sphere.y = 80 + (Math.random() * 40);
}
private function init3d():void {
//create the 3d-view
_view = new View3D();
_view.width = stage.stageWidth;
_view.height = stage.stageWidth;
addChild(_view);
//create a cub
_view.camera.z = -400;
_view.camera.y = 75;
_view.camera.lookAt(new Vector3D(0, 125, 0));
//create container
_container = new ObjectContainer3D();
_view.scene.addChild(_container);
//create sphere to tap
_sphere = new Mesh(new SphereGeometry(10));
_sphere.pickingCollider = PickingColliderType.AS3_FIRST_ENCOUNTERED;
_sphere.y = 100;
_sphere.z = -250;
_sphere.mouseEnabled = true;
_sphere.addEventListener(MouseEvent3D.CLICK , onClick_sphere);
_container.addChild(_sphere);
var stats:AwayStats = new AwayStats(_view);
addChild(stats);
addEventListener(Event.ENTER_FRAME, onEnter_frame);
}
}
NOTE: i also tried different framerates, more events(Mouse_up, down etc), different pickers.
As this problem also happens with 2d moving sprites (in starling) my guess is that is might be Stage3D or AIR related, instead of starling/away3d.
Anyone has some suggestions? Maybe i'm forgetting to set something in the app.xml?
Both framework have to deal with an event less system (Stage3D). Stage3D doesn't catch or receive any events and certianly not touch or mouse event. So both frameworks simulate those events with an internal system that catches events on the stage and try to reproduce an accurate estimation. I personally find those systems too CPU expensive and not accurate enough so my advice is to do like me, create your own system. Simply catch event on the stage and decide which of displayed objects (on Stage3D using their position) has been touched. I personally get a performance boost out of it (especially with starling) and much more accuracy.
So Im creating a game and all I wanted to add was a counter that increments constantly until a player loses the game.
I created my score class and it looks like this:
package
{
import flash.display.MovieClip;
import flash.display.Stage;
import flash.text.TextField;
import flash.events.Event;
import flash.utils.Timer;
import flash.events.TimerEvent;
public class Score extends MovieClip
{
public var second:Number = 0;
public var timer:Timer = new Timer(10);
private var stageRef:Stage;
public function Score(stageRef:Stage)
{
x = 537.95;
y = 31.35;
this.stageRef = stageRef;
timer.addEventListener(TimerEvent.TIMER, clock);
timer.start();
}
function clock(evt:TimerEvent):void
{
second += 1;
scoreDisplay.text = String("Score: " +second);
}
}
}
and this is my engine class that adds it to the stage:
package {
//list of our imports these are classes we need in order to
//run our application.
import flash.display.MovieClip;
import flash.display.Stage;
import flash.events.Event;
public class Engine extends MovieClip{
private var enemyList:Array = new Array();
private var ourBoat:Boat;
private var score:Score;
public function Engine() : void{
//create an object of our ship from the Ship class
ourBoat = new Boat(stage);
score = new Score(stage);
//add it to the display list
stage.addChild(ourBoat);
stage.addChild(score);
So that creates a timer on the stage and continuously increments, but when I compile, I get no errors and my timmer for some reason doesn't work, it just displays random numbers, please help! If there is a better way of doing this please enlighten me.
I'm assuming scoreDisplay is a named Object on stage. You may find it useful to add trace()s to the script in each function that is called. That way you can see which ones are being called correctly. For example trace("Engine Instantiated."); and trace("Timer Event Received"); will tell if your class is being instantiated correctly. If it is and the trigger is not working you know your issue is between these two points. Then work your way toward the middle of the code execution until you find the problem.
You could also add an event listener to the stage for enter frame events and use that to trigger your count function. This event is always broadcast, so using should use less resources than adding a timer.
Are you sure scoreDisplay is large enough? Your number would increase by 100 every second, which if your textfield is only 2 characters long you will see random numbers.
First, you do not need to pass the stage class to MovieClip childs, once they're added to stage, you can access the stage with the this.stage property.
Second, the Timer class delay parameter is described as following in the documentation:
delay:Number — The delay between timer events, in milliseconds. A
delay lower than 20 milliseconds is not recommended. Timer frequency
is limited to 60 frames per second, meaning a delay lower than 16.6
milliseconds causes runtime problems.
Therefore, if you are really tracking seconds, your timer should be :
public var timer:Timer = new Timer(1000);
EDIT:
Here's how I would implement the score you described on the comment:
public class Score extends MovieClip
{
public var second:Number = 0;
var pointsPerSecond : Number = 10;
private var stageRef:Stage;
public function Score(stageRef:Stage)
{
x = 537.95;
y = 31.35;
this.stageRef = stageRef;
this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
function clock(evt:Event):void
{
second += pointsPerSecond/stage.frameRate; // Note that if the game is running slow (flash cant keep the frameRate you asked for), the score will also grow slowly
scoreDisplay.text = String("Score: " +second);
}
}