从 SWF 中提取视频帧

发布于 2024-12-02 06:07:45 字数 404 浏览 1 评论 0原文

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

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

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

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

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

I have a SWF that I want to extract VideoFrames from. They appear under this name when the SWF is opened with 7-Zip (VideoFrame). Obviously, extracting them in that manner accomplishes nothing, as they are not in any recognizable image format.

I have loaded the SWF into Flash Professional CS5, and am able to view all the Bitmap objects (assembled in a "UI", overlaying the video animation at one corner) in the Library, but I cannot find an enumeration of the video frames anywhere, or even find the object displaying them.

Am I missing something really obvious here? (I am somewhat new to Flash development, so its very possible.)

Disclaimer: This isn't for profit nor is it involved in any copyright infringement. Its personal exercise.

EDIT: I do NOT want to simply export the entire frames of the SWF, as there are several UI elements overlaying the video. I know for a fact that the entire video frames are present (just partly covered up). I want to extract the frames of the embedded video, not the SWF frames.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

今天小雨转甜 2024-12-09 06:07:45

一种解决方案涉及创建 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格式。这是使用 来自 Mike Chambers 的开源 AS3CoreLib 的 PNG 编码器 完成的,这就是版权声明的原因。

更新:更新了代码以在 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);
}

One solution involves creating an AIR application to encode the individual frames of the SWF:

  1. Load the SWF using a Loader object
  2. Locate the video instance inside the SWF
  3. Capture each frame of the video to a BitmapDataObject
  4. Encode the BitmapDataObject as a PNG or JPEG
  5. Write the encoded frame to a file

Once all the frames have been output as separate images, they can be re-assembled into a video sequence using Flash, FFMPEG or other video editing software.

Below is the code which will encode the first 20 frames of a Video embedded in a SWF as PNG images. To use this, create an AIR application in Flash Pro as follows:

  1. Click the File menu
  2. Select New
  3. Select Flash File (Adobe AIR)
  4. Open the Actions panel (available on the Window menu)
  5. Paste the code into the actions panel
  6. Change the videoFilename variable to the name of the SWF which you want to extract
  7. Run the SWF from Flash Pro or publish it and run it as an AIR application

Since this is for educational purposes it hasn't been optimised, and so it will run very slowly and your computer may become unresponsive which is why it is limited to 20 frames. A better encoder would process the frames asynchronously to avoid overloading the CPU.

The reason AIR is required is that it has the ability to write the video frames directly to your hard-drive. We wouldn't be able to do this using the normal web version of the Flash player because it it prevents such actions for security reasons (you wouldn't want a web site writing files to your hard-drive).

The most complicated part of the operation is encoding the image into PNG format. This is done using the PNG encoder from the open-source AS3CoreLib by Mike Chambers, hence the reason for the copyright notice.

UPDATE: updated code to locate the video object inside the 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);
}
予囚 2024-12-09 06:07:45

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

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

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

Consider using the built-in export mechanism in Flash CS5. First import your SWF into Flash CS5 as a "compiled MovieClip" and then just add it to stage, making sure that the length of your timeline matches the one in the SWF. Under the File > Export menu, select "Movie" and choose PNG/JPEG sequence as the file format.

Depending on whether your SWF relies on code for it's animation/behavior or is just a simple timeline animation this may or may not yield the results you would expect.

EDIT: In order to get rid of any elements overlaying the video, try to (again) import the SWF into Flash CS5, or load it in at runtime, and recurse through the display list of that SWF. If you find the video, remove all children from it's parent, except the video itself. That should get rid of the UI elements and allow you to use the above approach to export the frames.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文