从SWF中提取VideoFrame

时间:2011-08-30 13:31:58

标签: video flash flash-cs5 frames

我有一个想要从中提取VideoFrame的SWF。当使用7-Zip(VideoFrame)打开SWF时,它们以此名称显示。显然,以这种方式提取它们没有任何结果,因为它们没有任何可识别的图像格式。

我已将SWF加载到Flash Professional CS5中,并且能够在库中查看所有Bitmap对象(在“UI”中组装,在一个角落覆盖视频动画),但是我找不到枚举视频帧在任何地方,甚至找到显示它们的对象。

我错过了一些非常明显的东西吗? (我对Flash开发有些新意,所以很有可能。)

免责声明:这不是为了盈利,也不涉及任何版权侵权。它的个人运动。

编辑:我不想简单地导出SWF的整个帧,因为有几个UI元素覆盖了视频。我知道整个视频帧都存在(只是部分掩盖)。我想提取嵌入视频的帧,而不是SWF帧。

2 个答案:

答案 0 :(得分:6)

一个解决方案涉及创建一个AIR应用程序来编码SWF的各个帧:

  1. 使用Loader对象加载SWF
  2. 在SWF中找到视频实例
  3. 将视频的每个帧捕获到BitmapDataObject
  4. 将BitmapDataObject编码为PNG或JPEG
  5. 将编码的帧写入文件
  6. 一旦所有帧都作为单独的图像输出,就可以使用Flash,FFMPEG或其他视频编辑软件将它们重新组合成视频序列。

    下面是将SWF中嵌入的视频的前20帧编码为PNG图像的代码。要使用此功能,请在Flash Pro中创建AIR应用程序,如下所示:

    1. 单击“文件”菜单
    2. 选择新
    3. 选择Flash文件(Adobe AIR)
    4. 打开“动作”面板(在“窗口”菜单上可用)
    5. 将代码粘贴到操作面板
    6. 将videoFilename变量更改为要提取的SWF的名称
    7. 从Flash Pro运行SWF或将其发布并作为AIR应用程序运行
    8. 由于这是出于教育目的,因此尚未进行优化,因此运行速度非常慢,而您的计算机可能会无响应,这就是限制为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);
      }
      

答案 1 :(得分:2)

考虑在Flash CS5中使用内置导出机制。首先将SWF作为“已编译的MovieClip”导入Flash CS5,然后将其添加到舞台,确保时间轴的长度与SWF中的长度相匹配。在文件&gt;下导出菜单,选择“电影”并选择PNG / JPEG序列作为文件格式。

根据您的SWF是依赖于代码的动画/行为,还是仅仅是一个简单的时间轴动画,这可能会或可能不会产生您期望的结果。

编辑:为了摆脱覆盖视频的任何元素,尝试(再次)将SWF导入Flash CS5,或在运行时加载它,并通过该SWF的显示列表进行递归。如果您找到该视频,请从其父级中删除所有子项,但视频本身除外。这应该摆脱UI元素,并允许您使用上述方法导出帧。