2

I'm using a NetStream in Data Generation Mode to play an embeded FLV using appendBytes. When the stream is finished playing, I'd like to loop the FLV file. I'm not sure how to achieve this. Here is what I have so far (this isn't a complete example):

        public function createBorderAnimation():void
        {
            // Load the skin image
            borderAnimation = Assets.BorderAnimation;

            // Convert the animation to a byte array
            borderAnimationBytes = new borderAnimation();

            // Initialize the net connection
            border_nc = new NetConnection();
            border_nc.connect( null );

            // Initialize the net stream
            border_ns = new NetStream( border_nc );
            border_ns.client = { onMetaData:function( obj:Object ):void{ trace(obj); } }
            border_ns.addEventListener( NetStatusEvent.NET_STATUS, border_netStatusHandler );
            border_ns.play( null );
            border_ns.appendBytes( borderAnimationBytes );

            // Initialize the animation
            border_vd = new Video( 1024, 768 );
            border_vd.attachNetStream( border_ns );

            // Add the animation to the stage
            ui = new UIComponent();
            ui.addChild( DisplayObject( border_vd ) );
            grpBackground.addElement( ui );             
        }

        protected function border_netStatusHandler( event:NetStatusEvent ):void
        {
            if( event.info.code == "NetStream.Buffer.Flush" || event.info.code == "NetStream.Buffer.Empty" )
            {
                border_ns.appendBytesAction( NetStreamAppendBytesAction.RESET_BEGIN );
                border_ns.appendBytes( borderAnimationBytes );
                border_ns.appendBytesAction( NetStreamAppendBytesAction.END_SEQUENCE );
            }
        }

This will loop the animation, but it starts chewing up memory like crazy. I've tried using NetStream.seek(0) and NetStream.appendBytesAction( NetStreamAppendBytesAction.RESET_SEEK ), but then I'm not sure what to do next. If you just try to call appendBytes again after that, it doesn't work, presumably because I'm appending the full byte array which has the FLV header and stuff? I'm not very familiar with how that all works.

Any help is greatly appreciated.

WesleyJohnson
  • 1,538
  • 1
  • 16
  • 30

2 Answers2

5

[Edit]

Initially I didn't see a memory problem with your code. However, the example app I was using (below) only listens for the NetStream.Buffer.Empty message from the NetStatusEvent. It does not listen for the NetStream.Buffer.Flush message. I just tried listening for both, as in your example, and the app goes haywire :)

The code below also shows how to use seek(0) to restart playback with appendBytes(). To make that work, you need to wait for NetStream.Seek.Notify from the NetStatusEvent, then append the bytes.

I also restarted playback without using seek(0) (the commented out line), and memory usage was fine.

I am using Mac OS X and measured memory with the top command and Activity Monitor. Activity Monitor's virtual memory stats fluctuated considerably, but nothing like a memory leak.

package
{
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.NetStatusEvent;
    import flash.media.Video;
    import flash.net.NetConnection;
    import flash.net.NetStream;
    import flash.net.NetStreamAppendBytesAction;
    import flash.utils.ByteArray;

    public class LoopEmbedFLV extends Sprite
    {

        [Embed(source="woot.flv",mimeType="application/octet-stream")]
        private var flv:Class;

        private var bytes:ByteArray;
        private var nc:NetConnection;
        private var ns:NetStream;
        private var video:Video;

        public function LoopEmbedFLV()
        {
            super();
            stage.scaleMode=StageScaleMode.NO_SCALE;
            stage.align=StageAlign.TOP_LEFT;
            bytes = new flv();
            nc=new NetConnection();
            nc.connect(null);
            ns=new NetStream(nc);
            video=new Video();
            addChild(video);
            video.attachNetStream(ns);
            ns.client = { onMetaData: function():void { } };
            ns.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
            ns.play(null);
            ns.appendBytes(bytes);
        }

        private function onNetStatus(event:NetStatusEvent):void
        {
            var code:String=event.info.code;
            trace(code);
            switch(code)
            {
                case "NetStream.Buffer.Empty":
                    ns.seek(0);
//                  seekToBeginning();
                    break;
                case "NetStream.Seek.Notify":
                    var seekPoint:Number = event.info.seekPoint;
                    trace("seeking to: ", seekPoint);
                    seekPoint == 0 ? seekToBeginning() : seekToOffset(seekPoint);
                    break;
            }
        }

        private function seekToBeginning():void
        {
            ns.appendBytesAction(NetStreamAppendBytesAction.RESET_BEGIN);
            ns.appendBytes(bytes);
            ns.appendBytesAction(NetStreamAppendBytesAction.END_SEQUENCE);
        }
        private function seekToOffset(seekPoint:Number):void
        {
            ns.appendBytesAction(NetStreamAppendBytesAction.RESET_SEEK);
            // todo
        }
    }
}
Sunil D.
  • 17,983
  • 6
  • 53
  • 65
  • Thanks, I'll have a look at this to see if it helps. I ended up finding out that the netStream was firing the "NetStream.Buffer.Flush" event relentlessly and because I was using that as a way to refill the buffer, I believe that's where the memory usage issue was introduced. I've since modified the code to something similar to yours by calling seek and then handling the notify event to essentially reset the buffer and begin appending bytes again. My only issue now is there is a very noticeable pause while this happens. In fairness, that wasn't part of the original question. – WesleyJohnson Aug 13 '12 at 17:39
  • Thanks, Sunil. I ended up using at timer to just continually stuff additional bytes into the NetStream, using appendBytes and by stripping out the header from the ByteArray and updating the underlying timestamps. This allows me to loop seamlessly, where as waiting on a NetStatusEvent of the buffer being empty to refill it always seemed to cause a noticeable delay. Still, your solution did fix the memory issue which was my original concern so I'm choosing your answer. Thanks for the help! – WesleyJohnson Aug 14 '12 at 15:16
  • Worth to mention this works starting from Flash 10.1 only. Still couldn't find a way to do it in Flash 10.0.. – rustyx Jan 26 '15 at 12:52
  • @rustyx the [appendBytes()](http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#appendBytes()) method was added in Flash 10.1 ... It's been so long since I worked w/Flash, but I'm assuming there is some other way to loop video that doesn't use "data generation mode" from Flash 10.1. – Sunil D. Jan 26 '15 at 19:30
1
package {

    import flash.display.MovieClip;
    import flash.events.AsyncErrorEvent;
    import flash.events.IOErrorEvent;
    import flash.events.NetStatusEvent;
    import flash.media.Video;
    import flash.net.NetConnection;
    import flash.net.NetStream;
    import flash.utils.ByteArray;


    public class VideoPlayer extends MovieClip {

        /** Проиграть массив данныx <b>byteArray</b> (flv) в видеоконтейнере <b>video</b>. Проигрывается 1 + n раз, при n >= 0 и infinity при n < 0
         * 
         * @param video - видеоконтейнер
         * @param fileArray - массив данныx (flv) 
         * @param repeat - количество повторений видеоролика (-1 - бесконечный цикл, 0 - проиграть без повторений, (n > 0) - n повторений)
         * 
         */     
        public static function playVideo(video:Video, byteArray:ByteArray, repeat:int = -1):void {
            var netConnection:NetConnection;
            var netStream:NetStream;
            var repeatCount:int = 0;

            var netStream_Status_Handler:Function = function(event:NetStatusEvent):void {
                switch(event.info.code || "") {
                    case 'NetStream.Buffer.Empty': {
                        update();
                    }
                    default: {
                        break;
                    }
                }
            }

            var netStream_IOError_Handler:Function = function(event:AsyncErrorEvent):void {

            }

            var netConnection_Status_Handler:Function = function(event:NetStatusEvent):void {
                switch(event.info.code || "") {
                    case 'NetConnection.Connect.Success': {
                        update();
                        break;
                    }

                    default: {
                        break;
                    }
                }
            }

            var update:Function = function():void {
                if(repeat > -1 && repeat < repeatCount) {
                    return;
                }

                repeatCount++;

                try {
                    if(netStream) {
                        netStream.close();
                        if(netStream.hasOwnProperty("dispose")) { // совместимость с fla|as3
                            netStream["dispose"]();
                        }
                        netStream = null;
                    }

                    netStream = new NetStream(netConnection);
                    netStream.addEventListener(NetStatusEvent.NET_STATUS, netStream_Status_Handler);
                    netStream.addEventListener(IOErrorEvent.IO_ERROR, netStream_IOError_Handler);
                    netStream.client = {};
                    netStream.play(null);
                    netStream["appendBytes"](byteArray); // совместимость с fla|as3

                    video.attachNetStream(netStream); 
                } catch(error:Error) {

                }
            }

            var netConnection_AsyncError_Handler:Function = function(event:AsyncErrorEvent):void {

            }

            var netConnection_IOError_Handler:Function = function(event:AsyncErrorEvent):void {

            }

            netConnection = new NetConnection();
            netConnection.addEventListener(NetStatusEvent.NET_STATUS , netConnection_Status_Handler);
            netConnection.addEventListener(AsyncErrorEvent.ASYNC_ERROR , netConnection_AsyncError_Handler);
            netConnection.addEventListener(IOErrorEvent.IO_ERROR , netConnection_IOError_Handler);
            netConnection.connect(null);
        }

    }
}
dimetrix
  • 11
  • 2
  • what is this an answer a question or what ? – NetStarter Jul 09 '13 at 14:20
  • Please don't post answers which are only code, flesh it out with some explanation. – Andy Hayden Jul 09 '13 at 15:04
  • 1
    More importantly, rewrite the comments in English. You cannot expect that future users understand the language used for the comments. – apaderno Jul 09 '13 at 17:13
  • +1. This works better than the previous solution, after a small modification to delay disposing of the old netStream the pause during rewind is all but gone. – rustyx Jan 26 '15 at 14:04