Close Show/hide page

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.

90 Responses to “SimpleFlvWriter.as – AS3 Class to Create FLV’s”

  1. giorgio says:

    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!

  2. Dan Course says:

    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

  3. Goabonga says:

    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.
    */

  4. 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??

  5. 한국말할줄알면 도와주세요 ㅋㅋㅋ

    FMS를 거치지않고 로컬에 바로 저장하는것이거든요.

    예를들면 일반 카메라에서 동영상 촬영하는것 있잔아요???

    그것처럼 만들려고 하고있습니다.

    FMS를 이용하면 publish에서 record 를 주고 영상끝나면 다운로드 바로 받게끔 하면

    상관없지만 만약 핸드폰 모바일에서 구현할때 인터넷이 있어야만 가능하잔아요 ㅠ ㅋ

    현재 air for android에서 제작중입니다. 안드로이드 2.2에 탑재시켜서 어플 개발하고 있거

    든요. ㅋㅋ 누군가의 한국인이 보는날까지….
    -한국인 최성민-

  6. COCA1 says:

    Hello,
    Can we see an exemple of this source CODE ?
    Because a lot people don’t understund how to use it in AS3 :)

  7. claudiu says:

    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

  8. ChaosWriter says:

    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.

  9. admin says:

    Yes cool, please do share if you make any progress on that!

  10. 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

  11. admin says:

    @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

  12. Mauricio Rubio says:

    Hi, do you plan to make it possible to write compressed FLVs? That feature would really boost a lot your class.

  13. admin says:

    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…

  14. aj says:

    Thanks for this. Is there a way to do it in reverse – FLV to Bitmap Array?

  15. admin says:

    Sure, the gist is this: myBitmapData.draw(myVideo);

  16. COCA1 says:

    Share please when you get AUDIO+FLV without RED5/FMS

    THANK U VERY MATCH

    Contact me here where u get it: rezk3r@yahoo.com

  17. Simon says:

    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

  18. admin says:

    Hi Simon, the FLV writer has been updated and rewritten. See the most recent couple posts for it.

  19. Matt says:

    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

  20. shammi says:

    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.??/

  21. MBall says:

    Great Job!

  22. Josh says:

    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)

  23. Ozkan says:

    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!

  24. murugan says:

    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

  25. Vadym says:

    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

  26. rd says:

    Can we see an exemple of this source CODE working using flash cs5
    cant get it working using AS3 :)

    PLEASE REALLY STUCK :(

  27. murugan says:

    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

  28. Olivier says:

    Heys,

    Just wanted to say thanks for the class, and everybody requesting code must be stupid, the post hasnt changed since 2007 so that example has always been there, theres also an example at the top of the class.

    The thing you will have to do for yourself is calling the saveFrame function with some bitmapdata, please know that you can get bitmapdata from anything (video, camera, stage) be it directly from flash, or trough adobes old school library (google for JPGEncoder.as and/or PNGEncoder.as, itll find you a bunch of classes by adobe, that include some image classes to make bitmapdata from displayobjects, the whole ‘library’ is very usefull btw)

    Anyway yea, thanks for your work Lee Felarca! gonna use this to allow people to record video with their cam, using ffmpeg in the backend to re-work the flv to have compression (read; converting it to a better codec)

  29. Paul says:

    I need a flash embed instead of AIR app
    coz i’m creating a component for the web with Flash Builder.
    Is this library usable for this purpose?

  30. aSnO says:

    Hi,
    Here are my questions
    1. Does this work only on AIR apps? Could I use it on a web app?
    2. Has anybody solved the issue with the sound? Can I see an example?
    3. Is it possible to record the flv generated with RED5?

    Thanks in advance

    J

  31. ihl0700 says:

    @aSnO:
    1. I think it is only works on AIR or .exe
    2. Unfortunately I haven’t found anyone solved the sound issue yet.
    3. Yes. I think Red5 (or FMS) is the only way to record any video or sound if you want to deploy your app on web.

    I had same problem before, but I manage to convince client to make stop motion animation from webcam instead.

  32. kumar says:

    great stuff . thanks ,

    is it possible for Web Application ?

  33. Great goods from you, man. I have understand your stuff previous to and you’re just too wonderful. I actually like what you have acquired here, really like what you are saying and the way in which you say it. You make it enjoyable and you still take care of to keep it wise. I can not wait to read far more from you. This is actually a wonderful site.

  34. Brilliant article with the subject on the flip side I ‘ve got to disagree on a portion of the things. I have written into this subject some time ago and revealed that my data presented a varying sentiment than your own. None the less I considered it stimulating and have since book-marked your web-site.

  35. amit says:

    It’s a great article. But i want to know is there any way to compress flvencoded bytes because as i send bytes to webservice using post method but couldn’t send.

    Any help appreciated!

    Thanks

  36. A round of applause for your blog article.Thanks Again. Will read on…

  37. 829657 436488hi, your web site is wonderful. I truly do several thanks for operate 573022

  38. I’m new to your blog and i really appreciate the nice posts and great layout.:.;,’

  39. Excellent post, I will be checking back regularly to look for updates.

Leave a Reply