Flex AIR: record audio locally and transfer to server as flv later

I have a Flash builder 4.6 mobile application that I'd like to record audio locally when not connected to the internet or 3/4G and then upload it when a network is in range to a server (Red5) as an .flv.

I'm able to record the audio but it's in .wav format as a byteArray.  I'd like to be able to connect to a netStream or something and publish the audio to the Red5 server as an .flv file.  Does anyone have an example of how to do that?.. or an alternative that would do the same thing? ie. capture audio locally in AIR on a mobile device and then get it to a server as an .flv.

StarDusterIIAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

dgofmanCommented:
You have to transfer WAV 16 bytesArray to your server using a Socket or IDataOutput writeBytes class and convert WAV to FLV using some third party tool on the server side.

http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/Socket.html


http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/utils/IDataOutput.html
0
StarDusterIIAuthor Commented:
dgofman, man you're here all the time!  ;-)

Yeah, I could do that.  I have it set up where I transfer the wav file to the server, make a video out of it with AVIsynth and then convert to flv with ffmepeg.  But that, which is basically your solution, requires a lot of clumbsy processing on the server side.  This must be all automated so the user doesn't have to know what's going on, so I was hoping that there was some way to like "publish" a stream using the wav as input to a netStream to Red5 and just have Red5 write the .flv in it's normal location.

Unfortunately, the upload and convert may be the only solution.  We'll hope someone has a better one.
0
dgofmanCommented:
If you are using AIR I can share my library but I am using saving images byteArray to FLV

var flvWriter:SimpleFlvWriter = SimpleFlvWriter.getInstance();

var bmp:BitmapData = Bitmap(event.target.content).bitmapData;
			if(flvWriter.isReady){
				var tag:uint = flvWriter.saveFrame(bmp);
				if(tag != lastTag && lastTag != -1){
					lastTag = -1;
					function delay():void{
						lastTag = tag;
						dispatchEvent(new Event(FRAME_CHANGE));
					};
					setTimeout(delay, 1000);
				}
				takeScreenShot();
			}


....
flvWriter.closeFile();

Open in new window


SimpleFlvWriter.as

package
{
	import flash.filesystem.File;
    import flash.filesystem.FileMode;
    import flash.filesystem.FileStream;
    import flash.display.BitmapData;
    import flash.utils.ByteArray;

	public class SimpleFlvWriter
	{
		static private var _instance:SimpleFlvWriter;
				
		private var frameWidth:int;
		private var frameHeight:int;
		private var frameRate:Number;
		private var duration:Number;

		private var fs:FileStream = new FileStream();
		private var f:File;
		private const blockWidth:int = 32;
		private const blockHeight:int = 32;
		private var previousTagSize:uint = 0;
		private var iteration:int = 0;
		private var bmp:BitmapData;
		
		[Bindable]
		public var isReady:Boolean = false;

		public static function getInstance():SimpleFlvWriter 
		{
			if(SimpleFlvWriter._instance == null) 
				SimpleFlvWriter._instance = new SimpleFlvWriter(new SingletonEnforcer());
			return SimpleFlvWriter._instance;
		}

		public function SimpleFlvWriter(singletonEnforcer:SingletonEnforcer)
		{
		}

		public function createFile(pFile:File, pWidth:int, pHeight:int, pFramesPerSecond:Number, pDurationInSeconds:Number=0):void
		{
			/*
				Parameters:
				
				pFile: The file which will be created and written to
				pWidth: Video height
				pWidth: Video width
				pFramesPerSecond: Determines framerate of created video
				pDurationInSeconds: Duration of video file to be created. Used for metadata only. Optional.
			*/
			
			isReady = true;
			frameWidth = pWidth;
			frameHeight = pHeight;
			frameRate = pFramesPerSecond;
			duration = pDurationInSeconds;

			f = pFile;
			fs.openAsync(f, FileMode.WRITE);

			// create header
			fs.writeBytes( header() );
			
			// create metadata tag
			fs.writeUnsignedInt( previousTagSize );
			fs.writeBytes( flvTagOnMetaData() );
		}

		public function saveFrame(pBitmapData:BitmapData):uint
		{
			// bitmap dimensions should of course match parameters passed to createFile()
			bmp = pBitmapData;
			fs.writeUnsignedInt( previousTagSize );
			fs.writeBytes( flvTagVideo() );	
			return previousTagSize;
		}
		
		public function closeFile():void
		{
			isReady = false;
			fs.close();	
		}		
				
		private function header():ByteArray
		{
			var ba:ByteArray = new ByteArray();
			ba.writeByte(0x46) // 'F'
			ba.writeByte(0x4C) // 'L'
			ba.writeByte(0x56) // 'V'
			ba.writeByte(0x01) // Version 1
			ba.writeByte(0x01) // misc flags - video stream only
			ba.writeUnsignedInt(0x09) // header length
			return ba;
		}		
		
		private function flvTagVideo():ByteArray
		{
			var tag:ByteArray = new ByteArray();
			var dat:ByteArray = videoData();
			var timeStamp:uint = uint(1000/frameRate * iteration++);

			// tag 'header'
			tag.writeByte( 0x09 ); 					// tagType = video
			writeUI24(tag, dat.length); 			// data size
			writeUI24(tag, timeStamp);				// timestamp in ms
			tag.writeByte(0);						// timestamp extended, not using *** 
			writeUI24(tag, 0);						// streamID always 0
			
			// videodata			
			tag.writeBytes( dat );
			
			previousTagSize = tag.length;
			return tag;
		}
		
		private function videoData():ByteArray
		{
			var v:ByteArray = new ByteArray;
			
			// VIDEODATA 'header'
			v.writeByte(0x13); // frametype (1) + codecid (3)
			
			// SCREENVIDEOPACKET 'header'			
			// blockwidth/16-1 (4bits) + imagewidth (12bits)
			writeUI4_12(v, int(blockWidth/16) - 1,  frameWidth);
			// blockheight/16-1 (4bits) + imageheight (12bits)
			writeUI4_12(v, int(blockHeight/16) - 1, frameHeight);			

			// VIDEODATA > SCREENVIDEOPACKET > IMAGEBLOCKS:

			var yMax:int = int(frameHeight/blockHeight);
			var yRemainder:int = frameHeight % blockHeight; 
			if (yRemainder > 0) yMax += 1;

			var xMax:int = int(frameWidth/blockWidth);
			var xRemainder:int = frameWidth % blockWidth;				
			if (xRemainder > 0) xMax += 1;
				
			for (var y1:int = 0; y1 < yMax; y1++)
			{
				for (var x1:int = 0; x1 < xMax; x1++) 
				{
					// create block
					var block:ByteArray = new ByteArray();
					
					var yLimit:int = blockHeight;	
					if (yRemainder > 0 && y1 + 1 == yMax) yLimit = yRemainder;

					for (var y2:int = 0; y2 < yLimit; y2++) 
					{
						var xLimit:int = blockWidth;
						if (xRemainder > 0 && x1 + 1 == xMax) xLimit = xRemainder;
						
						for (var x2:int = 0; x2 < xLimit; x2++) 
						{
							var px:int = (x1 * blockWidth) + x2;
							var py:int = frameHeight - ((y1 * blockHeight) + y2); // (flv's save from bottom to top)
							var p:uint = bmp.getPixel(px, py);

							block.writeByte( p & 0xff ); 		// blue	
							block.writeByte( p >> 8 & 0xff ); 	// green
							block.writeByte( p >> 16 ); 		// red
						}
					}
					block.compress();

					writeUI16(v, block.length); // write block length (UI16)
					v.writeBytes( block ); // write block
				}
			}
			return v;
		}

		private function flvTagOnMetaData():ByteArray
		{
			var tag:ByteArray = new ByteArray();
			var dat:ByteArray = metaData();

			// tag 'header'
			tag.writeByte( 18 ); 					// tagType = script data
			writeUI24(tag, dat.length); 			// data size
			writeUI24(tag, 0);						// timestamp should be 0 for onMetaData tag
			tag.writeByte(0);						// timestamp extended
			writeUI24(tag, 0);						// streamID always 0
			
			// data tag		
			tag.writeBytes( dat );
			
			previousTagSize = tag.length;
			return tag;
		}

		private function metaData():ByteArray
		{
			// onMetaData info goes in a ScriptDataObject of data type 'ECMA Array'

			var b:ByteArray = new ByteArray();
			
			// ObjectNameType (always 2)
			b.writeByte(2);	
		
			// ObjectName (type SCRIPTDATASTRING):
			writeUI16(b, "onMetaData".length); // StringLength
			b.writeUTFBytes( "onMetaData" ); // StringData
		
			// ObjectData (type SCRIPTDATAVALUE):
			
			b.writeByte(8); // Type (ECMA array = 8)
			b.writeUnsignedInt(7) // // Elements in array
		
			// SCRIPTDATAVARIABLES...
			
			if (duration > 0) {
				writeUI16(b, "duration".length);
				b.writeUTFBytes("duration");
				b.writeByte(0); 
				b.writeDouble(duration);
			}
			
			writeUI16(b, "width".length);
			b.writeUTFBytes("width");
			b.writeByte(0); 
			b.writeDouble(frameWidth);

			writeUI16(b, "height".length);
			b.writeUTFBytes("height");
			b.writeByte(0); 
			b.writeDouble(frameHeight);

			writeUI16(b, "framerate".length);
			b.writeUTFBytes("framerate");
			b.writeByte(0); 
			b.writeDouble(frameRate);

			writeUI16(b, "videocodecid".length);
			b.writeUTFBytes("videocodecid");
			b.writeByte(0); 
			b.writeDouble(3); // 'Screen Video' = 3

			writeUI16(b, "canSeekToEnd".length);
			b.writeUTFBytes("canSeekToEnd");
			b.writeByte(1); 
			b.writeByte(int(true));

			// VariableEndMarker1 (type UI24 - always 9)
			writeUI24(b, 9);
		
			return b;			
		}

		private function writeUI24(stream:*, p:uint):void
		{
			var byte1:int = p >> 16;
			var byte2:int = p >> 8 & 0xff;
			var byte3:int = p & 0xff;
			stream.writeByte(byte1);
			stream.writeByte(byte2);
			stream.writeByte(byte3);
		}
		
		private function writeUI16(stream:*, p:uint):void
		{
			stream.writeByte( p >> 8 )
			stream.writeByte( p & 0xff );			
		}

		private function writeUI4_12(stream:*, p1:uint, p2:uint):void
		{
			// writes a 4-bit value followed by a 12-bit value in two sequential bytes

			var byte1a:int = p1 << 4;
			var byte1b:int = p2 >> 8;
			var byte1:int = byte1a + byte1b;
			var byte2:int = p2 & 0xff;

			stream.writeByte(byte1);
			stream.writeByte(byte2);
		}		
		
	}
	
	
}

class SingletonEnforcer {}

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Become a Microsoft Certified Solutions Expert

This course teaches how to install and configure Windows Server 2012 R2.  It is the first step on your path to becoming a Microsoft Certified Solutions Expert (MCSE).

StarDusterIIAuthor Commented:
Wow, that looks REALLY promising.  I have to run for now but will definitely be trying that in the morning!!  I'll have to take a closer look on switching from bitmap data to the audio bytearray.  Major thanks on what you've provided.  
0
StarDusterIIAuthor Commented:
Interesting.  Didn't get me exactly what I wanted but it did lead me to where you got the code you posted, ZeroPointNine.  Looking at their site, I found the flvEncoder that encodes both audio and video to .flv.  I put it in my code and it works perfectly.  So now I can encode locally as an .flv and then upload that file to the server when I'm back on-line.
0
StarDusterIIAuthor Commented:
While the example given was for encoding a bitmap into an .flv file, it did lead me to searching the web for the example and that led to finding the solution.
0
dgofmanCommented:
But SimpleFlvWriter doesn't supporting audio. For audio you have to use different library


http://code.google.com/p/e4xu/source/browse/trunk/src/org/wvxvws/encoding/FLVTranscoder.as
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Apache Flex

From novice to tech pro — start learning today.