Извлечение видеокадров из SWF

У меня есть SWF, из которого я хочу извлечь видеокадры. Они появляются под этим именем, когда SWF открывается с помощью 7-Zip (VideoFrame). Очевидно, что их извлечение таким образом ничего не даст, поскольку они не представлены ни в одном распознаваемом формате изображения.

Я загрузил SWF-файл во Flash Professional CS5 и могу просматривать все объекты Bitmap (собранные в «UI», с наложением видеоанимации в одном углу) в библиотеке, но нигде не могу найти перечисление видеокадров. или даже найти объект, отображающий их.

Я пропустил что-то действительно очевидное здесь? (Я новичок в разработке Flash, так что это вполне возможно.)

Отказ от ответственности: это не для получения прибыли и не связано с нарушением авторских прав. Его личное упражнение.

РЕДАКТИРОВАТЬ: я НЕ хочу просто экспортировать все кадры SWF, так как есть несколько элементов пользовательского интерфейса, накладывающихся на видео. Я точно знаю, что присутствуют все кадры видео (просто частично прикрытые). Я хочу извлечь кадры встроенного видео, а не кадры SWF.


person Unsigned    schedule 30.08.2011    source источник


Ответы (2)


Одно из решений включает создание приложения AIR для кодирования отдельных кадров SWF:

  1. Загрузите SWF с помощью объекта Loader
  2. Найдите экземпляр видео внутри SWF
  3. Захватите каждый кадр видео в BitmapDataObject
  4. Кодируйте BitmapDataObject как PNG или JPEG
  5. Запишите закодированный кадр в файл

После того, как все кадры будут выведены в виде отдельных изображений, их можно повторно собрать в видеоряд с помощью Flash, FFMPEG или другого программного обеспечения для редактирования видео.

Ниже приведен код, который кодирует первые 20 кадров видео, встроенного в SWF, в виде изображений PNG. Чтобы использовать это, создайте приложение AIR в Flash Pro следующим образом:

  1. Щелкните меню «Файл»
  2. Выберите новый
  3. Выберите файл Flash (Adobe AIR)
  4. Откройте панель «Действия» (доступна в меню «Окно»)
  5. Вставьте код в панель действий
  6. Измените переменную videoFilename на имя SWF, который вы хотите извлечь.
  7. Запустите SWF из Flash Pro или опубликуйте его и запустите как приложение AIR.

Поскольку это для образовательных целей, оно не было оптимизировано, поэтому оно будет работать очень медленно, и ваш компьютер может перестать отвечать на запросы, поэтому он ограничен 20 кадрами. Лучший кодировщик будет обрабатывать кадры асинхронно, чтобы избежать перегрузки ЦП.

Причина, по которой требуется AIR, заключается в том, что он может записывать видеокадры прямо на ваш жесткий диск. Мы не смогли бы сделать это, используя обычную веб-версию Flash player, потому что она предотвращает такие действия из соображений безопасности (вы бы не хотели, чтобы веб-сайт записывал файлы на ваш жесткий диск).

Самая сложная часть операции — кодирование изображения в формат PNG. Это делается с помощью кодера PNG из библиотеки AS3CoreLib с открытым исходным кодом Майка Чемберса. , отсюда и причина уведомления об авторских правах.

ОБНОВЛЕНИЕ: обновлен код для поиска видеообъекта внутри 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);
}
person Luke Van In    schedule 30.08.2011
comment
Я бы порекомендовал хотя бы попытаться не кодировать изображения, а просто вывести их как необработанные 24-битные данные RGB, а затем написать отдельный кодировщик на более быстром языке, например. Питон. Чистое время преобразования может быть намного меньше, чем когда кодирование предоставляется ActionScript. - person richardolsson; 30.08.2011
comment
Полезно, но не то, что я искал, добавил дополнительное примечание к моему вопросу, чтобы (надеюсь) уточнить его. - person Unsigned; 30.08.2011
comment
@Unsigned-Code-Labs Я обновил код, чтобы найти и захватить кадры из первого экземпляра видео. Если вы хотите захватить необработанный видеопоток, вы можете попробовать декомпилятор SWF. Когда у вас есть кадры, их можно повторно собрать в видео с помощью Flash (импорт последовательности изображений) или утилиты командной строки ffmpeg. - person Luke Van In; 31.08.2011
comment
@richardolsson Я думал об этом, и, глядя на код PNG, видно, что на самом деле он просто хранит несжатые данные пикселей. Наибольшая потеря производительности связана с записью файлов на диск, поэтому снижение накладных расходов может окупиться больше. Было бы полезно дельта-кодировать кадры (разницу между текущим кадром и предыдущим) и использовать ByteArray.compress. Это также помогло бы обрабатывать кадры пакетами и записывать в файловый поток. Однако я думаю, что эти оптимизации также усложняют и снижают образовательную ценность примера. - person Luke Van In; 31.08.2011

Рассмотрите возможность использования встроенного механизма экспорта в Flash CS5. Сначала импортируйте свой SWF-файл в Flash CS5 как «скомпилированный мувиклип», а затем просто добавьте его в рабочую область, убедившись, что длина временной шкалы совпадает с продолжительностью SWF-файла. В меню «Файл» > «Экспорт» выберите «Фильм» и выберите последовательность PNG/JPEG в качестве формата файла.

В зависимости от того, полагается ли ваш SWF на код для анимации/поведения или это просто простая анимация временной шкалы, это может дать или не дать ожидаемые результаты.

РЕДАКТИРОВАТЬ: Чтобы избавиться от любых элементов, накладывающихся на видео, попробуйте (снова) импортировать SWF во Flash CS5 или загрузить его во время выполнения и просмотреть список отображения этого SWF. Если вы найдете видео, удалите все дочерние элементы его родителя, кроме самого видео. Это должно избавиться от элементов пользовательского интерфейса и позволить вам использовать описанный выше подход для экспорта кадров.

person richardolsson    schedule 30.08.2011
comment
Спасибо, но не то, что я пытался сделать. Я обновил свой комментарий дополнительным примечанием, чтобы, надеюсь, прояснить ситуацию. - person Unsigned; 30.08.2011
comment
Спасибо за уточнение. Посмотрите на мой обновленный ответ для предложения. - person richardolsson; 30.08.2011