Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Videoconference

Compatible ActionScript 2. Cliquer pour en savoir plus sur les compatibilités.

Article écrit le 07/05/2007 16:40.
Par Gauthier ( Gauthier de Girodon Pralong ).

Ici nous allons aborder une manière simple de faire votre premiere application de video conférence avec le server flash media.

Il y 'a quelque pre-requis a avoir :

  • avoir lus les autre tutoriaux au sujet de flash media server sur le wiki
  • avoir deja utilisé des classes
  • avoir deja utilisé les Delegate
  • avoir installer flash media server
  • s'appuyer sur l'aide en ligne adobe a propos de flash media server

http://livedocs.adobe.com/fms/2/docs/

ce que ce tutoriel n'est pas :

  • ce n'est pas une application dites “optimisé”
  • ce n'est pas LA solution a la videoconférence
  • sans fautes ^_^

alors a qu'est ce que c'est ? Tout simplement une application mettant en place quelques astuces pour la mise en place d'un system de video conference avec flash media server.

Nous allons voir :

  • l'objet NetConnection (method call, onStatus et connect);
  • l'objet NetStream (method play, publish, close)
  • le Shared Object en mode Remote
  • l'extension de l'objet client coté server.
  • le chargement de fichier .asc coté server.

sources disponible ici

Problèmatique

La problematique d'une video conférénce varie enormément de ce que l'on attend de cette dernière. Mais dans les grande ligne toute video conférence doivent permettre au utilisateurs de se voir et de s'entendre.

Pour notre application nous allons donc permettre aux utilisateurs de se voir, de s'entendre et de communiquer textuellement Pour cela nous allons créer une application coté server et coté client.

Dezippé le contenu du zip, deplacer le repertoire vfms se trouvant dans le repertoire server dans le repertoire application de votre flash media server.

Server

Le fichier le plus important est main.asc, ce fichier est la base de toute application flash media server. regardons de plus prés ce fichier

/**
* on charge le fichier vfmsClient.asc qui nous permet d'etendre l'objet client.
*/
load("vfmsClient.asc");
/**
* application.allowDebug definit l'instance de l'application en mode debugage 
* dans le panneau d'administration du server flash media cela nous permet de lire
* les informations des shared object par exemple.
*/
application.allowDebug=true;
/**
* Definition de la function de lancement de l'application.
* Quand l'instance de l'application est chargé elle execute cette portion de code.
* 
* Ici nous créons un objet GUL (global user list) qui contiendra l'ensemble des clients connecté a l'instance. 
* Ainsi qu'un shared object non persistant qui nous permettra de broadcaster les utilisateur connectée (arrivé depart message);
*/
application.onAppStart = function() 
{
	this.GUL= new Object();
	this.so = SharedObject.get("so", false);
};
/**
* Definition de la function lors d'une connexion d'un client a l'instance de l'application.
* Nous allons verifier deux choses :
* 
* 1) que l'object oUser est bien passé a l'instance lors de la connexion de l'utilisateur
* 
* 2) que le nom entré par l'utilisateur n'est pas deja pris par un autre connecté.
*/
application.onConnect = function(client, oUser) 
{
	//Verification que l'objet oUser est bien definit
	if(oUser)
	{
		//Verification que le nom de l'utilisateur n'est pas deja prit.
		if(this.so.getProperty(oUser.name)==null)
		{
			for(var prop in oUser)
			{
				client[prop]=oUser[prop];
			}
			this.acceptConnection(client);
			//Insertion de l'objet client dans le shared object (appelera la methode onSync du shared Object cote client)
			this.so.setProperty(client.name, client);
			//Insertion de l'objet client dans la liste global utilisateur
			this.GUL[client.name]=client;
		} 
		else 
		{
			//Si le nom est deja prit nous rejetons la connexion en stipulant un message d'erreur comme ci dessous
			//this.rejectConnection(objet client, objet d'erreur);
			var err = new Object();
			err.message = "Le nom est déja prit";
			this.rejectConnection(client, err);
		}
	}
};
/**
* Methode appele lorsque l'utilisateur se deconnect de l'instance
* l'objet client est automatiquement passer a celle ci.
* On recupere l'objet client et on vide l'objet GUL et le shared object 
* du client qui vient de se deconnecter
*/
application.onDisconnect = function(client) 
{
	delete this.GUL[client.name];
	this.so.setProperty(client.name, null);
};

le code est suffisement commenté pour comprendre son fonctionnement, cependant je vais revenir sur la première ligne de code :

load("vfmsClient.asc");

cette ligne stipule au flash media server de charger un fichier (comme un include) .asc . mais que contient donc ce fichier chargé?

/**
* Ici nous allons etendre l'objet client.
* Nous allons grace ce fichier pouvoir rajouter des fonction au client coté server.
* 
* Pour notre application nous donnerons la possibilité d'envoyer un message privé a une personne;
* 
* Client_sendPVM comporte deux arguments, to et msg.
* to est l'identifiant (name) du client vers qui nous voulons envoyer le message.
* Nous allons donc recupérer l'instance de l'objet client correspondant grace a l'objet GUL que nous avons crée
* dans l'application "onAppStart".
* 
* comme ceci :
* var client= application.GUL[to];
* 
* Une fois l'instance du client récupéré nous lui envoyons le message.
* Avec comme parametre le message (msg).
* Mais aussi notre nom, et pour se faire rien de plus simple vu que nous nous trouvons ici même dans notre propre objet client.
* Il suffit donc de juste passer this.name pour le recuperer.
* 
* Pour plus de detail sur l'objet client regardez la doc fms serverside objet client.
*/
Client_sendPVM = function (to, msg) {
	var client= application.GUL[to];
	client.call("receive_PVM",null, this.name, msg);
};
Client.prototype.sendPVM = Client_sendPVM;

Ce fichier a donc pour but d'etendre l'objet client, et cela est bien pratique, je vous conseil donc (mais cela n'engage que moi) d'utiliser un fichier

externe pour etendre les fonctionnalités de vos clients.

Voila notre application coté server terminé nous allons donc maintenant voir l'application client.

Client

L'application coté client se compose en trois fichier, un fichier .fla et deux class.

voici le code de la class principal Main.as

/**
* Importation de la classe Delegate
*/
import mx.utils.Delegate
class com.gauthier.vfms.Main extends MovieClip
{
	/**
	* login et room sont les deux clips servant pour l'application
	* _nc est l'objet netConnection qui nous servira a la connection avec le server flash media
	* 
	*/
	private var login:MovieClip;
	private var room:MovieClip;
	private var _nc:NetConnection;
	/**
	* Methode appele lorsque le movieclip est chargé
	* On rend Invisible le clip room
	* On initialise l'event du bouton connexion du movieclip login
	*/
	function onLoad():Void{
		this.room._visible=false;
		this.login.btnConnect.addEventListener("click", Delegate.create(this, Loggin));
	}
	/**
	* Methode appele lors du clique sur le bouton connexion
	* On verifie que le champs n'est pas vide
	*/
	private function Loggin(){
		if(this.login.loginArea.text!=""){
			connect(this.login.loginArea.text);
			return;
		}
	   this.login.loginLabel.text="Login [Error] : vous n'avez pas remplit votre login";
	}
	/**
	* methode connectant l'utilisateur au server
	* On cree l'objet de connection
	* @param	login le login rentré par l'utilisateur dans le champs de saisie
	*/
	private function connect(login:String):Void{
		var oUser:Object= new Object()
		//c'est dans cet objet que l'on place le nom de l'utilisateur pour le server.
		oUser.name= login;
		if(_nc==null){
			_nc= new NetConnection();
			room.setNC(_nc);
			_nc.onStatus=Delegate.create(this,onStatus);
		}
		// nous lançons la connexion en passant le l'objet oUser au server.
		_nc.connect("rtmp://localhost/vfms", oUser);
		// on declare le nom de l'utilisateur a l'objet room
		room.setClientName(this.login.loginArea.text);
 
	}
	/**
	* On attend le retour du server pour savoir si oui ou non on est autorisé a se connecter au server
	* si oui on appel la methode enterRoom de l'objet room.
	* sinon on affiche l'erreur.
	* @param	oInfo objet de status
	*/
	private function onStatus(oInfo:Object):Void{
		switch (oInfo.code){
 
			case "NetConnection.Connect.Success":
			this.login._visible=false;
			this.room._visible=true;
			room.enterRoom()
			break;
 
			case "NetConnection.Connect.Rejected":
			this.login.loginLabel.text="Login [Error] : "+oInfo.application.message;
			break
		}
	}
}

Cette class sert juste a creer la connexion et a connecter l'utilisateur au server flash media server via un champs de saisie. Elle inclus aussi le movieclip

room qui est en fait une class etendu de Movieclip comme vous allez le voir tout de suite

/**
* Importation des composant et de la classe Delegate necessaire a l'application
*/
import mx.controls.List;
import mx.controls.Button;
import mx.controls.TextArea;
import mx.controls.TextInput;
import mx.utils.Delegate;
class com.gauthier.vfms.Room extends MovieClip
{
	/**
	* Composant de l'application
	*/
	public var userList:List;
	public var chatZone:TextArea;
	public var btnSend:Button;
	public var btnPV:Button;
	public var messageArea:TextInput;
	public var btnCloseWebcam:Button;
	public var btnDiffuse:Button;
	/**
	* boolean servant pour le bouton de diffusion afin de garder un etat.
	*/
	private var _isDiffuse:Boolean=false;
	/**
	* Champs de text dynamique affichant un message d'accueil
	*/
	public var welcome:TextField;
	/**
	* Objet video, affichant ma cam et celle de l'utilisateur selectionné
	*/
	public var myWebCam:Video;
	public var userWebCam:Video;
	/**
	* Variable de network
	*/
	private var _nc:NetConnection;
	private var _webcamNS:NetStream;
	private var _myNS:NetStream;
	private var _so:SharedObject;
	/**
	* Nom du client qui vient de se logger (moi)
	*/
	private var _myName:String;
	/**
	* objet Microphone et camera necessaire pour la diffusion
	*/
	private var _myCamera:Camera;
	private var _myMicro:Microphone;
	/**
	* Variable contenant le nom de l'utilisateur selection.
	* utile pour lire la video de cet utilisateur et pour lui envoyer un message privé
	*/
	private var _userSelected:Object;
	/**
	* Method appelé lorsque le movie clip est chargé
	* Initialise les delegate des boutons et de la liste.
	* initialise le composant textArea.
	*/
	public function onLoad():Void
	{
		chatZone.editable = false;
		chatZone.html = true;
		btnSend.addEventListener("click", Delegate.create(this, sendMessage));
		btnPV.addEventListener("click", Delegate.create(this, sendPV));
		btnDiffuse.addEventListener("click", Delegate.create(this, diffuse));
		btnCloseWebcam.addEventListener("click", Delegate.create(this, stopViewCam));
		userList.addEventListener("change", Delegate.create(this, selectUser));
 
	}
	/**
	* Methode initialisant l'objet _nc
	* On crée le delegate pour la methode qui va nous recupéré les message privé pour le membre.
	* @param	nc est la connection au fms passer par la class Main.
	*/
	public function setNC(nc:NetConnection):Void
	{
		_nc=nc;
		_nc.receive_PVM=Delegate.create( this,onPVMessage);
	}
	/**
	* methode initialisant la variable _myName 
	* On affiche le message de bienvenue dans le textfield welcome.
	* @param	name le nom que l'utilisateur a passé dans le login.
	*/
	public function setClientName(name:String):Void
	{
		_myName=name;
		welcome.text="Bienvenue "+_myName;
		welcome.autoSize="left";
	}
	/**
	* Methode appele par Main indiquant que l'utilisateur a put se connecter au server.
	* On crée le Shared Object en remote.
	* On crée les delegate pour le sharedObject.
	* On crée les netStream pour la diffusion et la reception des flux audio et video.
	* On Connect le SharedObject au server.
	*/
	public function enterRoom():Void
	{
		_so= SharedObject.getRemote("so", _nc.uri, false);
		_so.onSync= Delegate.create(this, updateUserList);
		_so.sendMessage= Delegate.create(this, onMessage);
		_myNS= new NetStream(_nc);
		_webcamNS= new NetStream(_nc);
		_so.connect(_nc);
	}
	/**
	* Methode qui envois un message a tout le monde
	* la method send du shared object broadcast le message a l'ensemble des clients connecté au shared object.
	*/
	private function sendMessage():Void
	{
		_so.send("sendMessage",_myName, messageArea.text);
		messageArea.text="";
	}
	/**
	* Methode qui envois un message privé a l'utilisateur selectionné dans la liste
	* cette fois ci on utilise la methdoe call de l'objet netConnection car nous ne voulos pas que 
	* tout les utilisateur soit mis au courant de l'envois du message ni de son contenu.
	*/
	private function sendPV():Void
	{
		_nc.call("sendPVM",null, _userSelected, messageArea.text);
		var message:String = "<font color='#ff6600'><b>msg privé à " + _userSelected + "</b></font>: " +  messageArea.text;
		showMessage(message);
		messageArea.text="";
	}
	/**
	* Methode appele lorsque qu'un nouveau message est broadcasté par le shared Object
	* @param	from l'emetteur du message
	* @param	msg le contenu du message
	*/
	private function  onMessage(from:String, msg:String):Void
	{
		var message:String = "<b>" + from + "</b>: " + msg;
		showMessage(message);
	}
	/**
	* Methode appele lorsque qu'un nouveau message privé nous est destiné
	* @param	from l'emetteur du message
	* @param	msg le contenu du message
	*/
	private function onPVMessage(from:String, msg:String):Void
	{
		var message:String = "<font color='#ff0000'><b>msg privé de " + from + "</b></font>: " + msg;
		showMessage(message);
	}
	/**
	* Methode qui affiche le message dans la zone de text.
	* @param	msg le message a afficher
	*/
	private function showMessage(msg:String):Void{
		chatZone.text += msg;
		chatZone.vPosition = chatZone.maxVPosition;
		chatZone.redraw();
	}
	/**
	* Methode appele lorsqu'on selectionne un utilisateur dans la liste.
	* On force la lecture du stream même si ce dernier ne diffuse pas.
	* On place son nom dans la variable _userSelected.
	* On attache la video du flux dans l'objet video (userWebCam)
	*/
	private function selectUser():Void
	{
		_userSelected=userList.selectedItem.label;
		_webcamNS.play(_userSelected);
		userWebCam.attachVideo(_webcamNS);
	}
 
 
	/**
	* Methode appele lorsque le sharedObject a change (un utilisateur rentre ou sort).
	* On Efface la liste et on la recrée avec les nouvelles valeurs.
	*/
	private function updateUserList():Void
	{
		userList.removeAll();
		for (var prop:String in _so.data)
		{
			if (_so.data[prop] != null)
			{
				var client:Object =_so.data[prop];
				userList.addItem({label:client.name, data:client});
			}
		}
	}
 
	/**
	* Methode appele lorsque l'on clique sur le bouton (btnDiffuse)
	*/
	private function diffuse():Void{
		/**
		* On regarde l'etat du bouton, si il est sur true donc qu'on diffuse deja :
		* - On change le label du bouton en Marquant "Diffuser"
		* - On couple la diffusion (publish(false), close())
		* - On vide l'objet video (attachVideo(null), clear());
		*/
		if(_isDiffuse){
			btnDiffuse.label="Diffuser";
			_myNS.publish(false);
			_myNS.close();
			myWebCam.attachVideo(null);
			myWebCam.clear();
		}else {
			/**
			* Si l'etat est sur false (valeur par défaut lors de la connexion) :
			* On verifie si l'objet _myCamera et l'objet _myMicro sont deja definit
			* si ils ne le sont pas on les crée
			*/
			if(_myCamera==null){
				_myCamera= Camera.get();
			}
			if(_myMicro==null){
				_myMicro= Microphone.get();
			}
			/**
			* On attache la video de l'objet camera a l'objet video myWebCam
			* On attache la video de l'objet camera a l'objet netStream de diffusion (_myNS)
			* On attache le son de l'objet microphonoe a l'objet netStream de diffusion (_myNS)
			*/
			myWebCam.attachVideo(_myCamera);
			_myNS.attachVideo(_myCamera);
			_myNS.attachAudio(_myMicro);
			/**
			* On publie le flux video avec le nom de l'utilisateur.
			* On Change le label du bouton diffuser
			*/
			_myNS.publish(_myName);
			btnDiffuse.label="Arreter";
		}
		/**
		* On inverse l'etat du bouton.
		*/
		_isDiffuse=!_isDiffuse;
	}
	/**
	* Methode stoppant l'affichage de la webcam que l'utilisateur regarde.
	*/
	private function stopViewCam():Void{
		_webcamNS.play(false);
		_webcamNS.close();
		userWebCam.attachVideo(null);
		userWebCam.clear();
	}
}

Conclusion

Ce tutorial vous montre quelques methodes et directions a explorer pour creer rapidement une videoconférence. A vous de modifier le code selon vos besoin Bon test ;)

Pour tout commentaire ou question sur le sujet vous pouvez vous rendre sur mon blog (liens ci dessous ^_^).

En savoir plus