I'm a newbie to monodroid and I have two killers here I wish someone can help me to resolve:
I have a monodrid app which uploads an image to a web service, the images comes from the device camera or the Pictures gallery.
First thing is I use the following code to take a picture from the device camera:
var intent = new Intent(MediaStore.ActionImageCapture);
var availableActivities = this.PackageManager.QueryIntentActivities(intent, PackageInfoFlags.MatchDefaultOnly);
if (availableActivities != null && availableActivities.Count > 0)
{
var dir = new Java.IO.File(
Android.OS.Environment.GetExternalStoragePublicDirectory(
Android.OS.Environment.DirectoryPictures), "myapp");
if (!dir.Exists())
{
dir.Mkdirs();
}
_file = new Java.IO.File(dir, String.Format("image-{0}.jpg", Guid.NewGuid()));
StaticDataHolder.ImageUri = Android.Net.Uri.FromFile(_file);
intent.PutExtra(MediaStore.ExtraOutput, Android.Net.Uri.FromFile(_file));
StartActivityForResult(intent, RESULT_CAMERA_CAPTURE);
}
And this code to read scaled Bitmap from StaticDataHolder.ImageUri into an ImageView control so the user can confirm weather to use the selected image or not:
Bitmap largeBitmap = null;
try
{
image.SetImageBitmap(null);
if (Bitmap != null)
{
Bitmap.Recycle();
Bitmap.Dispose();
}
largeBitmap = MediaStore.Images.Media.GetBitmap(ContentResolver, StaticDataHolder.ImageUri);
Bitmap = GetScaledImage(largeBitmap);
SetImageFromBitmap(image, Bitmap);
return true;
}
catch (Java.Lang.OutOfMemoryError)
{
return false;
}
finally
{
if (largeBitmap != null)
{
largeBitmap.Recycle();
largeBitmap.Dispose();
}
}
The second issue is when the user confirm the selected image weather its taken from the camera or picked from the gallery, I create a new Bitmap to be compressed and sent to a web service which I host using the following code:
byte[] buffer = null;
MemoryStream ms = null;
Bitmap bmp = null;
ms = new MemoryStream();
bmp = MediaStore.Images.Media.GetBitmap(ContentResolver, StaticDataHolder.ImageUri);
bmp.Compress(Bitmap.CompressFormat.Jpeg, 60, ms);
buffer = ms.GetBuffer();
ms.Dispose();
bmp.Recycle();
bmp.Dispose();
and then I pass the buffer to the web service method.
This code will work perfectly the first time I start the app, But when I try to load a second image I get OutOfMemoryError exception and the image is not loaded.
How i can load a Bitmap and dispose it probably so I can avoid this exception?.
Please note that every step here is managed via separate activities:
1.Take or choose a photo activity.
2.Confirm the selected photo activity.
3.Load the selected photo and send it to the web service activity.
As far as I know the activity is disposed when finished so by default all resources used in it will also be disposed but in my case its appears to be not!.
I have searched it for 3 days with no luck, any help would be much appreciated.
Bitmaps in general are a pain on Android since they live in the native heap and are not directly garbage collected, well in Android < 2.3.x at least. Have you tried using BitmapFactory?
For example:
var opts = new BitmapFactory.Options() { InPurgeable = true, InInputShareable = true };
var bitmap = BitmapFactory.DecodeFile (pathToBitmap, opts);
Will cause the result bitmap to be purgeable from memory when needed (http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html)
Related
I have an AIR app that renders images from a Flash editor. You can customize several surfaces - they all have the same width and height. Each surface is then rendered by the AIR app.
I'm struggling with a memory leak I fail to address.
For each surface I need to render, I have a Sprite containing many components (other sprites - some having event listeners, BitmapDatas and other children components, sprites, etc).
I'm already aware of BitmapData issues to be garbage collected, I tested both :
creating a new BitmapData for each rendering then dispose() and point to null
reuse a single BitmapData for each rendering
Memory leak is still happening in equal proportion.
Here is my loop :
var bm:BitmapData = new BitmapData(destDim.x, destDim.y, true, bgColor);
var mtx:Matrix = new Matrix();
trace('before drawing :'+(System.privateMemory/1024));
bm.draw(myBigSprite, mtx, null, null, null, true);
trace('after drawing :'+(System.privateMemory/1024));
var result:Bitmap = new Bitmap(bm, PixelSnapping.NEVER, true);
//return result and encode Bitmap to png
result.bitmapData.dispose();
result.bitmapData = null;
result = null;
Result :
before drawing :208364
after drawing :302816
Wrote bitmap to file: surface0.png
before drawing :303296
after drawing :446160
Wrote bitmap to file: surface1.png
before drawing :446160
after drawing :565212
Wrote bitmap to file: surface2.png
before drawing :565924
after drawing :703100
Wrote bitmap to file: surface3.png
before drawing :703572
after drawing :834420
Wrote bitmap to file: surface4.png
I feel like I'm missing something in draw function behaviour. It seems like I have newly created instances of the components of myBigSprite that persists after draw operation.
I tried to completely destroy myBigSprite at the end of each loop, it does not change anything....
Any hint would be appreciated !
Ok guys, I eventually understood and fixed this issue.
First of all, I installed and ran Adobe Scout. Excellent tool.
As you may not see (plus it's in French language), I generated 3 surfaces corresponding to the edges. The "big" green bar on the right which is mass memory consuming represent "Bitmap display objects". Interesting ! Never heard of those before.
A Google search later, I found this article : https://help.adobe.com/en_US/as3/dev/WS5b3ccc516d4fbf351e63e3d118a9b90204-7e26.html
It explains
For example, in the code excerpt shown earlier, once the load
operation for the pict Loader object is complete, the pict object will
have one child display object, which is the bitmap, loaded. To access
this bitmap display object, you can write pict.getChildAt(0).
So I began to undestand that, somehow, maybe Bitmap object are attached as children on some objects of myBigSprite.
Finally, I created a recursive function to search and destroy all Bitmap, BitmapData and ByteArray objects contained in myBigSprite AFTER the draw operation
//inside render function
bm.draw(myBigSprite, mtx, null, null, null, true);
destroyDisplayObjects(myBigSprite);
...
private function destroyDisplayObjects(obj):void{
if ("numChildren" in obj){
for (var i:int = 0; i<obj.numChildren; i++)
{
destroyDisplayObjects(obj.getChildAt(i));
}
}
else {
if (flash.utils.getQualifiedClassName(obj) == "flash.display::Bitmap"){
//trace ('FREE BITMAP');
obj.bitmapData.dispose();
obj.bitmapData = null;
obj = null;
return;
}
else if (flash.utils.getQualifiedClassName(obj) == "flash.display::BitmapData"){
//trace ('FREE BITMAPDATA');
obj.dispose();
obj = null;
return;
}
else if (flash.utils.getQualifiedClassName(obj) == "flash.display::ByteArray"){
//trace ('FREE BYTEARRAY');
obj.clear();
obj = null;
return;
}
return;
}
}
Et voilĂ , memory is 100% cleaned after the draw operation, no more leak :)
You should declare you Bitmap and BitmapData outside of any functions and then simply recycle them for usage inside of your loop (instead of creating a new anything to be added in memory).
Use .dispose() only on the last image when you're sure you don't need bitmap data from bm variable anymore. Otherwse, if disposed, you'll have to create a new alternative var someThing :BitmapData = new BitmapData again for further usage.
////# declare globally (not inside some specific function..)
//var destDim :Point = new Point(your_X_num , your_Y_num);
//var bgColor :uint = 0x505050;
var bm:BitmapData = new BitmapData(destDim.x, destDim.y, true, bgColor);
var result:Bitmap = new Bitmap(bm, PixelSnapping.NEVER, true);
//result.bitmapData = bm; //can be set here but done within function for clarity...
var mtx:Matrix = new Matrix();
////# update bitmap by replacing its bitmapdata with new colour values of existing pixels
function Here_is_my_loop (): void
{
trace('before drawing :'+(System.privateMemory/1024));
//overwrite pixel colours in bitmap (result)
bm.draw(myBigSprite, mtx, null, null, null, true);
result.bitmapData = bm; //update bitmap
trace('after drawing :'+(System.privateMemory/1024));
//return result and encode Bitmap to png
//result.bitmapData.dispose();
//result.bitmapData = null;
//result = null;
}
I have a few buttons in my app which load a new object onto the scene when clicked. Below is the code being used for loading new objects:
character = new THREE.UCSCharacter();
var ucspath = "skinnedModel.json";
manager = new THREE.LoadingManager();
manager.onProgress = function(url, itemsLoaded, itemsTotal) {
console.log(url, itemsLoaded, itemsTotal);
};
var onProgress = function (xhr){
if(xhr.lengthComputable){
var bar = 150;
percentComplete = xhr.loaded / xhr.total * 100;
bar = Math.floor( xhr.loaded / xhr.total * bar );
document.getElementById("bar").style.width = bar + "px";
}
};
var loader = new THREE.XHRLoader(manager);
loader.crossOrigin = true;
loader.load(ucspath, function (text) {
var config = JSON.parse(text);
character.loadParts(config);
avatar = character.root;
avatar.name = "female";
scene.add(avatar);
}, onProgress);
After clicking the button 10~12 times, the loading becomes increasingly slow and eventually the page crashes. I could not detect any unsual behavior using Chrome DevTools.
Is there something that needs to be done in order to load as many objects as desired without overloading the browser?
[Update] Heap Snapshot
Thanks in advance.
It seems that you're facing a performance issue caused by your model file.
Fortunately, the problem of a having a huge model replicated with different traits is very common in the game industry so I can give you an hint though it's not that simple :
In your 3D editor, merge all of your different geometries into one single model and give to each of your model parts an easy name like hair_01, head_02, beard_03, ...
Load this model in your Three.js app just once. This is your template model.
Now, when you want to create a new model with a different appearance :
Clone the template model ;
Hide the parts you don't want ;
Show the parts you want.
Here's an example of the idea :
var copy = template.clone();
copy.getObjectByName('head_01').visible = false;
copy.getObjectByName('head_02').visible = true;
scene.add(copy);
Cloning the model prevents the engine from duplicating all geometry data (*).
This is a picture of a single 3D model file from an existing game that contains every character geometries and switch them in runtime to give a humanoid various appearances.
*See THREE.Mesh.clone()
I am wondering if someone who has handled NetStream.appendBytes in Flash knows how to get the bitmapData from a decoded video frame? I have already looked at this question but that is from 3 years ago and the more recent comment/answer seems to be gone. In 2014 has anyone managed to turn those bytes into a bitmap? I am working with Flash Player 11.8 and this is not a desktop/AIR app.
In the image below I can do steps 1) and 2) but there's a brick wall at step 3)
The problem is that simply using bitmapdata.draw(video_container); does not work but instead it throws a Policy File error even though I am using a byteArray (from local video file in the same directory as the SWF). No internet is even involved but Flash tells me that "No Policy File granted permission from the server" or such nonsense. I think the error is just a bail-out insteading of straight up saying "You are not allowed to do this.."
I have tried: trying to appease this Crossdomain.xml issue anyway and looking into all known security/domain settings. I came to the conclusion that the error is not the problem but a side effect of the issue.. The issue here being that: Flash Player is aware of the SWF's location and of any files local to it. That's okay when you pass a String as URL etc but when the Netstream data is not local to the SWF domain then it becomes a Policy File issue. Problem is my data is in the Memory not in a folder like the SWF and therefore cannot alllow bitmapData.draw since it cannot "police" an array of bytes, any known fixes for this?... (I can't even say the words I really wanted to use).
What I am trying to achieve: Is to essentially use Netstream as an H.263 or H.264 image decoder in the same way Loader is a JPEG-to-Bitmap decoder or LoadCompressed.. is an MP3-to-PCM decoder. You know, access the raw material (here RGB pixels), apply some effects functions and then send to screen or save to disk.
I know it is a little late, but I think I found a solution for your problem.
So, to avoid the Security sandbox violation #2123 Error, you have just to do like this :
// ...
net_stream.play(null);
net_stream.play('');
// ...
Hope that can help.
I know this question is a couple months old, but I wanted to post the correct answer (because I just had this problem as well and other will too).
Correct answer:
It's a bug that has been open at adobe for almost 2 years
Link to the bug on Adobe
Work Around until the bug gets fixed (I am using this and it works great):
Workaround using Sprite and graphics
To take a snapshot from a video stream we don't need NetStream.appendBytes which inject data into a NetStream object.
For that we can use BitmapData.draw which has some security constraints. That's why in many times we get a flash security error. About that, Adobe said :
"... This method is supported over RTMP in Flash Player 9.0.115.0 and later and in Adobe AIR. You can control access to streams on Flash Media Server in a server-side script. For more information, see the Client.audioSampleAccess and Client.videoSampleAccess properties in Server-Side ActionScript Language Reference for Adobe Flash Media Server. If the source object and (in the case of a Sprite or MovieClip object) all of its child objects do not come from the same domain as the caller, or are not in a content that is accessible to the caller by having called the Security.allowDomain() method, a call to the draw() throws a SecurityError exception. This restriction does not apply to AIR content in the application security sandbox. ...".
For crossdomain file creation and some other security config for AMS server, you can take a look on this post : Crossdomain Video Snapshot - Fixing BitmapData.draw() Security Sandbox Violation.
After allowing our script to get data from our video stream, we can pass to the code.
I wrote a code that play a video stream ( rtmp or http ) and take a snapshot to show it in the stage or save it as a file after applying a pixel effect :
const server:String = null; //'rtmp://localhost/vod'
const stream:String = 'stream'; // 'mp4:big_buck_bunny_480p_h264.mp4';
var nc:NetConnection;
var ns:NetStream;
var video:Video;
const jpg_quality:int = 80;
const px_size:int = 10;
nc = new NetConnection();
nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, function(e:AsyncErrorEvent):void{});
nc.addEventListener(NetStatusEvent.NET_STATUS, function(e:NetStatusEvent):void{
if(e.info.code == 'NetConnection.Connect.Success'){
ns = new NetStream(nc);
ns.addEventListener(NetStatusEvent.NET_STATUS, function(e:NetStatusEvent):void{});
ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, function(e:AsyncErrorEvent):void{});
video = new Video(320, 180);
video.x = video.y = 10;
video.attachNetStream(ns);
addChild(video);
ns.play(stream);
}
})
nc.connect(server);
btn_show.addEventListener(
MouseEvent.CLICK,
function(e:MouseEvent): void{
var bmp:Bitmap = pixelate(video, px_size);
bmp.x = 10;
bmp.y = 220;
addChild(bmp);
}
)
btn_save.addEventListener(
MouseEvent.CLICK,
function(e:MouseEvent): void{
var bmp:Bitmap = pixelate(video, px_size);
var jpg_encoder:JPGEncoder = new JPGEncoder(80);
var jpg_stream:ByteArray = jpg_encoder.encode(bmp.bitmapData);
var file:FileReference = new FileReference();
file.save(jpg_stream, 'snapshot_'+int(ns.time)+'.jpg');
}
)
function pixelate(target:DisplayObject, px_size:uint):Bitmap {
var i:uint, j:uint = 0;
var s:uint = px_size;
var d:DisplayObject = target;
var w:uint = d.width;
var h:uint = d.height;
var bmd_src:BitmapData = new BitmapData(w, h);
bmd_src.draw(d);
var bmd_final:BitmapData = new BitmapData(w, h);
var rec:Rectangle = new Rectangle();
rec.width = rec.height = s;
for (i = 0; i < w; i += s){
for (j = 0; j < h; j += s){
rec.x = i;
rec.y = j;
bmd_final.fillRect(rec, bmd_src.getPixel32(i, j));
}
}
bmd_src.dispose();
bmd_src = null;
return new Bitmap(bmd_final);
}
Of course, this is just a simple example to show the manner to get a snapshot from a video stream, you should adapt and improve it to your needs ...
I hope all that can help you.
In flex builder 4.5 i'm working on a project like cacoo.
I want to save diagrams(display object,ui components,text) before close the application into somewhere than I would be able to access after the application open again.
more clear:-If user edit some uml diagram on this project and save it for edit later and close application.after some days he/she want to edit previously saved diagram.
now how i'm save this diagram for future edit.
If save/open dialog will work for you, you can yse FileReference API. Before doing this, you have to implement serialization/deserialization of your state into/from String/ByteArray/XML object.
private var fileReference:FileReference;
// due to security restrictions, this method must be called from an
// event handler that responds to a user event (mouse click or key
// press), otherwise it will fail.
private function saveState(serializedState:*, fileName:String):void {
fileReference = new FileReference();
fileReference.addEventListener(Event.COMPLETE, onSaved);
fileReference.addEventListener(IOErrorEvent.IO_ERROR, onSavingError);
try {
fileReference.save(serializedState, fileName); // will open save dialog
} catch (e:Error) {
trace("error saving data: " + e.toString());
freeListeners();
}
}
private function onSaved(e:Event):void {
trace("saved!");
freeListeners();
}
private function onSavingError(e:ErrorEvent):void {
trace("error saving data: " + e.toString());
freeListeners();
}
private function freeListeners():void {
fileReference.removeEventListener(Event.COMPLETE, onSaved);
fileReference.removeEventListener(IOErrorEvent.IO_ERROR, onSavingError);
}
Similarly with restoring the state (use FileReference.browse(), then FileReference.load()).
If you need to save/restore app state without any dialogs, then you should probably use AIR (or SharedObject, as Raja Jaganathan suggested). But it seems to be not the case, as you want the user to be able to re-open the diagram in another system. To achieve this, you should allow the user to save his work to the appropriate place, so later he can move it to another machine/system and re-open it with your application.
Another alternative is to store everything on the server and provide the user with a list of saved files (like Cacoo does). If you go this way, you'll have to implement the corresponding server-side API. It may be REST API or smth like RTMP server. In the case of REST API, use FileReference.upload() to upload the data to your server, and URLLoader.load() to obtain it back.
You can store your diagram state through SharedObject for better you create one class which hold all of your state of Diagram so that later you can use
SharedObject using http://livedocs.adobe.com/flex/3/html/help.html?content=lsos_5.html
you can use registerClassAlias for custom class stored in sharedobject.
myClassInstance = new MyClass();
myClassInstance.x = 100;
myClassInstance.y = 100;
myClassInstance.text = "diagrams";
registerClassAlias("com.path.to.MyClass", MyClass);
myStuff = SharedObject.getLocal("myAppStuff");
myStuff.data.whatINamedIt = myClassInstance;
myStuff.flush();
now when get it back out... you can say:
myStuff = SharedObject.getLocal("myAppStuff");
var mySavedClass:MyClass = myStuff.data.whatINamedIt as MyClass;
Read mySavedClass instance value then inject to your diagram model when open again.
To implement application close event
http://www.flexer.info/2007/10/25/fabridge-warn-on-flex-application-exit/
Sprite or MovieClip other DisplayObject objects can not be direct serialized. So you should stored objects information (origin x,y, width, height, color, child info...). using a ByteArray or Array or Dictionary ... and that save to ShareObjects. later roll back from ShareObject and re-create Original Object. MovieClip or Sprite appropriate purpose is container.
Here is my test code.
1. create a Movieclip. purpose is container.
2. draw a rectangle using a graphics. And set the coordinates.
var drawWidth:Number = 500;
var drawHeight:Number = 300;
var rect:MovieClip = new MyRect();
rect.graphics.beginFill(0xffffff*Math.random(),1);
rect.graphics.drawRect(0,0,drawWidth,drawHeight);
rect.graphics.endFill();
rect.x= 300;
rect.y= 100;
3. Stores the information in the array.
var myRectInformation:Array = new Array();
myRectInformation.push(rect.x);
myRectInformation.push(rect.y);
myRectInformation.push(drawWidth);
myRectInformation.push(drawHeight);
var bmd:BitmapData = new BitmapData(rect.width, rect.height,true,0);
bmd.draw(rect);
//is byteArray.
myRectInformation.push(bmd.getPixels(new Rectangle(0,0,bmd.width,bmd.height)));
4. save to SharedObjects, array.
var mySaveData:SharedObject = SharedObject.getLocal("myStorage")
mySaveData.data.myRectInformation = myRectInformation;
mySaveData.flush();
5. this is load from SharedObject data stored. and recreate Objects.
var rect:MovieClip = new MyRect();
var loadBmd:BitmapData = new BitmapData(mySaveData.data.myRectInformation[2], mySaveData.data.myRectInformation[3], true, 1);
loadBmd.setPixels(new Rectangle(0,0,loadBmd.width,loadBmd.height), mySaveData.data.myRectInformation[4]);
var bmp:Bitmap = new Bitmap(loadBmd);
rect.addChild(bmp);
rect.x = mySaveData.data.myRectInformation[0];
rect.y = mySaveData.data.myRectInformation[1];
addChild(rect);
I found out that I could only get the metadata of the 1st video I clicked. How does metadata works? It could only load once before the video ends?
Here's some example what I'm doing
//will be adding new video when this function is called
public function set newVideo():void
{
videoProperties();
}
public function videoProperties():void
{
meta=new Object()
nc = new NetConnection();
nc.connect(null);
ns = new NetStream(nc);
nsArray[dList.currentIndex] = ns;
nsi = nsArray[dList.currentIndex];
// Add the buffer time to the video Net Stream
nsi.bufferTime = buffer;
// Set client for Meta Data Function
nsi.client = {};
nsi.client.onMetaData = onMetaData;
nsi.addEventListener(AsyncErrorEvent.ASYNC_ERROR,asyncErrorHandler);
nsi.addEventListener(NetStatusEvent.NET_STATUS, onNetStatusEvent);
nsi.play(videoURL);
nsi.pause();
nsi.seek(-1);
}
private function onMetaData(info:Object):void
{
//some video duration calculations
}
I tried to load all the metadata at once, but seems like it needs the video to be play only it will manage to get the metadata.
Are you trying to get the metadata without starting the video loading process? If so, that's not possible with actionscript alone. That said, since flvs load progressively you don't need to load an entire video to get at the meta data. You can load each video and stop loading it when you've got the metadata.