Знаниями нужно делится...

Передача потокового видео ActionScript 3 через ServerSocket.

Назад к списку | Просмотров: 2756

В одной из статей уже была рассмотрена тема взаимодействия с сокетами и организация сервера на основе класса ServerSocket «Сервер  на AS3». В этой статье речь пойдет о возможности передавать потоковое изображение на сервер и транслировать его на любое количество «подписчиков».

В начале мы подключаем камеру и с помощью класса ServerSocket мы открываем порт для просушивания. Далее клиент обращается к серверу. Мы добавляем его в список активных «подписчиков». По таймеру, несколько раз в секунду передаем всем «подписчикам» картинку отображаемую на камере. Примерно 14 обновлений изображений в секунду создаст иллюзию плавного движения видео.

Главный класс сервера Main.as:

package 
{
	import flash.display.Sprite;
	import flash.events.Event;
	
	import flash.media.Camera;
	import flash.media.Video;
	import flash.display.StageAlign;
    import flash.display.StageScaleMode;
	import flash.events.*;
	import flash.text.TextField;
	import flash.net.Socket;
	import flash.utils.ByteArray;
	import flash.display.BitmapData;
	import flash.display.Bitmap;
	import flash.geom.Rectangle; 
	import flash.utils.Timer;
	
	/**
	 * ...
	 * @author Vench
	 */
	public class Main extends Sprite 
	{
		private var tx:TextField = new TextField();
		private var server:ServerCam = null;
		private var video:Video;
		private var cam:Camera;
		private var showBitMap:Bitmap = new Bitmap();
		private var bitmapDataBufer:BitmapData = null;
		private var byteArrayBuff:ByteArray = new ByteArray(); 
		private var timer:Timer;
		
		public function Main():void 
		{
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		private function init(e:Event = null):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);	
			
			stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE
			
			tx.x = 330;
			tx.border = true;
			tx.y = 0;
			tx.width = 320;
			tx.height = 200;
			addChild(tx);
			
			cam = Camera.getCamera();
			if (cam == null) {
				tx.appendText("not isset camera");
				return ;
			}
			cam.setMode(320, 240, 25);
			cam.addEventListener(ActivityEvent.ACTIVITY, activityHandler); 
			 
			 
			video = new Video(320, 240);			
			addChild(video); 
			video.attachCamera(cam);  
 
			showBitMap.x = 0;
			showBitMap.y = video.height + 10;
			addChild(showBitMap); 
			
			tx.appendText('start; cam.name: ' + cam.name);   
			
			timer = new Timer(200, 1);
			timer.addEventListener(TimerEvent.TIMER_COMPLETE, timerComplite);
			timer.start();
		}
		
		private function  timerComplite(e:TimerEvent):void {
			sendData();
			timer.reset();
			timer.start();
		}
	 
		
		private function activityHandler(e:ActivityEvent):void { 
			if (server == null) {
				server = new ServerCam();
				server.addEventListener(ServerCam.ADD_CLIENT_CAM, addClientCam);
				tx.appendText('\nstart server;'); 
			} 
        }
		
		private function sendData():void {
			if (server == null) {
				return;
			}
			 
			var c:Array = server.getClients();
			if (c.length == 0) {
				return;
			}
		 
			var s:Socket;  
			if (bitmapDataBufer == null) {
				bitmapDataBufer = new BitmapData(video.width, video.height);
			}
			bitmapDataBufer.draw(video);
			showBitMap.bitmapData = bitmapDataBufer; 
		 
            byteArrayBuff = bitmapDataBufer.getPixels(bitmapDataBufer.rect);
            byteArrayBuff.position = 0;  
		    byteArrayBuff.deflate();//сжимаем
			
			for (var i:uint = 0; i < c.length; i ++ ) {
				try {	
					s = c[i];
					//указываем длину пакета
					s.writeUnsignedInt(byteArrayBuff.length);
					//пишем пакет
					s.writeBytes(byteArrayBuff, 0, byteArrayBuff.length);
					s.flush();
				} catch (er:Error) {
					server.removeSocet(i);	 
					tx.appendText('\nremoveClient ' + i + ';'); 
				}				
			}
		}
		 
	 
		
		private function addClientCam(e:Event = null):void {
			tx.appendText('\naddClient;'); 
		}
	}
	
}

Класс ServerCam.as:

package  
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.ServerSocketConnectEvent;
	import flash.net.Socket;
	import flash.net.ServerSocket;
	/**
	 * ...
	 * @author Vench
	 */
	public class ServerCam extends EventDispatcher
	{
		public static const ADD_CLIENT_CAM:String = "addClientCam";
		
	    private var _socket:ServerSocket;
		private var _port:int = 12345; 
		private var _clients:Array = new Array();
		
		
		public function ServerCam() 
		{
			try {
				_socket = new ServerSocket();
				_socket.addEventListener( ServerSocketConnectEvent.CONNECT, _onConnectServer );
				_socket.bind(_port);
				_socket.listen();
			} catch(e:Error) {
				trace("Err start " + e);
			}
		}
		
		public function removeSocet(index:int):void { 
			_clients.splice(index, 1);
		}
		
		/* */
		public function getClients():Array {
			return _clients;
		}
		
		/* */
		private function _onConnectServer(e:ServerSocketConnectEvent):void {
			_clients.push(e.socket as Socket);      
			dispatchEvent(new Event(ADD_CLIENT_CAM));
		} 
		
	
	}

}

Главный класс клиента Main.as

package 
{
	import flash.display.Sprite;
	import flash.events.*;
	import flash.net.*;
	import flash.display.StageAlign;
    import flash.display.StageScaleMode;
	import flash.utils.ByteArray;
	import flash.display.BitmapData;
	import flash.display.Bitmap;
	import flash.geom.Rectangle;
	import flash.utils.CompressionAlgorithm;
	/**
	 * ...
	 * @author Vench
	 */
	public class Main extends Sprite 
	{
		private var _sock:Socket = new Socket(); 
		
		private var showBitMap:Bitmap = new Bitmap();
		private var bitmapDataBufer:BitmapData;
		private var byteArrayBuff:ByteArray;
		private var len:uint = 0;
		
		public function Main():void 
		{
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		private function init(e:Event = null):void 
		{
			trace("start");
			byteArrayBuff = new ByteArray();
			bitmapDataBufer = new BitmapData(320, 240);
			showBitMap.x = 0;
			showBitMap.y = 0;
			addChild(showBitMap);
			
			
			removeEventListener(Event.ADDED_TO_STAGE, init);
			// entry point
			stage.scaleMode = StageScaleMode.NO_SCALE; 
            stage.align = StageAlign.TOP_LEFT;
			
			//
			_sock.addEventListener(ProgressEvent.SOCKET_DATA, _handleSocketData);
			_sock.addEventListener(DataEvent.DATA, _dataHandler); 
			_sock.addEventListener(IOErrorEvent.IO_ERROR, _ioError);
			//socket connect
			_sock.connect("10.0.2.15", 12345);
		}
		
		/* 
		 * Получаем данные с сокета
		 * bytesAvailable - сколько байт готова для чтения
		 * */
		private function _handleSocketData(e:ProgressEvent):void{
			var s:Socket = e.target as Socket; 
				 
			//первые четыре байта - длинна ожидаемого пакета
			if (len == 0 && s.bytesAvailable >= 4) {
				len = s.readUnsignedInt();
			}
				
			if (len > 0 && s.bytesAvailable >= len) {	
				//пакет готов для чтения
				try {  
					byteArrayBuff.clear(); 
					s.readBytes(byteArrayBuff, 0, s.bytesAvailable);   
					byteArrayBuff.inflate();
				    bitmapDataBufer.setPixels(new Rectangle(0, 0, 320, 240), byteArrayBuff); 
					showBitMap.bitmapData = bitmapDataBufer; 
					len = 0;  
				} catch (er:Error) {
					trace("handleSocketData: " + er); 
				}
			}	
		}
	 
		 

		private function _ioError(e:IOErrorEvent):void {
			trace("_ioError: " + e); 
		}

		/* */
		private function _dataHandler(e:DataEvent):void { 
            trace("_dataHandler: " + e.data); 
        }
		
	}
	
}

Скачать код полный сервера можно тут.
Скачать код полный клиента можно тут.

Этот способ трансляции нельзя назвать очень удачным по ряду причин, но он полезен для понимания потоковой передачи данных типа сервер клиент. В следующей своей статье я приведу более удачное решение, на основе которого можно будет создать многопользовательский чат.


дата 14/01/2013


Оставить комментарий
0 + 7 =