【问题标题】:Extracting VideoFrames from a SWF从 SWF 中提取 VideoFrames
【发布时间】:2011-08-30 13:31:57
【问题描述】:

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

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

我在这里遗漏了一些非常明显的东西吗? (我对 Flash 开发有点陌生,所以很有可能。)

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

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

【问题讨论】:

    标签: video flash flash-cs5 frames


    【解决方案1】:

    一种解决方案是创建一个 AIR 应用程序来对 SWF 的各个帧进行编码:

    1. 使用 Loader 对象加载 SWF
    2. 在 SWF 中找到视频实例
    3. 将视频的每一帧捕获到 BitmapDataObject
    4. 将 BitmapDataObject 编码为 PNG 或 JPEG
    5. 将编码帧写入文件

    所有帧都作为单独的图像输出后,可以使用 Flash、FFMPEG 或其他视频编辑软件将它们重新组合成视频序列。

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

    1. 点击文件菜单
    2. 选择新建
    3. 选择 Flash 文件 (Adobe AIR)
    4. 打开“操作”面板(在“窗口”菜单中可用)
    5. 将代码粘贴到操作面板中
    6. 将 videoFilename 变量更改为要提取的 SWF 的名称
    7. 从 Flash Pro 运行 SWF 或将其发布并作为 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);
    }
    

    【讨论】:

    • 我建议至少尝试不对图像进行编码,而只是将它们作为原始 24 位 RGB 数据输出,然后以更快的语言编写单独的编码器,例如Python。净转换时间可能比编码留给 ActionScript 时快得多。
    • 有帮助,但不是我想要的,在我的问题中添加了一个额外的注释(希望)澄清它。
    • @Unsigned-Code-Labs 我已经更新了代码以从第一个 Video 实例中定位和捕获帧。如果您想捕获原始视频流,您可能需要尝试使用 SWF 反编译器。获得帧后,可以使用 Flash(导入图像序列)或 ffmpeg 命令行实用程序将它们重新组合成视频。
    • @richardolsson 我确实考虑过这一点,并且查看 PNG 代码表明它实际上只是存储了未压缩的像素数据。最大的性能成本是将文件写入磁盘,因此减少开销可能会带来更多回报。这将有助于对帧进行增量编码(当前帧与前一帧之间的差异)并使用 ByteArray.compress。它还有助于批量处理帧,并写入文件流。但是我认为这些优化也会增加复杂性并降低示例的教育价值。
    【解决方案2】:

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

    取决于您的 SWF 是依赖其动画/行为的代码还是只是一个简单的时间线动画,这可能会或可能不会产生您期望的结果。

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

    【讨论】:

    • 谢谢,但不是我想要做的。我已经更新了我的评论,并添加了一条附加说明,希望能澄清一些事情。
    • 感谢您的澄清。请查看我的更新回复以获取建议。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-27
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-29
    相关资源
    最近更新 更多