ActionScript and RTMP Streaming
This is the third installment in a series of posts about setting up Red5 to record audio streams from an HTML page.
All of the code from this part and from the previous part is now available on my GitHub here.
The important bits of the ActionScript that makes this work are very simple, so I'll just go through setting up the Microphone and connecting to and communicating with the server and you can look at my source code to figure out the other things I added like the timer and the volume meter.
First we need two buttons: one to start recording and one to stop. Throw two button objects on the stage in Flash and name them RecordButton and StopButton and you can just write something like this to attach the appropriate actions to them:
RecordButton.addEventListener(MouseEvent.CLICK, recordAudio);
StopButton.addEventListener(MouseEvent.CLICK, stopRecordingAudio);
Here are the recordAudio
and stopRecordingAudio
functions:
// record the audio to the stream
function recordAudio(e)
{
    ns.attachAudio(mic);
    ns.publish("hostStream", "live");
    nc.call("StreamManager.startRecording",null);
    startTimer();
}
// stop the recording of audio to the stream
function stopRecordingAudio(e)
{
    ns.attachAudio(null);
    nc.call("StreamManager.stopRecording",null);
    stopTimer();
}
All these functions do is call the Java functions startRecording and stopRecording on the Red5 server and publish our RTMP stream. In order to use this stream, we have to attach a microphone (and/or camera) to it. Before we do any of that though, we have to connect to the server. Here's the code that's responsible for that:
var serverAddress = "rtmp://[some server]:1935/as-record";
trace("Connecting to server (" + serverAddress + ")...");
var ns:NetStream;
var mic:Microphone;
var nc:NetConnection = new NetConnection();
nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, netAsyncHandler);
nc.addEventListener(IOErrorEvent.IO_ERROR, netIOErrorHandler);
nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
nc.connect(serverAddress);
The event listeners that get assigned here are where all of the exciting things happen. The only one that does much of anything is netStatusHandler
, so let's look at that one in some detail.
function netStatusHandler(e)
{
    switch(e.info.code)
    {
        case "NetConnection.Connect.Success":
        {
            trace("Connected.");
            ns = new NetStream(nc);
            var clientObj:Object = new Object();
            clientObj.onBWDone = onBWDone;
            clientObj.onBWCheck = onBWCheck;
            nc.client = clientObj;
           Â
            mic = Microphone.getMicrophone();
            mic.codec = SoundCodec.SPEEX;
            mic.encodeQuality = 10;
            // Speex only works at 16kHz
            //mic.rate = 44;
            mic.gain = 45;
            mic.setUseEchoSuppression(false);
            mic.setLoopBack(false);
            mic.setSilenceLevel(0,60000);
            ns.attachAudio(mic);
            micReady = true;
            break;
        }
        case "NetConnection.Connect.Failed":
        {
            trace("Connection failed.");
            break;
        }
        case "NetConnection.Connect.Closed":
        {
            trace("Connection closed.");
            break;
        }
        default:
        {
            trace(e.info.code);
            break;
        }
    }
}
This code does a few important things. First of all, it creates our microphone object. This action triggers a dialog box on the client-side that asks the user to grant us permission to use the microphone. If they click no, obviously no sound will be recorded. Once this has been done, we set the microphone to use the Speex codec at maximum quality. I chose Speex because it's the only format supported by Flash that can also be decoded by ffmpeg, which is what I used to convert the files to WAVs for analysis.
I also disabled the echo suppression and the loopback to minimize Flash's interference in the audio stream. Once this is set up we can just attach the microphone object to the NetStream and we're all set. The stream is established so that once the record button is pressed, the Java app running on the server can just start dumping the stream into an FLV file.
That's basically all that's required to get the stream going between the client and the server. There's a lot more involved in making it pretty and making it work well, but I've skipped those steps for now. Check out the source on GitHub to see a reference implementation you can just dump on your Red5 server if you don't want to write it yourself or if you just want a reference to help you write your own code.