2013年1月7日星期一

BitmapData draw RTMP Security Error

Ticore's Blog

網路上看到 Flash AS3 從 Bytes 資料播放 Streaming 時
想要以 BitmapData.draw 對 Video 取樣結果得到 Security Error

SecurityError: Error #2123: 執行程序安全性違規:
BitmapData.draw: 無法存取 null。未授予原則檔案的存取權。

Error #2123: Security sandbox violation:
BitmapData.draw: cannot access null. No policy files granted access

其實對於 FMS RTMP Streaming,可以從 FMS App 控制 Client 權限
Client.videoSampleAccess, Client.AudioSampleAccess

至於 Flash Client RTMFP 也有相關的 API
NetStream.videoSampleAccess, NetStream.audioSampleAccess

很明顯以上的 API 對於自行產生或嵌入的 Stream Byte 並沒有幫助
搜尋網路之後發現有一個 NetStream Command 可以幫上忙
Error #2123: Security sandbox violation: BitmapData.draw for rtmfp video
發布端自行發送以下命令,就可以讓訂閱端能夠對 Stream 做取樣

stream.send("|RtmpSampleAccess", true, true);

這對已經錄製好的 FLV 能有什麼幫助呢?
嘿嘿,利用之前寫的 FMS App 小工具 - FMS SSAS 主動錄製遠端串流
SSAS 最後加上 ns.send("|RtmpSampleAccess", true, true);
將影片轉錄過一次

再配合另一個小工具 - FLV 轉 SWF 另類作法
加上取樣功能後來播放測試,發現已經可以對 Video 做 BitmapData.draw 取樣了

package {
 import flash.display.Bitmap;
 import flash.display.BitmapData;
 import flash.display.Loader;
 import flash.display.Sprite;
 import flash.display.StageAlign;
 import flash.display.StageScaleMode;
 import flash.events.Event;
 import flash.events.MouseEvent;
 import flash.events.NetStatusEvent;
 import flash.media.Video;
 import flash.net.NetConnection;
 import flash.net.NetStream;
 import flash.net.NetStreamAppendBytesAction;
 import flash.net.URLLoader;
 import flash.net.URLLoaderDataFormat;
 import flash.net.URLRequest;
 import flash.ui.ContextMenu;
 import flash.utils.ByteArray;

 [SWF(width="300", height="300")]
 public class FLVBytePlayer extends Sprite {
  
  public var nc:NetConnection = new NetConnection();
  public var ns:NetStream;
  public var video:Video = new Video();
  
  public var videoWidth:Number = 1;
  public var videoHeight:Number = 1;
  
  public var videoByte:ByteArray;
  
  public var urlLdr:URLLoader = new URLLoader();
  
  public function FLVBytePlayer() {
   
   stage.scaleMode = StageScaleMode.NO_SCALE;
   stage.align = StageAlign.TOP_LEFT;
   stage.addEventListener(Event.RESIZE, onStageResizeHandler);
   
   contextMenu = new ContextMenu();
   contextMenu.hideBuiltInItems();
   
   nc.connect(null);
   ns = new NetStream(nc);
   ns.client = {
    onMetaData: function(info:Object):void{
     trace("onMetaData:");
     for (var i:String in info) {
      trace("\t", i, info[i]);
     }
     videoWidth = info.width;
     videoHeight = info.height;
     onStageResizeHandler();
    }
   };
   ns.addEventListener(NetStatusEvent.NET_STATUS, onNsNetStatusHandler);
   
   video.smoothing = true;
   video.attachNetStream(ns);
   addChild(video);
   
   
   var ldr:Loader = new Loader();
   var req:URLRequest = new URLRequest(ldr.contentLoaderInfo.loaderURL);
   trace(ldr.contentLoaderInfo.loaderURL);
   
   urlLdr.dataFormat = URLLoaderDataFormat.BINARY;
   urlLdr.addEventListener(Event.COMPLETE, onLoadCompleteHandler);
   urlLdr.load(req);
   
   bmp.scaleX = bmp.scaleY = 0.5;
   bmp.alpha = 0.7;
   addChild(bmp);
   stage.addEventListener(MouseEvent.CLICK, drawVideo);
  }
  
  public function onStageResizeHandler(e:Event = null):void{
   var stageRatio:Number = stage.stageWidth / stage.stageHeight;
   var videoRatio:Number = videoWidth / videoHeight;
   if (stageRatio > videoRatio) {
    video.height = stage.stageHeight;
    video.width = stage.stageHeight * videoRatio;
    video.y = 0;
    video.x = (stage.stageWidth - video.width) / 2;
   } else {
    video.width = stage.stageWidth;
    video.height = stage.stageWidth / videoRatio;
    video.x = 0;
    video.y = (stage.stageHeight - video.height) / 2;
   }
  }
  
  public function onNsNetStatusHandler(e:NetStatusEvent):void{
   trace(e.info.code);
   switch (e.info.code) {
    case "NetStream.Buffer.Empty":
     break;
   }
  }
  
  public function onLoadCompleteHandler(e:Event):void{
   var data:ByteArray = urlLdr.data;
   
   for (var i:int = data.length - 1 ; i > 0  ; --i) {
    if (
     data[i + 0] == 70 && /* "F".charCodeAt(0) */
     (data[i + 1] == 76 || data[i + 1] == 52) &&
     /* "L".charCodeAt(0) *//* "4".charCodeAt(0) */
     data[i + 2] == 86   /* "V".charCodeAt(0) */
    ) {
     videoByte = new ByteArray();
     data.position = i;
     data.readBytes(videoByte, 0, 0);
     trace(data.length, i, videoByte.length);
     
     ns.play(null);
     ns.appendBytesAction(NetStreamAppendBytesAction.RESET_SEEK);
     ns.appendBytes(videoByte);
     break;
    }
   }
   
  }
  
  
  // Video 取樣
  public var bmp:Bitmap = new Bitmap();
  
  public function drawVideo(e:Event = null):void{
   var bmpData:BitmapData = new BitmapData(videoWidth, videoHeight, true, 0x0);
   try {
    bmpData.draw(video);
    bmp.bitmapData = bmpData;
   } catch (e:Error) {
    trace(e.getStackTrace());
    return;
   }
  }
 }
}

線上 Demo 範例,舞台上 Mouse Left Click 就會對 Video 做截圖


轉載請註明出處 http://ticore.blogspot.com/2013/01/bitmapdata-draw-rtmp-security-error.html

0 意見 :