Record h.264 from webcam as f4v from flash - actionscript-3

Using Flash Media Server, I have an pre-made application that records from webcam and microphone and publishes it to the FMS server as an FLV file.
This works fine, but I now have a need to stream this video that's been recorded to an iOS device. I've got FMS working and it can stream the hls-record/sample.f4v.m3u8 file to an iOS / quicktime device.
I've read some tutorials online and I've changed the publish(filename, "record") to publish("mp4:" + filename + ".f4v", record)
It records stores the file on the FMS server (i see it there, with the filename i gave it and as a .f4v file), it has content. When i goto play it though (via quicktime or safari) i see the length of the video (4 seconds as a test) but no video. The tracking bar doesn't move and the wait icon, waits.
FMS version 4.5 and Flash 11.3 is installed on my Mac. (CentOS 5.8 server)
I must be missing something.
package com
{
import fl.controls.ProgressBar;
import fl.controls.ProgressBarMode;
import flash.display.MovieClip;
import flash.events.Event;
import com.NetConnector
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.media.Camera;
import flash.media.Microphone;
import flash.media.Video;
import flash.net.navigateToURL;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.net.URLRequestMethod;
import flash.net.URLVariables;
import flash.text.TextField;
import flash.utils.setTimeout;
import flash.utils.Timer;
import flash.media.H264Level;
import flash.media.H264Profile;
import flash.media.H264VideoStreamSettings;
/**
* ...
* #author Alexander (flash2you) < >
*/
public class Recorder extends MovieClip
{
private var dataHolder:DataHolder = DataHolder.getInstance()
public var layer:MovieClip
public var activityLevel_pb:ProgressBar
public var aguja:MovieClip
public var aguja2:MovieClip
public var publishButton:MovieClip
public var timer_txt:TextField
public var recordStatus:MovieClip
public var recordBtn:MovieClip
private var netStream:NetStream
private var microphone:Microphone = Microphone.getMicrophone()
private var camera:Camera = Camera.getCamera()
public var video:Video
private var timer:Timer = new Timer(100)
private var clockTimer:Timer = new Timer(1000)
public var published:Boolean = false
private var isRecording:Boolean = false
private var minutero = 0;
private var crono = 0;
private var records = 0;
public var settings_mc:MovieClip
public static var recorder:Recorder
public var settings_icon:MovieClip
private var limitTimer:Timer
public function Recorder()
{
Recorder.recorder = this;
timer.addEventListener(TimerEvent.TIMER, on$timer)
clockTimer.addEventListener(TimerEvent.TIMER, on$clockTimer)
//visible = false
recordBtn.buttonMode = true
recordBtn.addEventListener(MouseEvent.CLICK , recordBtn$click)
recordBtn.addEventListener(MouseEvent.MOUSE_OVER, recordBtn$over)
recordBtn.addEventListener(MouseEvent.MOUSE_OUT, recordBtn$out)
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage)
limitTimer = new Timer(dataHolder.timelimit * 1000);
limitTimer.addEventListener(TimerEvent.TIMER, onLimitTimerHandler)
}
private function onLimitTimerHandler(e:TimerEvent):void
{
stopPublish()
}
/*
* when we comes to second frame
* */
private function onAddedToStage(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
init()
}
/*
* function for set up camera from settings module
* */
public function setCamera(_camera:Camera) {
camera = _camera
addCameraSettings()
video.attachCamera(camera)
if (netStream){
netStream.attachCamera(camera)
}
}
public function setMicrophone(mic:Microphone) {
microphone = mic;
if (netStream){
netStream.attachAudio(microphone)
}
addMicSettings()
}
private function addMicSettings() {
microphone.setUseEchoSuppression(true);
microphone.setSilenceLevel(1)
}
private function addCameraSettings():void
{
camera.setQuality(90000, 90);
camera.setMode(320, 240, 30, true);
camera.setKeyFrameInterval(15);
//camera.setMode(dataHolder.cameraWidth, dataHolder.cameraHeight, dataHolder.cameraFPS)
//camera.setQuality(0, dataHolder.cameraQuality)
}
public function init() {
startConnect()
}
/*
* main function for connection
* */
private function startConnect() {
visible = true
timer_txt.htmlText = "<b>00:00</b>";
initCamera()
initMicropone()
var nc:NetConnection = new NetConnection()
nc.connect(null)
netStream = new NetStream(nc)
netStream.attachAudio(microphone)
video.attachCamera(camera)
layer.visible = false
publishButton.gotoAndStop(1);
activityLevel_pb.mode = ProgressBarMode.MANUAL;
recordStatus.gotoAndStop("noRecord")
timer.start()
connection.addEventListener(NetConnector.CONNECTED, connectionComplete)
connection.startConnection()
}
public function get connection():NetConnector {
return dataHolder.connection
}
private function on$timer(e:TimerEvent) {
activityLevel_pb.setProgress(microphone.activityLevel, 100)
}
/*
* when connection to your stream server done
* */
private function connectionComplete(e:Event = null) {
netStream = new NetStream(connection)
netStream.attachAudio(microphone)
netStream.attachCamera(camera)
}
/*
* add 0 if less then 10secs
* */
private function addLeading(nbr) {
if (nbr<10) {
return ("0"+Math.floor(nbr));
} else {
return (Math.floor(nbr).toString());
}
}
/*
* update visible clock, rotate arrows
* */
private function updateTimer() {
timer_txt.htmlText = "<b>"+addLeading(crono/60)+":"+addLeading(crono%60)+"</b>";
aguja.rotation = aguja.rotation+6;
if (addLeading(crono/60)>minutero) {
aguja2.rotation = aguja2.rotation+6;
++minutero;
}
// end if
++crono;
}
private function on$clockTimer(e:TimerEvent):void
{
updateTimer()
}
private function startClockTimer() {
clockTimer.start()
}
/*
* update graphics and start recording
* */
private function recordBtn$click(e:MouseEvent):void
{
if (!isRecording) {
startRecording()
recordStatus.gotoAndStop("record")
recordBtn.visible = false
}
}
private function recordBtn$over(e:MouseEvent):void
{
if (!isRecording) {
this.gotoAndPlay(65);
}
}
private function recordBtn$out(e:MouseEvent):void
{
if (!isRecording) {
this.gotoAndPlay(61);
}
}
private function startRecording() {
if (connection.connected){
var h264Settings:H264VideoStreamSettings = new H264VideoStreamSettings();
h264Settings.setProfileLevel(H264Profile.BASELINE, H264Level.LEVEL_3_1);
netStream.videoStreamSettings = h264Settings;
netStream.publish("mp4:" + dataHolder.filename + ".f4v", "record");
var metaData:Object = new Object();
metaData.codec = netStream.videoStreamSettings.codec;
metaData.profile = h264Settings.profile;
metaData.level = h264Settings.level;
metaData.fps = camera.fps;
metaData.bandwith = camera.bandwidth;
metaData.height = camera.height;
metaData.width = camera.width;
metaData.keyFrameInterval = camera.keyFrameInterval;
metaData.copyright = "company";
netStream.send("#setDataFrame", "onMetaData", metaData);
}
isRecording = true
startClockTimer()
publishButton.gotoAndPlay(2)
publishButton.buttonMode = true
publishButton.addEventListener(MouseEvent.CLICK, publishButton$click);
limitTimer.start()
}
/*
* redirect to finishURL that was passed via flashvars
* */
private function publishButton$click(e:MouseEvent):void
{
stopPublish()
var request:URLRequest = new URLRequest(dataHolder.finishURL)
navigateToURL(request, "_self")
}
private function stopPublish():void
{
netStream.close();
connection.close();
limitTimer.stop();
clockTimer.stop();
isRecording = false
recordStatus.gotoAndStop("recordEnd")
updateTimer();
}
/*
* init microphone
* */
private function initMicropone():void
{
microphone = Microphone.getMicrophone()
addMicSettings()
}
/*
* init camera
* */
private function initCamera():void
{
camera = Camera.getCamera()
addCameraSettings()
}
}
}

I've settled on creating a php script that sits in the folder with the FLV files. Via a cron job, this php file checks the mysql database for new videos with a process tag of 1. Finds new video? Execute ffmpeg on the FLV file to convert it to mp4 and set the process tag to 0. This way, I maintain the FLV for desktop browser streaming and an MP4 file for iOS streaming. If I should ever need to create another file type for android or whatever may come out, I can simply do another ffmpeg conversion when the cron job triggers.
This would also cause less overhead on the server. The mp4 is encoded once by ffmpeg - not every time a request is made to stream the file via FMS. Now the MP4 is just another file on the web server.

Related

How to create local multiplayer functionality in AIR for mobile

I am working on a multiplayer game in AS3/AIR for mobile (both android & iOS) and I want to implement a local multiplayer (on same wifi network/bluetooth) functionality in the game. What could be the best option to do this ??
it would be great if it is cross platform (android <-> iOS)
if i use peer-to-peer and Adobe Cirrus service (RTMFP) for local multiplayer, can I get into any problems, since the project is in beta ??
I already know about this post : http://forum.starling-framework.org/topic/google-play-game-services-ane-1#post-74449
I have also gone through this post : http://www.as3gamegears.com/category/multiplayer/ But some of the options that I like uses RTMFP which I am doubtful about !! So please suggest me if its ok to use ??
can I use Datagram Sockets class provided by the AIR sdk..?? Although I am not sure but is there any connection between RTMFP and TCP/Datagram sockets ??
If I any more details are required I can specify !!!
Thanks
I use Adobe Cirrus and it's work well. I used available docs and it takes me about 30 min to start communication between two devices. In my opinion it is best choice.
You can use RTMFP without Cirrus using the LAN NetGroup method.
This method is described in the documentation for NetConnection/connect():
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetConnection.html#connect()
A good example of this can be found here:
http://tomkrcha.com/?p=1803
private var connection:NetConnection;
private var group:NetGroup;
private function connect():void {
connection = new NetConnection();
connection.connect("rtmfp:");
connection.addEventListener(NetStatusEvent.NET_STATUS, netStatus);
}
private function netStatus(event:NetStatusEvent):void {
switch(event.info.code){
case "NetConnection.Connect.Success":
joinGroup();
break;
case "NetGroup.Posting.Notify":
receive(event.info.message);
break;
}
}
private function joinGroup():void{
var groupspec:GroupSpecifier = new GroupSpecifier("myGroup/groupOne");
groupspec.postingEnabled = true;
groupspec.ipMulticastMemberUpdatesEnabled = true;
groupspec.addIPMulticastAddress("225.225.0.1:30303");
group = new NetGroup(connection, groupspec.groupspecWithAuthorizations());
group.addEventListener(NetStatusEvent.NET_STATUS, netStatus);
}
public function send(text:String):void {
var message:Object = {
sender: connection.nearId,
text: text
}
group.post(message);
}
public function receive(message:Object):void {
trace("Received message from " + message.sender + ": " + message.text);
}
This is in my experienced the easiest way to do multi-user apps on a local network, because it requires no server (even to discover neighbors) and just the Flash Player.
You can try to use Bluetooth Low Energy in AIR for iOS(and OSX).
Demonstration here: http://www.youtube.com/watch?v=tiRfYjq4wh0&index=1&list=PLw76-mHQ5mhdmHPJy05n424-DKde1XM8G . ANE library here: http://flashpress.ru/blog/ane/bluetooth/?lang=en
CentralManager:
package
{
import flash.display.Sprite;
import ru.flashpress.ane.queue.FPQueueData;
import ru.flashpress.ane.queue.FPQueueTypes;
import ru.flashpress.bluetooth.FPBluetooth;
import ru.flashpress.bluetooth.constants.FPbtState;
import ru.flashpress.bluetooth.data.FPBluetoothOptions;
import ru.flashpress.bluetooth.events.FPBluetoothEvent;
import ru.flashpress.bluetooth.events.FPCentralManagerEvent;
import ru.flashpress.bluetooth.events.FPCharacteristicEvent;
import ru.flashpress.bluetooth.events.FPPeripheralEvent;
import ru.flashpress.bluetooth.helpers.characteristic.FPCharacteristic;
import ru.flashpress.bluetooth.helpers.characteristic.FPCharacteristicValueFormat;
import ru.flashpress.bluetooth.helpers.characteristic.stream.FPStreamIn;
import ru.flashpress.bluetooth.helpers.peripheral.FPPeripheral;
import ru.flashpress.bluetooth.helpers.service.FPService;
import ru.flashpress.bluetooth.helpers.service.FPServiceEvent;
import ru.flashpress.bluetooth.managers.central.FPCentralManager;
import ru.flashpress.bluetooth.managers.central.FPcmScanOptions;
public class CentralView extends Sprite
{
public static const SERVICE_UUID:String = 'E20A39F4-73F5-4BC4-A12F-17D1AD07A961';
public static const CHARACTERISTIC_UUID:String = '08590F7E-DB05-467E-8757-72F6FAEB13D4';
//
public function CentralView()
{
var options:FPBluetoothOptions = new FPBluetoothOptions();
options.nativeLogEnabled = true;
FPBluetooth.init(options);
//
startBluetooth();
}
// Bluetooth Methods ****************
private var cm:FPCentralManager;
private function startBluetooth():void
{
trace('startBluetooth');
var queue:FPQueueData = new FPQueueData('ru.flashpress.chat.central', FPQueueTypes.SERIAL);
cm = new FPCentralManager(null, queue);
cm.addEventListener(FPBluetoothEvent.UPDATE_STATE, updateStateHandler);
cm.addEventListener(FPCentralManagerEvent.PERIPHERAL_DISCOVER, discoverPeripheralHandler);
}
private function updateStateHandler(event:FPBluetoothEvent):void
{
trace('updateStateHandler', event.state);
if (event.state != FPbtState.POWERED_ON) {
return;
}
var options:FPcmScanOptions = new FPcmScanOptions(true, null);
cm.startScan(options, SERVICE_UUID);
}
private var peripheral:FPPeripheral;
private function discoverPeripheralHandler(event:FPCentralManagerEvent):void
{
trace('discoverPeripheralHandler', event.peripheral);
if (peripheral) return;
peripheral = event.peripheral;
trace(' name:', peripheral.advertisementInited ? peripheral.advertisementInited.localName : null);
//
peripheral.addEventListener(FPPeripheralEvent.CONNECTED, peripheralConnectedHandler);
peripheral.addEventListener(FPPeripheralEvent.DISCONNECT, peripheralDisconnectedHandler);
peripheral.connect();
}
private function peripheralConnectedHandler(event:FPPeripheralEvent):void
{
trace('peripheralConnectedHandler');
peripheral.discoverServiceUUIDs(SERVICE_UUID);
peripheral.addEventListener(FPPeripheralEvent.DISCOVER_SERVICES, discoverServicesHandler);
}
private function peripheralDisconnectedHandler(event:FPPeripheralEvent):void
{
trace('peripheralDisconnectedHandler');
peripheral.removeEventListener(FPPeripheralEvent.CONNECTED, peripheralConnectedHandler);
peripheral.removeEventListener(FPPeripheralEvent.DISCONNECT, peripheralDisconnectedHandler);
peripheral = null;
}
private var service:FPService;
private function discoverServicesHandler(event:FPPeripheralEvent):void
{
trace('discoverServicesHandler');
if (event.error) {
trace('discoverServicesHandler, error:', event.error);
return;
}
//
service = peripheral.services.list[0];
trace(' service:', service);
//
service.discoverCharacteristicUUIDs(CHARACTERISTIC_UUID);
service.addEventListener(FPServiceEvent.DISCOVER_CHARACTERISTICS, discoverCharacteristicsHandler);
}
public var characteristic:FPCharacteristic;
private var streamIn:FPStreamIn;
private function discoverCharacteristicsHandler(event:FPServiceEvent):void
{
trace('discoverCharacteristicsHandler');
if (event.error) {
trace('discoverCharacteristicsHandler, error:', event.error);
return;
}
characteristic = service.characteristics.list[0];
trace(' characteristic:', characteristic);
characteristic.initValueFormat(FPCharacteristicValueFormat.MESSAGES);
streamIn = characteristic.streamIn;
characteristic.setNotify(true);
characteristic.addEventListener(FPCharacteristicEvent.UPDATE_NOTIFICATION, updateNotificationHandler);
//
characteristic.addEventListener(FPCharacteristicEvent.UPDATE_MESSAGES, updateMessageHandler);
}
private function updateNotificationHandler(event:FPCharacteristicEvent):void
{
trace('updateNotificationHandler:', event.isNotifying);
}
private function updateMessageHandler(event:FPCharacteristicEvent):void
{
trace('updateMessageHandler');
trace(' error:', event.error);
trace(' messages:', event.messages);
}
}
}
Peripheral Manager:
package
{
import flash.display.Sprite;
import ru.flashpress.bluetooth.FPBluetooth;
import ru.flashpress.bluetooth.constants.FPbtState;
import ru.flashpress.bluetooth.data.FPAdvertisementData;
import ru.flashpress.bluetooth.data.FPBluetoothOptions;
import ru.flashpress.bluetooth.events.FPBluetoothEvent;
import ru.flashpress.bluetooth.events.FPCharacteristicEvent;
import ru.flashpress.bluetooth.events.FPPeripheralManagerEvent;
import ru.flashpress.bluetooth.helpers.characteristic.FPCharacteristic;
import ru.flashpress.bluetooth.helpers.characteristic.FPCharacteristicPermissions;
import ru.flashpress.bluetooth.helpers.characteristic.FPCharacteristicProperties;
import ru.flashpress.bluetooth.helpers.characteristic.FPCharacteristicValueFormat;
import ru.flashpress.bluetooth.helpers.service.FPService;
import ru.flashpress.bluetooth.managers.peripheral.FPPeripheralManager;
public class PeripheralView extends Sprite
{
public static const SERVICE_UUID:String = 'E20A39F4-73F5-4BC4-A12F-17D1AD07A961';
public static const CHARACTERISTIC_UUID:String = '08590F7E-DB05-467E-8757-72F6FAEB13D4';
//
public function PeripheralView()
{
var options:FPBluetoothOptions = new FPBluetoothOptions();
options.nativeLogEnabled = true;
FPBluetooth.init(options);
//
startBluetooth();
}
// Bluetooth Methods ****************
private var pm:FPPeripheralManager;
private function startBluetooth():void
{
trace('startBluetooth');
pm = new FPPeripheralManager();
pm.addEventListener(FPBluetoothEvent.UPDATE_STATE, updateStateHandler);
pm.addEventListener(FPPeripheralManagerEvent.ADD_SERVICE, addServiceHandler);
}
private var characteristic:FPCharacteristic;
private var service:FPService;
private function updateStateHandler(event:FPBluetoothEvent):void
{
trace('updateStateHandler:', event.state);
if (event.state != FPbtState.POWERED_ON) {
return;
}
//
var properties:uint = FPCharacteristicProperties.NOTIFY;
var permissions:uint = FPCharacteristicPermissions.WRITEABLE;
var valueFormat:uint = FPCharacteristicValueFormat.MESSAGES;
characteristic = new FPCharacteristic(CHARACTERISTIC_UUID, properties, permissions, valueFormat);
characteristic.addEventListener(FPCharacteristicEvent.SUBSCRIBED_TO_CENTRAL, subscribedToCentralHandler);
characteristic.addEventListener(FPCharacteristicEvent.UNSUBSCRIBED_FROM_CENTRAL, unsubscribedFromCentralHandler);
characteristic.notifyMTU = 50;
//
service = new FPService(SERVICE_UUID, true);
service.addCharacteristic(characteristic);
//
pm.addService(service);
}
private function addServiceHandler(event:FPPeripheralManagerEvent):void
{
trace('addServiceHandler');
if (event.error) {
trace(' error:', event.error);
return;
}
if (!pm.isAdvertising) {
var uuids:Vector. = new [SERVICE_UUID];
var advertisementData:FPAdvertisementData = new FPAdvertisementData('My Local name', uuids);
pm.startAdvertising(advertisementData);
}
}
private function unsubscribedFromCentralHandler(event:FPCharacteristicEvent):void
{
trace('unsubscribedFromCentralHandlers', event.central);
}
private function subscribedToCentralHandler(event:FPCharacteristicEvent):void
{
trace('subscribedToCentralHandler', event.central);
//
characteristic.sendMessage('send my message');
}
}
}

AIR (As3) - Download large file trough iPad application

First I want to specify that I can't post source code because the project is huge.
I'm trying to download a large file (500+ MB) on iPad device.
Initially I tried with URLLoader, but than I realized that the iPad devices has a very limited resources regarding memory. Than I thought that the URLStream will download the file in chunks and with FileStream I can save this chunks on the device (like this AS3: URLStream saving files to desktop?), but I was wrong, the device crashes when I try to download a big file because the RAM of the device is not enough (more precisely this becomes too big: System.privateMemory)
Does anyone have any idea how to download a file in chunks and is it possible without using "socket connection"?
Thanks in advance.
EDIT:
Here is the code that I use (the commented lines are the version in which the FileStream las closed only after the file is downloaded.
package components.streamDownloader
{
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.OutputProgressEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.net.URLRequest;
import flash.net.URLStream;
import flash.system.System;
import flash.utils.ByteArray;
/**
*
*/
public class StreamDownloader extends EventDispatcher
{
[Event(name="DownloadComplete", type="com.tatstyappz.net.DownloadEvent")]
[Event(name="Error", type="com.tatstyappz.net.DownloadEvent")]
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
public function StreamDownloader()
{
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
private var file:File;
//private var fileStream:FileStream;
private var urlRequest:URLRequest;
private var urlStream:URLStream;
private var waitingForDataToWrite:Boolean = false;
//--------------------------------------------------------------------------
//
// API
//
//--------------------------------------------------------------------------
public function download(urlRequest:URLRequest, file:File):void {
init();
this.urlRequest = urlRequest;
this.file = file;
//fileStream.open(file, FileMode.WRITE);
urlStream.load(urlRequest);
}
//--------------------------------------------------------------------------
//
// Event handlers
//
//--------------------------------------------------------------------------
//----------------------------------
// urlStream events
//----------------------------------
protected function urlStream_openHandler(event:Event):void
{
waitingForDataToWrite = false;
dispatchEvent(event.clone());
}
protected function urlStream_progressHandler(event:ProgressEvent):void
{
trace("MEMORY:", System.totalMemoryNumber / 1024 / 1024, "MEMORY P:", System.privateMemory / 1024 / 1024, "FREE MEMORY:", System.freeMemory / 1024 / 1024, "PROGRESS:", event.bytesLoaded / event.bytesTotal );
if(waitingForDataToWrite){
writeToDisk();
}
}
protected function urlStream_completeHandler(event:Event):void
{
if(urlStream.bytesAvailable > 0)
{
writeToDisk();
}
//fileStream.close();
destory();
dispatchEvent(event.clone());
// dispatch additional DownloadEvent
dispatchEvent(new StreamDownloadEvent(StreamDownloadEvent.DOWNLOAD_COMPLETE, urlRequest, file));
}
protected function urlStream_securityErrorHandler(event:SecurityErrorEvent):void
{
dispatchEvent(new StreamDownloadEvent(StreamDownloadEvent.ERROR, urlRequest, file, event.errorID.toString()));
destory();
}
protected function urlStream_ioErrorHandler(event:IOErrorEvent):void
{
dispatchEvent(new StreamDownloadEvent(StreamDownloadEvent.ERROR, urlRequest, file, event.errorID.toString()));
destory();
}
//----------------------------------
// fileStream events
//----------------------------------
protected function fileStream_outputProgressHandler(event:OutputProgressEvent):void
{
waitingForDataToWrite = true;
}
protected function fileStream_ioErrorHandler(event:IOErrorEvent):void
{
dispatchEvent(new StreamDownloadEvent(StreamDownloadEvent.ERROR, urlRequest, file, event.errorID.toString()));
destory();
}
//--------------------------------------------------------------------------
//
// Utils
//
//--------------------------------------------------------------------------
private function init():void
{
urlStream = new URLStream();
//fileStream = new FileStream();
urlStream.addEventListener(Event.OPEN, urlStream_openHandler);
urlStream.addEventListener(ProgressEvent.PROGRESS, urlStream_progressHandler);
urlStream.addEventListener(Event.COMPLETE, urlStream_completeHandler);
urlStream.addEventListener(IOErrorEvent.IO_ERROR, urlStream_ioErrorHandler);
urlStream.addEventListener(SecurityErrorEvent.SECURITY_ERROR, urlStream_securityErrorHandler);
//fileStream.addEventListener(OutputProgressEvent.OUTPUT_PROGRESS, fileStream_outputProgressHandler)
//fileStream.addEventListener(IOErrorEvent.IO_ERROR, fileStream_ioErrorHandler);
}
private function destory():void
{
urlStream.removeEventListener(Event.OPEN, urlStream_openHandler);
urlStream.removeEventListener(ProgressEvent.PROGRESS, urlStream_progressHandler);
urlStream.removeEventListener(Event.COMPLETE, urlStream_completeHandler);
urlStream.removeEventListener(IOErrorEvent.IO_ERROR, urlStream_ioErrorHandler);
urlStream.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, urlStream_securityErrorHandler);
//fileStream.removeEventListener(OutputProgressEvent.OUTPUT_PROGRESS, fileStream_outputProgressHandler)
//fileStream.removeEventListener(IOErrorEvent.IO_ERROR, fileStream_ioErrorHandler);
urlStream = null;
//fileStream = null;
}
private function writeToDisk():void {
/*var fileData:ByteArray = new ByteArray();
urlStream.readBytes(fileData, 0, urlStream.bytesAvailable);
fileStream.writeBytes(fileData,0,fileData.length);
waitingForDataToWrite = false;*/
var bytes:ByteArray = new ByteArray();
urlStream.readBytes( bytes );
var fs:FileStream = new FileStream();
fs.open( file, FileMode.APPEND );
fs.writeBytes( bytes );
fs.close();
}
}
}
As I said in my comment to csomakk, I have successfully downloaded 300+ MB files through AIR for desktop, iOS, and Android using the URLStream chunking method.
Pseudo code:
var stream:URLStream = new URLStream();
stream.addEventListener( PROGRESS, progressHandler );
stream.addEventListener( COMPLETE, completeHandler );
stream.load( url );
private function progressHandler( e:ProgressEvent ):void {
this.writeDataToDisk();
}
private function completeHandler( e:Event ):void {
this.writeDataToDisk();
}
private function writeDataToDisk():void {
var bytes:ByteArray = new ByteArray();
this.stream.readBytes( bytes );
var fs:FileStream = new FileStream();
fs.open( file, FileMode.APPEND );
fs.writeBytes( bytes );
fs.close();
}
That basic logic works and works just fine up to 300MB (and likely further. Though I should have tested that, now that I think about it). That was written fairly quickly so there may be some errors and I definitely shorthanded a few things, but you get the idea.
If this does not work, we need a few things from you:
Post any errors
trace out file.size / 1024 / 1024 + "MB" after the fs.close() and see how far it gets before crashing
trace out System.memory / 1024 / 1024 + "MB" after thefs.close()` so we can monitor the memory usage
For 2 and 3, we should only need the last trace statements before the crash occurs.
Alternatively, you should know that you won't be able to do anything with that 500MB file in the application. Flash simply will not load it because of its size. The only reason I managed to get away with my 300MB video files is that we were streaming them from disk, not storing the entire things into memory.
Since I am not allowed to comment under Josh's answer for some reason, I am adding my version as a separate answer. But it is heavily based on his suggestion.
The code is also available with GitHub at: https://github.com/shishenkov/ActionscriptClasses/blob/master/us/flashmx/net/LargeFileLoader.as
The prerequisite - URLStream is an awesome class but it has a glitch in it that causes a memory leak/buildup to prevent large files from loading properly. The class I'm sharing here has been tested and was able to download a sequence of 1.5GB files into an iPad Air 2 (64GB) without an issue. I assume that larger files will be OK too, since it actually overcomes the RAM storage limitation (before the glitch fix it was crashing at around 200MB).
The glitch - the raw data bytearray where you copy over the loaded bytes never gets disposed of by GC (as noted here: http://blogs.splunk.com/2007/11/16/flashas3-urlstream-memory-leak/) so, the workaround is to implement a class that uses the Josh's technique and makes sure that the bytes are disposed off after the write.
... here's the code (NOTE: this is still pre-production):
package us.flashmx.net
{
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.HTTPStatusEvent;
import flash.events.IEventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.net.URLRequest;
import flash.net.URLStream;
import flash.system.System;
import flash.utils.ByteArray;
/**
* ...
* #author Nick Shishenkov <n#vc.am>
*/
public class LargeFileLoader extends EventDispatcher
{
private var _url:String = "";
private var _filePath:String = "";
private var _fileStream:FileStream = new FileStream;
private var _urlStream:URLStream = new URLStream;
private var _localFile:File;
private var _bytesLoaded:Number;
public function LargeFileLoader()
{
super(null);
//
_urlStream.addEventListener(Event.OPEN, _onOpen, false, int.MIN_VALUE, true);
_urlStream.addEventListener(ProgressEvent.PROGRESS, _onProgress, false, int.MIN_VALUE, true);
_urlStream.addEventListener(Event.COMPLETE, _onComplete, false, int.MIN_VALUE, true);
_urlStream.addEventListener(IOErrorEvent.IO_ERROR, _onError, false, int.MIN_VALUE, true);
_urlStream.addEventListener(SecurityErrorEvent.SECURITY_ERROR, _onSecurityError, false, int.MIN_VALUE, true);
_urlStream.addEventListener(HTTPStatusEvent.HTTP_RESPONSE_STATUS, _onHTTPStatus, false, int.MIN_VALUE, true);
_urlStream.addEventListener(HTTPStatusEvent.HTTP_STATUS, _onHTTPStatus, false, int.MIN_VALUE, true);
}
private function _onHTTPStatus(e:HTTPStatusEvent):void
{
dispatchEvent(e.clone());
}
public function load(remoteURL:String, localPath:String, overwrite:Boolean = true):void
{
_url = remoteURL;
_filePath = localPath;
//
_localFile = new File(_filePath);
_bytesLoaded = 0;
//
if (overwrite && _localFile.exists)
{
_localFile.deleteFile();
}
//
_urlStream.load(new URLRequest(url));
_fileStream.open(_localFile, FileMode.APPEND);
}
private function _onOpen(e:Event):void
{
dispatchEvent(e.clone());
}
private function _onSecurityError(e:SecurityErrorEvent):void
{
dispatchEvent(e.clone());
}
private function _onError(e:IOErrorEvent):void
{
dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, e.text));
}
private function _onProgress(e:ProgressEvent):void
{
//
trace(" -> _onProgress: " + _urlStream.length + " | " + e.bytesLoaded + " / " + e.bytesTotal);
//
_writeStreamBytes();
//
dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS, false, false, e.bytesLoaded, e.bytesTotal));
}
private function _onComplete(e:Event):void
{
_writeStreamBytes();
//
dispatchEvent(new Event(Event.COMPLETE));
}
private function _writeStreamBytes():void
{
var bytes:ByteArray = new ByteArray();
_urlStream.readBytes( bytes );
_fileStream.writeBytes( bytes );
//
_bytesLoaded += bytes.length;
//clear buffer (if the array stays non-null it will lead to a memmory leak
bytes = null;
}
public function get url():String
{
return _url;
}
public function get filePath():String
{
return _filePath;
}
public function get bytesLoaded():Number
{
//_localFile.size;
return _bytesLoaded;
}
public function dispose():void
{
try{ _fileStream.close(); }catch (err:Error){};
//
try{ _urlStream.close(); }catch (err:Error){};
//
_urlStream.removeEventListener(Event.OPEN, _onOpen);
_urlStream.removeEventListener(ProgressEvent.PROGRESS, _onProgress);
_urlStream.removeEventListener(Event.COMPLETE, _onComplete);
_urlStream.removeEventListener(IOErrorEvent.IO_ERROR, _onError);
_urlStream.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, _onSecurityError);
_urlStream.removeEventListener(HTTPStatusEvent.HTTP_RESPONSE_STATUS, _onHTTPStatus);
_urlStream.removeEventListener(HTTPStatusEvent.HTTP_STATUS, _onHTTPStatus);
//
_urlStream = null;
_fileStream = null;
//
System.gc();
}
}
}
I ran several device tests with Scout CC and the memory stays down at all times (no buildups whatsoever). I will be testing some older iOS devices later on this week. For the record: I am using Adobe AIR 24.0.0.180
here's a sample use:
package us.flashmx.net
{
import flash.display.DisplayObject;
import flash.events.Event;
import flash.events.ProgressEvent;
/**
* ...
* #author ...
*/
public class LargeFileLoader_DEMO extends DisplayObject
{
private var _largeFilesLoader:LargeFileLoader;
public function LargeFileLoader_DEMO()
{
super();
//
init_largeFilesLoader("http://A.Large.File.URL/", "/The/Absolute/Local/Path");
}
public function dispose_largeFilesLoader():void
{
//
if (_largeFilesLoader != null)
{
//clear listeners
_largeFilesLoader.removeEventListener(ProgressEvent.PROGRESS, _onFileLoaderProgress);
_largeFilesLoader.removeEventListener(Event.COMPLETE, _onFileLoaderComplete);
//dispose
_largeFilesLoader.dispose();
//free mem
_largeFilesLoader = null;
}
}
private function init_largeFilesLoader(fURL:String, fPath:String):void
{
//
_largeFilesLoader = new LargeFileLoader;
//
_largeFilesLoader.addEventListener(ProgressEvent.PROGRESS, _onFileLoaderProgress, false, int.MIN_VALUE, true);
_largeFilesLoader.addEventListener(Event.COMPLETE, _onFileLoaderComplete, false, int.MIN_VALUE, true);
//
_largeFilesLoader.load(fURL, fPath);
}
private function _onFileLoaderComplete(e:Event):void
{
trace("All done!");
dispose_largeFilesLoader();
}
private function _onFileLoaderProgress(e:ProgressEvent):void
{
_largeFilesLoader.bytesLoaded;
}
}
}
... I hope that helps !
cheers
-Nick

Record Sound Using AS3 Worker

Hello I am trying to record the user sound using AS3 Worker everything works well when it comes to communication between the worker and the main scene but I think the record functionnality have and issue here I am using the Record Clash from bytearray.org (http://www.bytearray.org/?p=1858)
and here's is my code am I missing something :
package objects
{
import flash.events.Event;
import flash.system.MessageChannel;
import flash.system.Worker;
import flash.system.WorkerDomain;
import flash.utils.ByteArray;
import starling.display.Button;
import starling.display.Image;
import starling.display.Sprite;
import starling.events.Event;
public class RecordComponent extends Sprite
{
private var audioContainer:Image;
private var recButton:Button;
private var playButton:Button;
private var stopButton:Button;
//background thread for recording
private var worker:Worker;
private var wtm:MessageChannel;
private var mtw:MessageChannel;
private var output:ByteArray;
public function RecordComponent()
{
super();
worker = WorkerDomain.current.createWorker(Workers.RecordWorker);
wtm = worker.createMessageChannel(Worker.current);
mtw = Worker.current.createMessageChannel(worker);
worker.setSharedProperty("wtm",wtm);
worker.setSharedProperty("mtw",mtw);
worker.start();
wtm.addEventListener(flash.events.Event.CHANNEL_MESSAGE,onChannelMessage);
this.addEventListener(starling.events.Event.ADDED_TO_STAGE, onAddedToStage);
}
protected function onChannelMessage(event:flash.events.Event):void
{
output = wtm.receive();
}
protected function onAddedToStage(event:starling.events.Event):void
{
this.removeEventListener(starling.events.Event.ADDED_TO_STAGE, onAddedToStage);
drawScreen();
}
private function drawScreen():void
{
audioContainer = new Image( Assets.getAtlas().getTexture("audioContainer") );
addChild(audioContainer);
//record button
recButton = new Button( Assets.getAtlas().getTexture("recordButton") );
recButton.x=Math.ceil(194/2 - recButton.width/2);
recButton.y=5;
addChild(recButton);
//stop button
stopButton = new Button( Assets.getAtlas().getTexture("stopButton") );
stopButton.x=recButton.x+10;
stopButton.y=10;
stopButton.visible = false;
addChild(stopButton);
//play button
playButton = new Button( Assets.getAtlas().getTexture("playButton") );
playButton.x=Math.ceil(194 - playButton.width)-25;
playButton.y=10;
playButton.visible = false;
addChild(playButton);
this.addEventListener(starling.events.Event.TRIGGERED, buttons_triggeredHandler);
}
private function buttons_triggeredHandler(event:starling.events.Event):void
{
var currentButton:Button = event.target as Button;
switch(currentButton)
{
case recButton:
startRecording();
break;
case stopButton:
stopRecording();
break;
case playButton:
playRecording();
break;
}
}
private function playRecording():void
{
mtw.send("RECORD_PLAY");
}
private function stopRecording():void
{
stopButton.visible = false;
recButton.x = 25;
playButton.visible = true;
recButton.visible = true;
mtw.send("RECORD_STOP");
//
}
private function startRecording():void
{
recButton.visible = false;
stopButton.visible = true;
playButton.visible = false;
mtw.send("RECORD_START");
}
}
}
RecordWorker
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.media.Microphone;
import flash.system.MessageChannel;
import flash.system.Worker;
import org.as3wavsound.WavSound;
import org.bytearray.micrecorder.MicRecorder;
import org.bytearray.micrecorder.encoder.WaveEncoder;
import org.bytearray.micrecorder.events.RecordingEvent;
public class RecordWorker extends Sprite
{
private var wtm:MessageChannel;
private var mtw:MessageChannel;
// volume in the final WAV file will be downsampled to 50%
private var volume:Number = .75;
// we create the WAV encoder to be used by MicRecorder
private var wavEncoder:WaveEncoder = new WaveEncoder( volume );
// we create the MicRecorder object which does the job
private var recorder:MicRecorder = new MicRecorder( wavEncoder,Microphone.getEnhancedMicrophone() );
public function RecordWorker()
{
wtm = Worker.current.getSharedProperty("wtm");
mtw = Worker.current.getSharedProperty("mtw");
mtw.addEventListener(Event.CHANNEL_MESSAGE,command_Handler);
}
//message received from the main component
protected function command_Handler(event:Event):void
{
switch(mtw.receive())
{
case "RECORD_START":
trace("recording.....");
recorder.record();
recorder.addEventListener(RecordingEvent.RECORDING, onRecording);
recorder.addEventListener(flash.events.Event.COMPLETE, onRecordComplete);
break;
case "RECORD_STOP":
trace("record stopped....");
recorder.stop();
break;
case "RECORD_PLAY":
if(recorder.output.length>0)
{
var player:WavSound = new WavSound(recorder.output);
player.play();
}
break;
}
}
private function onRecording(event:RecordingEvent):void
{
trace ( event.time );
}
private function onRecordComplete(event:flash.events.Event):void
{
}
}
}
anny help will be appriciated
Thanks,
Khaled
Looking at this write-up on Adobe's AS3 reference, it seems that because each created worker is "a virtual instance of the Flash runtime", it has no direct connection to any of the core runtime's input or output channels. The Microphone class is one such cut-off interface, which is why it can't reach the system mic inside a worker.

Better way to send voice over a socket in AS3

I made an AIR application that sends voice between two computers, this is the code:
package
{
import fl.controls.List;
import flash.events.ProgressEvent;
import flash.events.SampleDataEvent;
import flash.events.ServerSocketConnectEvent;
import flash.media.Microphone;
import flash.media.Sound;
import flash.net.ServerSocket;
import flash.net.Socket;
import flash.utils.ByteArray;
import flash.system.System;
public class VoiceCommunication
{
private var soundBytes:ByteArray;
private var mic:Microphone;
private var voiceReceiver:ServerSocket;
private var voiceSender:Socket;
private var mLocalIP:String;
private var mRemoteIP:String;
private var mPort:uint;
private var sound:Sound;
private var sample:Number;
public function VoiceCommunication(localIP:String, port:uint)
{
mLocalIP = localIP;
mPort = port;
}
public function startSendingVoice(remoteIP:String = null):void
{
mic = Microphone.getMicrophone();//2-null
mic.gain = 100;
mic.rate = 44;
voiceSender = new Socket();//3-null
mRemoteIP = remoteIP;//5-null
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);//1-remove eventlistener
}
protected function micSampleDataHandler(event:SampleDataEvent):void
{
if(mRemoteIP != null)
{
voiceSender.connect(mRemoteIP, mPort);
while(event.data.bytesAvailable)
{
sample = event.data.readFloat();
voiceSender.writeFloat(sample);
}
voiceSender.flush();
}
}
public function startReceivingVoice():void
{
voiceReceiver = new ServerSocket;//2
voiceReceiver.bind(mPort, mLocalIP);
voiceReceiver.addEventListener(ServerSocketConnectEvent.CONNECT, handleConnection);//1
voiceReceiver.listen();
}
protected function handleConnection(event:ServerSocketConnectEvent):void
{
var s:Socket = event.socket as Socket;
s.addEventListener(ProgressEvent.SOCKET_DATA, handleData);
}
protected function handleData(event:ProgressEvent):void
{
event.target.removeEventListener(ProgressEvent.SOCKET_DATA, handleData);
var s:Socket = event.target as Socket;
soundBytes = null;
soundBytes = new ByteArray();//3
s.readBytes(soundBytes);
playSound();
s = null;
}
private function playSound():void
{
soundBytes.position = 0;
sound = null;
sound = new Sound();//5
sound.addEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler, false, 0, true);//4
sound.play();
}
protected function playbackSampleHandler(event:SampleDataEvent):void
{
sound.removeEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler);
for (var i:int = 0; i < 8192 && soundBytes.bytesAvailable > 0; i++)
{
sample = soundBytes.readFloat();
event.data.writeFloat(sample);
event.data.writeFloat(sample);
}
System.gc();
}
public function destroySender():void
{
mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);
mic = null;
voiceSender = null;
mRemoteIP = null;
System.gc();
}
public function destroyReceiver():void
{
voiceReceiver.removeEventListener(ServerSocketConnectEvent.CONNECT, handleConnection);//1
voiceReceiver = null;
soundBytes = null;
sound.removeEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler);
sound = null;
System.gc();
}
}
}
This works fine and when I tried it, it did send the voice, but the problem is that sending the voice is too slow and gets split. So is there a better way to do it?
I rewrote it using datagram socket but that didn't work at all, the code:
package
{
import fl.controls.List;
import flash.events.DatagramSocketDataEvent;
import flash.events.ProgressEvent;
import flash.events.SampleDataEvent;
import flash.events.ServerSocketConnectEvent;
import flash.media.Microphone;
import flash.media.Sound;
import flash.net.DatagramSocket;
import flash.net.ServerSocket;
import flash.net.Socket;
import flash.system.System;
import flash.utils.ByteArray;
public class VoiceComm
{
private var soundBytes:ByteArray;
private var mic:Microphone;
private var socket:DatagramSocket
private var mList:List;
private var mLocalIP:String;
private var mRemoteIP:String;
private var mPort:uint;
private var sound:Sound;
private var sample:Number;
private var bytesToBeSent:ByteArray;
public function VoiceComm(localIP:String, port:uint)
{
socket = new DatagramSocket();
mLocalIP = localIP;
mPort = port;
}
public function startSendingVoice(remoteIP:String = null):void
{
mic = Microphone.getMicrophone();
mic.gain = 100;
mic.rate = 44;
mRemoteIP = remoteIP;
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);
}
protected function micSampleDataHandler(event:SampleDataEvent):void
{
var sample:Number;
if(mRemoteIP != null)
{
while(event.data.bytesAvailable)
{
sample = event.data.readFloat();
bytesToBeSent = null;
bytesToBeSent = new ByteArray();
bytesToBeSent.writeFloat(sample);
socket.send(bytesToBeSent, 0, 0, mRemoteIP, mPort);
}
}
}
public function startReceivingVoice():void
{
socket.bind(mPort, mLocalIP);
socket.addEventListener(DatagramSocketDataEvent.DATA, handleData);
socket.receive();
}
protected function handleData(event:DatagramSocketDataEvent):void
{
trace("Data inthere!");
soundBytes = null;
soundBytes = new ByteArray();//3
event.data.readBytes(soundBytes);
playSound();
}
private function playSound():void
{
soundBytes.position = 0;
sound = null;
sound = new Sound();//5
sound.addEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler, false, 0, true);//4
sound.play();
}
protected function playbackSampleHandler(event:SampleDataEvent):void
{
sound.removeEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler);
for (var i:int = 0; i < 8192 && soundBytes.bytesAvailable > 0; i++)
{
sample = soundBytes.readFloat();
event.data.writeFloat(sample);
event.data.writeFloat(sample);
}
System.gc();
}
public function destroySender():void
{
mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);
mic = null;
mList = null;
mRemoteIP = null;
System.gc();
}
public function destroyReceiver():void
{
soundBytes = null;
sound.removeEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler);
sound = null;
System.gc();
}
}
}
**Update:
The code after modification:
package
{
import fl.controls.List;
import flash.events.DatagramSocketDataEvent;
import flash.events.SampleDataEvent;
import flash.media.Microphone;
import flash.media.Sound;
import flash.net.DatagramSocket;
import flash.net.Socket;
import flash.system.System;
import flash.utils.ByteArray;
public class VoiceComm
{
private var mic:Microphone;
private var soundBytes:ByteArray;
private var sample:Number;
private var sound:Sound;
private var socket:DatagramSocket;
private var mLocalIP:String;
private var mPort:uint;
private var mRemoteIP:String;
private var receivedBytes:ByteArray;
public function VoiceComm(localIP:String, port:uint)
{
socket = new DatagramSocket();
mLocalIP = localIP;
mPort = port;
}
//destroyers:
public function destroySender():void
{
mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, sampleData);
mic = null;
soundBytes = null;
System.gc();
}
public function destroyReceiver():void
{
socket.removeEventListener(DatagramSocketDataEvent.DATA, playSound);
receivedBytes = null;
System.gc();
}
//sending part
public function startSendingVoice(ip:String = null):void
{
mRemoteIP = ip;
mic = Microphone.getMicrophone();
mic.rate = 44;
mic.gain = 100;
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, sampleData);
}
protected function sampleData(event:SampleDataEvent):void
{
soundBytes = new ByteArray();
while(event.data.bytesAvailable)
{
var sample:Number = event.data.readFloat();
soundBytes.writeFloat(sample);
}
if(mRemoteIP != null)
{
event.data.position = 0;
socket.send(soundBytes, 0, event.data.length, mRemoteIP, mPort);
}
}
//receiving part
public function startReceivingVoice():void
{
socket.bind(mPort, mLocalIP);
socket.addEventListener(DatagramSocketDataEvent.DATA, playSound);
socket.receive();
}
protected function playSound(event:DatagramSocketDataEvent):void
{
trace("got data!");
receivedBytes = new ByteArray();
event.data.readBytes(receivedBytes);
receivedBytes.position = 0;
var sound:Sound = new Sound();
sound.addEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler, false, 0, true);
sound.play();
}
protected function playbackSampleHandler(event:SampleDataEvent):void
{
trace("Playing Sound...");
for (var i:int = 0; i < 8192 && receivedBytes.bytesAvailable > 0; i++)
{
var sample:Number = receivedBytes.readFloat();
event.data.writeFloat(sample);
event.data.writeFloat(sample);
System.gc();
}
}
}
}
This is most likely the source of the issue: http://forums.adobe.com/message/3111816. The TCP Nagle Algorithm. What this basically does is, when you're writing a bunch of small chunks of data to the socket, instead of sending them all out individually it will hold them back and pool them together in order to send a single "full" packet rather than tons of small ones (which leads to a greater, constant load on the network).
If possible, re-write using Datagram sockets instead. To my knowledge there is currently no way to disable the Nagle algorithm in Flash.
Update
A couple things after looking at your code in depth: The first is that you're writing each byte one at a time to the TCP socket. Don't do that, write the entire bytearray and flush it.
if(mRemoteIP != null)
{
voiceSender.connect(mRemoteIP, mPort);
voiceSender.writeBytes(event.data, 0, event.data.length);
voiceSender.flush();
}
That may solve the issues you're having. If not, the reason why your UDP version is for two reasons. The first is that again, you're writing one byte at a time and sending but more important, you're telling it not to write the data to the socket at all. This line is the culprit:
socket.send(bytesToBeSent, 0, 0, mRemoteIP, mPort);
Note the third paramter, "0". That parameter is the amount of data from the supplied bytearray that should be written to the socket. So you're essentially saying, don't send anything. Change the code like so:
if(isConnected == true)
{
event.data.position = 0;
localSendingSocket.send(event.data, 0, event.data.length, remoteIP, remotePort);
}
Let me know if this works out for you. In the meantime I'm re-writing your code based on these suspicions so if I manage to get a project compiled and working before you, I'll post the project files here.
Where not use Adobe Cirrus (formerly Stratus to create a peer2peer application instead of the socket?
use the (OS) Red5 server.
http://www.red5.org/
the blurb:
"Red5 Media Server 1.0 delivers a powerful video streaming and multi-user solution to the ©Adobe ©Flash Player and other exciting client technologies. Based on Java and some of the most powerful open source frameworks, Red5 stands as a solid solution for business of all sizes including the enterprise.
Red5 includes support for the latest multi-user API’s including NetConnection, NetStream and SharedObject’s while providing a powerful RTMP / Servlet implementation. In addition to support for the RTMP protocol, the application server has an embedded Tomcat Servlet container for Java EE Web Applications. Application development draws additional benefits from the Spring Framework and Scope based event driven services.
By using the Open Source Red5 Media Server, you are developing with a truly open and extensible platform that can be used in Video Conferences, Multi-User Gaming and Enterprise Application Software.
Happy Coding and enjoy our powerful free community server"

sound stream stops loading

I have a little sound stream script here, but sometimes if you press play to the next track before the current has done loading the next track doesn't comeplete loading
package player {
import flash.events.Event;
import flash.display.Sprite;
import flash.external.ExternalInterface;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.net.URLRequest;
import player.Loader_bar;
public class Stream extends Sprite {
private var _Sound = null;
private var _Channel = null;
private var isLoading = false;
private var _Loader_bar = null;
public var loader_color = null;
public var loader_width = 0;
public var loader_height = 0;
private var i = 0;
public function Stream(){
this._Loader_bar = new Loader_bar();
addChild(this._Loader_bar);
}
public function cnstr(){
this._Loader_bar.color = this.loader_color;
this._Loader_bar.w = this.loader_width;
this._Loader_bar.h = this.loader_height;
this._Loader_bar.cnstr();
}
public function play(url){
this.stop();
this.close();
this._Sound = new Sound();
this.isLoading = true;
this.addEventListener(Event.ENTER_FRAME, listener_bytesLoaded);
this._Sound.addEventListener(Event.COMPLETE, listener_loadedComplete);
this._Sound.load(new URLRequest(url));
this._Channel = this._Sound.play();
}
public function stop(){
if(this._Channel){
this._Channel.stop();
}
}
private function close(){
if(this.isLoading){
this._Sound.close();
}
}
private function listener_loadedComplete(event){
this.close();
this.isLoading = false;
this.removeEventListener(Event.ENTER_FRAME, listener_bytesLoaded);
}
private function listener_bytesLoaded(event){
var float = this._Sound.bytesLoaded / this._Sound.bytesTotal;
this._Loader_bar.progress(float);
var data = {
i : this.i,
float : float,
loaded : this._Sound.bytesLoaded,
total : this._Sound.bytesTotal
};
ExternalInterface.call('swf2js', 'tst_progress', data);
this.i++;
}
}
}
close():void
Closes the stream, causing any download of data to cease.
try this:
create a boolean like hasLoaded, set it to false. When the sound successfully loads set it to true.
Then when you play a sound you can test for hasLoaded in your play() function. If you have called play() before your previous sound has loaded, hasLoaded will be false, in which case you call this._Sound.close() before creating and loading the new sound. The reason for testing instead of just calling close() is so if you have paused the stream you don't have to reload it to play it again.
ADDITION:
Concerning the load not reporting properly, you have your progress logic set up incorrectly. Try this:
1) import flash.events.ProgressEvent
2) For the listener, replace this.addEventListener(Event.ENTER_FRAME, listener_bytesLoaded); in your play() method with this._Sound.addEventListener(ProgressEvent.PROGRESS, listener_bytesLoaded);
3) Change your listener_bytesLoaded() method to the following:
private function listener_bytesLoaded(event:ProgressEvent)
{
var percent = event.bytesLoaded / event.bytesTotal;
this._Loader_bar.progress(percent);
var data = {
i : this.i,
float : percent,
loaded : event.bytesLoaded,
total : event.bytesTotal
};
ExternalInterface.call('swf2js', 'tst_progress', data);
this.i++;
}
4) Change this.removeEventListener(Event.ENTER_FRAME, listener_bytesLoaded);
in your listener_loadedComplete() method to this._Sound.removeEventListener(ProgressEvent.PROGRESS, listener_bytesLoaded); and then move it out of that method and place it inside the conditional in the close() method.
NOTE - I didn't actually compile this but I think its good to go. Hope that helps. :)