For this part you will use the Flash Media Encoding Server, which operates as the encoding engine on the server side. There are multiple ways to add jobs to the encoding queue including simple file-based watch folders, a watch folder for XML-based jobs, a command line interface, and a socket connection for advanced communication and interaction. For this application, you will use the socket connection.
Important note: To establish a connection between a Flex application and a socket server, even on the same domain, you'll need to run a policy server on port 843 to return a socket policy file on request. It sounds complicated, but there are existing scripts that support an easy setup. For more information on this required step, see Setting up a socket policy file server.
You can use Flash Media Encoding Server to set up your own preset. To set up a preset that fits your needs, choose Tools > Preset Editor and create a preset (see Figure 3) with 800 kbps H.264 encode and 720 × 480 resolution (480p).

Figure 3. List of encoding profiles
To identify the preset when submitting the job through the socket connection, find the GUID in the preset editor. The GUID will be a string of hexadecimal values, for example: "{85DCCE70-0B4F-4328-86D1-5FC7756D9777}".
The socket connection logic requires additional code on the Flex side to ensure appropriate visual feedback, which includes a progress bar to display the progress of the encode and updated status messages. You may want to open the sample source code to follow the next steps.
Flash Media Encoding Server requires a valid XML command, in this case sent through a socket connection, to add a job to the rendering queue. For better maintainability, I have externalized the commands in the submit.xml and checkstatus.xml files with placeholders for the file to encode. Please also make sure you replace the PresetGUID value in the XML below with the GUI of your encoding profile.
<cnpsXML TaskType="JobQueue">
<Sources>
<Module_0 Filename="c:\videoshare\uploaded\:::filename:::"/>
</Sources>
<Destinations><Module_0
PresetGUID="{85DCCE70-0B4F-4328-86D1-5FC7756D9777}"
ModuleGUID="{85DCCE70-0B4F-4328-86D1-5FC7756D9777}"
AssignedTags=""><ModuleData CML_P_Path="C:\Program
Files\Adobe\Flash Media Server
3.5\applications\videoshare\streams\_definst_" CML_P_BaseFileName="%s"/><PostconversionTasks/><Filter_1/><Filter_0/></Module_0></Destinations>
</cnpsXML>
The submit command encodes the file and then moves to it the Flash Media Server application folder to allow the playback of the file. The filename is a unique placeholder. The Flex code will replace this value with the filename of the current job.
<cnpsXML TaskType="JobStatusList">
<Filter>
<Criteria_0 Parameter ="Guid" Operator="EQUAL" Value=":::guid:::" />
</Filter>
</cnpsXML>
The checkstatus XML triggers the JobStatusList command. This
Flash Media Encoding Server command normally returns all jobs. In this case you
want to limit it to the active job initiated by the active user; therefore it
contains limitation criteria with the <filter> tag.
The following code establishes the socket connection and
sends commands through it. It first loads the XML submission files
via loadFMESAPIConfig() (for details on this
method, see the sample source code), establishes a connection to Flash Media
Server (I'll cover this in more detail below), and then, once the user has
successfully uploaded a video file, triggers the encoding process.
To better understand the flow of the application, here are the steps that follow a successful file upload:
// Initializes the interface and connects to the Flash Media Server
private function init() : void {
nc = new NetConnection();
fmsserver = "myserver.com";
phpserver = "myserver.com";
fmesserver = "myserver.com";;
fmesport = 1547;
nc.connect("rtmp://"+fmsserver+"/videoshare/");
nc.addEventListener(NetStatusEvent.NET_STATUS,onConnect);
playbackstatus = "stop";
// Loads the FMES XML command files
loadFMESAPIConfig();
progressbar.mode = "manual";
progressbar.label = "";
// Event listener for the fullscreen mode
Application.application.stage.addEventListener(FullScreenEvent.FULL_SCREEN, fullScreenHandler);
}
// Once the upload is complete, initialize the video conversion
private function completeHandler(event:Event):void {
status.text = "Upload complete";
fmessocket = new Socket();
configureListeners(fmessocket);
sendFMESCommand("submitjob");
}
// Establishes a new connection and connects to FMES
private function sendFMESCommand(command:String) : void {
activecommand = command;
if (fmessocket.connected) fmessocket.close();
if (fmesserver && fmesport) {
fmessocket.connect(fmesserver, fmesport);
}
}
// Configures all listeners for the socket connection primarily for debug reasons
private function configureListeners(dispatcher:IEventDispatcher):void {
dispatcher.addEventListener( ProgressEvent.SOCKET_DATA, dataHandler );
dispatcher.addEventListener( IOErrorEvent.IO_ERROR, ioErrorHandler );
dispatcher.addEventListener( Event.CONNECT, connectHandler );
dispatcher.addEventListener( Event.CLOSE, closeHandler );
dispatcher.addEventListener( SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler );
}
// Triggered once connection closes
private function closeHandler(event:Event):void {
debug("closeHandler: " + event);
}
// Triggered once connection is established. This event should happen at the beginning of the encode
// Replaces the filename property of the XML command template with the uploaded file name value
private function connectHandler(event:Event):void {
if (activecommand == "submitjob") {
var replaceFileNameExp:RegExp = /:::filename:::/;
submitFMESCommand(submitapixml.replace(replaceFileNameExp,uploadedFile));
}
}
// Submits a FMES command to the server through the established socket connect
private function submitFMESCommand(commandxml:String) : void {
debug(commandxml);
var msg:String = commandxml;
var bytes:ByteArray = new ByteArray();
bytes.writeMultiByte( msg, "iso-8859-1" );
var messageBytes:ByteArray = new ByteArray();
var messageString:String = "CarbonAPIXML1 " + bytes.length.toString() + " " + msg;
messageBytes.writeMultiByte( messageString, "iso-8859-1" );
fmessocket.writeBytes( messageBytes );
fmessocket.flush();
}
// Handles the response from FMES
private function dataHandler(event:ProgressEvent):void {
var bytesAvailable:int = fmessocket.bytesAvailable;
var type:String = fmessocket.readMultiByte( 13, "utf-8" );
fmessocket.readMultiByte( 1, "utf-8" );
var ttlBytes:int = int( fmessocket.readMultiByte( 2, "utf-8" ) );
fmessocket.readMultiByte( 1, "utf-8" );
var str:String = fmessocket.readUTFBytes( fmessocket.bytesAvailable );
// Parses and assigns the response to a XML variable
fmesresponse = new XML( str );
// Data can be received following the start job or the monitoring command
switch (activecommand) {
case "submitjob" :
// Reads and stores the active job id
activejobguid = (fmesresponse.attributes()[0].toString());
var replaceGuidExp:RegExp = /:::guid:::/;
checkstatusxml = checkstatusapixml.replace(replaceGuidExp,activejobguid);
activecommand = "checkjobstatus";
// Checks for the job status after 2 seconds
setTimeout(checkEncodingStatus,2000);
break;
case "checkjobstatus" :
// Reads the received status variable from the XML response
var currentstatus:String = fmesresponse.children()[0].children()[0].attribute("Status");
// Reads the received progress from the XML response
var encodingprogress:String = fmesresponse.children()[0].children()[0].attribute("Progress.DWD");
// Updates the progress bar component
progressbar.setProgress(Number(encodingprogress),100);
// Updates the label for the progress bar depending on the current status
if (currentstatus!="STARTED" && currentstatus!="COMPLETED") {
progressbar.label = "Waiting in encoding queue";
}
if (encodingprogress=="0") {
progressbar.label = "Preparing encoding..";
}
else {
progressbar.label = "Encoding .. " +encodingprogress+ "% completed";
}
// Once encoding is complete, updates the UI and closes the socket connection to FMES
if (currentstatus=="COMPLETED") {
fmessocket.close();
progressbar.label = "Encoding complete";
refreshVideos();
browse_btn.enabled = true;
upload_btn.enabled = false;
}
// If encoding is not complete, check again in 2 secs what the current status is
else {
setTimeout(checkEncodingStatus,2000);
}
break;
}
}
// Triggers the FMES command
private function checkEncodingStatus() : void {
submitFMESCommand(checkstatusxml);
}
// Error handler
private function ioErrorHandler(event:IOErrorEvent):void {
debug("ioErrorHandler: " + event);
}
// Security handler
private function securityErrorHandler(event:SecurityErrorEvent):void {
debug("securityErrorHandler: " + event);
for (var foo:String in event) {
debug(foo + " " + event[foo]);
}
}
I won't explain the source line by line, since the concept is relatively simple and with comments the code should be fairly self-explanatory.
I have defined socket connection event handlers for I/O and security issues. If, for example, a security error occurs, the security handler will be triggered. This can happen when the security policy server on port 843 is not properly set up.
Since it employs the same socket connection for both
commands with a single data handler, the application uses an activecommand state to differentiate between submitting the encoding call and querying the
current status of the encoding process.