actionscript 3.0 - p2p filesharing troubles (max upload file size) - actionscript-3

i've designed a p2p apliccation which can transfer files without a server. and everything works fine. i can transfer files between peers. but as it turns out if file size is greater than 16mb (coz the biggest file i could transfer had a size of 15810 kb) it does't transfer to peer. this is the code i'm using:
private function browseFile(farIds:String = ""):void {
fIds = farIds;
file = new FileReference();
file.addEventListener(Event.SELECT, selectHandler);
file.browse();
}
private function selectHandler(event:Event):void {
var btn = getChild("browseFile_" + fIds)
if (btn && btn.alpha) btn.alpha = 0.5;
file = FileReference(event.target);
file.addEventListener(ProgressEvent.PROGRESS, progressHandler);
file.addEventListener(Event.COMPLETE, completeHandler);
file.load();
}
private function progressHandler(event:ProgressEvent):void{
ExternalInterface.call("fileLoadProgress", event.target.name, event.bytesTotal, event.bytesLoaded)
}
private function completeHandler(event:Event):void{
ExternalInterface.call("onFileLoaded")
var fileData:Object = new Object();
fileData.file = event.target.data
fileData.name = event.target.name;
var btn = getChild("browseFile_" + fIds)
if (btn && btn.alpha) btn.alpha = 1;
sendSomeData(fileData, fIds, "receiveFile");
}
public function receiveFile(info:Object, peerID:String):void{
ExternalInterface.call("alert", "receivedFile")
}
private function sendSomeData(data,farIds:String,func:String = "receiveSomeData"):void{
for(var id:String in sendStreams){
sendStreams[id].send(func, data, myPeerID);
}
}
can you tell me how can i allow transferring all files of any sizes?
thanks for your help!

You can split the file in chunks of say 8KB, and send them one by one. However you must check for the order of received chunks and any possible losses on the way.

Related

Out of memory with NetStream.appendBytes() or How to Play Large Video?

The following is a fully-functioning pure actionscript project for AIR
When run, try opening a large FLV (I'm testing it with a 3GB file)
With DEBUG_UNUSED_BUFFER and DEBUG_APPEND_VIDEO set to false, it works fine- reads through the entire file without a problem.
However, with either of those set to true, it crashes with an OUT OF MEMORY error.
For practical purposes, I'm more interested in why appendBytes() fails, but for the sake of interest, the DEBUG_UNUSED_BUFFER only makes it to like 6% of the file while DEBUG_APPEND_VIDEO makes it to around 46% or so.
Question: How then are we supposed to play a large video?!
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.MouseEvent;
import flash.events.ProgressEvent;
import flash.events.TimerEvent;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.net.FileFilter;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.net.NetStreamAppendBytesAction;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
import flash.utils.ByteArray;
import flash.utils.Timer;
public class MEMORY_TEST extends Sprite
{
//Set this to throttle data processing to once every DEBUG_THROTTLE_TIME milliseconds
// 0 = no throttling at all
// Note this this seems to make little difference, other than making it easier to see what's happening
private static const DEBUG_THROTTLE_TIME:Number = 100;
//Set this to write all bytes to an unused buffer.
//THIS FAILS (at around 237912064 bytes)!!!!!
private static const DEBUG_UNUSED_BUFFER:Boolean = false;
//Set this to write the video data via appendBytes.
//THIS FAILS (at around 1360003072 bytes)!!!!
private static const DEBUG_APPEND_VIDEO:Boolean = true;
/****************************************************************/
/******* Nothing else to configure below this line **************/
/****************************************************************/
private var openButton:Sprite;
private var statusTextField:TextField;
private var inputFile:File = null;
private var inputFileStream:FileStream = null;
private var netStream:NetStream = null;
private var netConnection:NetConnection = null;
private var readBytes:ByteArray = null;
private var totalBytesRead:Number = 0;
private var throttleTimer:Timer = null;
private var unusedBuffer:ByteArray = null;
private static const READSIZE:uint = 2048;
public function MEMORY_TEST()
{
this.addEventListener(Event.ADDED_TO_STAGE, onStage);
}
/*************************
*
* UI SETUP
*
**************************/
private function onStage(evt:Event) {
this.removeEventListener(Event.ADDED_TO_STAGE, onStage);
makeButtonAndStatus();
updateStatus('Click the button to begin');
}
private function makeButtonAndStatus(buttonText:String = 'Open File') {
var textField:TextField = new TextField();
var fmt:TextFormat = new TextFormat();
var padding:Number = 20;
var halfPadding:Number = padding/2;
//Button
fmt.color = 0xFFFFFF;
fmt.size = 24;
fmt.font = "_sans";
fmt.align = TextFormatAlign.LEFT;
textField.autoSize = TextFieldAutoSize.LEFT;
textField.multiline = false;
textField.wordWrap = false;
textField.defaultTextFormat = fmt;
textField.text = buttonText;
openButton = new Sprite();
openButton.graphics.beginFill(0x0B8CC3);
openButton.graphics.drawRoundRect(-halfPadding,-halfPadding,textField.width + padding, textField.height + padding, 20, 20);
openButton.graphics.endFill();
openButton.addChild(textField);
openButton.buttonMode = true;
openButton.useHandCursor = true;
openButton.mouseChildren = false;
openButton.addEventListener(MouseEvent.CLICK, selectFile);
openButton.x = (stage.stageWidth - openButton.width)/2;
openButton.y = (stage.stageHeight - openButton.height)/2;
addChild(openButton);
//Status
statusTextField = new TextField();
fmt = new TextFormat();
fmt.color = 0xFF0000;
fmt.size = 17;
fmt.font = "_sans";
fmt.align = TextFormatAlign.CENTER;
statusTextField.defaultTextFormat = fmt;
statusTextField.multiline = true;
statusTextField.wordWrap = false;
statusTextField.width = stage.stageWidth;
statusTextField.text = '';
statusTextField.x = 0;
statusTextField.y = openButton.y + openButton.height + padding;
statusTextField.mouseEnabled = false;
addChild(statusTextField);
}
private function selectFile(evt:MouseEvent) {
var videoFilter:FileFilter = new FileFilter("Videos", "*.flv");
var inputFile:File = File.desktopDirectory;
inputFile.addEventListener(Event.SELECT, fileSelected);
inputFile.browseForOpen('Open', [videoFilter]);
}
private function fileSelected(evt:Event = null) {
inputFile = evt.target as File;
openButton.visible = false;
startVideo();
startFile();
if(DEBUG_THROTTLE_TIME) {
startTimer();
}
}
private function updateStatus(statusText:String) {
statusTextField.text = statusText;
trace(statusText);
}
/*************************
*
* FILE & VIDEO OPERATIONS
*
**************************/
private function startVideo() {
netConnection = new NetConnection();
netConnection.connect(null);
netStream = new NetStream(netConnection);
netStream.client = {};
// put the NetStream class into Data Generation mode
netStream.play(null);
// before appending new bytes, reset the position to the beginning
netStream.appendBytesAction(NetStreamAppendBytesAction.RESET_BEGIN);
updateStatus('Video Stream Started, Waiting for Bytes...');
}
private function startFile() {
totalBytesRead = 0;
readBytes = new ByteArray();
if(DEBUG_UNUSED_BUFFER) {
unusedBuffer = new ByteArray();
}
inputFileStream = new FileStream();
inputFileStream.readAhead = READSIZE;
inputFileStream.addEventListener(ProgressEvent.PROGRESS, fileReadProgress);
inputFileStream.addEventListener(IOErrorEvent.IO_ERROR,ioError);
inputFileStream.openAsync(inputFile, FileMode.READ);
}
private function fileReadProgress(evt:ProgressEvent = null) {
while(inputFileStream.bytesAvailable) {
inputFileStream.readBytes(readBytes, readBytes.length, inputFileStream.bytesAvailable);
if(!DEBUG_THROTTLE_TIME) {
processData();
}
}
}
private function processData(evt:TimerEvent = null) {
var statusString:String;
if(readBytes.length) {
if(DEBUG_APPEND_VIDEO) {
//Here's where things get funky...
netStream.appendBytes(readBytes);
}
totalBytesRead += readBytes.length;
statusString = 'bytes processed now: ' + readBytes.length.toString();
statusString += '\n total bytes processed: ' + totalBytesRead.toString();
statusString += '\n percentage: ' + Math.round((totalBytesRead / inputFile.size) * 100).toString() + '%';
if(DEBUG_UNUSED_BUFFER) {
//Here too....
unusedBuffer.writeBytes(readBytes);
statusString += '\n Unused Buffer size: ' + unusedBuffer.length.toString();
}
updateStatus(statusString);
readBytes.length = 0;
if(totalBytesRead == inputFile.size) {
fileReadComplete();
}
}
}
private function fileReadComplete(evt:Event = null) {
closeAll();
updateStatus('Finished Reading! Yay!');
}
private function ioError(evt:IOErrorEvent) {
closeAll();
updateStatus('IO ERROR!!!!');
}
/*************************
*
* TIMER OPERATIONS
*
**************************/
private function startTimer() {
throttleTimer = new Timer(DEBUG_THROTTLE_TIME);
throttleTimer.addEventListener(TimerEvent.TIMER, processData);
throttleTimer.start();
}
/*************************
*
* CLEANUP
*
**************************/
private function closeAll() {
if(inputFile != null) {
inputFile.cancel();
inputFile = null;
}
if(inputFileStream != null) {
inputFileStream.removeEventListener(ProgressEvent.PROGRESS, fileReadProgress);
inputFileStream.removeEventListener(IOErrorEvent.IO_ERROR,ioError);
inputFileStream.close();
inputFileStream = null;
}
if(readBytes != null) {
readBytes.clear();
readBytes = null;
}
if(unusedBuffer != null) {
unusedBuffer.clear();
unusedBuffer = null;
}
if(throttleTimer != null) {
throttleTimer.removeEventListener(TimerEvent.TIMER, processData);
throttleTimer.stop();
throttleTimer = null;
}
if(netConnection != null) {
netConnection.close();
netConnection = null;
}
if(netStream != null) {
netStream.close();
netStream = null;
}
openButton.visible = true;
}
}
}
UPDATE:
NetStream.seek() will flush the content appended by appendBytes()... in other words, it seems appendBytes() will just keep adding whatever data you throw at it, which makes sense. However- the core of this question still stands...
In theory, I guess calling seek() at every 10 seconds worth of keyframes would work... That is really kindof weird, not at all what "seek" typically is used for, and it would require a whole bunch of manual calculations to make it work right since in Data Generation Mode using seek to continue playing would require calling appendBytesAction(NetStreamAppendBytesAction.RESET_SEEK), and that in turn requires that the next call to appendBytes() needs to begin on the next byte location for an FLV tag (which hopefully exists in the metadata).
Is this the right solution? Adobe Team, is this what you had in mind? Any sample code?!
Help! :)
I'm pretty sure that once data leaves the playout buffer of the NetStream that the memory is released. In your sample you are not attaching the NetStream to a Video object, so I'm wondering if the NetStream is actually playing (and therefore releasing) the bytes you push in.
Try adding a trace on a timer and check netStream.bufferLength. This should be an ever changing value if the video is actually playing out. If bufferLength just goes forever, the bytes are never playing and never released. If you find that happening, try attaching the NetStream to a Video so that the content actually plays and run the same test on bufferLength.
I would also recommend using the Flash Builder Profile or Adobe Scout to watch the memory usage. With NetStream the memory usages should go up and down as bytes are played out and released.
My other thought is that you may be reading bytes in too quickly. You push bytes basically as fast as they are loaded. NetStream cannot play them out that quickly and so the bytes get stuck in memory until it's time to play that data. You could read out the data in chunks; the video should be a set of discrete FLV tags that you can read one by one. You can figure out how long the chunk is (there should be a property of the chunk that tells it's length, or you could figure it out via the timestamps) and then only load more data when you have to.
Lastly, I believe that AIR is still only 32bit. At least that's what some googling is telling me. So that means that it can only get a limited amount of memory from the OS.. So I'm betting that you're hitting the ceiling and crashing the process.
Try this: Check out System.totalMemory to see how much memory AIR is using before it crashes. In theory it should be the same between thetwo use cases, but I bet NetStream is throwing away a lot more bytes sooner.
I suspect the underlying problem is having multiple copies of a 3GB file in memory.

why file can not be read in adobe flash , air or actionscript on server

I used this function to load a text file
public function load_ways(zoom)
{
finish_working = false;
_zoom = zoom;
var data:URLLoader = new URLLoader();
data.addEventListener(Event.COMPLETE, onLoaded_KV);
data.load(new URLRequest("zoom\\" + _zoom + ".txt"));
}
That worked correctly when i ran my application on my computer.
But when I moved the file to a server, the file won't load.
Actually, onLoaded_KV function was called but with empty data.
You can't have a backslash in an URL change it to:
public function load_ways(zoom)
{
finish_working = false;
_zoom = zoom;
var data:URLLoader = new URLLoader();
data.addEventListener(Event.COMPLETE, onLoaded_KV);
data.load(new URLRequest("zoom/" + _zoom + ".txt"));
}

Details about as3 Sound.extract() format?

Using the sound object .extract() method, it's possible to retrieve the uncompressed sound data as a ByteArray. What is the format of the extracted sound data? Custom internal format, or established format? Endian type?
I would like to be able to load sounddata directly into a byteArray and feed sound playback from this data (bypassing step 1 below).
Here are my experiments so far:
Step 1. Here I load a sound, extract the data content and save it to disc:
private var sound:Sound;
private var byteArray:ByteArray;
// loading a sound...
private function init():void {
sound = new Sound();
sound.addEventListener(Event.COMPLETE, onComplete);
sound.load(new URLRequest('short.mp3'));
}
// saving it to disc...
private function onComplete(e:Event):void {
byteArray = new ByteArray();
sound.extract(byteArray, 100000000);
byteArray.position = 0;
var file:FileReference = new FileReference();
file.save(byteArray, 'short.data');
}
Step 2. Here I load the data from disc and feed the playback of a new sound object with this data:
private var sound:Sound;
private var byteArray:ByteArray;
// loading a sound...
private function init():void {
file = new FileReference();
file.addEventListener(Event.COMPLETE, onLoaded);
file.addEventListener(Event.SELECT, onSelected);
file.browse();
}
private function onSelected(e:Event):void {
file.load();
}
private function onLoaded(e:Event):void {
trace('loaded');
trace(file.data.length);
byteArray = file.data;
sound2 = new Sound();
sound2.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
sound2.play();
}
private function onSampleData(e:SampleDataEvent):void {
trace(e.position);
for (var i:int = 0; i < 2048; i++) {
var left:Number = byteArray.readFloat();
var right:Number = byteArray.readFloat();
e.data.writeFloat(left * 0.2);
e.data.writeFloat(right * 0.2);
}
}
I would like to be able of accomplishing this second step (loading data from disc and feeding a sound object playback) by using an external converter utility (sox or something).
/ Jonas
It should be standard 32bit floats, one per channel per sample. So I would try -e floating-point for sox, I haven't tried though but I would do it with a certain level of confidence... :) Floats are endian-independent, I believe...

fzip: zipping a file takes full memory

I'm using fzip to zip the contents of a folder. The folder contains 4 files of 500MB, the problem is that this uses all of my computers memory. Is there a way to prevent this? And lock the memory at 20% or something?
this is my code:
public function packageFileParser(filesToZip:Array):void {
for (var i:uint = 0; i < filesToZip.length; i++) {
if (filesToZip[i]["isDirectory"] == true) {
packageFileParser(filesToZip[i].getDirectoryListing());
} else {
if (filesToZip[i]["isHidden"] == false) {
var byteLoader:UrlURLLoader = new UrlURLLoader();
byteLoader.dataFormat = URLLoaderDataFormat.BINARY;
byteLoader.addEventListener (flash.events.Event.COMPLETE, urlLoaderCompleteHandler);
var fileRequest:URLRequest = new URLRequest ("/"+filesToZip[i]["nativePath"]);
byteLoader.load (fileRequest);
}
}
}
}
private function urlLoaderCompleteHandler(event:flash.events.Event):void {
var saveZip : Function = function(zip : FZip) : void {
var out : ByteArray = new ByteArray();
zip.serialize(out);
var fs : FileStream = new FileStream;
targetFile = File.desktopDirectory.resolvePath(zipName);
fs.open(targetFile, FileMode.WRITE);
fs.writeBytes(out);
out.clear();
fs.close();
};
var fullpath:String = event.target.urlRequest.url;
var filename:String = fullpath.substr(fullpath.lastIndexOf("/")+1,fullpath.length);
var filepath:String = fullpath.substr(0,fullpath.lastIndexOf("/")+1);
filepath = filepath.split("/Users/Thibaut/Desktop/testfolder").join("");
zip.addFile(filepath+filename, event.target.data);
saveZip(zip);
}
AFAIK, FZip library creates entire archive in memory and then saves it to disk. There's nothing you can do except to change library. Try airxzip from coltware, it served me well (I think there was no big memory loads even on large archives, because they were flushed to disk while adding files to them.)
Also, if you're using AIR capabilities (FileStream class), you should add "air" tag to the question, it changes a lot compared to plain AS3.

Game session Sound Playback / Recording

So I'm making a simple flash game. Basically I have a turntable that goes on for 30 seconds with a couple of sample music that adds up together in multiple layers of sound to form a final song.
Now I would like to be able to record and play the sounds at the end of the game.
I've created a SoundFx class that takes mp3 audio and turns it into byteArrays with the hope to mix the audios in to the same Sound channel.
Now I've reached a certain impass since I cannot properly mix the bytearrays. I'm starting to think it's not possible to encode the byte Arrays as you add the channels to the mix.
I'd love to be guided in the right direction. I'm not sure if the best way to proceed from here, even just the playback would be nice. Creating a button log would probably fix the playback and mixing the audio in a second run to go straight to the file. but it sure seems like a long path to achieve this.
Many thanks and apologies for my crappy english in advance
David R.
some code on the matter:
private var srcSound:Sound;
private var sound1:Sound;
private var sound2:Sound;
private var soundChannel:SoundChannel;
private var bytes:ByteArray;
private var incbytes:ByteArray;
private var mixedBytes:ByteArray;
public var pitchShiftFactor:Number;
public var position:Number;
public var AddSound:Boolean = false;
public var incremental:Number;
public var left1:Number;
public var left2:Number;
public var right1:Number;
public var right2:Number;
public var mixedBytes1:Number;
public var mixedBytes2:Number;
public function SoundFx() {
}
public function playFx(srcSound:Sound):void{
this.srcSound = srcSound;
position = 0;
var morphedSound:Sound = new Sound();
morphedSound.addEventListener(SampleDataEvent.SAMPLE_DATA, sampleDataHandler);
soundChannel = morphedSound.play();
}
public function addSound(sound1:Sound , sound2:Sound):void{
this.sound1 = sound1;
this.sound2 = sound2;
sound1.addEventListener(SampleDataEvent.SAMPLE_DATA, addSampleData);
position = 0;
soundChannel = sound1.play();
soundChannel = sound2.play();
AddSound = true;
}
private function addSampleData(event:SampleDataEvent):void{
position = 0;
var incbytes:ByteArray = new ByteArray();
position += sound1.extract(incbytes, 4096, position);
var bytes:ByteArray = new ByteArray();
position += sound2.extract(bytes, 4096, position);
event.data.writeBytes(mixBytes(bytes, incbytes));
}
private function sampleDataHandler(event:SampleDataEvent):void
{
var bytes:ByteArray = new ByteArray();
position += srcSound.extract(bytes, 4096, position);
event.data.writeBytes(editBytes(bytes));
}
private function mixBytes(bytes1:ByteArray , bytes2:ByteArray ):ByteArray{
bytes.position = 0;
incbytes.position = 0;
var returnBytes:ByteArray = new ByteArray();
while(bytes1.bytesAvailable > 0)
{
left1 = bytes1.readFloat();
left2 = bytes2.readFloat();
right1 = bytes1.readFloat();
right2 = bytes2.readFloat();
mixedBytes1 = left1 + left2;
mixedBytes2 = right1 + right1;
mixedBytes.writeFloat(mixedBytes1);
mixedBytes.writeFloat(mixedBytes2);
}
return mixedBytes;
}
private function editBytes(bytes:ByteArray):ByteArray{
//var skipCount:Number = 0;
var returnBytes:ByteArray = new ByteArray();
bytes.position = 0;
while(bytes.bytesAvailable > 0)
{
//skipCount++;
returnBytes.writeFloat(bytes.readFloat());
returnBytes.writeFloat(bytes.readFloat());
}
return returnBytes;
}
}
i think i get your idea,hope i did.
You should try to pass all of your SoundChannel classes (sound layers being added) to a SoundMixer class (there is plenty of info on this in adobe site),like a funnel,and then add a SAMPLEDATA event listener to that soundmixer, and capture the byte array of that sound mixer,it is like getting the byte array of the final mixdown...then encode those bytes to wav or mp3 .
Sorry i can not write that code down for you,i am not very good with the code yet, but im good at mixing sounds.
Here is an example form adobe site itself:
function loaded(event:Event):void
{
outputSnd.addEventListener(SampleDataEvent.SAMPLE_DATA, processSound);
outputSnd.play();
}
Useful link:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/Sound.html
soundmixer class link:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/SoundMixer.html
experimenting with stratus (cirrus) internet radio i used SoundMixer.computeSpectrum() on the server side and passed that data to the client side with NetStream.send() for drawing and playback.
It worked, but was very ram hungry with client and server on one computer (and with several songs loaded and converted to raw audio data :)
so, try computeSpectrum(), it seems perfect for creating an "audio log"