4

I have setup RED5 1.0 Final on my Windows 8 system and I am trying to get recording to work correctly. A tutorial I read said to buffer the data on the client side ( in Flash) and then once the buffer is empty close the connection.

The issue I am having is that when I begin recording the buffer immediately reports that it is empty (NetStream.Buffer.Empty). I have had it work once or twice where the buffer does actually fill up but for some reason it stopped working that way.

I can see that the client is still sending data to the server even after I detach the camera from the netstream because the file on the server side continues to grow. My solution at the moment is to wait 60 seconds after recording has stopped before closing the connection.

One thing of interest is that when there is no more packets to be sent, I see on the server side that the file switches from mystream.ser to mystream.flv and stops growing in size. I was thinking of writing some code on the server side to wait for this event to happen and then let the client know that it can close the stream.

This is my first foray into action script so I may be doing something completely wrong here. Please let me know.

EDIT Here's the client code

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:s="library://ns.adobe.com/flex/spark"
               xmlns:mx="library://ns.adobe.com/flex/mx"
               xmlns:ns1="*"
               minWidth="955" minHeight="600" applicationComplete="init()" >

    <fx:Script>
        <![CDATA[

            import flash.display.DisplayObject;
            import flash.display.Sprite;
            import flash.events.NetStatusEvent;
            import flash.media.Camera;
            import flash.media.H264Level;
            import flash.media.H264Profile;
            import flash.media.H264VideoStreamSettings;
            import flash.media.Video;
            import flash.net.NetConnection;
            import flash.net.NetStream;


            var cam:Camera = Camera.getCamera();
            var mic:Microphone = Microphone.getMicrophone();
            var nc:NetConnection = new NetConnection();
            var activeStream:NetStream;
            private var bufferCheckTimer:Timer;
            var recordHalted:Boolean = false;


            protected function init(): void{
                recordButton.enabled = false;
                stopButton.enabled = false;
                nc.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);            
                nc.connect("rtmp://localhost/oflaDemo");
                nc.client = this;
            }

            public function onMetaData(info:Object):void {

                trace("playback called onMetaData");        
            }

            public function onBWDone(... rest) : void {
                // have to have this for an RTMP connection
                trace('onBWDone');
            }

            public function onBWCheck(... rest) : uint {
                trace('onBWCheck');
                //have to return something, so returning anything :)
                return 0;
            }


            protected function onNetStatus(event:NetStatusEvent):void{
                trace(event.info.code);
                if(nc.connected)
                {
                    SetupCameraAndMic();
                    recordButton.enabled = true;
                    stopButton.enabled = true;
                }           
            }

            protected function SetupCameraAndMic(): void{
                activeStream = new NetStream(nc);
                activeStream.bufferTime = 60;   
                activeStream.client = this;
                activeStream.addEventListener(NetStatusEvent.NET_STATUS, handleStreamStatus,false,0,true);


                var h264Settings:H264VideoStreamSettings = new H264VideoStreamSettings();
                h264Settings.setProfileLevel(H264Profile.BASELINE, H264Level.LEVEL_2);
                activeStream.videoStreamSettings = h264Settings;

                cam.addEventListener(StatusEvent.STATUS, handleCameraStatus, false, 0, true);
                mic.addEventListener(StatusEvent.STATUS, handleMicrophoneStatus, false, 0, true);

                cam.setMode(320,240, 15);
                cam.setQuality(0, 80);
                cam.setKeyFrameInterval(7);

                mic.rate = 44;
                mic.gain = 75;
                mic.setSilenceLevel(0);
                mic.setUseEchoSuppression(true);



                activeStream.attachCamera(cam);
                activeStream.attachAudio(mic);  
                videoContainer.attachCamera(cam);






            }

            private function handleCameraStatus(e:StatusEvent):void {
                trace("handleCameraStatus - " + e.code);
                switch(e.code) {
                    case 'Camera.muted':
                        // Show a message
                        break;
                    case 'Camera.Unmuted':              
                        //finishCamAndMicSetup();
                        break;
                }
            }


            private function handleMicrophoneStatus(e:StatusEvent):void {
                trace("handleMicrophoneStatus - " + e.code);
                switch(e.code) {
                    case 'Microphone.Muted':
                        // Show a message
                        break;
                    case 'Microphone.Unmuted':              
                        //finishCamAndMicSetup();
                        break;
                }
            }


            private function handleStreamStatus(e:NetStatusEvent):void {
                switch(e.info.code) {
                    case 'NetStream.Buffer.Empty':
                        trace("NetStream.Buffer.Empty");
                        break;
                    case 'NetStream.Buffer.Full':
                        trace("NetStream.Buffer.Full");
                        break;
                    case 'NetStream.Buffer.Flush':
                        trace("NetStream.Buffer.Flush");
                        break;
                }
            }

            protected function recordButton_clickHandler(event:MouseEvent):void
            {
                if(activeStream == null)
                {
                    SetupCameraAndMic();
                }
                if(activeStream != null){
                    var tempDate:Date = new Date();
                    var uniqueFileName:String = "RecordME_" + String(tempDate.getMinutes()) + String(tempDate.getMilliseconds());

                    bufferLabel.text = ""+ activeStream.bufferTime;
                    activeStream.publish(uniqueFileName, "record");
                    bufferCheckTimer = new Timer(100);
                    bufferCheckTimer.addEventListener(TimerEvent.TIMER, handleBufferCheck, false, 0, true);
                    bufferCheckTimer.start();

                }

            }

            private function handleBufferCheck(e:TimerEvent):void {
                if(activeStream != null) {
                    trace("Buffer: " + activeStream.bufferLength);
                    statusLabel.text = "Buffer: " + activeStream.bufferLength;
                    if (recordHalted == true) {
                        if ( activeStream.bufferLength == 0 ) {
                            activeStream.close();
                            activeStream = null;



                            bufferCheckTimer.stop();
                            bufferCheckTimer.removeEventListener(TimerEvent.TIMER, handleBufferCheck);
                            bufferCheckTimer = null;

                            // OK - playback time
                            //doRecordingPlayback();
                        }
                    }


                if (bufferCheckTimer != null) {
                    bufferCheckTimer.reset();
                    bufferCheckTimer.start();
                }
            }
            }

            protected function stopButton_clickHandler(event:MouseEvent):void
            {

                activeStream.attachCamera(null);
                activeStream.attachAudio(null); 
                videoContainer.attachCamera(null);                      
                recordHalted = true;

            }

        ]]>
    </fx:Script>

    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>
    <mx:VideoDisplay id="videoContainer" x="158" y="53" width="640" height="480"
                    chromeColor="#3C2020" />
    <s:Button id="recordButton" x="396" y="546" label="Record"
              click="recordButton_clickHandler(event)"/>
    <s:Button id="stopButton" x="491" y="546" label="Stop Recording"
              click="stopButton_clickHandler(event)"/>
    <s:Label id="statusLabel" x="158" y="555" width="207"/>
    <s:Label x="14" y="408" text="Buffer Set to:"/>
    <s:Label id="bufferLabel" x="91" y="408" text="0"/>
</s:Application>

Thanks

Raheel G
  • 301
  • 2
  • 6
  • Can't really help without seeing your client code. We need to see what you're doing to setup/start the recording. – Sunil D. Dec 19 '12 at 01:43

3 Answers3

1

I don't have a running RTMP server right now, so I'm just commenting on what I see in your code.

I think the advice you got about buffering stuff first may not be such a good idea when publishing (recording) a stream. Perhaps the tutorial was not about publishing but rather subscribing to an existing stream, in which case buffering is a great idea.

You are setting the bufferTime to 60 seconds. The docs say that you should set bufferTime to 0 for a live recording. That is, you want to send the data as soon as it's generated by the camera/mic.

The next thing is the Timer you are using. This seems to be checking the buffer length, to detect that the recording has stopped. There are really only two occasions when recording will stop:

  • when the user clicks the "stop" button, and your code stops it
  • any scenario where the server or something else causes it to stop (network issue, etc.)

I would suggest using your NetStatusEvent handler method (handleStreamStatus()) to check for the message "NetStream.Record.Stop" instead of using a timer to check the bufferLength. This allows your code to detect when the recording has stopped for some other reason than the user clicking "stop".

The timer is the likely cause of the issue. Even though you've set a big bufferTime value, it may not work or behave differently with a Red 5 server, or it could be overridden by server side setting. Regardless, the point is don't use bufferLength to detect if the recording has stopped.

There are a bunch of useful messages that are dispatched with NetStatusEvent, I recommend reading them over and seeing if any of them are useful in your scenario. They are pretty reliable, and seem to handle most every situation that can arise.

One last thing I noticed (not the issue, but worth correcting): you enable echo suppression on the microphone, but that won't work unless you get the enhanced microphone:

var mic:Microphone = Microphone.getEnhancedMicrophone();
Sunil D.
  • 17,983
  • 6
  • 53
  • 65
  • Thanks Sunil for your detailed response I appreciate it very much. I will try using NetStream.Record.Stop instead of the timer method I have and respond back shortly. I was following the tutorials from these two locations https://gist.github.com/987076 and http://www.technogumbo.com/tutorials/Recording-Decent-Quality-Video-And-Audio-With-Flash-and-Red5/index.php both of which mention to use a buffer of length 60. – Raheel G Dec 20 '12 at 05:03
  • Interesting. I've worked on a couple of applications where we did not buffer anything while recording. I'm not sure what the benefit of buffering is in this context. You certainly wouldn't want to buffer in a "live video chat" application, for example. But maybe there is some use for it in other scenarios. – Sunil D. Dec 20 '12 at 17:19
  • The issue is if there is no buffer and I just close the stream, the saved video is only a short segment of the full recording.What I am noticing is that when I stop the recording and set activeStream.attachAudio(null) and activeStream.attachCamera(null) the server stops receiving packets but continues to write the FLV file to disk. So the issue may not be on the client side because red5 has all the packets. Once the file is done writing to disk only then can I close the activeStream. I tried waiting for the Record.Stop event but that is only triggered after I perform activeStream.close. – Raheel G Dec 20 '12 at 18:16
1

I think this is a result of a bug in the RED5 final V1.0 release. I reverted back to the 0.80 version and I can record fine.

Raheel G
  • 301
  • 2
  • 6
0

Recording works well with Red5 V1.0.2 by changing the queue threshold. I have seen a video resume website called jobma is recording the video very well. seems they are using red5

Rijo Joy
  • 335
  • 3
  • 6