Как преобразовать растровые изображения в видео?

Мое приложение создает изображения из фракталов, и мне нравится ощущение "летания" вокруг фрактала. Я однажды сохранил около 2000 битмапов для файла и создал AVI с помощью Premiere. Этот опыт довольно расстраивает, хотя мне удалось создать фильм. Это было впечатляюще. Конечно, я хочу создать видео из моего приложения. На самом деле меня не волнует превосходство кодека, сжатие или что-то еще. Я просто хочу иметь видео, которое можно воспроизвести на большинстве систем.

В прошлом я пробовал это, но так и не смог. Я недавно был вызван question, но, увы, не смог запустить FFMpeg.

Обновление

Я решил немного подправить вопрос и поставить за него щедрость. Я видел несколько решений, но наиболее привлекательным (потому что это просто) кажется мне TAviWrite. Я попробовал TAviWriter, но не смог. В процедуре TAviWriter.Write; вызов функции вокруг строки 370

  AVIERR := AVISaveV(s,
//                   pchar(FileName),
               nil,                    // File handler
               nil,                    // Callback
               nStreams,               // Number of streams
               Streams,
               CompOptions);           // Compress options for VideoStream

не возвращает AVIERR_OK.

Обновление 2

Причиной вышеупомянутой ошибки является неправильное объявление AVISaveV, оно должно быть объявлено как AVISaveVW по указанию TLama. Ниже приведен правильный код для создания файлов AVI из файлов BMP. Исходный код ниже был загружен с efg с помощью примерного блока.

Использование Delphi XE в Windows 7.

unit AviWriter;

/////////////////////////////////////////////////////////////////////////////
//                                                                         //
//       AviWriter -- a component to create rudimentary AVI files          //
//                  by Elliott Shevin, with large pieces of code           //
//                  stolen from Anders Melander                            //
//       version 1.0. Please send comments, suggestions, and advice        //
//       to [email protected]                                               //
/////////////////////////////////////////////////////////////////////////////

interface

uses
  Windows,Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ole2;

////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//                      Video for Windows                                     //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//                                                                            //
// Adapted from Thomas Schimming VFW.PAS                                    //
// (c) 1996 Thomas Schimming, [email protected]                  //
// (c) 1998,99 Anders Melander                                                //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//                                                                            //
// Ripped all COM/ActiveX stuff and added some AVI stream functions.          //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
// Unicode version created by Arnold and TLama (2012)                         //
////////////////////////////////////////////////////////////////////////////////

type
  LONG = Longint;
  PVOID = Pointer;

const
// TAVIFileInfo dwFlag values
   AVIF_HASINDEX          = $00000010;
   AVIF_MUSTUSEINDEX      = $00000020;
   AVIF_ISINTERLEAVED     = $00000100;
   AVIF_WASCAPTUREFILE    = $00010000;
   AVIF_COPYRIGHTED       = $00020000;
   AVIF_KNOWN_FLAGS       = $00030130;

   AVIERR_UNSUPPORTED     = $80044065; // MAKE_AVIERR(101)
   AVIERR_BADFORMAT       = $80044066; // MAKE_AVIERR(102)
   AVIERR_MEMORY          = $80044067; // MAKE_AVIERR(103)
   AVIERR_INTERNAL        = $80044068; // MAKE_AVIERR(104)
   AVIERR_BADFLAGS        = $80044069; // MAKE_AVIERR(105)
   AVIERR_BADPARAM        = $8004406A; // MAKE_AVIERR(106)
   AVIERR_BADSIZE         = $8004406B; // MAKE_AVIERR(107)
   AVIERR_BADHANDLE       = $8004406C; // MAKE_AVIERR(108)
   AVIERR_FILEREAD        = $8004406D; // MAKE_AVIERR(109)
   AVIERR_FILEWRITE       = $8004406E; // MAKE_AVIERR(110)
   AVIERR_FILEOPEN        = $8004406F; // MAKE_AVIERR(111)
   AVIERR_COMPRESSOR      = $80044070; // MAKE_AVIERR(112)
   AVIERR_NOCOMPRESSOR    = $80044071; // MAKE_AVIERR(113)
   AVIERR_READONLY        = $80044072; // MAKE_AVIERR(114)
   AVIERR_NODATA          = $80044073; // MAKE_AVIERR(115)
   AVIERR_BUFFERTOOSMALL  = $80044074; // MAKE_AVIERR(116)
   AVIERR_CANTCOMPRESS    = $80044075; // MAKE_AVIERR(117)
   AVIERR_USERABORT       = $800440C6; // MAKE_AVIERR(198)
   AVIERR_ERROR           = $800440C7; // MAKE_AVIERR(199)

// TAVIStreamInfo dwFlag values
   AVISF_DISABLED         = $00000001;
   AVISF_VIDEO_PALCHANGES = $00010000;
   AVISF_KNOWN_FLAGS      = $00010001;

type
  TAVIFileInfoW = record
    dwMaxBytesPerSec,// max. transfer rate
    dwFlags,         // the ever-present flags
    dwCaps,
    dwStreams,
    dwSuggestedBufferSize,

    dwWidth,
    dwHeight,

    dwScale,
    dwRate, // dwRate / dwScale == samples/second
    dwLength,

    dwEditCount: DWORD;

    szFileType: array[0..63] of WideChar; // descriptive string for file type?
  end;
  PAVIFileInfoW = ^TAVIFileInfoW;

  TAVIStreamInfoW = record
    fccType,
    fccHandler,
    dwFlags,        // Contains AVITF_* flags
    dwCaps: DWORD;
    wPriority,
    wLanguage: WORD;
    dwScale,
    dwRate, // dwRate / dwScale == samples/second
    dwStart,
    dwLength, // In units above...
    dwInitialFrames,
    dwSuggestedBufferSize,
    dwQuality,
    dwSampleSize: DWORD;
    rcFrame: TRect;
    dwEditCount,
    dwFormatChangeCount: DWORD;
    szName:  array[0..63] of WideChar;
  end;
  TAVIStreamInfo = TAVIStreamInfoW;
  PAVIStreamInfo = ^TAVIStreamInfo;

  PAVIStream = pointer;
  PAVIFile   = pointer;
  TAVIStreamList = array[0..0] of PAVIStream;
  PAVIStreamList = ^TAVIStreamList;
  TAVISaveCallback = function (nPercent: integer): LONG; stdcall;

  TAVICompressOptions = packed record
    fccType     : DWORD;
    fccHandler      : DWORD;
    dwKeyFrameEvery : DWORD;
    dwQuality       : DWORD;
    dwBytesPerSecond    : DWORD;
    dwFlags     : DWORD;
    lpFormat        : pointer;
    cbFormat        : DWORD;
    lpParms     : pointer;
    cbParms     : DWORD;
    dwInterleaveEvery   : DWORD;
  end;
  PAVICompressOptions = ^TAVICompressOptions;

// Palette change data record
const
  RIFF_PaletteChange: DWORD = 1668293411;

type
  TAVIPalChange = packed record
    bFirstEntry     : byte;
    bNumEntries     : byte;
    wFlags      : WORD;
    peNew       : array[byte] of TPaletteEntry;
  end;
  PAVIPalChange = ^TAVIPalChange;

  APAVISTREAM          = array[0..1] of PAVISTREAM;
  APAVICompressOptions = array[0..1] of PAVICompressOptions;


procedure AVIFileInit; stdcall;
procedure AVIFileExit; stdcall;
function AVIFileOpen(var ppfile: PAVIFile; szFile: PChar; uMode: UINT; lpHandler: pointer): HResult; stdcall;
function AVIFileCreateStream(pfile: PAVIFile; var ppavi: PAVISTREAM; var psi: TAVIStreamInfo): HResult; stdcall;
function AVIStreamSetFormat(pavi: PAVIStream; lPos: LONG; lpFormat: pointer; cbFormat: LONG): HResult; stdcall;
function AVIStreamReadFormat(pavi: PAVIStream; lPos: LONG; lpFormat: pointer; var cbFormat: LONG): HResult; stdcall;
function AVIStreamWrite(pavi: PAVIStream; lStart, lSamples: LONG; lpBuffer: pointer; cbBuffer: LONG; dwFlags: DWORD; var plSampWritten: LONG; var plBytesWritten: LONG): HResult; stdcall;
function AVIStreamRelease(pavi: PAVISTREAM): ULONG; stdcall;
function AVIFileRelease(pfile: PAVIFile): ULONG; stdcall;
function AVIFileGetStream(pfile: PAVIFile; var ppavi: PAVISTREAM; fccType: DWORD; lParam: LONG): HResult; stdcall;
function CreateEditableStream(var ppsEditable: PAVISTREAM; psSource: PAVISTREAM): HResult; stdcall;
function AVISaveV(szFile: PChar; pclsidHandler: PCLSID; lpfnCallback: TAVISaveCallback;
  nStreams: integer; pavi: APAVISTREAM; lpOptions: APAVICompressOptions): HResult; stdcall;

const
  AVIERR_OK       = 0;

  AVIIF_LIST      = $01;
  AVIIF_TWOCC     = $02;
  AVIIF_KEYFRAME  = $10;

  streamtypeVIDEO = $73646976; // DWORD( 'v', 'i', 'd', 's' )
  streamtypeAUDIO = $73647561; // DWORD( 'a', 'u', 'd', 's' )


type
  TPixelFormat = (pfDevice, pf1bit, pf4bit, pf8bit, pf15bit, pf16bit, pf24bit, pf32bit, pfCustom);

type
  TAviWriter = class (TComponent)
  private
    TempFileName   : string;
    pFile          : PAVIFile;
    fHeight        : integer;
    fWidth         : integer;
    fStretch       : boolean;
    fFrameTime     : integer;
    fFileName      : string;
    fWavFileName   : string;
    VideoStream    : PAVISTREAM;
    AudioStream    : PAVISTREAM;

    procedure AddVideo;
    procedure AddAudio;
    procedure InternalGetDIBSizes(Bitmap: HBITMAP; var InfoHeaderSize: Integer;
        var ImageSize: longInt; PixelFormat: TPixelFormat);
    function InternalGetDIB(Bitmap: HBITMAP; Palette: HPALETTE;
        var BitmapInfo; var Bits; PixelFormat: TPixelFormat): Boolean;
    procedure InitializeBitmapInfoHeader(Bitmap: HBITMAP; var Info: TBitmapInfoHeader;
           PixelFormat: TPixelFormat);
    procedure SetWavFileName(value : string);

  public
    Bitmaps : TList;
    constructor Create(AOwner : TComponent); override;
    destructor  Destroy; override;
    procedure Write;

  published
    property Height   : integer read fHeight  write fHeight;
    property Width    : integer read fWidth   write fWidth;
    property FrameTime: integer read fFrameTime write fFrameTime;
    property Stretch  : boolean read fStretch write fStretch;
    property FileName : string  read fFileName write fFileName;
    property WavFileName  : string  read fWavFileName write SetWavFileName;
  end;

procedure Register;

implementation

procedure AVIFileInit; stdcall; external 'avifil32.dll' name 'AVIFileInit';
procedure AVIFileExit; stdcall; external 'avifil32.dll' name 'AVIFileExit';
function AVIFileOpen; external 'avifil32.dll' name 'AVIFileOpenW';
function AVIFileCreateStream; external 'avifil32.dll' name 'AVIFileCreateStreamW';
function AVIStreamSetFormat; external 'avifil32.dll' name 'AVIStreamSetFormat';
function AVIStreamReadFormat; external 'avifil32.dll' name 'AVIStreamReadFormat';
function AVIStreamWrite; external 'avifil32.dll' name 'AVIStreamWrite';
function AVIStreamRelease; external 'avifil32.dll' name 'AVIStreamRelease';
function AVIFileRelease; external 'avifil32.dll' name 'AVIFileRelease';
function AVIFileGetStream; external 'avifil32.dll' name 'AVIFileGetStream';
function CreateEditableStream; external 'avifil32.dll' name 'CreateEditableStream';
function AVISaveV; external 'avifil32.dll' name 'AVISaveVW';

constructor TAviWriter.Create(AOwner : TComponent);
begin
    inherited Create(AOwner);
    fHeight    := screen.height div 10;
    fWidth     := screen.width  div 10;
    fFrameTime := 1000;
    fStretch   := true;
    fFileName  := '';
    Bitmaps    := TList.create;
    AVIFileInit;
    TempFileName := {tempdir +} 'temp.avi';
end;

destructor TAviWriter.Destroy;
begin
    Bitmaps.free;
    AviFileExit;
    inherited;
end;

procedure TAviWriter.Write;
var
  ExtBitmap             : TBitmap;
  nstreams              : integer;
  i                     : integer;
  Streams               : APAVISTREAM;
  CompOptions           : APAVICompressOptions;
  AVIERR                : integer;
  refcount              : integer;
begin
   AudioStream := nil;
   VideoStream := nil;

   // If no bitmaps are on the list, raise an error.
   if Bitmaps.count < 1
      then raise Exception.Create('No bitmaps on the Bitmaps list');

   // If anything on the Bitmaps TList is not a bitmap, raise
   // an error.
   for i := 0 to Bitmaps.count - 1 do
   begin
      ExtBitmap := Bitmaps[i];
      if not(ExtBitmap is TBitmap)
         then raise Exception.Create('Bitmaps[' + inttostr(i)
                       + '] is not a TBitmap');
   end; // for

   try
      AddVideo;

      if WavFileName <> ''
         then AddAudio;

      // Create the output file.
      if WavFileName <> ''
         then nstreams := 2
         else nstreams := 1;

      Streams[0] := VideoStream;
      Streams[1] := AudioStream;
      CompOptions[0] := nil;
      CompOptions[1] := nil;

      AVIERR := AVISaveV(
                   pchar(FileName),
                   nil,                    // File handler
                   nil,                    // Callback
                   nStreams,               // Number of streams
                   Streams,
                   CompOptions);           // Compress options for VideoStream
      if AVIERR <> AVIERR_OK then
             raise Exception.Create('Unable to write output file');
   finally
      if assigned(VideoStream)
         then AviStreamRelease(VideoStream);
      if assigned(AudioStream)
         then AviStreamRelease(AudioStream);

      try
         repeat
            refcount := AviFileRelease(pFile);
         until refcount <= 0;
      except
         // ignore exception
      end; // try..except

      DeleteFile(TempFileName);
   end; // try..finally
end;

procedure TAviWriter.AddVideo;
var
  Pstream:         PAVISTREAM;
  StreamInfo:      TAVIStreamInfo;
  BitmapInfo:      PBitmapInfoHeader;
  BitmapInfoSize:  Integer;
  BitmapSize:      longInt;
  BitmapBits:      pointer;
  Bitmap:          TBitmap;
  ExtBitmap:       TBitmap;
  Samples_Written: LONG;
  Bytes_Written:   LONG;
  AVIERR:          integer;
  i:               integer;
  ok: Int64;
  mode: uInt32;
  fn: pChar;
  err: string;
begin
    // Open AVI file for write
    pfile := nil;
    mode  := OF_CREATE or OF_WRITE or OF_SHARE_EXCLUSIVE;
    fn    := pchar (TempFileName);

    ok := AVIFileOpen (pFile, fn, mode, nil);
    if ok = AVIERR_BADFORMAT    then err := 'The file could not be read, indicating a corrupt file or an unrecognized format.';
    if ok = AVIERR_MEMORY       then err := 'The file could not be opened because of insufficient memory.';
    if ok = AVIERR_FILEREAD     then err := 'A disk error occurred while reading the file.';
    if ok = AVIERR_FILEOPEN     then err := 'A disk error occurred while opening the file.';
    if ok = REGDB_E_CLASSNOTREG then err := 'According to the registry, the type of file specified in AVIFileOpen does not have a handler to process it.';
    if err <> '' then raise Exception.Create (err);

    // Allocate the bitmap to which the bitmaps on the Bitmaps Tlist
    // will be copied.
    Bitmap        := TBitmap.create;
    Bitmap.Height := self.Height;
    Bitmap.Width  := self.Width;

    // Write the stream header.
    try
       FillChar (StreamInfo, sizeof (StreamInfo), 0);

       // Set frame rate and scale
       StreamInfo.dwRate  := 1000;
       StreamInfo.dwScale := fFrameTime;
       StreamInfo.fccType := streamtypeVIDEO;
       StreamInfo.fccHandler := 0;
       StreamInfo.dwFlags := 0;
       StreamInfo.dwSuggestedBufferSize := 0;
       StreamInfo.rcFrame.Right  := self.width;
       StreamInfo.rcFrame.Bottom := self.height;

       // Open AVI data stream
       if (AVIFileCreateStream(pFile, pStream, StreamInfo) <> AVIERR_OK) then
           raise Exception.Create('Failed to create AVI video stream');

       try
          // Write the bitmaps to the stream.
          for i := 0 to Bitmaps.count - 1 do
          begin
             BitmapInfo := nil;
             BitmapBits := nil;
             try

               // Copy the bitmap from the list to the AVI bitmap,
               // stretching if desired. If the caller elects not to
               // stretch, use the first pixel in the bitmap as a
               // background color in case either the height or
               // width of the source is smaller than the output.
               // If Draw fails, do a StretchDraw.
               ExtBitmap := Bitmaps[i];
               if fStretch
                  then Bitmap.Canvas.StretchDraw
                           (Rect(0,0,self.width,self.height),ExtBitmap)
                  else try
                         with Bitmap.Canvas do begin
                            Brush.Color := ExtBitmap.Canvas.Pixels[0,0];
                            Brush.Style := bsSolid;
                            FillRect(Rect(0,0,Bitmap.Width,Bitmap.Height));
                            Draw(0,0,ExtBitmap);
                         end;
                       except
                         Bitmap.Canvas.StretchDraw
                            (Rect(0,0,self.width,self.height),ExtBitmap);
                       end;

               // Determine size of DIB
               InternalGetDIBSizes(Bitmap.Handle, BitmapInfoSize, BitmapSize, pf8bit);
               if (BitmapInfoSize = 0) then
                  raise Exception.Create('Failed to retrieve bitmap info');

               // Get DIB header and pixel buffers
               GetMem(BitmapInfo, BitmapInfoSize);
               GetMem(BitmapBits, BitmapSize);
               InternalGetDIB
                     (Bitmap.Handle, 0, BitmapInfo^, BitmapBits^, pf8bit);

               // On the first time through, set the stream format.
               if i = 0 then
                  if (AVIStreamSetFormat(pStream, 0, BitmapInfo, BitmapInfoSize) <> AVIERR_OK) then
                      raise Exception.Create('Failed to set AVI stream format');

               // Write frame to the video stream
               AVIERR :=
                  AVIStreamWrite(pStream, i, 1, BitmapBits, BitmapSize, AVIIF_KEYFRAME,
                             Samples_Written, Bytes_Written);
               if AVIERR <> AVIERR_OK then
                    raise Exception.Create
                            ('Failed to add frame to AVI. Err='
                               + inttohex(AVIERR,8));
             finally
               if (BitmapInfo <> nil) then
                 FreeMem(BitmapInfo);
               if (BitmapBits <> nil) then
                 FreeMem(BitmapBits);
             end;
          end;

          // Create the editable VideoStream from pStream.
          if CreateEditableStream(VideoStream,pStream) <> AVIERR_OK then
                    raise Exception.Create
                            ('Could not create Video Stream');
       finally
          AviStreamRelease(pStream);
       end;

    finally
      Bitmap.free;
    end;
end;

procedure TAviWriter.AddAudio;
var
   InputFile    : PAVIFILE;
   InputStream  : PAVIStream;
   err: string;
   ok: Int64;
begin
   // Open the audio file.
    ok := AVIFileOpen(InputFile, pchar(WavFileName),OF_READ, nil);
    if ok = AVIERR_BADFORMAT    then err := 'The file could not be read, indicating a corrupt file or an unrecognized format.';
    if ok = AVIERR_MEMORY       then err := 'The file could not be opened because of insufficient memory.';
    if ok = AVIERR_FILEREAD     then err := 'A disk error occurred while reading the file.';
    if ok = AVIERR_FILEOPEN     then err := 'A disk error occurred while opening the file.';
    if ok = REGDB_E_CLASSNOTREG then err := 'According to the registry, the type of file specified in AVIFileOpen does not have a handler to process it.';
    if err <> '' then raise Exception.Create (err);

   // Open the audio stream.
   try
     if (AVIFileGetStream(InputFile, InputStream, 0, 0) <> AVIERR_OK) then
         raise Exception.Create('Unable to get audio stream');

     try
       // Create AudioStream as a copy of InputStream
       if (CreateEditableStream(AudioStream,InputStream) <> AVIERR_OK) then
            raise Exception.Create('Failed to create editable AVI audio stream');
     finally
       AviStreamRelease(InputStream);
     end;

   finally
     AviFileRelease(InputFile);
   end;
end;

// --------------
// InternalGetDIB
// --------------
// Converts a bitmap to a DIB of a specified PixelFormat.
//
// Note: The InternalGetDIBSizes function can be used to calculate the
// nescessary sizes of the BitmapInfo and Bits buffers.
//
// From graphics.pas, "optimized" for our use

function TAviWriter.InternalGetDIB
(
   Bitmap: HBITMAP;   // The handle of the source bitmap
   Palette: HPALETTE; // The handle of the source palette
   var BitmapInfo;    // The buffer that will receive the DIB TBitmapInfo structure.
                      // A buffer of sufficient size must have been allocated prior to
                      // calling this function
   var Bits;          // The buffer that will receive the DIB pixel data
   PixelFormat: TPixelFormat // The pixel format of the destination DIB
): Boolean; // True on success, False on failure
var
  OldPal    : HPALETTE;
  DC        : HDC;
begin
  InitializeBitmapInfoHeader(Bitmap, TBitmapInfoHeader(BitmapInfo), PixelFormat);
  OldPal := 0;
  DC := CreateCompatibleDC(0);
  try
    if (Palette <> 0) then
    begin
      OldPal := SelectPalette(DC, Palette, False);
      RealizePalette(DC);
    end;
    Result := (GetDIBits(DC, Bitmap, 0, abs(TBitmapInfoHeader(BitmapInfo).biHeight),
      @Bits, TBitmapInfo(BitmapInfo), DIB_RGB_COLORS) <> 0);
  finally
    if (OldPal <> 0) then
      SelectPalette(DC, OldPal, False);
    DeleteDC(DC);
  end;
end;


// -------------------
// InternalGetDIBSizes
// -------------------
// Calculates the buffer sizes nescessary for convertion of a bitmap to a DIB
// of a specified PixelFormat.
// See the GetDIBSizes API function for more info.
// From graphics.pas, "optimized" for our use

procedure TAviWriter.InternalGetDIBSizes
(
   Bitmap: HBITMAP;             // The handle of the source bitmap
   var InfoHeaderSize: Integer; // The returned size of a buffer that will receive
                                // the DIB TBitmapInfo structure
   var ImageSize: longInt;      // The returned size of a buffer that will receive the DIB pixel data
   PixelFormat: TPixelFormat    // The pixel format of the destination DIB
);
var
  Info: TBitmapInfoHeader;
begin
  InitializeBitmapInfoHeader(Bitmap, Info, PixelFormat);
  // Check for palette device format
  if (Info.biBitCount > 8) then
  begin
    // Header but no palette
    InfoHeaderSize := SizeOf(TBitmapInfoHeader);
    if ((Info.biCompression and BI_BITFIELDS) <> 0) then
      Inc(InfoHeaderSize, 12);
  end else
    // Header and palette
    InfoHeaderSize := SizeOf(TBitmapInfoHeader) + SizeOf(TRGBQuad) * (1 shl Info.biBitCount);
  ImageSize := Info.biSizeImage;
end;

// --------------------------
// InitializeBitmapInfoHeader
// --------------------------
// Fills a TBitmapInfoHeader with the values of a bitmap when converted to a
// DIB of a specified PixelFormat.
// From graphics.pas, "optimized" for our use

procedure TAviWriter.InitializeBitmapInfoHeader
(
   Bitmap: HBITMAP;              // The handle of the source bitmap
   var Info: TBitmapInfoHeader;  // The TBitmapInfoHeader buffer that will receive the values
   PixelFormat: TPixelFormat     // The pixel format of the destination DIB
);
var
  DIB       : TDIBSection;
  Bytes     : Integer;
  function AlignBit(Bits, BitsPerPixel, Alignment: Cardinal): Cardinal;
  begin
    Dec(Alignment);
    Result := ((Bits * BitsPerPixel) + Alignment) and not Alignment;
    Result := Result SHR 3;
  end;
begin
  DIB.dsbmih.biSize := 0;
  Bytes := GetObject(Bitmap, SizeOf(DIB), @DIB);
  if (Bytes = 0) then
    raise Exception.Create('Invalid bitmap');
//    Error(sInvalidBitmap);

  if (Bytes >= (sizeof(DIB.dsbm) + sizeof(DIB.dsbmih))) and
    (DIB.dsbmih.biSize >= sizeof(DIB.dsbmih)) then
    Info := DIB.dsbmih
  else
  begin
    FillChar(Info, sizeof(Info), 0);
    with Info, DIB.dsbm do
    begin
      biSize := SizeOf(Info);
      biWidth := bmWidth;
      biHeight := bmHeight;
    end;
  end;
  case PixelFormat of
    pf1bit: Info.biBitCount := 1;
    pf4bit: Info.biBitCount := 4;
    pf8bit: Info.biBitCount := 8;
    pf24bit: Info.biBitCount := 24;
  else
//    Error(sInvalidPixelFormat);
    raise Exception.Create('Invalid pixel format');
    // Info.biBitCount := DIB.dsbm.bmBitsPixel * DIB.dsbm.bmPlanes;
  end;
  Info.biPlanes := 1;
  Info.biCompression := BI_RGB; // Always return data in RGB format
  Info.biSizeImage := AlignBit(Info.biWidth, Info.biBitCount, 32) * Cardinal(abs(Info.biHeight));
end;

procedure TAviWriter.SetWavFileName(value : string);
begin
   if lowercase(fWavFileName) <> lowercase(value)
      then if lowercase(ExtractFileExt(value)) <> '.wav'
             then raise Exception.Create('WavFileName must name a file '
                             + 'with the .wav extension')
             else fWavFileName := value;
end;

procedure Register;
begin
  RegisterComponents('Samples', [TAviWriter]);
end;

end.

Ответы

Ответ 1

Измените часть импорта функции AVISaveV в ее версию Unicode. Когда функция в справочнике Windows API имеет примечание Unicode and ANSI names, это означает, что для вас в Delphi вам нужно выбрать либо из Unicode-версии функции, либо из ANSI в зависимости от того, какой компилятор вы будете использовать.

Вы пытаетесь вызвать AVISaveV, который физически не существует. Там только AVISaveVA и AVISaveVW в avifil32.dll, и поскольку вы хотите преобразовать этот код в Unicode, попробуйте изменить функцию import следующим образом:

function AVISaveV; external 'avifil32.dll' name 'AVISaveVW';

enter image description here

Это всего лишь первая мысль, код с определением вроде не может работать даже в не-Unicode-версиях Delphi, потому что он вызвал не существующую функцию.

Ответ 2

На самом деле действительно простой способ сделать это, используя ffmpeg без какого-либо кода. Просто укажите все ваши файлы изображений примерно так:

image-0001.png

Затем используйте ffmpeg для их объединения в видео:

ffmpeg -i 'img-%04d.png' -r 10 out.avi

(-r - частота выходного кадра.)

Ответ 3

Я использовал видео-библиотеку Mitov с большим успехом на задачах, подобных тем, которые вы описываете. Он бесплатный для некоммерческого использования, и вы можете скачать его копию. И это удивительно быстро. Если вы используете чип Intel, он использует библиотеку Intel IPP, чтобы выжать максимум из вашей архитектуры процессора. http://software.intel.com/en-us/articles/intel-ipp/#support

Я постоянно теряю силу этой многозадачной библиотеки. Я уже писал об этом раньше, и может показаться, что я для них платный работник, но я нет. Я просто удивлен, как повезло (да, повезло), я должен был выбрать эту библиотеку для крупного проекта. Это меня не подвело. За некоторые деньги стоит заплатить, а библиотеки Митова - одна из них. Хороший код, частые обновления и быстрая поддержка от разработчиков, если это необходимо.

Обязательно ознакомьтесь с образцовыми программами Mitov, а также даст вам представление о том, как это работает.

Здесь сайт: www.mitov.com

Извините, у меня нет источника, с которым я могу поделиться.