从SWF提取视频帧
我有一个SWF,我想从中提取视频帧。当使用7-Zip(VideoFrame)打开SWF时,它们出现在这个名称下。显然,以这种方式提取它们并不会产生任何效果,因为它们不具有任何可识别的图像格式。从SWF提取视频帧
我已经将SWF加载到Flash Professional CS5中,并且能够查看库中的所有Bitmap对象(在“UI”中组合,在一个角落覆盖视频动画),但是我找不到枚举任何地方的视频帧,甚至可以找到显示它们的对象。
我在这里错过了一些非常明显的东西吗? (我对Flash开发有点新,所以很有可能。)
声明:这不是为了营利,也不涉及任何侵犯版权的行为。它的个人练习。
编辑:我不想简单地导出整个SWF框架,因为有几个UI元素覆盖视频。我知道整个视频帧都存在(仅部分覆盖)。我想提取嵌入视频的帧,而不是SWF帧。
的一种解决方案涉及创建AIR应用程序来编码的SWF的各个帧:
- 负载使用Loader对象
- 找到SWF内部视频实例
- 捕获的每个帧中的SWF视频到BitmapDataObject
- 编码BitmapDataObject作为PNG或JPEG
- 收件经编码的帧到文件
一旦所有帧都作为单独的图像输出,就可以使用Flash,FFMPEG或其他视频编辑软件将它们重新组合为视频序列。
下面是将SWF文件中前20帧视频嵌入到PNG图像中的代码。要使用此功能,如下创建Flash Pro中的AIR应用程序:
- 单击文件菜单
- 选择新
- 选择Flash文件(Adobe AIR)
- 打开动作面板(上可用窗口菜单)
- 将代码粘贴到相应的操作面板
- 更改videoFilename变量要提取
- 运行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);
}
内考虑使用内置闪光灯CS5导出机制。首先将SWF导入到Flash CS5中作为“编译的MovieClip”,然后将其添加到舞台上,确保时间轴的长度与SWF中的时间轴的长度相匹配。在文件>导出菜单下,选择“电影”,然后选择PNG/JPEG序列作为文件格式。
取决于您的SWF是依靠它的动画/行为的代码还是仅仅是一个简单的时间轴动画,这可能会或可能不会产生您所期望的结果。
编辑:为了摆脱覆盖视频的任何元素,请尝试(再次)将SWF导入到Flash CS5中,或者在运行时加载它,然后通过该SWF的显示列表进行递归。如果您找到视频,请从视频本身中移除所有孩子。这应该摆脱UI元素,并允许您使用上述方法导出帧。
谢谢,但不是我正在尝试做的。我已经用额外的笔记更新了我的评论,希望能够澄清事情。 – Unsigned
谢谢澄清。看看我的更新回应中的建议。 – richardolsson
我建议至少不要尝试对图像进行编码,而只是将它们作为原始的24位RGB数据输出,然后以更快的语言编写单独的编码器,例如,蟒蛇。净转换时间可能比编码留给ActionScript时快得多。 – richardolsson
有帮助,但不是我在寻找的内容,在我的问题中添加了一个附注(希望)可以澄清它。 – Unsigned
@ Unsigned-Code-Labs我已更新代码来定位和捕获来自第一个Video实例的帧。如果你想捕捉原始视频流,你可能想尝试一个SWF反编译器。一旦你有了帧,他们可以使用Flash(导入图像序列)或ffmpeg命令行实用程序重新组装成视频。 –