I am playing multiple short video clips behind each other and I want it to look like it is one continuous video. Is there a way to stich them together so that it's unnoticable when you set a new video source.
At the moment I am having a problem where a black frame is inserted when I set a new video source. The order of events seems to be something like this
--> Video Finishes
--> Complete event dispatched
--> Set source to new video
--> Screen turns blank (this should not happen, instead it should keep the last frame of the video until the new video is loaded)
--> a few miliseconds pass
--> the new video starts playing.
Is it possible to avoid the screen turning blank or to keep the last frame showing until the next video is loaded.
Thanks,
Dennis
Technically, it is doable, however I doubt you'll be able to get this right 100% of the time. Here is how you'll need to do this.
Just before the first (or previous) video finishes, you'll have to load the next video in a new player instance. Of course depending a number of factors, how soon before the first finishes that you have to load the next depends on a number of factors
1. The bit rate of the next video
2. The Internet bandwidth of the client machine.
3. If the previous video has not loaded completely before you need to load the next, you'll have to account for that as well a not hamper the playing of the currently playing video.
4. Some client computers may not be able to do this without stuttering and glitches.
The best solution to the problem is to really stitch the videos together before hand. of course if you don't know the sequence before hand you can't do this, so you're left with doing what I mentioned earlier.
Here is the double video player that Shiv describes. This works for the most part, except for that there is a short pause when the next video loads... still not ideal.
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" width="400" height="300">
<fx:Metadata>
[Event(name="complete", type="org.osmf.events.TimeEvent")]
</fx:Metadata>
<fx:Script>
<![CDATA[
import org.osmf.events.MediaPlayerStateChangeEvent;
import org.osmf.events.TimeEvent;
import org.osmf.media.MediaPlayer;
import org.osmf.media.MediaPlayerState;
import spark.components.VideoPlayer;
var _source:String = "";
var activePlayer:VideoDisplay = null;
var otherPlayer:VideoDisplay = null;
var playerIndex:int = 0;
public function set source (value:String) {
trace('set source: ' + value)
if(value == '' || value == null || value == _source) {
trace('rejected source');
return;
}
if(activePlayer != null) {
activePlayer.depth = 3;
}
activePlayer = getElementAt(playerIndex) as VideoDisplay;
playerIndex = playerIndex == 0?1:0;
otherPlayer = getElementAt(playerIndex) as VideoDisplay;
trace('active player: ' + activePlayer.id);
activePlayer.addEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE, player1_mediaPlayerStateChangeHandler);
activePlayer.source = value;
_source = value;
}
public function get source ():String {
return _source;
}
protected function player1_mediaPlayerStateChangeHandler(event:MediaPlayerStateChangeEvent):void
{
trace('state change: ' + event.state + ', player: ' + (event.currentTarget.id));
if(event.state == MediaPlayerState.READY) {
player1.removeEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE,player1_mediaPlayerStateChangeHandler);
player2.removeEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE,player1_mediaPlayerStateChangeHandler);
activePlayer.play();
activePlayer.depth = 2;
otherPlayer.depth = 1;
}
}
protected function completeHandler(event:TimeEvent):void
{
if(event.currentTarget == activePlayer) {
trace('video complete');
dispatchEvent(event);
}
}
]]>
</fx:Script>
<s:VideoDisplay id="player1" autoRewind="false" complete="completeHandler(event)" autoPlay="true" opaqueBackground="false"/>
<s:VideoDisplay id="player2" complete="completeHandler(event)" autoRewind="false" autoPlay="true" opaqueBackground="false" />
</s:Group>
Related
Is there a way to measure the average time it takes my code to run and each frame to render for my Flex app? More specifically, I know how to use getTimer() but I'm not sure about which events I should listen to in order to do this. I was reading this post and am not sure how you'd figure out how long the actual rendering took (it would seem like it may be the time between the RENDER event fires and the next ENTER_FRAME event fires, but I'm not sure). Also, not exactly sure where the user code happens, or whether I should care about EXIT_FRAME and FRAME_CONSTRUCTED.
Any help much appreciated!
EDIT ----
here's a snippet of code showing the main events for each repetition of the second frame in a super simple flex app. What I'm trying to figure out is whether there is a clear relationship between the "user code" and "rendering" parts of the classic Flex racetrack and the intervals between the four main signals that I'm tracing from.
The code:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
import flash.utils.getTimer;
public var t:Timer;
protected function button1_clickHandler(event:MouseEvent):void
{
t = new Timer(40, 50);
t.addEventListener(TimerEvent.TIMER, handleTimeTick);
t.addEventListener(TimerEvent.TIMER_COMPLETE, timerDone);
addEventListener(Event.RENDER, application1_renderHandler);
addEventListener(Event.ENTER_FRAME, application1_enterFrameHandler);
addEventListener(Event.EXIT_FRAME, application1_exitFrameHandler);
addEventListener(Event.FRAME_CONSTRUCTED, application1_frameConstructedHandler);
t.start();
}
protected function handleTimeTick(e:TimerEvent):void
{
shape.x += 5;
}
protected function timerDone(e:TimerEvent):void
{
t.stop();
t.removeEventListener(TimerEvent.TIMER, handleTimeTick);
t.removeEventListener(TimerEvent.TIMER_COMPLETE, timerDone);
removeEventListener(Event.RENDER, application1_renderHandler);
removeEventListener(Event.ENTER_FRAME, application1_enterFrameHandler);
removeEventListener(Event.EXIT_FRAME, application1_exitFrameHandler);
removeEventListener(Event.FRAME_CONSTRUCTED, application1_frameConstructedHandler);
}
protected function application1_renderHandler(event:Event):void
{
trace("render fire", getTimer());
}
protected function application1_enterFrameHandler(event:Event):void
{
trace("enter frame fire", getTimer());
}
protected function application1_exitFrameHandler(event:Event):void
{
trace("exit frame fire", getTimer());
}
protected function application1_frameConstructedHandler(event:Event):void
{
trace("frame constructed fire", getTimer());
}
]]>
</fx:Script>
<s:Rect id="shape" x="0" y="0" height="20" width="20">
<s:fill>
<s:SolidColor color="0xff0000"/>
</s:fill>
</s:Rect>
<s:Button x="10" y="100" click="button1_clickHandler(event)" label="go"/>
</s:Application>
Easy way to test out the time to takes for a "frame" to run. Here's a quick example. This is untested but you get the idea. You should also look at Grant Skinners talk about performance and framerates.
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:parsley="http://www.spicefactory.org/parsley"
creationComplete="onCreationComplete()">
<fx:Script>
<![CDATA[
private var _timer:Timer = new Timer(1000);
private var _previousTime:int;
private var _avgTime:int;
private var _times:Array = [];
private function onCreationComplete():void
{
// Add event listener for enter frame
this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
// Listen to timer
this._timer.addEventListener(TimerEvent.TIMER, onTimer);
// start timer
this._timer.start();
}
private function onEnterFrame(e:Event):void
{
var time:int = getTimer() - this._previousTime;
trace("timer frame took "+ time +"ms");
this._previousTime = getTimer();
this._times.push(time);
}
private function onTimer(e:Event):void
{
var total:int = 0;
for(var i:uint = 0, len:uint = this._times.length; i < len ; i++)
{
total += this._times[i];
}
this._avgTime = total/len;
this._times = [];
trace("Average frame time is "+ this._avgTime +"ms");
}
]]>
</fx:Script>
</s:Application>
Any Flex app is only two frames. The first frame is the startup/initialization of the framework and the second frame is your app.
The framerate is set on the Flex Application tag and the default is 24 frames per second. So, therefore it takes Flex 1/24 of a second to render the frames of your application.
However, it is entirely probably that your code takes more than one frame to execute, and I believe that is what you want to measure.
For some background reading, you should investigate the Flex Elastic Racetrack about how Flex divides each frame for different types of processing.
Then read up, if you haven't already, about the Flex Component LifeCycle. ( Flex 3 Version ) .
You already mentioned the getTimer() method. The gist is to use getTimer() at two points and compare the two values. What those two points are depends entirely on what you want to measure.
If you want to measure the time it takes a Flex component to go through the startup process, use getTimer() before you create it (AKA new Component() ) and then in a listener for that component's creationComplete event.
If you want to time the full application setup, you're best bet is to probably get the value in a preinitialize event handler of the main Application tag and on the applicationComplete handler of the same tag.
Does that help?
Ok, after reading plenty around the web I think this presentation gives the best info about how to measure elastic race track times (see for instance this piece of code from the presentation).
Reward: Has been claimed.
Overview: The code giving me the problem is deployed here: http://www.johnuckele.com/MastersOfTime.html
The problem I am running into is that a certain sequence of actions (I don't fully understand how or why) is causing my calls to invalidateDisplayList to fail to produce a subsequent call to updateDisplayList. What I know is that during this period, some other visual effects will fail to occur (such as changing the width of a component or the addition of a new child).
Example: The program below draws two columns of horizontal lines. The column on the left is drawn during commitProperties, the column on the right is drawn during updateDisplayList. A certain sequence of actions can cause the right column to stop updating.
To trigger this bug: First add a new item. Now hit the start button and a bar starts filling up. If you press the add row button, the right column and the filling bar both stop growing. The left column continues unfettered. The extra component won't appear until the last line of the if statement in TEComputeRow.tick() doesn't execute for a frame. Click on the stop button to halt the execution of the block inside the if statement in TEComputeRow.tick() and everything goes back to normal.
Question: What is going on here?
I can force it to behave by using validate now but it doesn't cure the problem, it merely covers it up for a frame. It also seems like a really sloppy hack. Is there a nicer way to deal with the loss of updateDisplayList than using validateNow? Are there any ways to accurately identify the state of the world?
MastersOfTime.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
backgroundColor="white"
backgroundGradientAlphas="[1,1]"
initialize="init()"
enterFrame="tick()"
creationComplete="addComputeArray()">
<mx:Script>
<![CDATA[
import mx.containers.HBox;
import mx.controls.Button;
import mx.containers.VBox;
import flash.utils.getTimer;
private var global:int = 0;
private function addComputeArray():void
{
var addButton:Button = new Button;
addButton.label = "Add Row Item";
addButton.addEventListener(MouseEvent.CLICK, addComputeBox);
box.addChild(addButton);
}
private function addComputeBox(a:* = null):void
{
box.addChild(new TEComputeRow());
}
private function init():void
{
box.clipContent = false;
box.graphics.lineStyle(1);
}
private function tick():void
{
global++;
this.invalidateDisplayList();
this.invalidateProperties();
//this.validateNow();
}
protected override function commitProperties():void
{
super.commitProperties();
box.graphics.moveTo(100, (global*3)%800);
box.graphics.lineTo(200, (global*3)%800);
}
protected override function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
box.graphics.moveTo(200, (global*3)%800);
box.graphics.lineTo(300, (global*3)%800);
}
]]>
</mx:Script>
<mx:VBox id="box"/>
</mx:Application>
TEComputeRow.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
height="60"
width="352"
verticalGap="0"
borderStyle="solid"
enterFrame="tick()">
<mx:Script>
<![CDATA[
public var doStuff:Boolean = false;
private var parameter:Number = 0;
private function tick(e:Event = null):void
{
var value:*;
if(doStuff)
{
parameter = parameter+1;
value = parameter;
fill.width = value;
}
}
]]>
</mx:Script>
<mx:Button label="turn on" click="{doStuff = true;}" height="20"/>
<mx:Container id="fill" x="7" width="0" height="20" backgroundColor="0x8888AA"/>
<mx:Button label="turn off" click="{doStuff = false;}" height="20"/>
</mx:VBox>
Well for starters, you are abusing the Flex life-cycle horribly, and doing things you are not meant to do... Changing the width of the fill in the tick of the row kicks off another invalidation cycle is the one that immediately jumps out. If you drove things via the Timer rather than on enterFrame, you'd immediately be better off.
My guess is that you spend so much time per frame re-invalidating the properties (changing the width will invalidate properties), the player never fits in the updateDisplayList.
Read up on both the elastic race track, and Deepa's presentation on the Flex 3 lifecycle.
The use of .width is what is triggering this problem. If I replace .width with .setActualSize the problem stops. These pieces of code travel through separate paths and .width and .height apparently have the capacity to skip part of the frame cycle (the updateDisplayList part).
I am writing a flex application that involves modifying a textarea very frequently. I have encountered issues with the textarea sometimes not displaying my modifications.
The following actionscript code illustrates my problem:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" minWidth="955" minHeight="600">
<mx:TextArea x="82" y="36" width="354" height="291" id="textArea" creationComplete="initApp()"/>
<mx:Script>
<![CDATA[
private var testSentence:String = "The big brown fox jumps over the lazy dog.";
private var testCounter:int = 0;
private function initApp():void {
var timer:Timer = new Timer(10);
timer.addEventListener(TimerEvent.TIMER, playSentence);
timer.start();
}
private function playSentence(event:TimerEvent):void {
textArea.editable = false;
if (testCounter == testSentence.length) {
testCounter = 0;
textArea.text += "\n";
}
else {
textArea.text += testSentence.charAt(testCounter++);
}
textArea.editable = true;
}
]]>
</mx:Script>
</mx:Application>
When you run the above code in a flex project, it should repeatedly print, character by character, the sentence "The big brown fox jumps over the lazy dog.". But, if you are typing into the textarea at the same time, you will notice the text the timer prints is distorted.
I am really curious as to why this happens. The single-threaded nature of flex and disabling user input for the textarea when I make modifications should prevent this from happening, but for some reason this doesn't seem to be working.
I must note too that, when running the timer at larger intervals (around 100ms) it seems to work perfectly, so I am tempted to think it's some kind of synchronization issue in the internals of the flex framework.
Any ideas on what could be causing the problem?
My son is having a rough go at this teething thing, hense my being awake. AND, I'm not really sure how I happened upon this random question/answer since I'm so rarely on stackoverflow.... but...
You SHOULD NOT have to increase your framerate by 4x because of a textArea! We're talking about system resources, not to mention a very elegant flex framework that you could instead leverage:
Here's a very quick, and flex-sdk compliant and happy fix:
create a component that extends TextArea and add the following property and override (if this in fact what you're trying to do):
private var _tackOnText : String = "";
private var _tackOnTextChanged : Boolean = false;
public function set tackOnText(value:String):void
{
_tackOnText = value;
_tackOnTextChanged = true;
invalidateProperties();
}
public function get tackOnText():String
{
return _tackOnText;
}
override protected function commitProperties():void
{
super.commitProperties();
if(_tackOnTextChanged) {
this.textField.text += _tackOnText;
_tackOnText = "";
_tackOnTextChanged = false;
}
}
Then change your playSentence to do this:
if (testCounter == testSentence.length) {
testCounter = 0;
textArea.tackOnText += "\n";
}
else {
textArea.tackOnText += testSentence.charAt(testCounter++);
}
This is one fix to a semi-common problem that should allow you to not increase your frameRate by 4x to get your app to function correctly AND you'll be better working with the flex sdk.
My 2p.
Have a good one,
Jeremy
the problem you are facing seems to be when the addition ticker function is called at the same time that you are entering text, by entering text manually it seems that it is then missing the addition of the text. though im not sure why.
the solution i found seems to be to make sure that the framescript and timer dont meet (for the faster text input, increase the framerate) for 10ms timer 100fps seems to work without any problems (add frameRate="100" to mx:Application line).
Below is my code, and the question is explained after it.
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:cal="cal.*"
layout="absolute"
applicationComplete="init()"
xmlns:geometry="com.degrafa.geometry.*"
xmlns:degrafa="com.degrafa.*"
xmlns:paint="com.degrafa.paint.*"
xmlns:containers="flexlib.containers.*"
xmlns:flexlib_controls="flexlib.controls.*"
xmlns:mdi_containers="flexlib.mdi.containers.*"
xmlns:auto="com.hillelcoren.components.*"
xmlns:local="*"
xmlns:components="CollapsibleAccordion.*"
modalTransparency="0.8"
modalTransparencyColor="0x000000"
backgroundSize="100%">
<mx:Script>
<![CDATA[
import c7.views.components.PhotoViewer.PhotoViewer;
import c7.config.ServerConfig;
import mx.core.Application;
import mx.containers.*;
import c7.models.GlobalModel;
private var pv_slideshow:PhotoViewer = null;
private function toggleFullScreen():void
{
if(stage.displayState == StageDisplayState.NORMAL)
{
this.pv_slideshow = new PhotoViewer;
Application.application.addChild(this.pv_slideshow); //added as top-most component to application itself
//set new sizes & go full-screen
this.pv_slideshow.x = 0;
this.pv_slideshow.y = 0;
this.pv_slideshow.width = stage.fullScreenWidth;
this.pv_slideshow.height = stage.fullScreenHeight;
try
{
stage.displayState = StageDisplayState.FULL_SCREEN;
}
catch(err:Error)
{
Alert.show(err.toString());
}
stage.addEventListener(FullScreenEvent.FULL_SCREEN, fullScreenEventHandler, false, 0, true); //intentionally weak referenced
//refresh the display sizes & display list
invalidateSize();
invalidateDisplayList();
}
/*else
stage.displayState = StageDisplayState.NORMAL;*/
}
private function fullScreenEventHandler(event:FullScreenEvent):void
{
if (event.fullScreen) //nothing to do in case when switching to full-screen
return;
//Alert.show(pv_slideshow.width.toString());
//application.removeChild(this.pv_slideshow);
Application.application.removeChild(pv_slideshow); //remove the full-screen container
this.pv_slideshow = null; //reset
//refresh the display sizes & display list
invalidateSize();
invalidateDisplayList();
}
The toggleFullScreen is fired on the click of a button... and it working absolutely fine. But the issue is in "exit" . When I click escape key fullScreenEventHandler is fired and it should remove the pv_slideshow.
This is where I get a null object reference error on the line:
Application.application.removeChild(pv_slideshow); //remove the full-screen container
I have tried using this.pv_slideshow and other such things.
Plz help me figure it out. what am i doing wrong and how should I make it work.
This is exact error message I get:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at mx.core::Container/http://www.adobe.com/2006/flex/mx/internal::removingChild()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\Container.as:3315]
at mx.core::Container/removeChild()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\Container.as:2263]
at index_cloud/fullScreenEventHandler()[C:\development\flex_src\index_cloud.mxml:1661]
I wouldn't use Application.application either.
Please check first pv_slideshow really exists, you can get it's parent ( if has one ) and remove it later.
get the parent
pv_slideshow.parent
remove it from the parent_item
parent_item.removeChild( pv_slideshow )
Make sure also the version of Flex you are using, you may need to remove it with removeElement.
The intended architectural use of Application.application is not to use it as a uiComponent or displayObject, or whatever. If you're building a library project that is doing some calculations based on the application properties, or the application's systemManager, or you need access to the outter application from a loaded module, then you have good reason to use App.app.
You example is not one of those cases. Your best bet is to add your component to 'this' (as long as you're not going to use a "view" container), and do a this.removeChild. That should solve a good chunk of your issues.
Best of luck,
Jeremy
When I listen for key up and key down events with wmode="transparent", I receive 2 key down events followed by a single key up event for the following keys: F-keys, arrow keys, ins, del, home, end, page up, page down, pause, print screen, application key, windows key, and the equivalent numeric keypad keys. The other keys work normally. This is occurring with FF 3.5, but not with IE 6.
Below is a simple Flex application that illustrates the problem if you run it with wmode="transparent".
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="init()">
<mx:Script>
<![CDATA[
import mx.controls.Label;
private function init():void {
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKey);
stage.addEventListener(KeyboardEvent.KEY_UP, onKey);
}
private function onKey(event:KeyboardEvent):void {
var msg:Label = new Label();
msg.text = event.toString();
eventLog.addChildAt(msg, 0);
}
]]>
</mx:Script>
<mx:VBox width="100%" height="100%">
<mx:TextInput width="100%" text="Hello"/>
<mx:VBox id="eventLog" width="100%" height="100%"/>
</mx:VBox>
</mx:Application>
Our application requires wmode="transparent" and needs to handle both key up and key down events for the problematic keys, so I'm looking for the best way to solve this. What would be the best workaround for this problem? Is there some Flash player parameter that I can use to get this working? (FF configuration changes aren't viable for our application, but might be interesting for understanding the cause of this.)
By the way, the workaround I'm currently favoring is to discard additional key down events for a key during a brief dead-band (maybe 50-100 ms) following its initial key down event. This solution has the merits of being relatively simple to implement while still supporting key repeats, though with an additional delay before repeat begins.
Here's basically what I've added to the test code:
private var keyDownTs:Array = new Array();
private function onKey(event:KeyboardEvent):void {
var msg:Label = new Label();
msg.text = getTimer() + "-" + (isDead(event) ? "**DEAD**" : "") + event.toString();
eventLog.addChildAt(msg, 0);
}
private function isDead(event:KeyboardEvent):Boolean {
var dead:Boolean = false;
switch (event.type) {
case KeyboardEvent.KEY_DOWN:
var ts:int = keyDownTs[event.keyCode];
if (ts == 0) {
// save timestamp for the initial key down event
keyDownTs[event.keyCode] = getTimer();
} else if (getTimer() - ts < 50) {
// this key down is within the dead-band of the initial key down
dead = true;
}
break;
case KeyboardEvent.KEY_UP:
// clear previous key down timestamp
keyDownTs[event.keyCode] = 0;
break;
}
return dead;
}
On my system, the spurious key down events are happening within a few ms of the initial key down (2-6 ms), so 50 ms is looking like a pretty good value. For my production implementation, I think I'm going to put this logic into an EventDispatcher that KeyboardEvent listeners will use instead of listening to the stage directly.
I don't have an answer for you, but can confirm that wmode="transparent" has caused this exact same behaviour with an item on our website. Using transparent and opaque wmodes was causing this unidentified (until now) double keypress behaviour. Due to the numerous issues with opaque and transparent modes, we instead worked around all the other issues that surround using standard mode, because it's the only mode in which keyboard IO works correctly.
Other issues include incorrect key mappings, issues with screen-reading/accessibility software, flash movie does not start executing until it is scrolled into the viewport and others.
If there's any way you can survive without wmode transparent, then consider it, because otherwise, as far as I could see, everything is borked.
The reason we initially chose transparent mode was so we could overlay other HTML elements over the Flash movie. Now instead, if we're going to overlay, we "hide" the flash:
Don't use css display:none because this resets the movie (but not using AX version in IE).
The movie can be hidden by either setting its height to 0.001px via css or to visibility:hidden in css, or both.