2010-06-22

用 AIR2 透過 DatagramSocket (UDP) 及截圖做簡陋的監視服務

安裝 PicServer.air 的電腦必須要有 Webcam,當然也要有網路功能,使用 port 24688,啟動後會等待 client 登入。安裝 PicClient.air 的電腦是監看端,使用 port 24689,記得防火牆要讓訊息通過才行。由於使用 UDP,所以無法得知連線狀態,因為 client 必須傳 session 給 server,server 才會傳畫面資料給 client。client 端還可以發展成調整時間間隔、畫面品質、及存檔等。
原始碼如下(請使用 Flash CS5)。

PicServer
package  {
 import com.adobe.crypto.MD5;
 import com.adobe.images.JPGEncoder;
 import fl.controls.TextInput;
 import flash.display.MovieClip;
 import flash.net.DatagramSocket;
 import flash.events.DatagramSocketDataEvent;
 import flash.utils.ByteArray;
 import flash.utils.Dictionary;
 import flash.utils.getTimer;
 import flash.media.Video;
 import flash.media.Camera;
 import flash.geom.Matrix;
 import flash.display.BitmapData;
 
 public class PicServer extends MovieClip {
  public var password_txt:TextInput;
  private var UDPSocket:DatagramSocket;
  private var clients:Dictionary = new Dictionary();
  private var video:Video;
  private var camera:Camera;
  private var jpgEnc:JPGEncoder = new JPGEncoder();
  
  public function PicServer() {
   password_txt.text = '新德測試';
   UDPSocket = new DatagramSocket();
   UDPSocket.addEventListener(DatagramSocketDataEvent.DATA, dataReceived);
   UDPSocket.bind(24688);
   UDPSocket.receive();
   
   camera = Camera.getCamera();
   video = new Video();
   this.addChild( video );
   video.y = 55;
   video.attachCamera( camera );
   video.width = camera.width;
   video.height = camera.height;
  }
  
  private function dataReceived(event:DatagramSocketDataEvent):void {
   // 傳入的是 AMF 格式的 Object
   var obj:Object = event.data.readObject();
   var ba:ByteArray;
   // 如果傳入的是密碼
   if( obj.password && obj.password == password_txt.text) {
    obj.srcAddress = event.srcAddress;
    obj.srcPort = event.srcPort;
    // 產生個認證碼
    obj.session = MD5.hash(''+Math.random());
    clients[ obj.session ] = obj;
    
    ba = new ByteArray();
    ba.writeObject( {session: obj.session} );
    // 傳回 session id
    UDPSocket.send(ba, 0, 0, obj.srcAddress, obj.srcPort);
   } else if ( obj.session && clients[obj.session] ) {
    var matrix:Matrix = new Matrix();
    matrix.tx = - video.x;
    matrix.ty = - video.y;
    var bmd:BitmapData = new BitmapData(video.width, video.height);
    // 抓畫面
    bmd.draw( this, matrix);
    ba = jpgEnc.encode( bmd );
    var client:Object = clients[ obj.session ];
    UDPSocket.send(ba, 0, 0, client.srcAddress, client.srcPort);
   }
  }
 }
}

PicServer
package {
 import com.adobe.serialization.json.JSON;
 import fl.controls.Button;
 import fl.controls.TextInput;
 import flash.display.MovieClip;
 import flash.net.DatagramSocket;
 import flash.events.DatagramSocketDataEvent;
 import flash.events.MouseEvent;
 import flash.utils.ByteArray;
 import flash.display.Loader;
 import flash.utils.Timer;
 import flash.events.TimerEvent;

 public class PicClient extends MovieClip {
  public var login_btn:Button;
  public var ip_txt:TextInput;
  public var password_txt:TextInput;
  private var UDPSocket:DatagramSocket;
  private var session_ba:ByteArray;
  private var loader:Loader;
  private var timer:Timer;

  public function PicClient() {
   password_txt.text = "新德測試";
   UDPSocket = new DatagramSocket();
   UDPSocket.addEventListener(DatagramSocketDataEvent.DATA, dataReceived);
   UDPSocket.bind(24689);
   UDPSocket.receive();
   login_btn.addEventListener(MouseEvent.CLICK, clickHandler);

   loader = new Loader();
   this.addChild( loader );
   loader.y = 50;
  }

  private function dataReceived(event:DatagramSocketDataEvent):void {
   if (session_ba == null) {
    var obj:Object = event.data.readObject();
    if (obj && obj.session) {
     login_btn.enabled = false;
     ip_txt.editable = false;
     password_txt.visible = false;
     session_ba = event.data;
     // 1 秒傳 1 次
     timer = new Timer(1000,1);
     timer.addEventListener(TimerEvent.TIMER_COMPLETE, timerCompleted);
     timer.start();
    }
   } else {
    loader.loadBytes( event.data );
   }
  }

  private function clickHandler(event:MouseEvent):void {
   var byteArray:ByteArray = new ByteArray();
   byteArray.writeObject( {password:password_txt.text} );
   UDPSocket.send(byteArray, 0, 0, ip_txt.text, 24688);
  }

  private function timerCompleted( event:TimerEvent):void {
   UDPSocket.send(session_ba, 0, 0, ip_txt.text, 24688);
   timer.start();
  }
 }
}

8 則留言:

匿名 提到...

老師您好,我有一個問題想請問您,我最近在使用FLASH AIR2寫UDP連線的部分,在本機上編譯後執行都沒有問題,但是透過網頁執行時,在有new DatagramSocket建購式的FUNCTION就停住沒有繼續了,請問這是什麼原因?我用的是FLASH CS5 檔案是開 ADOBE AIR

Shinder 提到...

請找看看是不是遇到 crossdomain 的問題, 或者被防火牆擋到了。

匿名 提到...

我直接執行SWF檔案時會出現
VerifyError: Error #1014: 找不到類別 flash.net::DatagramSocket。
這樣的訊息,但是我已經是用AIR2並且在編譯時都正常

Shinder 提到...

一開始沒注意看你的問題...囧, DatagramSocket 只能用在 AIR 不能放在網頁上執行, 雖然都是 swf 但不能在網頁上跑

匿名 提到...

那麼請問老師如果我要在網頁上使用UDP連線還有其他方法嗎QQ?

Shinder 提到...

Flash只能用 Socket(TCP/IP), 你可以找找看 Java Applet 是否能用 UDP...

匿名 提到...

不好意思再請問有在網頁上使用AIR2的方法?
或是可以由網頁載入AIR2來使用嗎?

Shinder 提到...

不行, 整個安全性是不相同的

FB 留言