play flv using netStream appendBytes - actionscript-3

I know that there are many ways to play an FLV file but considering my project requirements, I need to play the flv using URLStream and NetStream
here's the complete sample code that I'm doing my tests on:
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.NetStatusEvent;
import flash.events.ProgressEvent;
import flash.utils.ByteArray;
import flash.net.URLRequest;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.NetStreamAppendBytesAction;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.media.Video;
import flash.net.URLStream;
/**
* ...
* #author Hadi Tavakoli
*/
public class Main extends Sprite
{
private var netConnection:NetConnection;
private var netStream:NetStream;
private var ul:URLStream;
private var video:Video;
private var bytes:ByteArray = new ByteArray();
private var _isSeek:Boolean = false;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
video = new Video();
addChild(video);
netConnection = new NetConnection();
netConnection.addEventListener(NetStatusEvent.NET_STATUS, netConnectionStatusHandler);
netConnection.connect(null);
}
private function netConnectionStatusHandler(ev:NetStatusEvent):void
{
switch(ev.info.code)
{
case 'NetConnection.Connect.Success':
ul = new URLStream();
ul.addEventListener(ProgressEvent.PROGRESS, onProgress);
ul.load(new URLRequest('01.flv'));
break;
}
}
private function onProgress(e:ProgressEvent):void
{
ul.readBytes(bytes, bytes.length);
if (!netStream)
{
netStream = new NetStream(netConnection);
netStream.client = { };
video.attachNetStream(netStream);
netStream.play(null);
trace("BEGIN")
netStream.appendBytesAction(NetStreamAppendBytesAction.RESET_BEGIN);
}
else
{
if (!_isSeek)
{
trace("SEEK")
netStream.appendBytesAction(NetStreamAppendBytesAction.RESET_SEEK);
_isSeek = true;
}
}
if (bytes.length == e.bytesTotal)
{
trace("END")
netStream.appendBytesAction(NetStreamAppendBytesAction.END_SEQUENCE);
}
netStream.appendBytes(bytes);
trace("-")
}
}
}
I'm not sure if I am using "appendBytes" method correctly? the video is shown but only a very few first frames will play and then the video stops!
in my eyes it seems all ok! do you have any advice on where my problem is?

I don't think you need the if (!_isSeek) block. It looks like you are pushing the bytes as you receive them in a sequential order and so there's never a seek. It looks like it will push the first set of bytes and then append a seek action and append the rest of the bytes. Try just removing that block and see if it works.
Otherwise I think it's ok.

in "ul.readBytes(bytes, bytes.length);" line, there is a bug i guess. It's never worked for me also. It always return full length (from 0 to the available bytes). So It have a huge memory leak. But if you are using flash player 11.4 or later, you can change it like this.
ul.position = bytes.length;
ul.readBytes(bytes);

Related

AS3 debugger stops responding while trying to load image into sprite using Loader

I'm trying to create a simple Menu in AS3. There is a sprite called startButton, which when pressed will call the function startGame, and that's it! But, not so easy. I'm using flashdevelop IDE so I'm trying to call a loader to get a .png image file for the spite startButton. But, it doesn't work. There are no error messages, the debugger just does not respond. Any help? Here is the code for both files
Main code:
package {
//Other Files
import Menu;
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.ui.Mouse;
public class Main extends Sprite {
//Game values
public static var gameWidth:int = 750;
public static var gameHeight:int = 750;
public function Main() {
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
addChild(Menu.startButton);
Menu.startButton.addEventListener(MouseEvent.CLICK, startGame);
stage.addEventListener(Event.ENTER_FRAME, update);
}
private function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
}
//Function starts game
public function startGame(evt:MouseEvent):void {
removeChild(Menu.startButton);
}
//Updates every 60 seconds
public function update():void {
trace("Updated");
}
}
}
And Menu Image code:
package {
//Other files
import Main;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLRequest;
public class Menu extends Sprite {
public static function imageLoaded():void {
startButton.addChild(loader);
//initizlize values for startButton Bitmap
startButton.x = (Main.gameWidth / 2) - (startButton.width / 2);
startButton.y = (Main.gameHeight / 2) - (startButton.height / 2);
}
//create startButton Bitmap
public static var startButton:Sprite = new Sprite();
public static var loader:Loader = new Loader();
loader.load(new URLRequest("lib/menustartbutton.png"));
loader.addEventListener(Event.COMPLETE, imageLoaded);
}
}
By the way, I wait for the loader to successfully load the image before working with it, just in case the image takes more time and it draws errors.
The problem is that you misuse static. all static methods/properties are initialized before the classes themselves. As a result static can receive values but they cannot run any code. Running code has to happen after all classes are ready to go which is not the case when static is initialized. In your case startButton and loader are created correctly but the next line never runs 'loader.load'.
Don't misuse static, you are obviously trying to use static to make you code writing and life easier but at the end because you are misusing it you will always end up with more problems.

AS3 Architecture : How to tween a single instance of an object on multiple calls?

I am new to actionscript and am having a trouble displaying a single instance of an object, using FlashDevelop.
I have a main.as in which I am displaying an image as background. Then I display a rectangle containing some text that tweens as the mouse hovers a target (appearing/disappearing on the stage). The rectangle is in a class TextBox.as .
I know my code is quite messy because it creates a new instance of the rectangle everytime I reach the target (calling the tween). But if I try to switch it around it gives me errors. Also I cannot seem to remove my rectangle (with removeChild()) once it is created, it cannot find the child.
Could anyone indicate me what is the architecture I should use so that only one instance of the rectangle is created?
Here's a bit of my code:
//IMPORT LIBRARIES
import Classes.TextBox;
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import com.greensock.TweenLite;
// Setup SWF Render Settings
[SWF(width = "620", height = "650")]
public class Main extends Sprite
{
//DEFINING VARIABLES
[Embed(source="../lib/myimage.jpg")]
private var picture:Class;
private var myTween:TweenLite;
//CONSTRUCTOR
public function Main():void
{
addChild(new TextBox);
addChild(new picture);
addEventListener(MouseEvent.MOUSE_OVER, appear);
}
//ROLLDOWN FUNCTION
public function appear(e:MouseEvent):void
{
trace("Appear");
var text:TextBox = new TextBox();
addChild(text);
addChild(new picture);
if (picture) {
removeEventListener(MouseEvent.MOUSE_OVER, appear);
//addEventListener(Event.COMPLETE, appearComplete);
myTween = new TweenLite(text, 1, { y:340 , onComplete:appearComplete, onReverseComplete:disappearComplete} );
}
}
Thanks in advance.
i dont know what tweening you want to achieve but you should reuse your textbox instance, e.g.:
import Classes.TextBox;
import com.greensock.TweenLite;
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
[SWF(width = "620", height = "650")]
public class Main extends Sprite {
[Embed(source="../lib/myimage.jpg")]
private var pictureClass:Class;
private var picture:Bitmap;
private var textbox:TextBox;
public function Main():void {
if (stage)
init();
else
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
picture = new pictureClass();
textbox = new TextBox();
addChild(picture);
addChild(textbox);
addEventListener(MouseEvent.MOUSE_OVER, tween);
}
public function tween(e:MouseEvent):void {
removeEventListener(MouseEvent.MOUSE_OVER, tween);
TweenLite.to(textbox, 1, { y:340, onComplete:reverse } );
}
private function reverse():void {
TweenLite.to(textbox, 1, { y:0, onComplete:tweenComplete } );
}
private function tweenComplete():void {
addEventListener(MouseEvent.MOUSE_OVER, tween);
}
}

"SecurityError: Error #2122" on loading content from a redirected image

This happens on many occasions when loading content from the web, but for us, it's most common loading images through Facebook's graph shortcut call.
Something as simple as:
package
{
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLRequest;
import flash.system.LoaderContext;
public class RedirectTestFail extends Sprite
{
private const url:String = 'https://graph.facebook.com/4/picture';
private const context:LoaderContext = new LoaderContext(true);
public function RedirectTestFail()
{
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
loader.load(new URLRequest(this.url), this.context);
}
protected function onComplete(event:Event):void
{
this.addChild((event.target as LoaderInfo).content);
}
}
}
Gives a dreaded "SecurityError: Error #2122" error.
Despite other answers suggesting something as simple as:
Security.loadPolicyFile("https://fbcdn-profile-a.akamaihd.net/crossdomain.xml");
This isn't clear, nor comprehensive enough. Facebook have different image servers, that I've which I've been caught with before. This could be deemed a Flash Player Bug, which I'd accept, but as a security concern, I can understand them not allowing a redirect by default, as in you should handle it yourself.
I now use below. You try to do your normal behaviour, but wrap it in a try/catch for SecurityError. If one is thrown, catch it, and if the domain of the loaderInfo is different to the domain you requested, you run a 'Security.allowDomain' and 'Security.loadPolicyFile' on it, and attempt to load it one more times. This works perfectly in practise, with only a tiny amount of overhead.
package
{
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.system.Security;
public class RedirectTest extends Sprite
{
private const url:String = 'https://graph.facebook.com/4/picture';
private const context:LoaderContext = new LoaderContext(true);
public function RedirectTest()
{
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
loader.load(new URLRequest(this.url), this.context);
}
protected function onComplete(event:Event):void
{
try
{
this.addChild((event.target as LoaderInfo).content);
}
catch(error:SecurityError)
{
trace(error);
var loaderInfo:LoaderInfo = (event.target as LoaderInfo);
var loaderDomain:String = loaderInfo.loader.contentLoaderInfo.url;
if(-1 == this.url.indexOf(loaderDomain))
{
Security.loadPolicyFile(loaderDomain + 'crossdomain.xml');
if( 0 == loaderDomain.indexOf('https') )
{
Security.allowDomain(loaderDomain);
}
else
{
Security.allowInsecureDomain(loaderDomain)
}
loaderInfo.loader.load(new URLRequest(this.url), this.context);
return;
}
throw error;
}
}
}
}
if you no need to manipulate pixels with loaded image BitmapData object, then you can just remove context from the loader.load
but without context.checkPolicyFile = true you will cant add smoothing to image

Workaround for Security.allowDomain

I have number of SWF files which should be accessed from external server. To be able to do this I need to have Security.allowDomain in each SWF file. The problem here is that I don't have FLA for those files and there are thousands of SWFs.
If there a better way to configure those files to be accessible from other domain?
Like having some kind of config file or so.
Yes, there is one workaround, but I think it's a security hole, so it can be fixed in any release of flash player. Meanwhile it works now so here is solution - use URLLoader with BINARY dataFormat as preloader for swf bytes:
swf without Security permissions for it scripting:
package
{
import flash.display.MovieClip;
public class astest extends MovieClip
{
public function astest()
{
}
public function externalCheck():void
{
graphics.beginFill(0xFF0000);
graphics.drawCircle(100, 100, 100);
}
}
}
Loader swf that wants to load previous swf and call the externalCheck method:
package
{
import flash.display.Loader;
import flash.display.MovieClip;
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.utils.ByteArray;
public class astest2 extends MovieClip
{
private var loader:Loader;
private var urlLoader:URLLoader;
public function astest2()
{
init();
}
//this method works fine
protected function init():void
{
urlLoader = new URLLoader();
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.load(new URLRequest("http://domain_with_your_swfs/astest.swf"));
urlLoader.addEventListener(Event.COMPLETE, function(event:Event):void
{
addChild(loader = new Loader());
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoader);
loader.loadBytes(urlLoader.data as ByteArray);
});
}
//this method will fire SecurityError when calling the 'externalCheck' method
protected function init2(event:Event = null):void
{
addChild(loader = new Loader());
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoader);
loader.load(new URLRequest("http://domain_with_your_swfs/astest.swf"));
}
protected function onLoader(event:Event = null):void
{
var swf:Object = loader.content;
swf.externalCheck();
}
}
}
Don't forget to place the crossdomain.xml file to the root of your server with swf files to load, without it URLLoader will not able to load the bytes, it's the only security requirement.

Playing successive wav sounds in as3

I am trying to play through AS3 external wav sounds in a successive way. The obvious way to do it is to use this algorithm:
1. play sound 1
2. when sound 1 is done, play sound 2
etc.
The problem is that I fail the step number 2 (the "when sound 1 is done" part).
Please answer for wav sounds only, NOT mp3.
Here is my code:
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.events.Event;
import flash.utils.ByteArray;
import flash.media.Sound;
import org.as3wavsound.WavSound;
import org.as3wavsound.WavSoundChannel;
public final class Main extends Sprite
{
public final function Main():void
{
playB.addEventListener(MouseEvent.MOUSE_UP, loadWav);
}
private final function loadWav(e:MouseEvent):void
{
var urlRequest:URLRequest = new URLRequest('Phone.wav');
var wav:URLLoader = new URLLoader();
wav.dataFormat = 'binary';
wav.load(urlRequest);
wav.addEventListener(Event.COMPLETE, playWav);
}
private final function playWav(e:Event):void
{
var tts:WavSound = new WavSound(e.target.data as ByteArray);
tts.play();
}
}
}
(This is step 1 of course)
Thanx
Try putting your sound into a SoundChannel - which is returned from the play command, then listen for the complete event:
private final function playWav(e:Event):void
{
var tts:WavSound = new WavSound(e.target.data as ByteArray);
var channel:WavSoundChannel = tts.play();
channel.addEventListener(Event.SOUND_COMPLETE, completeHandler)
}
private function completeHandler(e:Event):void {
//play next sound
}