How to export AS3 animations to livestreaming softwares like OBS, vMix? - actionscript-3

Do you know any library to export AS3 animations to streaming softwares like OBS, vMix, etc?
I don't want to use the "chroma key" technique because it has limitations. I want to export the "real transparency" animations from AS3 to the streaming software.
I also tried to use draw() in BitmapData to render every frame from AS3 to NDI output, but it is very slow. The app use draw() in AS3 to render the graphic on the stage as BitmapData to FFMPEG, and FFMPEG will give it to NDI output.
I think that doing AS3-->FFmpeg-->NDI is a slow process. I want to find another better solution. OBS screen capturing has the good speed, but it doesn't have transparency.
Here is my code for transfer from AS3 to FFmpeg (and FFmpeg will transfer to NDI output):
import flash.desktop.*;
import flash.filesystem.File;
import flash.display.Bitmap;
var NP_StartupInfo:NativeProcessStartupInfo;
var process:NativeProcess=new NativeProcess();
do_NativeProcess_FFMPEG();
function do_NativeProcess_FFMPEG () : void
{
NP_StartupInfo = new NativeProcessStartupInfo();
var Args_FFMPEG:Vector.<String> = new Vector.<String>();
Args_FFMPEG.push("-f", "rawvideo");
Args_FFMPEG.push("-pix_fmt", "argb");
Args_FFMPEG.push("-s", "1920x1080");
Args_FFMPEG.push("-r", "30");
Args_FFMPEG.push("-i", "-");
Args_FFMPEG.push("-f", "libndi_newtek");
Args_FFMPEG.push("-pix_fmt","rgba","OPreview");
NP_StartupInfo.executable = File.applicationDirectory.resolvePath("ffmpeg.exe");
NP_StartupInfo.arguments = Args_FFMPEG;
process = new NativeProcess();
process.start( NP_StartupInfo );
write_STDIn_Data();
}
function write_STDIn_Data () : void
{
stage.addEventListener(Event.ENTER_FRAME, update);
}
function update(e:Event) : void
{
makeVideoFrame();
}
var snapShot_BMD:BitmapData= new BitmapData(1920, 1080, true, 0x00000000);
var frame_BMP:Bitmap = new Bitmap();
var ba_Pixels:ByteArray = new ByteArray();
var sendingBA:ByteArray = new ByteArray();
function makeVideoFrame() : void
{
snapShot_BMD.drawWithQuality(stage, null, null, null, null, false, StageQuality.MEDIUM);
frame_BMP.bitmapData = snapShot_BMD;
ba_Pixels = frame_BMP.bitmapData.getPixels( frame_BMP.bitmapData.rect);
sendingBA = new ByteArray();
sendingBA.writeBytes(ba_Pixels);
process.standardInput.writeBytes(sendingBA);
snapShot_BMD= new BitmapData(1920, 1080, true, 0x00000000);
frame_BMP = new Bitmap();
ba_Pixels.clear();
}
For 10fps animations, the process is ok. But for 20-30fps, it so slow and does not have real-time output.

Related

Creating inline function after loading an image causes memory leak

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();
}
}

Recording Flash Webcam FMS 4.5 to Mp4 results in terrible quality

I have successfully setup recording webcam to FLV using FMS 4.5 developer edition, so I wanted to attempt recording to an Mp4 next. I am doing a silent save of the video file because the goal here is to be able to have these videos playable outside of Flash/FMS. I set the program up to save the Mp4 file generated by FMS, but the quality is terrible. I am seeing green distortion when movement is captured, and heavy pixelation. Here is my test application code that saves the video file after 5 seconds of recording. Can anyone please point out where I am going wrong? Any help is greatly appreciated.
package com
{
import flash.display.*;
import flash.net.*;
import flash.utils.Timer;
import flash.events.*;
import flash.filesystem.File;
import flash.media.*;
public class Main extends MovieClip
{
private var nc:NetConnection;
private var ns:NetStream;
private var nsPlayer:NetStream;
private var vid:Video;
private var vidPlayer:Video;
private var cam:Camera;
private var mic:Microphone;
private static const LOCAL_VIDEO:String = "myCamera";
private static const VIDEO_FPS:uint = 30;
private static const SAVE_FOLDER_NAME:String = "Saved_Videos";
private static const PATH_TO_FMS:String = "C:/Program Files/Adobe/Flash Media Server 4.5";
private var timer:Timer = new Timer(5000, 1);
public function Main()
{
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(evt:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
nc = new NetConnection();
nc.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
nc.connect("rtmp://localhost/PublishLive/myCamera");
}
function onNetStatus(evt:NetStatusEvent):void
{
if(evt.info.code == "NetConnection.Connect.Success")
{
publishCamera();
displayPublishingVideo();
timer.addEventListener(TimerEvent.TIMER_COMPLETE, timerCompleted);
timer.start();
}
}
private function timerCompleted(evt:TimerEvent)
{
trace("timer completed");
timer.stop();
ns.close();
ns = null;
var saveFile:File = new File(PATH_TO_FMS + "/applications/PublishLive/streams/" + LOCAL_VIDEO + "/" + LOCAL_VIDEO + ".mp4");
var fileName:String = "Video" + ".mp4";
var dir:File = File.documentsDirectory.resolvePath(SAVE_FOLDER_NAME);
dir.createDirectory();
var fileToSave = dir.resolvePath(fileName);
if(fileToSave.exists)
{
fileToSave.deleteFile();
}
saveFile.copyTo(fileToSave, true);
}
private function publishCamera()
{
var h264Settings:H264VideoStreamSettings = new H264VideoStreamSettings();
h264Settings.setProfileLevel(H264Profile.BASELINE, H264Level.LEVEL_3_1);
cam = Camera.getCamera();
cam.setMode(stage.stageWidth, stage.stageHeight, VIDEO_FPS, true);
cam.setQuality(0, 90);
cam.setKeyFrameInterval(15);
cam.setMotionLevel(100);
mic = Microphone.getMicrophone();
mic.setSilenceLevel(0);
mic.rate = 11;
ns = new NetStream(nc);
ns.videoStreamSettings = h264Settings;
ns.attachCamera(cam);
ns.attachAudio(mic);
ns.publish("mp4:myCamera.mp4", "record");
}
private function displayPublishingVideo():void
{
vid = new Video();
vid.width = stage.stageWidth;
vid.height = stage.stageHeight;
vid.attachCamera(cam);
addChild(vid);
}
}
}
Ok, so I am answering my own question. I found the answer here along with the link to the tool to process: adobe help site
You must convert the files after recording them using a post-processing tool so they can be viewed in other video players.
Edit: VLC can actually play the unprocessed file, so I thought it was a quality issue at first!
You could try to change the quality of the video stream VideoStreamSettings.setQuality(bandwidth:int, quality:int) (link).
You only set one of the bandwidth or quality values and leave the other to 0. I would try to set bandwidth (measured in bytes per second) to 750000 (6 Mbps), which should be plenty for anything less than full HD.
So, in your case, you could try:
h264Settings.setQuality(750000, 0);

How to save a cropped image on stage and load into another swf with air

i have a problem. The main problem is in my apk , there are 3 swf file.
first swf file decides who can be the second swf between swfA and swfB.
for example swfA is chosen. swfA takes a screenshot of an cropped area and load main swf again. i need to load this screenshot into main swf right now. but it is not visible on the screen. it doesnt load. where is the point that i miss ?
save an image code is : (class main for swf1)
public function takeScreenshot():void
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.utils.ByteArray;
import flash.net.FileReference;
import PNGEnc;
var myCroppedImage:Bitmap = crop(232, 121, 188, 570, this);
var d:BitmapData = new BitmapData(myCroppedImage.width, myCroppedImage.height);
d.draw(myCroppedImage);
var bild:ByteArray = PNGEnc.encode(d);
var newImage:File = File.applicationDirectory.resolvePath("app:/yeniResim.png");
var fileStream:FileStream = new FileStream();
fileStream.open(newImage , FileMode.UPDATE);
fileStream.writeBytes(bild);
fileStream.close();
//isimGoster.visible = false;
}
public function crop( _x:Number, _y:Number, _width:Number, _height:Number, displayObject:DisplayObject = null):Bitmap
{
var cropArea:Rectangle = new Rectangle( 0, 0, _width, _height );
var croppedBitmap:Bitmap = new Bitmap( new BitmapData( _width, _height ), PixelSnapping.ALWAYS, true );
croppedBitmap.bitmapData.draw( (displayObject!=null) ? displayObject : stage, new Matrix(1, 0, 0, 1, -_x, -_y) , null, null, cropArea, true );
return croppedBitmap;
}
load an image into another swf : (class main for swf 2)
public function frame4():void {
stop();
resim = new Loader();
var meRequest:URLRequest = new URLRequest('app:/yeniResim.png');
resim.load(meRequest);
var mce:MovieClip = resim.content as MovieClip;
mce.x = 480;
mce.y = 320;
}
It looks like you're not waiting for the Loader to finish loading the file before you access its content. For example, it is likely that "content" in this line below does not contain the loaded image at the time you try and use it:
var mce:MovieClip = resim.content as MovieClip;
It's been a long time since I did anything in Flash, so this code may not be quite right. But I'm very certain you need to do something like this:
var mce:MovieClip;
resim = new Loader();
resim.contentLoaderInfo.addEventListener(Event.COMPLETE, imgLoaded);
var meRequest:URLRequest = new URLRequest('app:/yeniResim.png');
resim.load(meRequest);
function imgLoaded(event:Event):void
{
mce = resim.content as MovieClip;
mce.x = 480;
mce.y = 320;
}
The other alternative is not waiting for the COMPLETE event and just adding the Loader to the stage. However, since you want to cast Loader.content into a MovieClip you need to wait for the content to be loaded. Check this example for more details.
It's also good to attach an event listener to handle any failures. Loader.contentLoaderInfo is a LoaderInfo object, you can see the other events that it dispatches in case you need to trouble shoot further.

Don't load xml data to list when publishing by Flash CS 5.5

I build a flash video player which works fine when exported to swf. However when I publish it (F12) does not loading the xml file. As a result video player stuck to frame 2 (where presented a List with video titles and with click on title plays the specific video). The xml link does not ends to .xml. Here is my code about xml loading. I try to change the Flash Player from 10.2 to 9 but I have the same problem.In addition some buttons in frame 2 does not works I suppose because of "Stuck" from xml loading.( Work fine when exported in swf!)
Flash Player version: 10.2, Actionscript:3.0, Flash Professional CS 5.5, fps:24, size: 850px(w)x480px(h). Code from FRAME 2 . Thanks for your time guys.
Security.allowDomain( "*" );
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.events.Event;
import fl.controls.Label;
var loader:URLLoader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.TEXT;
var request:URLRequest = new URLRequest("http://www.olympiacos.org/feeds/videos")
loader.load(request);
loader.addEventListener(Event.COMPLETE, handleComplete);
loader.addEventListener(IOErrorEvent.IO_ERROR, onIOError)
loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError)
loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, onHTTPStatus2)
function onIOError(evt:IOErrorEvent){
trace("IOError: "+evt.text);
}
function onHTTPStatus2(evt:HTTPStatusEvent){
trace("HTTPStatus: "+evt.status);
}
function onSecurityError(evt:SecurityErrorEvent){
trace("SecurityError: "+evt.text);
}
function handleComplete(event:Event):void
{
try
{
var rawXML:XML = new XML(event.target.data);
trace(rawXML);
var list:XMLList = rawXML.channel.item;
for (var i:uint=0; i<list.length(); i++)
{
t_text.text = list.title.text()[i];
var data1:String = (list[i].description);
var srcRegExp:RegExp = /src="(.*?)"/;
var data2:String = srcRegExp.exec(data1)[1];
var lwr = data2.search(/1_/i);
var her = lwr + 10;
var data3 = data2.substring(lwr,her);
trace("Entry Id"+i+": "+data3);
List1.addItem({label:list.title.text()[i],data:data3});
}
}
catch (e:TypeError)
{
//Could not convert the data, probavlu because
//because is not formated correctly
trace("Could not parse the XML");
trace(e.message);
}
}
List1.addEventListener(Event.CHANGE, itemChange);
openbtn.addEventListener(MouseEvent.CLICK, opentween);
closebtn.addEventListener(MouseEvent.CLICK, closetween);
closebtn.visible=true;
openbtn.visible=false;
function itemChange(e:Event):void
{
if ( currentFrame == 2)
{
trace("Video selected");
gotoAndPlay(3);
}
t_text.text = List1.selectedItem.label;
var videoslcd = List1.selectedItem.data;
}
stop();
what;s your publishing setting regarding playback security? Should be "Access network only"

Fast or asynchronous AS3 JPEG encoding

I'm currently using the JPGEncoder from the AS3 core lib to encode a bitmap to JPEG
var enc:JPGEncoder = new JPGEncoder(90);
var jpg:ByteArray = enc.encode(bitmap);
Because the bitmap is rather large (3000 x 2000) the encoding takes a long while (about 20 seconds), causing the application to seemingly freeze while encoding. To solve this, I need either:
An asynchronous encoder so I can keep updating the screen (with a progress bar or something) while encoding
An alternative encoder which is simply faster
Is either possible, and how can I do it?
I found an asynchronous encoder:
http://www.switchonthecode.com/tutorials/flex-tutorial-an-asynchronous-jpeg-encoder
Setting up the encoder to be asynchronous would likely be your best bet.
Here are two examples from Adobe
This example is with actionscript/flex, but its the same idea.
You can do it much faster with Alchemy:
http://www.websector.de/blog/2009/06/21/speed-up-jpeg-encoding-using-alchemy/
http://segfaultlabs.com/devlogs/alchemy-asynchronous-jpeg-encoding-2
You can use the alchemy encoder. It is really fast and you can encode images asynchronously. You can use this class to abstract it.
public class JPGAlchemyEncoder {
private static var alchemyWrapper:Object;
private var quality:Number;
public function JPGAlchemyEncoder(quality:Number) {
this.quality = quality;
if (!alchemyWrapper){
var loader:CLibInit = new CLibInit;
alchemyWrapper = loader.init();
}
}
public function encode(bitmapData:BitmapData):ByteArray{
var data: ByteArray = bitmapData.clone().getPixels( bitmapData.rect );
data.position = 0;
return alchemyWrapper.write_jpeg_file(data, bitmapData.width, bitmapData.height, 3, 2, quality);
}
public function encodeAsync(bitmapData:BitmapData, completeHandler:Function):void{
var encodedData:ByteArray = new ByteArray();
var data: ByteArray = bitmapData.clone().getPixels(bitmapData.rect);
data.position = 0;
var encodeComplete:Function = function():void{
completeHandler(encodedData);
};
alchemyWrapper.encodeAsync(encodeComplete, data, encodedData, bitmapData.width, bitmapData.height, quality);
}
}
}
asynchronous decode the png picture in separate thread ,supported by new version ...
var loaderContext:LoaderContext = new LoaderContext();
loaderContext.imageDecodingPolicy = ImageDecodingPolicy.ON_LOAD;
var loader:Loader = new Loader();
loader.load(new URLRequest("...png"),loaderContext);
addChild(loader);
that's official.