Este exemplo pode usar o serviço fitcDemo do Red5, mais preferi criar o meu próprio por necessitar de muitas alterações e novas implementações.
Basicamente é o serviço fitcDemo que vou adicionar algumas funcionalidades novas ao serviço e para isso você precisa saber um pouco de Java ou pelo manos a não básica.
/** * Se você não sabe nada de Java baixe o Zip que conterá toda a biblioteca compilada. */
Vamos começar criando um projeto no Eclipse For Java com o nome de confDemo. conforme imagens abaixo:
Imagem 01:Novo projeto
Imagem 02: Criando o projeto no Java
Agora antes de prosseguir temos que importar a biblioteca do Red5 no projeto. Para isso clique com o botão direito sobre o projeto e vá em Proprietes. Em Java Build Path >> Libraries clique em Add External JARs e selecione Red5.jar que se encontra na raiz da instalação do Red5 e clique em OK.
Figura 03: Importando o JAR
Agora que o projeto esta criado configurado, vamos criar um novo arquivo java e nomeá-lo de Application.java no Package org.red5.confDemo que estenderá org.red5.server.adapter.ApplicationAdapter e implementará org.red5.server.api.service.IPendingServiceCallback.
Figura 04: Nova Classe
O arquivo gerado esta abaixo e
package org.red5.confDemo; import org.red5.server.adapter.ApplicationAdapter; import org.red5.server.api.service.IPendingServiceCall; import org.red5.server.api.service.IPendingServiceCallback; public class Application extends ApplicationAdapter implements IPendingServiceCallback { @Override public void resultReceived(IPendingServiceCall call) { // TODO Auto-generated method stub } }
O método resultReceived implementado server para tratar o retorno do Flex em métodos invocados no Java. Criar também uma classe User para definir os dados dos usuários conectados. Esta classe terá apenas campo USER_ID e USER_NOME.
package org.red5.confDemo; public class User { public String USER_ID = null; public String USER_NOME = null; public User(String USER_ID, String USER_NOME) { this.USER_ID = USER_ID; this.USER_NOME = USER_NOME; } }
Bom, agora vamos rescrever os seguintes métodos:
E criar os seguintes métodos:
Abaixo esta toda a classe montada:
package org.red5.confDemo; import java.util.HashMap; import java.util.Iterator; import java.util.Set; import org.red5.server.adapter.*; import org.red5.server.api.*; import org.red5.server.api.service.*; public class Application extends ApplicationAdapter implements IPendingServiceCallback { protected HashMap users = new HashMap(); @Override public boolean appStart(IScope scope) { return true; } @Override public boolean appConnect(IConnection conn, Object[] params) { // quem não envia nome, não ganha conexão if (params.length != 1) { rejectClient("Sem permissão de acesso ao sistema!"); return false; } String username = params[0].toString(); String uid = conn.getClient().getId(); IServiceCapableConnection service = (IServiceCapableConnection) conn; User user = new User(uid, username); users.put(uid, user); service.invoke("listaUsuarios", new Object[] { listaUsuarios() }, this); enviarNovosUsuarios(); System.out.println("Novo usuario: " + uid); return true; } @Override public boolean appJoin(IClient client, IScope scope) { super.appJoin(client, scope); // envia a lista de usuários conectados a todos os conectados enviarNovosUsuarios(); return true; } @Override public synchronized void disconnect(IConnection conn, IScope scope) { // ID da conexão do usuário String uid = conn.getClient().getId(); // O usuário que desconectou users.remove(uid); enviarNovosUsuarios(); System.out.println("Desconectado: " + uid); super.disconnect(conn, scope); } @Override public void resultReceived(IPendingServiceCall call) { System.out.println(call); } /** * Envia a lista de usuários conectados a todos os usuarios */ protected void enviarNovosUsuarios() { Object[] params = new Object[] { listaUsuarios() }; ServiceUtils.invokeOnAllConnections(scope, "listaUsuarios", params); } /** * Retorna a lista de usuários sem os campos nulos */ protected HashMap listaUsuarios() { HashMap params = new HashMap(); Integer key = 0; Set s = users.keySet(); for (Iterator it = s.iterator(); it.hasNext();) { User value = users.get(it.next()); if (value != null) { params.put(key, value); key++; } } return params; } }
Baixe aqui os arquivos java:
Agora dentro da pasta Red5/webapps vamos criar uma nova pasta chamada confDemo. Dentro desta pasta criar uma pastas chamada WEB-INF que receberá as bibliotecas e configurações deste serviço. Dentro desta pasta você criará três arquivos:
Agora exporte o seu aplicativo java e deposite dentro da pasta Red5/webapps/confDemo/WEB-INF/lib, reinicia o serviço do Red5. Para isso vá em File >> Export e abrirá a tela abaixo:
Imagem 05: exportando o projeto java
Agora marque os arquivos que devem ser exportados e selecione a pasta a salvar o Jar.
Figura 6: Finalizando a exportação
Clique em Finish e reinicie o serviço do Red5.
/** * Sempre que alterar o jar na pasta lib terás que reiniciar o o serviço do Red5. */
Agora que o serviço esta criado, vamos criar o aplicativo Flex que trabalhará com este serviço. Esta parte já deve ser mais fácil para a maioria dos leitores.
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" horizontalAlign="left" backgroundColor="#f6f6f6" backgroundGradientColors="[#f6f6f6, #bbbbbb]" creationComplete="init()" viewSourceURL="srcview/index.html"> <mx:Script> <![CDATA[ import mx.controls.Alert; // Conexão com o Red5 private var netConnection:NetConnection; // NetStream responsável por apresentar o vídeo private var netStream_play:NetStream; // NetStream responsável por publicar o vídeo private var netStream_publish:NetStream; // Canal para o Chat private var sharedObject:SharedObject; // Componente para apresnetar o vídeo private var video:Video // Nome da publicação do vídeo private var streamingNome:String="publishVideo" /** * executa após carregar o aplicativo */ private function init():void { video=new Video(); video.height=uiCaixaDoVideo.height; video.width=uiCaixaDoVideo.width; } /** * Inicia a aplicação e faz a conexão */ private function conectar():void { if ( hostRTMP.text.length < 15 ) { Alert.show( "URL muito pequena!" ) return; } if ( nome.text.length < 3 ) { Alert.show( "Nome tem que ter mais de 3 caracteres!" ) return; } if ( netConnection != null ) netConnection=null; netConnection=new NetConnection(); netConnection.addEventListener( NetStatusEvent.NET_STATUS, netStatus ); netConnection.addEventListener( AsyncErrorEvent.ASYNC_ERROR, asyncError ) netConnection.connect( hostRTMP.text, nome.text ); netConnection.client=this; } /** * Método que recebe a lista de usuários enviado pleo Red5 */ public function listaUsuarios( lista:Array ):void { datagridUsuarios.dataProvider=lista; } /** * Método que recebe o Status da conexão */ private function netStatus( e:NetStatusEvent ):void { switch ( e.info.code ) { case "NetConnection.Connect.Success": connectado() viewstack.selectedIndex=1; break; case "NetConnection.Connect.Closed": viewstack.selectedIndex=0; break; case "NetConnection.Connect.Rejected": viewstack.selectedIndex=0; break; case "NetConnection.Connect.Failed": viewstack.selectedIndex=0; break; default: viewstack.selectedIndex=0; } } /** * Chamado quando deu sucesso na conexão com o Red5 */ private function connectado():void { sharedObject=SharedObject.getRemote( "chat", netConnection.uri, true ) sharedObject.addEventListener( SyncEvent.SYNC, OnSync ); sharedObject.addEventListener( AsyncErrorEvent.ASYNC_ERROR, asyncError ); sharedObject.connect( netConnection ); sharedObject.client=this; netStream_play=new NetStream( netConnection ) video.attachNetStream( netStream_play ); uiCaixaDoVideo.addChild( video ); netStream_play.play( streamingNome ); } /** * Método responsável por tratar os AsyncErrorEvent */ private function asyncError( e:AsyncErrorEvent ):void { trace( e ) } /** * Método responsável por tratar os SyncEvent */ private function OnSync( e:SyncEvent ):void { trace( e ) } /** * Trata o click do Botão Publicar WebCam * * Se marcado publica, ao desmarcar para a publicação. */ private function publicar():void { if ( btPublicar.selected ) { btPublicar.label="Parar publicação!" netStream_publish=new NetStream( netConnection ); netStream_publish.attachCamera( Camera.getCamera()); netStream_publish.attachAudio( Microphone.getMicrophone()); // nome que será publicado netStream_publish.publish( streamingNome ); } else { btPublicar.label="Publicar no servidor RTMP!" netStream_publish.close() netStream_publish=null } } /** * Envia o testo por Chat e limpa o campo. */ private function enviarMensagem():void { var msg:String="<p><b>" + nome.text + " diz:</b> " + mensagem.text + "</p>"; sharedObject.send( "recebemsg", msg ); mensagem.text=''; } /** * Recebe as mensagens postada no Chat */ public function recebemsg( msg:String ):void { areaMensagem.htmlText+=msg; areaMensagem.validateNow(); areaMensagem.verticalScrollPosition=areaMensagem.maxVerticalScrollPosition; } ]]> </mx:Script> <mx:ViewStack id="viewstack" width="100%" height="100%" creationPolicy="all"> <mx:VBox width="100%" height="100%" verticalAlign="middle" horizontalAlign="center"> <mx:Form> <mx:FormItem label="URL do servidor:" fontWeight="bold"> <mx:TextInput id="hostRTMP" text="rtmp://localhost/confDemo" width="200" fontWeight="normal"/> </mx:FormItem> <mx:FormItem label="Nome:" fontWeight="bold"> <mx:TextInput id="nome" width="200" fontWeight="normal"/> </mx:FormItem> <mx:FormItem> <mx:Button label="Conectar no Red5" click="conectar()" fontWeight="normal"/> </mx:FormItem> </mx:Form> </mx:VBox> <mx:VBox width="100%" height="100%"> <mx:HBox width="100%" height="100%"> <mx:VBox height="100%"> <mx:Canvas left="10" top="85" width="160" height="120" backgroundColor="#000000"> <mx:UIComponent width="100%" height="100%" id="uiCaixaDoVideo"/> </mx:Canvas> <mx:Button id="btPublicar" label="Publicar WebCam" click="publicar()" toggle="true"/> <mx:DataGrid id="datagridUsuarios" width="100%" height="100%"> <mx:columns> <mx:DataGridColumn headerText="Usuários" dataField="USER_NOME"/> </mx:columns> </mx:DataGrid> </mx:VBox> <mx:VBox height="100%" width="100%"> <mx:TextArea id="areaMensagem" width="100%" height="100%" borderStyle="none" alpha="0.63" editable="false"/> <mx:HBox width="100%"> <mx:TextInput id="mensagem" width="100%" enter="enviarMensagem()"/> <mx:Button label="Enviar" click="enviarMensagem()"/> </mx:HBox> </mx:VBox> </mx:HBox> </mx:VBox> </mx:ViewStack> </mx:Application>
Basicamente o funcionamento é o seguinte: Quando clicar o em conectar, é criado uma instancia do NetConnection e enviado junto o nome da pessoa que esta conectando. Assim que a classe que criamos receber esta conexão, ela replica a todas as todas as conexões enviando a lista de usuários conectados. Estalista é recebida pelo método listaUsuarios do Flex.
No Red5 é verificado se foi enviado o nome e e aceita a conexão. Assim que a conexão for aceita, o Flex cria uma instancia do SharedObject e da play no canal publishVideo para quando alguém publicar um vídeo já estar instanciado.
A instancia do SharedObject é responsável pelo chat. para enviar uma mensagem, utiliza-se o método send, passando como parametro a o método que irá receber a resposta e a mensagem.
Quando clicamos em “publicar vídeo!”, uma instancia do NetStream é criada e publicamos no mesmo canal que foi dados play na lista acima.
Abaixo segue o exemplo finalizado, mais para testar terás que possuir um servidor de Red5.
Fonte do aplicativo Flex esta aqui.
O Aplicativo confDemo compilado para ser depositado no Red5 esta aqui.