Updated FLV Encoder, 3.5x faster with Alchemy30 Mar 2011
Demo: Record and encode FLV's from webcam in realtime, using Alchemy
Or, download AIR desktop version
This is an update to FLV Encoder which adds an optional Alchemy routine that's about 3.5x faster, as well as FileStream support for writing directly to a local file using AIR. The library has been architected in such a way that you can use the package while targeting either the browser (no FileStream support) or AIR, and either Flash 9 (no Alchemy support) or Flash 10 - without getting class dependency compiler errors.
The package on Github includes a couple examples (including the one from the last post). The API has changed a little so be sure to also see the example code below. A method
updateDurationMetadata() has been added so the video duration does not have to be declared at the start. Also, a bug where the top-most line of pixels was not being written has been fixed.
Realtime encoding demo:
Because of the increased speed of the Alchemy version, it is now viable to encode FLV's in realtime as the audio and video is being captured, at least within certain limits. Click on the thumbnail above for an online demo that encodes webcam video and audio to a file at 320x240 in real time. If your system is fast enough, you can keep the framerate set to 15FPS with minimal hiccups. The browser-based version must store the entire FLV in memory before saving to disk, but the equivalent AIR version can save its contents directly to a file so that the only limiting factor is disk space. I'm using a dynamic timing and queuing system to keep video and audio in sync which could be the topic of another post. (Note that this implementation is a proof of concept, and is not included in the github repo.)
Updated usage examples:
[A] To encode an FLV to a ByteArray, start by instantiating
ByteArrayFlvEncoder. The rest is similar to the last version, but you can now call
updateDurationMetadata() when you're done to update the duration property in the metadata. Finally, call
kill() to prepare the object for garbage collection:
var baFlvEncoder:ByteArrayFlvEncoder = new ByteArrayFlvEncoder(myFrameRate); // Omit the 3rd argument to NOT use Alchemy baFlvEncoder.setVideoProperties(myWidth, myHeight, VideoPayloadMakerAlchemy); baFlvEncoder.setAudioProperties(BaseFlvEncoder.SAMPLERATE_44KHZ, true, false, true); baFlvEncoder.start(); baFlvEncoder.addFrame(myBitmapData, myAudioByteArray); baFlvEncoder.addFrame(myBitmapData, myAudioByteArray); // etc. baFlvEncoder.updateDurationMetadata(); saveOutMyFileUsingFileReference( baFlvEncoder.byteArray ); baFlvEncoder.kill(); // for garbage collection
[B] To encode an FLV directly to a local file (in AIR), instantiate
FileStreamFlvEncoder with a
File reference, and open up the exposed
FileStream, and then close it when you're all done:
var myFile:File = File.documentsDirectory.resolvePath("video.flv"); var fsFlvEncoder:FileStreamFlvEncoder = new FileStreamFlvEncoder(myFile, myFrameRate); fsFlvEncoder.fileStream.openAsync(myFile, FileMode.UPDATE); fsFlvEncoder.setVideoProperties(myWidth, myHeight, VideoPayloadMakerAlchemy); fsFlvEncoder.setAudioProperties(BaseFlvEncoder.SAMPLERATE_44KHZ, true, false, true); fsFlvEncoder.start(); fsFlvEncoder.addFrame(myBitmapData, myAudioByteArray); fsFlvEncoder.addFrame(myBitmapData, myAudioByteArray); // etc. fsFlvEncoder.updateDurationMetadata(); fsFlvEncoder.fileStream.close(); fsFlvEncoder.kill();
A new instance of FlvEncoder should be created for every new FLV to be generated.
If you're using the Alchemy version, the SWC lives in
leelib/util/flvEncoder/alchemy, so be sure add that folder to your project library path. The C++ code is there as well.
Note that the current Alchemy video encoding routine is being done synchronously, which means that although it's much faster than the regular AS3 version, the main Flash thread must still wait for the Alchemy encoding to be done before continuing. An asynchronous version would allow for a real-time encoding scenario that is more flexible and probably with less 'dropouts', and would probably prompt the need an internal queuing system. Anyway, this warrants further development.