Recording and streaming video with FM2 and AS3

For some reason all of the code examples for using FM2 are in AS2. Converting the examples to AS3 can be a bit challenging, especially since there is one really key piece of information that is left out that is new to AS3 and specific to the use of FM2. The problem is the that FM2 only supports AMF0, but in AS3 the default setting for the NetConnection class' defaultObjectEncoding property is AMF3. Without changing this setting to AMF0, your code will seem to be correct, but will fail to connect without really telling you why. Check out the class that I created from the Adobe example and the MXML to use it.
package
{
    import flash.events.*;
    import flash.media.Microphone;
    import flash.media.Camera;
    import flash.media.Video;
    import flash.net.NetConnection;
    import flash.net.NetStream;
    import mx.controls.VideoDisplay;

    public class StreamingFLV extends EventDispatcher {
        private var cam:Camera;
        private var mic:Microphone;
        private var rtmp:String;
        private var nc:NetConnection;
        private var ns:NetStream;
        private var local_video:VideoDisplay;
        private var _rtmp:String;
        private var _source:String;

        public function StreamingFLV(_lv:VideoDisplay,_r:String,_s:String) {
            _rtmp = _r;
            _source = _s;

            cam = Camera.getCamera();
            cam.addEventListener(ActivityEvent.ACTIVITY, activityHandler);
            cam.addEventListener(StatusEvent.STATUS, handleCameraStatus);

            mic = Microphone.getMicrophone();
            mic.addEventListener(ActivityEvent.ACTIVITY, activityHandler);
            mic.addEventListener(StatusEvent.STATUS, handleCameraStatus);

            local_video = _lv;

            NetConnection.defaultObjectEncoding = flash.net.ObjectEncoding.AMF0;
            nc = new NetConnection();
            nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
            nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
            nc.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
            nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
            nc.connect(_rtmp);
        }

        public function startRecordFLV():void {
            local_video.attachCamera(cam);
            ns.attachAudio(mic);
            ns.attachCamera(cam);
            ns.publish(_source, "record");
        }

        public function stopRecordFLV():void {
            ns.close();
            local_video.close();
        }

        public function startPlayback():void {
            local_video.source = _rtmp +"/"+ _source + ".flv";
        }

        public function stopPlayback():void {
            local_video.close();
        }

        private function connectStream():void {
            ns = new NetStream(nc);
            ns.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
            ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
            ns.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
            ns.client = this;

            dispatchEvent(new Event("onVideoReady"));
        }

        private function handleCameraStatus(event:StatusEvent):void {
            trace("handleCameraStatus: " + event);
        }

        private function activityHandler(event:ActivityEvent):void {
            trace("activityHandler: " + event);
        }

        private function netStatusHandler(event:NetStatusEvent):void {
            trace(event.info.code);
            switch (event.info.code) {
                case "NetConnection.Connect.Success":
                    connectStream();
                    trace("Connected");
                    break;
                case "NetStream.Play.StreamNotFound":
                    trace("No Connection!");
                    break;
            }
        }
        private function securityErrorHandler(event:SecurityErrorEvent):void {
            trace("securityErrorHandler: " + event);
        }
        private function ioErrorHandler(event:AsyncErrorEvent):void {
            trace("ioErrorHandler: " + event);
        }
        private function asyncErrorHandler(event:AsyncErrorEvent):void {
            trace("asyncErrorHandler: " + event);
        }
        private function onMetaData(info:Object):void {
            trace("metadata: duration=" + info.duration + " width=" + info.width + " height=" + info.height + " framerate=" + info.framerate);
        }
        private function onCuePoint(info:Object):void {
            trace("cuepoint: time=" + info.time + " name=" + info.name + " type=" + info.type);
        }
        private function onPlayStatus(info:Object):void {
            trace(info.toString());
        }

    }
}
And now the MXML:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    paddingTop="0"
    paddingLeft="0"
    paddingBottom="0"
    paddingRight="0"
    horizontalAlign="center"
    verticalAlign="middle"
    backgroundGradientAlphas="[0,0]"
    backgroundColor="#CCCCCC"
    applicationComplete="init();">
    <mx:Script>
        <![CDATA[
            import StreamingFLV;
            import flash.media.Camera;
            import flash.display.StageAlign;
            import flash.display.StageScaleMode;

            private var rFLV:StreamingFLV;
            private var pFLV:StreamingFLV;
            private var isPlaying:Boolean = false;
            private var isRecording:Boolean = false;

            private function init():void {
                stage.scaleMode = StageScaleMode.NO_SCALE;
                stage.align = StageAlign.TOP_LEFT;
            }

            private function initRecordStream():void {
                rFLV = new StreamingFLV(video_record,"rtmp://los1aps-flash/zflv/test","recordSample");
                rFLV.addEventListener("onVideoReady",initUI);
            }

            private function initPlayStream():void {
                pFLV = new StreamingFLV(video_play,"rtmp://los1aps-flash/zflv/test","recordSample");
                pFLV.addEventListener("onVideoReady",initUI);
            }

            private function recordToggle():void {
                if (!isPlaying) {
                    record.label = "RECORDING";
                    rFLV.startRecordFLV();
                } else {
                    record.label = "RECORD";
                    rFLV.stopRecordFLV();
                }
                isPlaying = !isPlaying;
            }

            private function playToggle():void {
                if (!isPlaying) {
                    play.label = "PLAYING";
                    pFLV.startPlayback();
                } else {
                    play.label = "PLAY";
                    pFLV.stopPlayback();
                }
                isPlaying = !isPlaying;
            }

            private function initUI(evt:Event):void {
                record.enabled = true;
                play.enabled = true;
            }
        ]]>
    </mx:Script>
    <mx:VBox horizontalAlign="center">
        <mx:HBox>
            <mx:VBox horizontalAlign="right">
                <mx:VideoDisplay id="video_record" width="320" height="240" creationComplete="initRecordStream();" />
                <mx:Button id="record" label="record" enabled="false" click="recordToggle();" />
            </mx:VBox>
            <mx:VBox horizontalAlign="left">
                <mx:VideoDisplay id="video_play" width="320" height="240" creationComplete="initPlayStream();" />
                <mx:Button id="play" label="play" enabled="false" click="playToggle();" />
            </mx:VBox>
        </mx:HBox>
    </mx:VBox>
</mx:Application>
Enjoy!

Comments are closed.