2011-08-30 204 views
1

我有一個SWF,我想從中提取視頻幀。當使用7-Zip(VideoFrame)打開SWF時,它們出現在這個名稱下。顯然,以這種方式提取它們並不會產生任何效果,因爲它們不具有任何可識別的圖像格式。從SWF提取視頻幀

我已經將SWF加載到Flash Professional CS5中,並且能夠查看庫中的所有Bitmap對象(在「UI」中組合,在一個角落覆蓋視頻動畫),但是我找不到枚舉任何地方的視頻幀,甚至可以找到顯示它們的對象。

我在這裏錯過了一些非常明顯的東西嗎? (我對Flash開發有點新,所以很有可能。)

聲明:這不是爲了營利,也不涉及任何侵犯版權的行爲。它的個人練習。

編輯:我不想簡單地導出整個SWF框架,因爲有幾個UI元素覆蓋視頻。我知道整個視頻幀都存在(僅部分覆蓋)。我想提取嵌入視頻的幀,而不是SWF幀。

回答

6

的一種解決方案涉及創建AIR應用程序來編碼的SWF的各個幀:

  1. 負載使用Loader對象
  2. 找到SWF內部視頻實例
  3. 捕獲的每個幀中的SWF視頻到BitmapDataObject
  4. 編碼BitmapDataObject作爲PNG或JPEG
  5. 收件經編碼的幀到文件

一旦所有幀都作爲單獨的圖像輸出,就可以使用Flash,FFMPEG或其他視頻編輯軟件將它們重新組合爲視頻序列。

下面是將SWF文件中前20幀視頻嵌入到PNG圖像中的代碼。要使用此功能,如下創建Flash Pro中的AIR應用程序:

  1. 單擊文件菜單
  2. 選擇新
  3. 選擇Flash文件(Adobe AIR)
  4. 打開動作面板(上可用窗口菜單)
  5. 將代碼粘貼到相應的操作面板
  6. 更改videoFilename變量要提取
  7. 運行SWF的SWF的名稱從Flash Pro或發佈並運行它作爲一個AIR應用程序

由於這是它並沒有被優化的教育目的,因此它將運行非常緩慢,您的計算機可能會變得沒有反應這就是爲什麼它是限制在20幀。一個更好的編碼器會異步處理幀,以避免CPU過載。

AIR需要的原因是它能夠將視頻幀直接寫入硬盤。我們無法使用Flash播放器的普通網絡版本來執行此操作,因爲它出於安全原因(您不希望網站將文件寫入您的硬盤驅動器)阻止此類操作。

該操作最複雜的部分是將圖像編碼爲PNG格式。這是通過使用PNG encoder from the open-source AS3CoreLib by Mike Chambers完成的,因此是版權聲明的原因。

更新:更新的代碼來查找視頻對象的SWF

// location to load the SWF file which you want to capture 
var videoFilename:String = "Untitled-1.swf"; 

// initialise the frame counter 
// the frame counter is used to number the individual output images 
var frameCount:int = 0; 

// create a folder to store the output files 
// this creates a folder on the desktop called "video_frames" 
var path:File = File.desktopDirectory.resolvePath("video_frames"); 
path.createDirectory(); 

var bitmapData:BitmapData; 
var bitmap:Bitmap; 

// create a loader to load the SWF file 
// when the SWF file is loaded we start capturing the frames 
var loader:Loader = new Loader(); 
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderCompleteHandler); 
loader.load(new URLRequest(videoFilename)); 

var video:Video; 

// this is called when the SWF is loaded and we can start capturing frames  
function loaderCompleteHandler(event:Event):void 
{ 
    // find the video in the loaded SWF 
    findVideo(loader.content); 

    if (video == null) 
     throw new Error("cannot find video"); 

    // create a bitmap to capture the frames into 
    // the bitmap is later encoded into PNG format and written to a file 
    bitmapData = new BitmapData(video.width, video.height, false, 0xFFFF00FF); 
    bitmap = new Bitmap(bitmapData, PixelSnapping.ALWAYS, false); 
    addChild(bitmap); 

    addEventListener(Event.ENTER_FRAME, frameHandler); 
} 

function findVideo(input:DisplayObject):void 
{ 
    if (!(input is DisplayObjectContainer)) 
     return; 

    var container:DisplayObjectContainer = input as DisplayObjectContainer; 

    for (var i:int = 0; i < container.numChildren; i++) { 
     var child:DisplayObject = container.getChildAt(i); 
     if (child is Video) { 
      video = child as Video; 
        return; 
      } 
     else { 
      findVideo(child); 
      } 
    } 
} 

function frameHandler(event:Event):void { 

    // count the individual frames and stop capturing after 20 frames 
    frameCount ++; 
    if (frameCount > 20) { 
     removeEventListener(Event.ENTER_FRAME, frameHandler); 
     return; 
    } 

    // capture the current frame of the SWF to the bitmap 
    // this grabs the pixels into a usable for for encoding 
    bitmapData.draw(video); 

    // encode bitmap into PNG format 
    // you can also easily use JPEG format from AS3CoreLib 
    var data:ByteArray = encode(bitmapData); 

    // write the PNG image to a file 
    // this is the most time-consuming action 
    var file:File = path.resolvePath("frame" + frameCount + ".png"); 
    var stream:FileStream = new FileStream(); 
    stream.open(file, FileMode.WRITE); 
    stream.writeBytes(data); 
    stream.close(); 
} 

/* 
    Copyright (c) 2008, Adobe Systems Incorporated 
    All rights reserved. 

    Redistribution and use in source and binary forms, with or without 
    modification, are permitted provided that the following conditions are 
    met: 

    * Redistributions of source code must retain the above copyright notice, 
    this list of conditions and the following disclaimer. 

    * Redistributions in binary form must reproduce the above copyright 
    notice, this list of conditions and the following disclaimer in the 
    documentation and/or other materials provided with the distribution. 

    * Neither the name of Adobe Systems Incorporated nor the names of its 
    contributors may be used to endorse or promote products derived from 
    this software without specific prior written permission. 

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
    IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
    THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
    PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
*/ 
    import flash.geom.*; 
    import flash.display.Bitmap; 
    import flash.display.BitmapData; 
    import flash.utils.ByteArray; 

/** 
* Created a PNG image from the specified BitmapData 
* 
* @param image The BitmapData that will be converted into the PNG format. 
* @return a ByteArray representing the PNG encoded image data. 
* @langversion ActionScript 3.0 
* @playerversion Flash 9.0 
* @tiptext 
*/   
function encode(img:BitmapData):ByteArray { 
    // Create output byte array 
    var png:ByteArray = new ByteArray(); 
    // Write PNG signature 
    png.writeUnsignedInt(0x89504e47); 
    png.writeUnsignedInt(0x0D0A1A0A); 
    // Build IHDR chunk 
    var IHDR:ByteArray = new ByteArray(); 
    IHDR.writeInt(img.width); 
    IHDR.writeInt(img.height); 
    IHDR.writeUnsignedInt(0x08060000); // 32bit RGBA 
    IHDR.writeByte(0); 
    writeChunk(png,0x49484452,IHDR); 
    // Build IDAT chunk 
    var IDAT:ByteArray= new ByteArray(); 
    for(var i:int=0;i < img.height;i++) { 
     // no filter 
     IDAT.writeByte(0); 
     var p:uint; 
     var j:int; 
     if (!img.transparent) { 
      for(j=0;j < img.width;j++) { 
       p = img.getPixel(j,i); 
       IDAT.writeUnsignedInt(
        uint(((p&0xFFFFFF) << 8)|0xFF)); 
      } 
     } else { 
      for(j=0;j < img.width;j++) { 
       p = img.getPixel32(j,i); 
       IDAT.writeUnsignedInt(
        uint(((p&0xFFFFFF) << 8)| 
        (p>>>24))); 
      } 
     } 
    } 
    IDAT.compress(); 
    writeChunk(png,0x49444154,IDAT); 
    // Build IEND chunk 
    writeChunk(png,0x49454E44,null); 
    // return PNG 
    return png; 
} 

var crcTable:Array; 
var crcTableComputed:Boolean = false; 

function writeChunk(png:ByteArray, 
     type:uint, data:ByteArray):void { 
    if (!crcTableComputed) { 
     crcTableComputed = true; 
     crcTable = []; 
     var c:uint; 
     for (var n:uint = 0; n < 256; n++) { 
      c = n; 
      for (var k:uint = 0; k < 8; k++) { 
       if (c & 1) { 
        c = uint(uint(0xedb88320)^
         uint(c >>> 1)); 
       } else { 
        c = uint(c >>> 1); 
       } 
      } 
      crcTable[n] = c; 
     } 
    } 
    var len:uint = 0; 
    if (data != null) { 
     len = data.length; 
    } 
    png.writeUnsignedInt(len); 
    var p:uint = png.position; 
    png.writeUnsignedInt(type); 
    if (data != null) { 
     png.writeBytes(data); 
    } 
    var e:uint = png.position; 
    png.position = p; 
    c = 0xffffffff; 
    for (var i:int = 0; i < (e-p); i++) { 
     c = uint(crcTable[ 
      (c^png.readUnsignedByte()) & 
      uint(0xff)]^uint(c >>> 8)); 
    } 
    c = uint(c^uint(0xffffffff)); 
    png.position = e; 
    png.writeUnsignedInt(c); 
} 
+0

我建議至少不要嘗試對圖像進行編碼,而只是將它們作爲原始的24位RGB數據輸出,然後以更快的語言編寫單獨的編碼器,例如,蟒蛇。淨轉換時間可能比編碼留給ActionScript時快得多。 – richardolsson

+0

有幫助,但不是我在尋找的內容,在我的問題中添加了一個附註(希望)可以澄清它。 – Unsigned

+0

@ Unsigned-Code-Labs我已更新代碼來定位和捕獲來自第一個Video實例的幀。如果你想捕捉原始視頻流,你可能想嘗試一個SWF反編譯器。一旦你有了幀,他們可以使用Flash(導入圖像序列)或ffmpeg命令行實用程序重新組裝成視頻。 –

2

內考慮使用內置閃光燈CS5導出機制。首先將SWF導入到Flash CS5中作爲「編譯的MovieClip」,然後將其添加到舞臺上,確保時間軸的長度與SWF中的時間軸的長度相匹配。在文件>導出菜單下,選擇「電影」,然後選擇PNG/JPEG序列作爲文件格式。

取決於您的SWF是依靠它的動畫/行爲的代碼還是僅僅是一個簡單的時間軸動畫,這可能會或可能不會產生您所期望的結果。

編輯:爲了擺脫覆蓋視頻的任何元素,請嘗試(再次)將SWF導入到Flash CS5中,或者在運行時加載它,然後通過該SWF的顯示列表進行遞歸。如果您找到視頻,請從視頻本身中移除所有孩子。這應該擺脫UI元素,並允許您使用上述方法導出幀。

+0

謝謝,但不是我正在嘗試做的。我已經用額外的筆記更新了我的評論,希望能夠澄清事情。 – Unsigned

+0

謝謝澄清。看看我的更新迴應中的建議。 – richardolsson