Extracting VideoFrames from a 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 o开发者_Python百科bjects (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.
One solution involves creating an AIR application to encode the individual frames of the SWF:
- Load the SWF using a Loader object
- Locate the video instance inside the SWF
- Capture each frame of the video to a BitmapDataObject
- Encode the BitmapDataObject as a PNG or JPEG
- 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:
- Click the File menu
- Select New
- Select Flash File (Adobe AIR)
- Open the Actions panel (available on the Window menu)
- Paste the code into the actions panel
- Change the videoFilename variable to the name of the SWF which you want to extract
- 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);
}
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.
精彩评论