SimpleFlvWriter.as – AS3 Class to Create FLV’s
An Actionscript 3 class for use with Adobe AIR to create uncompressed Macromedia Flash Video (FLV) files to the local filesystem. It will add proper onMetaData info as well. It’s very simple to use.
Example usage:
var myWriter:SimpleFlvWriter = SimpleFlvWriter.getInstance(); myWriter.createFile(myFile, 320,240, 30, 120); myWriter.saveFrame( myBitmapData1 ); myWriter.saveFrame( myBitmapData2 ); myWriter.saveFrame( myBitmapData3 ); // ... etc. myWriter.closeFile();
See the comment blocks in the source for more info. It doesn’t support audio.
This comes out of the app I made a little while ago, Webcam DVR. I’m posting the class file now because the app is fresh in my mind, as I just updated it to make it compatible with the current AIR runtime to enter it into the Adobe AIR Developer Derby.
Download Webcam DVR for AIR, updated for the beta AIR runtime (See the previous Webcam DVR post for usage notes).
Licensed under a Creative Commons Attribution 3.0 License.

Hello,
First of all – thank you for the script!
I have problems with the audio support.
Seems it has some synchronizing problems – it appears at the end of the video
Any suggestions or links where to look for solution?
Thanks!
Nice script y’all,
Does this work with Flash in a browser? We need to record what’s going on, on stage. So if a user drag and drops a box, we need to record a few of those frames, and turn it into a video.
Thanks!
DanC
hey hey
thanks !
package {
import flash.display.BitmapData;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.media.Camera;
import flash.media.Video;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.net.NetStreamAppendBytesAction;
import flash.net.Responder;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
/**
* @author BECKER Christophe
*/
public class Main extends Sprite {
//
public var vid : Video;
public var cam : Camera;
public var bt : Sprite;
public var txt : TextField ;
//
public var boucle : int;
public var flv_writer : FlvWriter;
//
public var nc : NetConnection;
public var responder : Responder;
//
public function Main() {
cam = Camera.getCamera();
cam.setMode(640, 480, 15);
bt = new Sprite();
var sh : Shape = new Shape();
sh.graphics.beginFill(0×000000);
sh.graphics.drawRect(0, 0, 640, 20);
sh.graphics.endFill();
bt.addChild(sh);
var txtFormat : TextFormat = new TextFormat();
txtFormat.color = 0xffffff;
txt = new TextField();
txt.defaultTextFormat = txtFormat;
txt.text = ‘click me for start video recorer’;
txt.autoSize = TextFieldAutoSize.LEFT;
txt.selectable = false;
txt.x = (sh.width / 2) – (txt.width / 2);
bt.addChild(txt);
bt.y = 0;
bt.addEventListener(MouseEvent.CLICK, run);
addChild(bt);
vid = new Video(cam.width, cam.height);
vid.attachCamera(cam);
vid.y = 30;
addChild(vid);
}
private function run(e : MouseEvent) : void {
flv_writer = FlvWriter.getInstance();
flv_writer.createFile(cam.width, cam.height, 5);
bt.removeEventListener(MouseEvent.CLICK, run);
txt.text = ‘recording’;
txt.x = (bt.width / 2) – (txt.width / 2);
this.addEventListener(Event.ENTER_FRAME, add_image);
}
private function add_image(e : Event) : void {
if (!cam.muted) {
trace(“add_image”);
boucle++;
if (boucle == 25) {
this.removeEventListener(Event.ENTER_FRAME, add_image);
nc = new NetConnection();
nc.connect(null);
var ns : NetStream = new NetStream(nc);
ns = new NetStream(nc);
ns.client = {};
ns.client.onMetaData = metaDataHandler;
vid.attachNetStream(ns);
ns.play(null);
ns.appendBytesAction(NetStreamAppendBytesAction.RESET_BEGIN);
ns.appendBytes(flv_writer.byte);
} else {
add_picture();
}
}
}
private function metaDataHandler(meta : Object) : void {
if (vid) {
vid.width = meta.width;
vid.height = meta.height;
}
}
public function add_picture() : void {
var img : BitmapData = new BitmapData(cam.width, cam.height);
img.draw(vid);
flv_writer.image = img;
}
}
}
import flash.display.BitmapData;
import flash.utils.ByteArray;
/*
SimpleFlvWriter.as
Lee Felarca
http://www.zeropointnine.com/blog
5-2007
v0.8
Singleton class to create uncompressed FLV files.
Does not handle audio. Feel free to extend.
Source code licensed under a Creative Commons Attribution 3.0 License.
http://creativecommo…icenses/by/3.0/
Some Rights Reserved.
EXAMPLE USAGE:
var myWriter:SimpleFlvWriter = SimpleFlvWriter.getInstance();
myWriter.createFile(myFile, 320,240, 30, 120);
myWriter.saveFrame( myBitmapData1 );
myWriter.saveFrame( myBitmapData2 );
myWriter.saveFrame( myBitmapData3 ); // etc.
myWriter.closeFile();
*/
class FlvWriter {
static private var _instance : FlvWriter;
private var frameWidth : int = 320;
private var frameHeight : int = 240;
private var frameRate : Number = 15;
private var duration : Number = 10;
private const blockWidth : int = 32;
private const blockHeight : int = 32;
private var previousTagSize : uint = 0;
private var iteration : int = 0;
private var bmp : BitmapData;
//
private var out : ByteArray;
public static function getInstance() : FlvWriter {
if (FlvWriter._instance == null)
FlvWriter._instance = new FlvWriter(new SingletonEnforcer());
return FlvWriter._instance;
}
public function FlvWriter(singletonEnforcer : SingletonEnforcer) {
out = new ByteArray();
}
public function createFile(pWidth : int, pHeight : int, pFramesPerSecond : Number, pDurationInSeconds : Number = 0) : void {
frameWidth = pWidth;
frameHeight = pHeight;
frameRate = pFramesPerSecond;
duration = pDurationInSeconds;
out.writeBytes(header());
// create metadata tag
out.writeUnsignedInt(previousTagSize);
out.writeBytes(flvTagOnMetaData());
}
public function set image(pBitmapData : BitmapData) : void {
// bitmap dimensions should of course match parameters passed to createFile()
bmp = pBitmapData;
out.writeUnsignedInt(previousTagSize);
out.writeBytes(flvTagVideo());
}
public function get byte() : ByteArray {
return out;
}
private function header() : ByteArray {
var ba : ByteArray = new ByteArray();
ba.writeByte(0×46);
// ‘F’
ba.writeByte(0x4C);
// ‘L’
ba.writeByte(0×56);
// ‘V’
ba.writeByte(0×01);
// Version 1
ba.writeByte(0×01);
// misc flags – video stream only
ba.writeUnsignedInt(0×09);
// 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(0×09);
// 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(0×13);
// 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 0 && y1 + 1 == yMax) yLimit = yRemainder;
for (var y2 : int = 0;y2 0 && x1 + 1 == xMax) xLimit = xRemainder;
for (var x2 : int = 0;x2 > 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));
var mdc : String = “SimpleFLVWriter.as v0.8 zeropointnine.com”;
writeUI16(b, “metadatacreator”.length);
b.writeUTFBytes(“metadatacreator”);
b.writeByte(2);
writeUI16(b, mdc.length);
b.writeUTFBytes(mdc);
// 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 <> 8;
var byte1 : int = byte1a + byte1b;
var byte2 : int = p2 & 0xff;
stream.writeByte(byte1);
stream.writeByte(byte2);
}
}
class SingletonEnforcer {
}
/*
FLV structure summary:
header
previoustagsize
flvtag
[info]
videodata
[info]
screenvideopacket
[info]
imageblocks
imageblocks
…
previoustagsize
flvtag
…
FLV file format:
header
last tag size
FLVTAG:
tagtype
datasize
timestamp
timestampextended
streamid
data [VIDEODATA]:
frametype
codecid
videodata [SCREENVIDEOPACKET]:
blockwidth ub[4]
imagewidth ub[12]
blockheight ub[4]
imageheight ub[12]
imageblocks [IMAGEBLOCKS[]]:
datasize ub[16]
data..
last tag size
FLVTAG
etc.
*/
i’m no speak to english.
I hope, i have a webcam.
not use FMS3.5 , *.flv Files to the save in this localcomputer and android mobile
sorry ㅠ ㅠ i’m no speak to english.;;;;;^^;
if create the FLV FILE, I don’t know what to do
action script 3.0 method = byteArray???? file??? filereference??? nstream?? Camera??
Video??
한국말할줄알면 도와주세요 ㅋㅋㅋ
FMS를 거치지않고 로컬에 바로 저장하는것이거든요.
예를들면 일반 카메라에서 동영상 촬영하는것 있잔아요???
그것처럼 만들려고 하고있습니다.
FMS를 이용하면 publish에서 record 를 주고 영상끝나면 다운로드 바로 받게끔 하면
상관없지만 만약 핸드폰 모바일에서 구현할때 인터넷이 있어야만 가능하잔아요 ㅠ ㅋ
현재 air for android에서 제작중입니다. 안드로이드 2.2에 탑재시켜서 어플 개발하고 있거
든요. ㅋㅋ 누군가의 한국인이 보는날까지….
-한국인 최성민-
Hello,
Can we see an exemple of this source CODE ?
Because a lot people don’t understund how to use it in AS3 :)
i tried this class to make a flv movie out of a movieclip, and it works great, but the background is opaque black. how can i make it transparent ? Thanks
This is a great start thanks! I am still trying to implement audio with a timer based recording solution I think I would have better luck slamming my head against a concrete wall. With the new release of stageVideo everything has become even more complicated aahhh! Then they stripped the web attachCamera method out of videoDisplay me thinks a subclass is in order with an override.
Yes cool, please do share if you make any progress on that!
Thanks for the SimpleFLVWriter Class. Its simply great. I have extended that to make a SWC for Screen Capture which you may find here at http://deepanjandas.wordpress.com/2011/02/05/screen-capture-with-actionscript/
Warm Regards
Deepanjan Das
@claudiu,
The class doesn’t support alpha, which is something that never crossed my mind (!).
Thanks for pointing it out. I’ve been revisiting this class recently and will look into that.
Lee
Hi, do you plan to make it possible to write compressed FLVs? That feature would really boost a lot your class.
Mauricio, I don’t have any plans myself to add compressed video support although I think that would be amazing. I’ve been wondering for a long time now whether it’s possible to use the x264 library via Alchemy to do the job. Unfortunately, I don’t have the C++ chops for such an undertaking. :)
However, I have been revisiting this class over the past couple days to get Alchemy-based video encoding working. When it’s in presentable form, I will make an update…
Thanks for this. Is there a way to do it in reverse – FLV to Bitmap Array?
Sure, the gist is this: myBitmapData.draw(myVideo);
Share please when you get AUDIO+FLV without RED5/FMS
THANK U VERY MATCH
Contact me here where u get it: rezk3r@yahoo.com
Are there any updates yet? I’ve tested it and it works fine i could really use it however the filesize of the created flv are a bit too high
Hi Simon, the FLV writer has been updated and rewritten. See the most recent couple posts for it.
hi, it is possible to encode a audio stream in flv. try this file. http://code.google.com/p/e4xu/source/browse/trunk/src/org/wvxvws/encoding/FLVTranscoder.as
Is it possible to display this in your site ? I’m new to AS3 and flex… I can’t not check this 1,2,3
hi can i use these methods to publish on a media server. like red5
ns.appendBytesAction(NetStreamAppendBytesAction.RESET_BEGIN);
ns.appendBytes(flv_writer.byte);
and then ns.publish(“feedname”,”record”)? or something.??/
Great Job!
Awesome!
I used this to make recordable live light-paintings.
https://plus.google.com/photos/116588964268975779696/albums/5680545804517491425
http://positlabs.com/weblog/2011/light-paint-live/ (this function only exists in the desktop version)
Hi,
Is there a way to not use flash.file.* libraries? Because they limit the application to AIR, but I am trying to use this as a web application.
Cheers!
Hai,
I successfully done screenCapture in web application from your site.Then how can i do “Audio with screenCapture” in web application.If any resource u have please update it
Thanks in advance,
SivaMurugan.A
Thanks for posting this.
But looks like if audio recording is required this solution will not pass.
Check this way to record the video from AIR using embedded Red5 media server. Quite simple. The only disadvantage, that Java should be present in PC as well.
http://mydevrecords.blogspot.com/2012/01/local-recording-in-adobe-air-using-red5.html
Can we see an exemple of this source CODE working using flash cs5
cant get it working using AS3 :)
PLEASE REALLY STUCK :(
hay how to video recorder with sound in screen capture with RED5
Contact me here where u get it: sivamurugan.ma@yahoo.com
Thanks in advance,
Siva