Se esta querendo aprender ou iniciar novos projetos em Flex, clique aqui

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:

  • appStart
    É invocado quando o primeiro usuário se conectar na aplicação. Ou seja, quando a aplicação for iniciada;
  • appConnect
    Invocado quando um novo usuário se conectar;
  • disconnect
    Invocado quando o usuário sair da aplicação. Neste processo deve se excluir o usuário da lista de conectados;
  • appJoin
    Invocado quando após o usuario ser aceito na conexão.

E criar os seguintes métodos:

  • enviarNovosUsuarios
    Envia a lista de usuários conectados a todos os usuários conectados;
  • listaUsuarios
    Retorna a lista de usuários que estão conectados. O problema é que na variável users os desconectados ficam como null, então

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.

Gostou? Não esqueça de curtir!

Ou compartilhe o link com seus amigos

21 comentários

Deixe uma resposta

  1. ola,
    eu usei o diretorio do rtmp://localhost/oflaDemo
    e o chat funcionou bem, mas nao aparece os usuarios conectados,
    fiz como foi passado.
    crei o diretorio no servidor red5 C:\Program Files\Red5\webapps\confDemo descompactei o arquivo disponibilizado.
    mas nao funciona.

    o que esta dando errado.

    uma sugestção.

    alguem poderia gravar e colocar o passo a passo no youtube.
    muito obrigado.

     
  2. @gabriel Após descompactar é necessário reiniciar o serviço do Red5.

    O Serviço do oflaDemo não tem suporte a lista de usuários.

     
  3. Segui todos os passos que o Eduardo colocou aqui mas nem tão pouco consigo sequer fazer o connect não faz nada simplesmente e se trocar o url de conexão rtmp://localhost/confDemo para utilizar o fitcDemo funciona logo será que me pode ajudar? Muito Obrigado

     
  4. @Wemerson Este foi um erro meu. Já alterei o POST.

    Normalmenmte sempre deixo no appConnect. Há poucos e bem específicos que não é invocado o appJoin. Nos que não é chamado, os testes com FMS deram mesmos resultados, podendo ser pela versão do Flash Player.

     
  5. Obrigado pela resposta.
    Nao querendo explorar e ja explorando.
    Quanto ao metodo abaixo como que a lista e populado como e  feita a ligacao com o Red5?

          
    * Método que recebe a lista de usuários enviado pleo Red5
           */
          public function listaUsuarios( lista:Array ):void
          {
            datagridUsuarios.dataProvider=lista;
          }

    Outra duvida quanto aos metodos abaixo como vc explictou o appConnect quanto conecta e appJoin quando e aceito a conexao, vc esta chamando enviarNovosUsuarios() nos 2 metodos com isto vc nao esta enviando 2 vezes a lista de usuarios nao seria suficiento somente em appJoin?

    @Override
        public boolean appConnect(IConnection conn, Object[] params) {
            ...
            service.invoke("listaUsuarios", new Object[] { listaUsuarios() }, this);
            enviarNovosUsuarios();
         ...
        }

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

     
  6. Parabens mesmo pelo artigo, mostra como e cada vez mais facil utilizar Flex.
    Deixa tirar uma duvida. Com o Flex sabe que o metodo (public function recebemsg) vai receber as novas mensagens do Red5 onde vc fez esta ligacao que nao consigo encontrar?

     
  7. Ai esta a mágica, o próprio Flex envia isso para o servidor "espalhar".

    Então isso é graças ao <em>sharedObject.send( "recebemsg", msg );</em> que envia uma mensagem ao Red5 e o Red5 irá chamar isso em todos os clientes conectados nesta mesma sala.

    Veja que podes alterar a qualquer momento o <em>"recebemsg"</em> para qualquer outro nome, desde que alteres o nome da função <em>"public function recebemsg"</em> também.

     
  8. Parabens pelo artigo, ta mais objetivo doque a maioria dos tutorias espalhados por ai. Eu estou estudando o Red5 a uma semana e meia, e teria ficado feliz se sua página aparecesse nas pesquizas iniciais, ^_^ mas foi bom reaprender a aprender e implementei uma aplicação com Spring.

     
  9. @Joiseney Há duas maneiras de fazer isso!

    1° Você atráves do netconnection chama o um método remoto atráves do <b>call</b> e no server ao receber este call você envia somente ao usuário que deseja enviar.

    2° Eu em chat que envie privado eu envio através do <b>sharedobject.send</b> e passo o ID. Ao receber esta solicitação se o ID estiver nulo, eu mostro a todos, se estiver definido só mostro se o ID for igual ao informado.

    // Privado
    sharedObject.send('recebemsg', mensagem, paraUsrId)
    // Publico
    sharedObject.send('recebemsg', mensagem)

    public function recebemsg( msg:String, userId:String=null ):void
    {
      if(userId == null)
      {
        areaMensagem.htmlText+=msg;
      }
      else if(userId == UsuarioConectado.userID)
      {
        areaMensagem.htmlText+=msg;
      }
      areaMensagem.validateNow();
      areaMensagem.verticalScrollPosition=areaMensagem.maxVerticalScrollPosition;
    }

     
  10. Boa tarde, achei excepcional seu artigo, muito bom mesmo, a net hj é muito precaria de artigos bons como o seu nesta aréa. Gostaria de saber se tem como enviar uma mensagem para um dos usuarios em especifico, tipo selecionar um usuario conectado e enviar uma mensagem?
    Tem como fazer isto? Se tem, você poderia fazer um proximo artigo falando sobre o assunto ne?

     
  11. @Julio o arquivo confDemo.zip deve ser descompactado na pasta [Red5Install]/webapps/

     
  12. Boa tarde Eduardo,
    tentei sim, uma duvida este arquivo tenho que colocar dentro da pasta raiz do red5 onde tem tambem uma pasta web-inf ou dentro da pasta do projeto confDemo?

    Muito obrigado pela ajuda...

     
  13. @Julio, Você esta usando o serviço condDemo? tentasse este <a href="http://code.mxml.com.br/242/confDemo.zip" rel="nofollow">http://code.mxml.com.br/242/confDemo.zip</a>;

     
  14. Bom dia Eduardo,

    Parabens pelo trabalho de ajudar a todos!
    so uma duvida o video e o chat funcionam normalmente mas o servico da lista de usuarios nao esta aparecendo nada... tenho que fazer algo com o jar? ou somente colocar na pasta WEB-INF/lib ele ja adiciona ao projeto?

    Muito obrigado

     
  15. Você esta seguindo todos os passos direitinho?

     
  16. Eduardo,

    a minha aplicação está acusando InvalidApp quando eu clico em conectar, debuguei e noite que na hora do netStatus ele chega o código de InvalidApp e não há nenhum tratamento para isso, como faço para consertar este problema?

    Abraços,
    Roberto Mello.

     
  17. @Renan, quando você publica o vídeo em <i>netStream_publish.publish( streamingNome );</i> adicione o termo <b>record</b> como segundo parâmetro:

    <i>netStream_publish.publish( streamingNome, "record" );</i>

    Veja mais em http://livedocs.adobe.com/flex/3/langref/flash/net/NetStream.html#publish()

     
  18. Eduardo tenho outra duvida. Como faço para fazer o stream ao vivo e ao mesmo tempo gravar o vídeo?

     
  19. Olá Eduardo, eu fiz tudo certinho como você explicou no post, mas aqui em casa  não deu certo. Eu clico em conectar ao servidor Red5 e nada acontece. O que será?

     
  20. Olá Eduardo!

    Ótimo post! muito interessante e acredito que vai ajudar bastante gente.
    Parabéns

     
  21. Olá Alemão ;) (Eduardo)  muito bom aqui seu sítio...estou no meus inicios do Flex, Java sim é o meu forte.
    Assumo que isto é só um exemplo...uma pequena questão seria como posso fazer com que veja o imagem publicada pelos outros users conectados já que eu só me vejo a mim mesmo ...o sea...o que eu estou publicando.

    Não espero por uma solução mais uma pequena ajuda e que direcção tomar para conseguir o desejado.

    Muito bom seu trabalho...vou chequear todos dias ;)