Mostrando entradas con la etiqueta Java. Mostrar todas las entradas
Mostrando entradas con la etiqueta Java. Mostrar todas las entradas

lunes, 3 de octubre de 2011

Using Embed Jetty and BlazeDS Remoting with Adobe AIR

When we need to communicate with server side data from an Adobe AIR application we usually use HTTP services, Web Services or Remoting this works great, but what happens when we need to create an standalone application? We have options like merapi, flerry and transmission...

I want to show you how to embed Jetty Server in an Adobe Air application and use BlazeDS for communication. The use case is simple access a MySQL server directly from Adobe AIR but with this option you might experiment and create better use cases for this.


We need to download the following:
  • Jetty library (i use jetty-all-7.4.0.RC0.jar)
  • Servlet api (i use servlet-api-2.5.jar)
  • Mysql Connector
  • BlazeDS 4

Let's coding:

First we create a simple table for sample purposes:


Java side:
First we create a simple Java Project and add the 2 jars in the classpath.

Product.java

Here i use plain JDBC but you can use JPA, Hibernate, etc. 
ProductDAO.java

ProductServices.java

This is a very important class, here we are going to create the Server instance and start it. 
Note: I assume that you know something about embedding servers, if you want some background here you can find a great resource.

BlazeDSServer.java

Now we need to extract the BlazeDS.war content. create a folder called webapp inside your src folder with this content, your project structure should look like this.



The last thing you need to do is declare a remote destination on your services-config.xml



Now we code the Air side:
Fist create an asset folder inside your air src folder and copy all the bin folder from your java project

As you can see i made the same copy into the bin-debug folder, i don't know why it doesn't export all the files to the bin-debug.

Now let's code the application, first in the descriptor file enable the extended desktop profile so you can use Native Process




Now  the code:

EmbedJetty.mxml


Now let's take a more closer view to the code:
  • First the executable file will be the Java path so it depends on your machine.
  • I use NativeProcess to start the embed Jetty instance and a socket to shutdown the embed jetty instance.
  • The arguments for the NativeProcessStartupInfo are the necessary classes to make the Java desktop app run. In the application:
args.push("-classpath");
args.push(".;lib/jetty-all-7.4.0.RC0.jar;lib/servlet-api-2.5.jar;lib/mysql-connector.jar");
args.push("BlazeDSServer"); 
  •  Once it connects you can use RemoteObjects to make rpc call to your Java code.
  • You can use messaging too.
Before you run check your ip match with the socket connection ip, and verify the JDBC setting in the Java code.

When you click the start server button, in the text area you see the log comming from the server.

Now click the Get Data button and in my case the screen will be the following:
 

Stop the server by clicking the Stop Server button.

And that's all, hope this will help you, any question or bug please let me know.
Here is the source code for the Java Project
Here is the Source code for the Flex Project

viernes, 26 de agosto de 2011

Using BlazeDS Remote Object and Java to Upload and get Files

In a Java/Flex project i have been working on we need to upload and download images, simply task no? Yes... is simple we use the FileReference class and point this to a servlet then simply use the upload method.

Really easy when you run in your local machine but what happens when we run this in a remote machine... let me think... ah! yes the lovely Flash Player Sandbox Security Exception... ok! we can handle it, just use crossdomain.xml file and run your application again and... Flash Player Sandbox Security Exception again (dear this is becoming my best friend), then i read, to upload a file Flash player uses the socket api and now we need a socket policy file... so i give up and try to do it in another way.

Today i show you how you can do a kind of  "upload" and "show" a remote file using Remote Objects and the lovely BlazeDS. So let's start

We use a Java class (not servlet) to manage the upload and the get of a file.

In the Java side we have two methods uploadFile and getFile, the first one uses the name, the directory and the content as an byte array to write a file in the disk using the FileOutputStream class. For the second method we need the full file name (directory + file name) and use the FileInputStream to read it and put the data in a byte array and the send it to Flex.

FileUtils.java  


Don't forget to change your remoting-config.xml :-)

And in the flex side we still use the FileReference but this time only for load the file, and then we use remote objects to send the chunk of data, the file name and the directory to the upload method, for getting the file the java side just send the chunk of data and we manage it with a ByteArray and show it.



That's all now you can upload a file using a remote object and show this file with this two methods, and remember to secure your endpoints and externalize your server configuration. Any question or mistakes just let me know. Thanks for reading.

domingo, 15 de mayo de 2011

Creating Dynamic Remote Destinations with BlazeDS

Doing remoting with Flex and BlazeDS is great, you set up your remoting-config.xml with your destinations and them consume this destination with your Flex client, that's good if you have your destinations hardcoded in your xml file, but there are times when you want to create your remoting destinations dynamically, taking this post as a base i will show you how to do this with BlazeDS in a simple example.

First we create a class called DestinationHelper this will contain all the classes you want to create at run time, read the comments so you know exactly what are you doing for a deeper explanation please read this post.




I take the great explanations of Nick Kwiatkowski  and change a little so this can match the example, please all the credits go to him.


The first thing we do is call the createDestination() function on the remoting-service and cast this to a RemotingDestination.  This will pass back a reference to the new destination that was created for us with the name we passed in. Next, we set the source property.  Since we are working within Java, this could be the dot-path-name of the Java class we are working with, or * to allow our dynamic destination to talk to ANY Java Class.   
The next thing we do is set up the Adapter.  The adapter is what actually processes the requests from our connected Flex client and sends it over to Java to be worked on.  In our case, I am having a Java Adapter do the processing, so I went with the “java-object” adapter.  There are a variety of adapters available, and depending on your situation, you may want to choose from a list of adapters, or use the getDefaultAdapter() method to find out what is the default for the service you are working on.  Creating an instance of the adapter involves calling the createAdapter() method on the destination.  What is returned to you is a reference to the adapter that isn’t initialized yet.  When diving through the source code — this is what screwed me up the most, as I was under the impression that the createAdapter() was all I had to call to get things working.  If that is all you do, you will get NullPointerException errors when you try to pass data to the destination. 

In order to initialize the adapter, you need to pass in a property with the type of a ConfigMap.  The ConfigMap holds the configuration properties that the adapter is expecting.  There are lots of properties that could be set (and if you want to get a general idea of what they are, take a look at the XML config).  In theory, you should be able to pass in an empty ConfigMap to the initialize method and it will take all the defaults (which work in my case), but there is a bug in the JavaAdapter (Yes in the Java adapter too) that requires to you set the use-mappings and method-access-level properties to something or you can’t send any data to the destination.  These two properties are within a ConfigMap named “access” (again, take a look at the XML in the remoting-config.xml to see how this translates).  Finally, in order to initialize the adapter, we pass in the ConfigMap we just setup to the initialize() method and we are set.

Our final steps include binding our newly created adapter instance to the destination by using the setAdapter() method, and setting the channel that the destination will bind to.  If I wasn’t making the assumption that the channel “my-amf” already exists, I could call the getChannels() method on the Message Broker to verify that it is one of the ones available.  If the channel wasn’t available, I could create using a similar method as above.  And finally, we need to start the instance.  Call the start() method to start it.  If everything is set up right, you should be able to send data to your new destination!

martes, 14 de diciembre de 2010

Java AMF Client: Using AMF with JSP

In this post i will be posting how to get the efficient AMF connection to work with a Java client specifically JSP, you can use any other java client but for this port i will use JSP.

There are times when you want an efficient protocol to transport data, ok you can use AMF but what about the client... most of people think that since AMF is a protocol created by Adobe you should use a Flash Player client to get this work, the true is that you could use another type of client like Java for example, so let see how can we use this.

BlazeDS has a class called AMFConnection this will help us to get the work done so let get stated:

 Requirements:

  • Eclipse IDE
  • BlazeDS: You must have some knowledge in setup BlazeDS, if not check my tutorial About Connect BlazeDS with Java (Spanish) or you can check this good tutorial of Technical Evangelist Sujit Reddy - Setting up BlazeDS.
  • MySQL and Mysql Driver.
So first create a database, call it test_db and a table called person, this table has the following columns:

Now populate the table with some data, if you work with little amount of data you can't see the real power of AMF, but for this example some records will be enough, here there is a small Sql script for doing that. 

In the Eclipse IDE, we need to import our BlazeDS war file and add the Mysql driver, i call the project AMF_Client, the project should look like this.


Now we create a package hierarchy for our project:
    

In the dto package create a Person class and add the following:

Person.java

package dto;

public class Person {
private int id_person;
private String first_name;
private String last_name;
private double salary;
public int getId_person() {
return id_person;
}
public void setId_person(int idPerson) {
id_person = idPerson;
}
public String getFirst_name() {
return first_name;
}
public void setFirst_name(String firstName) {
first_name = firstName;
}
public String getLast_name() {
return last_name;
}
public void setLast_name(String lastName) {
last_name = lastName;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}


In our dao package create a DBHelper class for the connection, an interface PersonDAO and a class PersonDAOImp for the implementation.

DBHelper.java // There will be a lot of ways to do a database connection but i prefer this.

package dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import com.mysql.jdbc.Driver;

public class DBHelper {
private static Connection conexion;
private DBHelper(){
}
public static Connection getConnection() throws SQLException{
Class driver = Driver.class;
if(conexion == null){
try{
conexion = DriverManager.getConnection("jdbc:mysql://localhost:3306/test_db","","");
}catch(SQLException ex){
throw new SQLException(ex.getMessage());
}
}
return conexion;
}
}


PersonDAO.java

package dao;

import java.sql.SQLException;
import java.util.List;

import dto.Person;

public interface PersonDAO {
public List getPeople() throws SQLException;
}


PersonDAOImp.java


package dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;


import dto.Person;

public class PersonDAOImp implements PersonDAO{
private Connection conexion;
@Override
public List getPeople() throws SQLException {
List people = new ArrayList();
try{
conexion = DBHelper.getConnection();
PreparedStatement pstmt = conexion.prepareStatement("SELECT * FROM person");
ResultSet rs = pstmt.executeQuery();
while(rs.next()){
Person person = new Person();
person.setId_person(rs.getInt(1));
person.setFirst_name(rs.getString(2));
person.setLast_name(rs.getString(3));
person.setSalary(rs.getDouble(4));
people.add(person);
}
}catch(SQLException ex){
throw new SQLException(ex.getMessage());
}
return people;
}
}    

Now create another class in the package services call this PersonService and put the following.

PersonService.java

package services;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import dao.PersonDAO;
import dao.PersonDAOImp;
import dto.Person;

public class PersonService {
private PersonDAO personDAO = new PersonDAOImp();
public List getPeople() throws SQLException {
List people = new ArrayList();
try{
people = personDAO.getPeople();
}catch(SQLException ex){
throw new SQLException(ex.getMessage());
}
return people;
}
}

We need to configure BlazeDS so we can use the Java AMF Client in our JSP page, open the remoting-config.xml located in WebContent/WEB-INF/flex add the following:

remoting-config.xml
    

<?xml version="1.0" encoding="UTF-8"?>
<service id="remoting-service" 
    class="flex.messaging.services.RemotingService">
    <adapters>
        <adapter-definition id="java-object" class="flex.messaging.services.remoting.adapters.JavaAdapter" default="true"/>
    </adapters>
    <default-channels>
        <channel ref="my-amf"/>
    </default-channels>
<destination id="PersonService">
<properties>
<source>services.PersonService</source>
</properties>
</destination>
</service>



Now we are going to create a JSP page for handling the PersonService result.

index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<%@page import="flex.messaging.io.amf.client.AMFConnection"%>
<%@page import="flex.messaging.io.amf.client.exceptions.ClientStatusException"%>
<%@page import="java.util.List"%>
<%@page import="dto.Person"%>
<%@page import="java.util.ArrayList"%>
<%@page import="flex.messaging.io.amf.client.exceptions.ServerStatusException"%><html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Java AMF Client</title>
</head>
<body>
<%
AMFConnection amfConexion = new AMFConnection();
String amfUrl = "http://localhost:8080/AMF_Client/messagebroker/amf";
try{
//Make the conexion
amfConexion.connect(amfUrl);
}catch(ClientStatusException ex){
out.write("An error has ocurred");
}
//Make the remote call to out service
List<Person> people = new ArrayList<Person>();
try{
people = ((ArrayList<Person>)amfConexion.call("PersonService.getPeople"));
}catch(ClientStatusException ex){
out.write("An error has ocurred");
}catch(ServerStatusException ex){
out.write("An error has ocurred");
}
%>
<table border="1">
<tr>
<td>ID</td>
<td>First Name</td>
<td>Last Name</td>
<td>Salary</td>
</tr>
<%
for(Person p : people){
%>
<tr>
<td><%out.print(p.getId_person());%></td>
<td><%out.print(p.getFirst_name());%></td>
<td><%out.print(p.getLast_name());%></td>
<td><%out.print(p.getSalary());%></td>
</tr>
<%
}
%>
</table>
</body>
</html>


Now your run the project and you should have something like this:


That's all you have running a project with Java AMF Client, remember the true power of AMF comes when you deal with large amounts of data, this tutorial only explains the basics with few rows. 

Here is the complete source code of the Project, any trouble or suggestion let me know. 




sábado, 4 de diciembre de 2010

Red5 + BlazeDS = Realtime Video Chat Tutorial Part 4: BlazeDS chat.

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

Red5 + BlazeDS = Realtime Video Chat Tutorial Part 3: Red5 video streaming

Hi, today i will show you how to enhance the application that we build in Tutorial part 1 and part 2, let add some video and voice to make things a little bit interesting. This tutorial continues part 1 and part 2 so take this parts first before reading this part.

So let's continue...

First we need to know a little about NetConnection and how to attach video and audio to the RTMP connection that we made in the last part of this tutorial. NetConnection comunicates with the server establishing a full duplex open connection in both the server and the client so it's perfect for use with the RTMP protocol.

Let's make some changes in the UI so we can add our camera, open Red5_BlazeDS_Flex.mxml and change the following (some update are made so we can use Flex 4.5 new components)

Red5_BlazeDS_Flex.mxml

<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"/>
                    </s:HGroup>
                </s:FormItem>
            </s:Form>
            <mx:UIComponent id="inVideoWrapper" width="300" height="200"/>
        </s:VGroup>
    </s:HGroup>
    <s:TextArea id="txtLog" width="100%" height="100" color="red" bottom="0"
                editable="false" includeIn="main"/>

This code add to our user interface 2 UIComponent this components will be the video wrappers in our application.

Now let’s attach the video, for make this possible we must do some steps:
  1. Make the  connection (done in the last step)
  2. Create the streams in/out
  3. Setup  the devices
  4. Create the video.
First we declare the variables:

In our Red5_BlazeDS_Flex.mxml in after the <s:Script>:

            import mx.collections.ArrayCollection;
            import mx.controls.Alert;
            import mx.managers.PopUpManager;
            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;

Then we need to add click event to our Broadcast button and then write a click handler function has follow:


           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";
                }
            }

Now add click event to our Subscribe button and then write a click handler function has follow:

           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);
                }
            }

Now run your application, write a text to Broadcast and then test this in a different browser client open the same application put in the subscribe textinput the name of your broadcast and voila!! you will have the following:


That's all and of course here is the complete code, If you found any error or have any suggestion, please let me know.