Geting info from SWF file
A little theory
Any SWF file consists of 2 main parts: Header and Body. So reading header is enough to get info about the movie. Header record is described by the following structure:
TSWFHeader = packed record Signature: array [0..2] of char; Version: byte; FileLength: cardinal; FrameSize: TSWFRect; FrameRate: byte; FrameRateRemainder: byte; FrameCount: cardinal; end; TSWFRect = packed record Xmin: integer; // in twips Xmax: integer; // in twips Ymin: integer; // in twips Ymax: integer; // in twips end;
Header reading is complicated by two features:
1. Size of header record is not a fixed value because of the bit-packing techniques used for Rect record storing. Rect structure: Nbits, Xmin, Xmax, Ymin, Ymax. The unsigned Nbits field occupies the first five bits and indicates how long the next four signed fields are.
2. In compressed format a part of header is stored in the packed part of the file. Has been uncompressed using the open standard ZLIB. The data format used by the ZLIB library is described by Request for Comments (RFCs) documents 1950 to 1952.
Function GetSwfFileHeader
function GetSwfFileHeader(const FileName: String; var Header: TSWFHeader): boolean;
Function returns true if the file is read successfully. Rect record fields are in twips (1 px = 20 twips). Frame rate consists of 2 parts: FrameRate is integer and FrameRateRemainder is fractional. The complete value is calculated from
FrameRate + FrameRateRemainder / 256.
How it works:
1. Reading first 8 bytes. This is non compressed part of header.
2. Signature comparing. Unpacking in case compressed format.
3. Reading the Rect bit structure and other data.
Function GetSwfFileHeader(const FileName: string; var Header: TSWFHeader): boolean; const BuffSize = 25; var Buffer: PByteArray; NBitsField: byte; Poz: longword; FileStream: TFileStream; MemStream: TMemoryStream; ZStream: TDecompressionStream; begin Result:= FALSE; if not FileExists(FileName) then Exit; FileStream:= TFileStream.Create(FileName, fmOpenRead); try FileStream.Position:= 0; if FileStream.Size > 22 then begin GetMem(Buffer, BuffSize); try FileStream.Read(Header, 8); if(Header.Signature = 'CWS') and (Header.Version >= 6) then begin Result:= TRUE; MemStream:= TMemoryStream.Create; try MemStream.CopyFrom(FileStream, FileStream.Size-8); MemStream.Position:= 0; ZStream:= TDecompressionStream.Create(MemStream); try ZStream.Read(Buffer^, BuffSize); finally ZStream.Free; end; finally MemStream.Free; end; end else begin FileStream.Read(Buffer^, BuffSize); Result:= Header.Signature = 'FWS'; end; if Result then with Header do begin Poz:= 0; NBitsField:= TBitWidth(ReadNBits(Buffer^, Poz, 5)); Inc(Poz, 5); FrameSize.Xmin:= Integer(ReadNBits(Buffer^, Poz, NBitsField)); Inc(Poz, NBitsField); FrameSize.Xmax:= Integer(ReadNBits(Buffer^, Poz, NBitsField)); Inc(Poz, NBitsField); FrameSize.Ymin:= Integer(ReadNBits(Buffer^, Poz, NBitsField)); Inc(Poz, NBitsField); FrameSize.Ymax:= Integer(ReadNBits(Buffer^, Poz, NBitsField)); Inc(Poz, NBitsField); NBitsField:= Poz mod 8; Poz:= Poz div 8; if (NBitsField > 0) then Inc(Poz); FrameRateRemainder:= Buffer^[Poz]; // 8.[8] FrameRate:= Buffer^[Poz+1]; FrameCount:= Buffer^[Poz+2] or (Buffer^[Poz+3] shl 8); end; finally FreeMem(Buffer); end; end; finally FileStream.Free; end; end;
The full source is SwfFileInfo.pas.