This is the last part of the Series Red5 + BlazeDS = Realtime Video Chat. In this part we are going to build a normal chat with BlazeDS so users can chat with al the people connected on the application. To accomplish this we need explore the messaging features in BlazeDS and also see a little about Consumer / Producer in Flex.
First in our server side open the services-config.xml and add the following channel:
services-config.xml
<channel-definition id="my-streaming-amf" class="mx.messaging.channels.StreamingAMFChannel">
<endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/streamingamf"
class="flex.messaging.endpoints.StreamingAMFEndpoint"/>
<properties>
<idle-timeout-minutes>0</idle-timeout-minutes>
<max-streaming-clients>10</max-streaming-clients>
<server-to-client-heartbeat-millis>5000</server-to-client-heartbeat-millis>
</properties>
</channel-definition>
In the code above we define a new StreamingAMF channel called my-streaming-channel now we need to specify that we are going to use this channel in our chat, now open the messaging-config.xml and add:
<destination id="chat">
<channels>
<channel ref="my-streaming-amf"/>
</channels>
</destination>
We are creating a destination for out chat and specify that our chat will use a Streaming channel. That’s all in our server side, now let’s make an interface for out chat.
Open Red5_BlazeDS_Flex.mxml and add a TextArea and a TextInput inside a form like this:
<s:states>
<s:State name="login"/>
<s:State name="main"/>
</s:states>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:Group verticalCenter="0" horizontalCenter="0" includeIn="login">
<s:Form>
<s:FormItem label="Username">
<s:HGroup>
<s:TextInput id="txtUsername"/>
<s:Button id="btnConnect" label="Login" click="btnConnect_clickHandler(event)"/>
</s:HGroup>
</s:FormItem>
</s:Form>
</s:Group>
<s:HGroup width="100%" height="100%" includeIn="main">
<s:VGroup width="30%" height="100%" paddingBottom="10" paddingLeft="10" paddingRight="10"
paddingTop="10">
<s:Form width="100%">
<s:FormItem label="Broadcast">
<s:HGroup>
<s:TextInput id="txtBroadcast"/>
<s:Button id="btnBroadcast" label="Broadcast" click="btnBroadcast_clickHandler(event)"/>
</s:HGroup>
</s:FormItem>
</s:Form>
<mx:UIComponent id="outVideoWrapper" width="300" height="200"/>
<s:Form>
<s:FormItem label="Subscribe">
<s:HGroup>
<s:TextInput id="txtSubscribe"/>
<s:Button id="btnSubscribe" label="Subscribe" click="btnSubscribe_clickHandler(event)"/>
</s:HGroup>
</s:FormItem>
</s:Form>
<mx:UIComponent id="inVideoWrapper" width="300" height="200"/>
</s:VGroup>
<s:VGroup width="100%" height="80%" paddingBottom="10" paddingLeft="10" paddingTop="10" paddingRight="10">
<mx:Form width="100%" height="100%">
<mx:FormItem width="100%" height="100%">
<s:TextArea id="txtConversation" width="100%" height="100%" editable="false"/>
</mx:FormItem>
<mx:FormItem width="100%">
<s:TextInput id="txtChat" width="100%" enter="txtChat_enterHandler(event)"/>
<s:Button id="btnSend" label="Send" click="btnSend_clickHandler(event)"/>
</mx:FormItem>
</mx:Form>
</s:VGroup>
</s:HGroup>
<s:TextArea id="txtLog" width="100%" height="100" color="red" bottom="0"
editable="false" includeIn="main"/>
Now we must add the chat logic as follows:
- Add a Producer and Consumer in the declarations tags:
<fx:Declarations>
<s:Producer id="producer" destination="chat"/>
<s:Consumer id="consumer" destination="chat" message="consumer_messageHandler(event)" fault="consumer_faultHandler(event)"/>
<mx:DateFormatter id="hourFormatter" formatString="KK:NN:SS"/>
</fx:Declarations>
- Create the events handlers for txtChat enter event and for btnSend click event:
protected function btnSend_clickHandler(event:MouseEvent):void
{
var message:AsyncMessage = new AsyncMessage();
message.body = txtChat.text;
message.headers.user = txtUsername.text;
producer.send(message);
txtChat.text = "";
}
protected function consumer_messageHandler(event:MessageEvent):void
{
var hora:Date = new Date();
var message:String = event.message.body as String;
var usuario:String = event.message.headers.user as String;
txtConversation.text += "[" + hourFormatter.format(hora) + "]" + usuario + " says: " + message + "\n";
}
protected function txtChat_enterHandler(event:FlexEvent):void
{
var message:AsyncMessage = new AsyncMessage();
message.headers.user = txtUsername.text;
message.body = txtChat.text;
producer.send(message);
txtChat.text = "";
}
protected function consumer_faultHandler(event:MessageFaultEvent):void
{
txtLog.text += event.faultString + "\n";
}
Now test your application and your application should look like this:
Here is the complete Source 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"
creationComplete="initApp()" width="100%" height="100%">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.events.FlexEvent;
import mx.managers.PopUpManager;
import mx.messaging.events.MessageEvent;
import mx.messaging.events.MessageFaultEvent;
import mx.messaging.messages.AsyncMessage;
import mx.rpc.events.FaultEvent;
private var connection:NetConnection;
private var userWindow:UsersWindow;
private var timer:Timer;
//Streams
private var inStream:NetStream;
private var outStream:NetStream;
//Devices
private var camera:Camera;
private var microphone:Microphone;
//Video
private var inVideo:Video;
private var outVideo:Video;
private function initApp():void {
this.systemManager.stage.scaleMode = StageScaleMode.NO_SCALE;
}
protected function btnConnect_clickHandler(event:MouseEvent):void
{
if(txtUsername.text.length >= 3){
producer.connect();
consumer.subscribe();
userWindow = new UsersWindow();
timer = new Timer(2000);
timer.start();
timer.addEventListener(TimerEvent.TIMER, onTimerEvent);
currentState = "main";
connection = new NetConnection();
connection.connect("rtmp://localhost/Red5_BlazeDS_Java", txtUsername.text);
connection.addEventListener(NetStatusEvent.NET_STATUS, onConnectionStatus);
connection.client = this;
}else{
txtUsername.errorString = "Enter a valid name";
}
}
protected function onTimerEvent(event:TimerEvent):void {
connection.call("getConnectedClients", new Responder(onResult, onFault));
}
protected function onResult(obj:Object):void {
userWindow.setUsers(new ArrayCollection(obj as Array));
}
protected function onFault(obj:Object):void {
txtLog.text += "Error " + obj.fault.message + "\n";
}
protected function onConnectionStatus(event:NetStatusEvent):void {
if(event.info.code == "NetConnection.Connect.Success"){
txtLog.text += "Connection to RTMP successfully established\n";
connection.call("getConnectedClients", new Responder(onResult, onFault));
userWindow = UsersWindow(PopUpManager.createPopUp(this, UsersWindow, false));
PopUpManager.centerPopUp(userWindow);
userWindow.usuarioActual = txtUsername.text;
}else{
txtLog.text += "Connection to RTMP fail\n";
}
}
protected function btnBroadcast_clickHandler(event:MouseEvent):void
{
if(txtBroadcast.text.length > 3){
txtBroadcast.errorString = "";
//setup devices
camera = Camera.getCamera();
microphone = Microphone.getMicrophone();
//setup the streams
outStream = new NetStream(connection);
outStream.attachAudio(microphone);
outStream.attachCamera(camera);
outStream.publish(txtBroadcast.text);
//setup out video
outVideo = new Video(300,200);
outVideo.attachCamera(camera);
outVideoWrapper.addChild(outVideo)
}else{
txtBroadcast.errorString = "Put a valid broadcast name";
}
}
protected function btnSubscribe_clickHandler(event:MouseEvent):void
{
if(txtSubscribe.text.length > 3){
inStream = new NetStream(connection);
inStream.play(txtSubscribe.text);
inVideo = new Video(300,200);
inVideo.attachNetStream(inStream);
inVideoWrapper.addChild(inVideo);
}
}
protected function btnSend_clickHandler(event:MouseEvent):void
{
var message:AsyncMessage = new AsyncMessage();
message.body = txtChat.text;
message.headers.user = txtUsername.text;
producer.send(message);
txtChat.text = "";
}
protected function consumer_messageHandler(event:MessageEvent):void
{
var hora:Date = new Date();
var message:String = event.message.body as String;
var usuario:String = event.message.headers.user as String;
txtConversation.text += "[" + hourFormatter.format(hora) + "]" + usuario + " says: " + message + "\n";
}
protected function txtChat_enterHandler(event:FlexEvent):void
{
var message:AsyncMessage = new AsyncMessage();
message.headers.user = txtUsername.text;
message.body = txtChat.text;
producer.send(message);
txtChat.text = "";
}
protected function consumer_faultHandler(event:MessageFaultEvent):void
{
txtLog.text += event.faultString + "\n";
}
]]>
</fx:Script>
<s:states>
<s:State name="login"/>
<s:State name="main"/>
</s:states>
<fx:Declarations>
<s:Producer id="producer" destination="chat"/>
<s:Consumer id="consumer" destination="chat" message="consumer_messageHandler(event)" fault="consumer_faultHandler(event)"/>
<mx:DateFormatter id="hourFormatter" formatString="KK:NN:SS"/>
</fx:Declarations>
<s:Group verticalCenter="0" horizontalCenter="0" includeIn="login">
<s:Form>
<s:FormItem label="Username">
<s:HGroup>
<s:TextInput id="txtUsername"/>
<s:Button id="btnConnect" label="Login" click="btnConnect_clickHandler(event)"/>
</s:HGroup>
</s:FormItem>
</s:Form>
</s:Group>
<s:HGroup width="100%" height="100%" includeIn="main">
<s:VGroup width="30%" height="100%" paddingBottom="10" paddingLeft="10" paddingRight="10"
paddingTop="10">
<s:Form width="100%">
<s:FormItem label="Broadcast">
<s:HGroup>
<s:TextInput id="txtBroadcast"/>
<s:Button id="btnBroadcast" label="Broadcast" click="btnBroadcast_clickHandler(event)"/>
</s:HGroup>
</s:FormItem>
</s:Form>
<mx:UIComponent id="outVideoWrapper" width="300" height="200"/>
<s:Form>
<s:FormItem label="Subscribe">
<s:HGroup>
<s:TextInput id="txtSubscribe"/>
<s:Button id="btnSubscribe" label="Subscribe" click="btnSubscribe_clickHandler(event)"/>
</s:HGroup>
</s:FormItem>
</s:Form>
<mx:UIComponent id="inVideoWrapper" width="300" height="200"/>
</s:VGroup>
<s:VGroup width="100%" height="80%" paddingBottom="10" paddingLeft="10" paddingTop="10" paddingRight="10">
<mx:Form width="100%" height="100%">
<mx:FormItem width="100%" height="100%">
<s:TextArea id="txtConversation" width="100%" height="100%" editable="false"/>
</mx:FormItem>
<mx:FormItem width="100%" direction="horizontal">
<s:TextInput id="txtChat" width="100%" enter="txtChat_enterHandler(event)" />
<s:Button id="btnSend" label="Send" click="btnSend_clickHandler(event)"/>
</mx:FormItem>
</mx:Form>
</s:VGroup>
</s:HGroup>
<s:TextArea id="txtLog" width="100%" height="100" color="red" bottom="0"
editable="false" includeIn="main"/>
</s:Application>
Here is the complete project and the other project that I show in the video demo.
So that’s all, this is my first series and I don’t want to be the last. If you found any error or have any suggestion, please let me know. Thanks
Dear Victor,
ResponderEliminarWhen I am running the demo code. I get the error
[MessagingError message='Destination 'chat' either does not exist or the destination has no channels defined (and the application does not define any default channels.)']
at mx.messaging.config::ServerConfig$/internalGetChannelSet()[E:\dev\4.x\frameworks\projects\rpc\src\mx\messaging\config\ServerConfig.as:727]
at mx.messaging.config::ServerConfig$/getChannelSet()[E:\dev\4.x\frameworks\projects\rpc\src\mx\messaging\config\ServerConfig.as:295]
at mx.messaging::MessageAgent/initChannelSet()[E:\dev\4.x\frameworks\projects\rpc\src\mx\messaging\MessageAgent.as:1333]
at mx.messaging::MessageAgent/internalSend()[E:\dev\4.x\frameworks\projects\rpc\src\mx\messaging\MessageAgent.as:1268]
at mx.messaging::Producer/internalSend()[E:\dev\4.x\frameworks\projects\rpc\src\mx\messaging\Producer.as:171]
at mx.messaging::AbstractProducer/connect()[E:\dev\4.x\frameworks\projects\rpc\src\mx\messaging\AbstractProducer.as:494]
at Red5_BlazeDS_Flex/btnConnect_clickHandler()[C:\Documents and Settings\Administrator\Adobe Flash Builder 4 Plug-in\Red5_BlazeDS_Flex\flex_src\Red5_BlazeDS_Flex.mxml:37]
at Red5_BlazeDS_Flex/__btnConnect_click()[C:\Documents and Settings\Administrator\Adobe Flash Builder 4 Plug-in\Red5_BlazeDS_Flex\flex_src\Red5_BlazeDS_Flex.mxml:156]
hi,please check the messaging-config.xml under the java project and make sure that the "chat" destination is in there, another thing check if you setup the streaming channel in your services-config.xml. Hope this solve your problem
ResponderEliminarred5 server which version you are using?
ResponderEliminarThank you
Ronald
In this video i use 0.9.1 Final
ResponderEliminarI think your example very well.
ResponderEliminarUnfortunately, I do not have enough knowledge.
How can I add a MySQL database with user data?
I would be delighted if you could help me.
Thank you
Ronald