首页  编辑  

获取AVI文件头信息

Tags: /超级猛料/Multi-Media.多媒体相关/   Date Created:

获取AVI文件头信息

Unit AVIinfo;

Interface

Uses Classes, Consts, SysUtils, Windows, Math;

Type

 FOURCC = DWORD;

 PropertyStrings = (prAviInfo = 2, prFileName, prFileSize, prStreams,

   prVideoStream,

   prCompression, prVidBitrate, prResolution, prColorDepth,

   prRunningTime, prFrameRate, prMsPerFrame, prFrames,

   prKeyFrames, prAudioStream, prWaveType, prAudBitrate,

   prSamplerate, prBitDepth, prChannels, prAudioDelay);

 TagInfo = Record

   Name: String; // Name of the tag

   Position, Size: Int64; // Position to Size of this ta int64

 End;

 TMainAVIHeader = Packed Record

   dwMicroSecPerFrame: DWORD; // frame display rate (or 0L)

   dwMaxBytesPerSec: DWORD; // max. transfer rate

   dwPaddingGranularity: DWORD; // pad to multiples of this

                                           // size; normally 2K.

   dwFlags: DWORD; // the ever-present flags

   dwTotalFrames: DWORD; // # frames in file

   dwInitialFrames: DWORD;

   dwStreams: DWORD;

   dwSuggestedBufferSize: DWORD;

   dwWidth: DWORD;

   dwHeight: DWORD;

   dwReserved: Array[0..3] Of DWORD;

 End;

 TAVIStreamHeader = Packed Record

   fccType: FOURCC;

   fccHandler: FOURCC;

   dwFlags: DWORD; // Contains AVITF_* flags

   wPriority: WORD;

   wLanguage: WORD;

   dwInitialFrames: DWORD;

   dwScale: DWORD;

   dwRate: DWORD; // dwRate / dwScale == samples/second

   dwStart: DWORD;

   dwLength: DWORD; // In units above...

   dwSuggestedBufferSize: DWORD;

   dwQuality: DWORD;

   dwSampleSize: DWORD;

   rcFrame: TRECT;

 End;

 WAVEFORMATEX = Packed Record

   wFormatTag: Word; { format type }

   nChannels: Word; { number of channels (i.e. mono, stereo, etc.) }

   nSamplesPerSec: DWORD; { sample rate }

   nAvgBytesPerSec: DWORD; { for buffer estimation }

   nBlockAlign: Word; { block size of data }

   wBitsPerSample: Word;

   cbSize: Word;

 End;

 TAuthorInfo = Record

   Artist, Comment, Copyright, Name, Product, Source, Subject, DisplayName,

     Genre, Language, Software:

   String;

 End;

 TBackup = Record

   AviH: TMainAVIHeader;

   VidSH: TAVIStreamHeader;

   AudSH: TAVIStreamHeader;

 End;

 EAviInfoError = Class(Exception);

 TAviStreamInfo = Class(TObject)

 Private

   fAviHeader: TMainAviHeader;

   fVideoStreamHeader: TAviStreamHeader;

   fVideoStreamFormat: BitmapInfoHeader;

   fAudioStreamHeader: TAviStreamHeader;

   fAudioStreamFormat: WaveformatEx;

   fHasVideo: Boolean;

   fHasAudio: Boolean;

   fFrameRate: Double;

   fRunningTime: Double;

   fAuthorInfo: TAuthorInfo;

   fKeyFrames: DWord;

   fVideoBitrate: Double;

   fAudioBitrate: Double;

   fSize: Int64;

   fAudioDelay: Double;

   fChanged: Boolean;

   PropertyStringList: TStringList;

 Protected

   TagList: Record

     Count, Idx1Pos, MoviPos, InfoPos, VidsPos, AudsPos, AvihPos: Byte;

     Tags: Array[0..40] Of TagInfo;

   End;

   Backup: TBackup;

   AviStream: TStream;

   Procedure CreateTagList(AStream: TStream);

   Procedure ParseTagList(AStream: TStream);

   Procedure CalcKeyFrames(AStream: TStream);

   Procedure FillPropertyString;

   Procedure FillPropertyStringNoDescription;

   Function TimeToShortString(myTime: Double): String;

   Procedure SetAuthorInfo(Value: TAuthorInfo); Virtual;

   Procedure SetFrameRate(Value: Double); Virtual;

   Procedure SetAudioDelay(Value: Double); Virtual;

   // CheckAVI should make sure before altering the AVI that it's the same

   // file/stream that was used with LoadAVI

   // Implement it if you need to use TAviStreamInfo,

   // currently it always returns false.

   Function CheckAVI(AStream: TStream): Boolean;

 Public

   Constructor Create;

   Destructor Destroy; Override;

  // procedure LoadAVI(AStream: TStream);

   Procedure LoadAVI(AStream: TStream; Description: Boolean);

   Procedure UndoChanges;

   Procedure SaveToFile(aFilename: TFileName);

   Procedure SaveToStream(AStream: TStream);

   Function PropertyString(pr: PropertyStrings): String;

   Property AviHeader: TMainAviHeader Read fAviHeader;

   Property VideoStreamHeader: TAviStreamHeader Read fVideoStreamHeader;

   Property VideoStreamFormat: BitmapInfoHeader Read fVideoStreamFormat;

   Property AudioStreamHeader: TAviStreamHeader Read fAudioStreamHeader;

   Property AudioStreamFormat: WaveFormatEx Read fAudioStreamFormat;

   Property HasVideo: Boolean Read fHasVideo;

   Property HasAudio: Boolean Read fHasAudio;

   Property FrameRate: Double Read fFrameRate Write SetFrameRate;

   Property RunningTime: Double Read fRunningTime;

   Property KeyFrames: DWord Read fKeyFrames;

   Property VideoBitrate: Double Read fVideoBitrate;

   Property AudioBitrate: Double Read fAudioBitrate;

   Property AudioDelay: Double Read fAudioDelay Write SetAudioDelay;

   Property AuthorInfo: TAuthorInfo Read fAuthorInfo Write SetAuthorInfo;

   Property Size: Int64 Read fSize;

   Property Changed: Boolean Read fChanged;

 Published

 End;

 TAviFileInfo = Class(TAviStreamInfo)

 Private

 Protected

   fAviFile: TFileName;

   Function CheckAVI(Const Filename: String): Boolean;

   Procedure SetAuthorInfo(Value: TAuthorInfo); Override;

   Procedure SetFrameRate(Value: Double); Override;

   Procedure SetAudioDelay(Value: Double); Override;

 Public

   Procedure UndoChanges;

   Property AviFile: TFileName Read fAviFile;

   Procedure LoadAVI(Const FileName: TFilename; Description: Boolean);

   //    procedure LoadAVI(const FileName: TFileName);

 End;

Function fcc2String(fcc: FourCC): String;

Function Wavetype2String(WaveType: Word): String;

Function CodecIdent2String(fcc: FourCC): String;

Implementation

// **********************************

// **  USEFUL MAKROS

// **********************************

Function fcc2String(fcc: FourCC): String;

Var

 Buffer: Array[0..3] Of Char;

Begin

 Move(fcc, Buffer, SizeOf(Buffer));

 Result := Buffer;

End;

Function GetStringFromResource(Name: String): String;

Begin

 With TResourceStream.Create(HInstance, Name, RT_RCDATA) Do

   Try

     SetLength(Result, Size);

     Read(Result[1], Size);

   Finally

     Free;

   End;

End;

Function Wavetype2String(WaveType: Word): String;

Var

 StrLst: TStringList;

 I, P: Integer;

 S: String;

Begin

 StrLst := TStringList.Create;

 Try

   StrLst.Text := GetStringFromResource('AudioCodecs');

   For i := 0 To StrLst.Count - 1 Do

     Begin

       S := StrLst[i];

//      TStrings.NameValueSeperator property isn't there on D6 or lower,

//      So i just use '=' here (as this is the default).

//      P := AnsiPos(StrLst.NameValueSeparator, S);

       P := AnsiPos('=', S);

       If P > 0 Then

         Begin

           If WaveType = StrToInt(Copy(S, 1, P - 1)) Then

             Begin

//          Again, ValueFromIndex doesn't seem to be there on D6 or lower.

//          Result:=StrLst.ValueFromIndex[i];

               Result := StrLst.Values[StrLst.Names[i]];

               Exit;

             End;

         End;

     End;

 Finally

   StrLst.Free;

 End;

End;

Function CodecIdent2String(fcc: FourCC): String;

Var

 Codec: String;

 S: TStringList;

Begin

 Result := '';

 Codec := Fcc2String(Fcc);

 S := TStringList.Create;

 Try

   S.Text := GetStringFromResource('VideoCodecs');

   Result := S.Values[Codec];

 Finally

   S.Free;

 End;

 If Result = '' Then

   Result := 'Unknown';

End;

{ TAviFileInfo }

Function TAviFileInfo.CheckAVI(Const Filename: String): Boolean;

Begin

 Result := (Filename = fAviFile);

End;

Procedure TAviFileInfo.LoadAVI(Const FileName: TFilename; Description:

 Boolean);

Var

 tempStream: TFileStream;

Begin

 fAviFile := FileName;

 tempStream := TFileStream.Create(Filename, fmOpenRead Or fmShareDenyWrite);

 Try

   Inherited LoadAVI(tempStream, Description);

 Finally

   tempstream.Free;

 End;

 PropertyStringList[ord(prFileName)] :=

   StringReplace(PropertyStringList[ord(prFileName)],

   'None [Loaded from Stream]',

   ExtractFileName(FileName), []);

End;

Procedure TAviFileInfo.SetAudioDelay(Value: Double);

Var

 tempStream: TFileStream;

Begin

 tempStream := TFileStream.Create(fAviFile, fmOpenReadWrite Or

   fmShareDenyWrite);

 Try

   AviStream := tempStream;

   Inherited SetAudioDelay(Value);

 Finally

   tempstream.Free;

 End;

End;

Procedure TAviFileInfo.SetAuthorInfo(Value: TAuthorInfo);

Var

 tempStream: TFileStream;

Begin

 tempStream := TFileStream.Create(fAviFile, fmOpenReadWrite Or

   fmShareDenyWrite);

 Try

   AviStream := tempStream;

   Inherited SetAuthorInfo(Value);

 Finally

   tempstream.Free;

 End;

End;

Procedure TAviFileInfo.SetFrameRate(Value: Double);

Var

 tempStream: TFileStream;

Begin

 tempStream := TFileStream.Create(fAviFile, fmOpenReadWrite Or

   fmShareDenyWrite);

 Try

   AviStream := tempStream;

   Inherited SetFrameRate(Value);

 Finally

   tempstream.Free;

 End;

End;

Procedure TAviFileInfo.UndoChanges;

Var

 tempStream: TFileStream;

Begin

 tempStream := TFileStream.Create(fAviFile, fmOpenReadWrite Or

   fmShareExclusive);

 Try

   AviStream := tempStream;

   Inherited UndoChanges;

 Finally

   tempstream.Free;

 End;

End;

{ TAviStreamInfo }

Procedure TAviStreamInfo.UndoChanges;

Begin

//  AStream.Seek(0,soBeginning);

//  AStream.Write(BackupHdr,sizeof(BackupHdr));

 AviStream.Seek(TagList.Tags[TagList.AudsPos].Position + 4, soBeginning);

 AviStream.WriteBuffer(BackUp.AudSH, SizeOf(BackUp.AudSH));

 AviStream.Seek(TagList.Tags[TagList.VidsPos].Position + 4, soBeginning);

 AviStream.WriteBuffer(BackUp.VidSH, SizeOf(BackUp.VidSH));

 AviStream.Seek(TagList.Tags[TagList.AvihPos].Position + 16, soBeginning);

 AviStream.WriteBuffer(BackUp.AviH, SizeOf(BackUp.AviH));

End;

Procedure TAviStreamInfo.LoadAVI(AStream: TStream; Description: Boolean);

Begin

 fSize := AStream.Size;

 fChanged := false;

 AviStream := AStream;

 CreateTagList(AStream); // Fill the array TagList

                                  // It contains the positions of the

                                  // tags in the file.

 ParseTagList(AStream); // Interpret the TagList and fill

                                  // the public properties with the info

 CalcKeyFrames(AStream);

 If Description Then

   FillPropertyString

 Else

   FillPropertyStringNoDescription;

End;

Procedure TAviStreamInfo.CreateTagList(AStream: TStream);

Var

 Code: String;

 ChunkSize: DWord;

 StreamSize, ChunkPos: Int64;

 Buffer: Array[0..3] Of Char;

Begin

 StreamSize := Astream.Size; // Astream.Size is a

                                            // property -> Slower

 AStream.Seek(0, soBeginning);

 TagList.Count := 0;

 While Astream.Position < StreamSize - 4 Do

   Begin

     AStream.Read(Buffer, Sizeof(Buffer));

     ChunkPos := Astream.Position;

     Code := AnsiUpperCase(Buffer); {-GENRE,,}

     If (Code = 'INFO') Or (Code = 'AVIH') Or (Code = 'IART') Or (Code =

       'ICMT') Or

       (Code = 'ICOP') Or (Code = 'INAM') Or (Code = 'IPRD') Or (Code = 'ISBJ')

       Or

       (Code = 'ISRC') Or (Code = 'STRF') Or (Code = 'DISP') Or (Code = 'STRH')

       Or

       (Code = 'STRD') Or (Code = 'MOVI') Or (Code = 'IDX1') Or (Code = 'JUNK')

       Or

       (Code = 'PRMI') Or (Code = 'LIST') Or (Code = 'IGNR') Or (Code = 'ILNG')

       Or (Code = 'ISFT') Then

       Begin

         AStream.Read(ChunkSize, Sizeof(ChunkSize));

         If Code = 'MOVI' Then

           Begin

             AStream.Seek(-12, soCurrent);

             AStream.Read(ChunkSize, Sizeof(ChunkSize));

             AStream.Seek(ChunkSize, soCurrent);

           End;

         If Code = 'INFO' Then

           Begin

             AStream.Seek(-12, soCurrent);

             AStream.Read(ChunkSize, Sizeof(ChunkSize));

             AStream.Seek(4, soCurrent);

           End;

         If (Code = 'JUNK') Or (Code = 'DISP') Or (Code = 'IDX1') Then

           AStream.Seek(ChunkSize, soCurrent);

         TagList.Tags[TagList.Count].Name := Code;

         TagList.Tags[TagList.Count].Position := ChunkPos;

         TagList.Tags[TagList.Count].Size := ChunkSize;

         Inc(TagList.Count);

       End

     Else

       Begin

         AStream.Seek(-3, soCurrent);

       End;

   End;

End;

Procedure TAviStreamInfo.ParseTagList(AStream: TStream);

 Function ReadAuthorInfo(AStream: TStream; i: Byte): AnsiString;

 Var

   BigBuffer: Array[0..2147] Of Char; //2147

   InfoSize: LongInt;

   s, sc: String;

   ii: Integer;

 Begin

   AStream.Seek(TagList.Tags[i].Position, soBeginning);

   InfoSize := AStream.Read(BigBuffer, Min(TagList.Tags[i].Size + 3,

     Sizeof(BigBuffer)));

   s := '';

   For ii := 1 To InfoSize - 1 Do

     Begin

       sc := BigBuffer[ii];

       If sc <> #0 Then

         s := s + sc;

     End;

   Result := s; //Copy(s, 0, InfoSize);

 End;

Var

 i: byte;

 Code: String;

 StreamHeader: TAviStreamHeader;

Begin

 For i := 0 To TagList.Count Do

   Begin

     If TagList.Tags[i].Name = 'AVIH' Then

       Begin

         AStream.Seek(TagList.Tags[i].Position + 4, soBeginning);

         AStream.Read(fAviHeader, Sizeof(fAviHeader));

       End;

     If TagList.tags[i].Name = 'STRH' Then

       Begin

         AStream.Seek(TagList.Tags[i].Position + 4, soBeginning);

         AStream.Read(StreamHeader, SizeOf(StreamHeader));

         Code := AnsiUpperCase(fcc2String(StreamHeader.fccType));

         If code = 'VIDS' Then

           Begin

             fHasVideo := true;

             TagList.VidsPos := i;

             fVideoStreamHeader := StreamHeader;

             fFrameRate := StreamHeader.dwRate / StreamHeader.dwScale;

             fRunningTime := AviHeader.dwTotalFrames / FrameRate;

             AStream.Read(fVideoStreamFormat, Sizeof(fVideoStreamFormat));

           End;

         If code = 'AUDS' Then

           Begin

             fHasAudio := true;

             TagList.AudsPos := i;

             fAudioStreamHeader := StreamHeader;

             AStream.Read(fAudioStreamFormat, Sizeof(fAudioStreamFormat));

           End;

       End; // Ende STRH

     If TagList.tags[i].Name = 'IART' Then //director

       fAuthorInfo.Artist := ReadAuthorInfo(AStream, i);

     If TagList.tags[i].Name = 'ICMT' Then

       fAuthorInfo.Comment := ReadAuthorInfo(AStream, i);

     If TagList.tags[i].Name = 'ICOP' Then //CPYRIGHT

       fAuthorInfo.Copyright := ReadAuthorInfo(AStream, i);

     If TagList.tags[i].Name = 'INAM' Then //title

       fAuthorInfo.Name := ReadAuthorInfo(AStream, i);

     If TagList.tags[i].Name = 'IPRD' Then //product

       fAuthorInfo.Product := ReadAuthorInfo(AStream, i);

     If TagList.tags[i].Name = 'ISRC' Then

       fAuthorInfo.Source := ReadAuthorInfo(AStream, i);

     If TagList.tags[i].Name = 'ISBJ' Then //SUBJECT

       fAuthorInfo.Subject := ReadAuthorInfo(AStream, i);

     If TagList.tags[i].Name = 'DISP' Then

       fAuthorInfo.DisplayName := ReadAuthorInfo(AStream, i);

     {________}

     If TagList.tags[i].Name = 'IGNR' Then //genre

       fAuthorInfo.Genre := ReadAuthorInfo(AStream, i);

     If TagList.tags[i].Name = 'ILNG' Then //lang

       fAuthorInfo.Language := ReadAuthorInfo(AStream, i);

     If TagList.tags[i].Name = 'ISFT' Then //software

       fAuthorInfo.Software := ReadAuthorInfo(AStream, i);

     {________}

     If TagList.Tags[i].Name = 'IDX1' Then

       TagList.Idx1Pos := i;

     If TagList.Tags[i].Name = 'INFO' Then

       TagList.InfoPos := i;

     If TagList.Tags[i].Name = 'MOVI' Then

       TagList.MoviPos := i;

   End;

 fAudioDelay := (AudioStreamHeader.dwStart * AudioStreamHeader.dwScale /

   AudioStreamHeader.dwRate) - (VideoStreamHeader.dwStart / FrameRate);

 // Backup Everything

 BackUp.AviH := AviHeader;

 Backup.VidSH := VideoStreamHeader;

 Backup.AudSH := AudioStreamHeader;

End;

Constructor TAviStreamInfo.Create;

Begin

 fHasVideo := false;

 fHasAudio := false;

 fChanged := false;

 PropertyStringList := TStringList.Create;

End;

Procedure TAviStreamInfo.CalcKeyFrames(AStream: TStream);

Var

 Idx1End, BytesRead: Int64;

 Keyfr: LongInt;

 Buffer: Array[1..2048] Of byte;

 i: word;

 VidStreamSize, AudStreamSize: Dword;

Begin

 KeyFr := 0;

 VidStreamSize := 0;

 AudStreamSize := 0;

 AStream.Seek(TagList.Tags[TagList.Idx1Pos].Position + 4, soBeginning);

 Idx1End := TagList.Tags[TagList.Idx1Pos].Size +

   TagList.Tags[TagList.Idx1Pos].Position + 4;

 While AStream.Position < Idx1End Do

   Begin

     // Sorry for this terrible type of code, but

     // reading big chunks is considerably faster

     // than getting small chunks.

     BytesRead := AStream.Read(Buffer, Sizeof(Buffer)) Div 16 - 1;

     For i := 0 To BytesRead Do

       Begin

     // 00 is normally the video stream

         If (Buffer[1 + 16 * i] = Ord('0')) And (Buffer[2 + 16 * i] = Ord('0'))

           Then

           Begin

       // keyframe flag set?

             If ((Buffer[5 + 16 * i] And $10) <> 0) Then

               Begin

                 Inc(Keyfr);

               End;

             VidStreamSize := VidStreamSize + Buffer[13 + i * 16] + Buffer[14

               + i * 16] * $100 + Buffer[15 + i * 16] * $10000 + Buffer[16 + i

               *

               16] * $1000000;

           End;

     // 01 is the audio stream

         If (Buffer[1 + 16 * i] = Ord('0')) And (Buffer[2 + 16 * i] = Ord('1'))

           Then

           AudStreamSize := AudStreamSize + Buffer[13 + i * 16] + Buffer[14 + i

             * 16] * $100 + Buffer[15 + i * 16] * $10000 + Buffer[16 + i * 16]

             *

             $1000000;

       End;

   End;

 fKeyframes := KeyFr;

 fVideoBitrate := VidStreamSize * FrameRate / (125 * AviHeader.dwTotalFrames);

 fAudioBitrate := AudStreamSize * FrameRate / (125 * AviHeader.dwTotalFrames);

End;

Function TAviStreamInfo.CheckAVI(AStream: TStream): Boolean;

Begin

 // Donno what to do here, perhaps compare the first 8 Kb with BackupHdr.

 // Just a dummy function for CheckAVI in TAviFileInfo.

 // If you want to use TAviStreamInfo implement this function!

 Result := false;

End;

Procedure TAviStreamInfo.SetAuthorInfo(Value: TAuthorInfo);

Begin

 // yet to implement

End;

Procedure TAviStreamInfo.SetFrameRate(Value: Double);

Begin

 // Check here if AviStream is still valid

 If Value <= 0 Then

   Raise

     EAviInfoError.Create('Framerate can''t set the framerate to a negative value. (' + FloatToStr(Value) + ')');

 fChanged := true;

 fVideoStreamHeader.dwScale := 10000;

 fVideoStreamHeader.dwRate := Round(Value * fVideoStreamHeader.dwScale);

 fAviHeader.dwMicroSecPerFrame :=

   Round(fVideoStreamHeader.dwScale / fVideoStreamHeader.dwRate * 1000000);

 AviStream.Seek(TagList.Tags[TagList.VidsPos].Position + 4, soBeginning);

 AviStream.WriteBuffer(fVideoStreamHeader, SizeOf(fVideoStreamHeader));

 AviStream.Seek(TagList.Tags[TagList.AvihPos].Position + 16, soBeginning);

 AviStream.WriteBuffer(fAviHeader, SizeOf(fAviHeader));

 fFrameRate := Value;

End;

Procedure TAviStreamInfo.SetAudioDelay(Value: Double);

Begin

 fChanged := true;

 If Value >= 0 Then

   Begin

   // The following is something as an "Audio Framerate":

   // AudioStreamHeader.dwRate/AudioStreamHeader.dwScale

     fAudioStreamHeader.dwStart := Round(Value * AudioStreamHeader.dwRate /

       AudioStreamHeader.dwScale);

     fVideoStreamHeader.dwStart := 0;

   End;

 If Value < 0 Then

   Begin

     fAudioStreamHeader.dwStart := 0;

     fVideoStreamHeader.dwStart := -Round(Value * FrameRate);

   End;

 AviStream.Seek(TagList.Tags[TagList.AudsPos].Position + 4, soBeginning);

 AviStream.WriteBuffer(fAudioStreamHeader, SizeOf(fAudioStreamHeader));

 AviStream.Seek(TagList.Tags[TagList.VidsPos].Position + 4, soBeginning);

 AviStream.WriteBuffer(fVideoStreamHeader, SizeOf(fVideoStreamHeader));

 fAudioDelay := Value;

End;

{____________________________________________}

Procedure TAviStreamInfo.FillPropertyStringNoDescription;

Begin

 PropertyStringList.Clear;

 PropertyStringList.Add('Yet Another Avi Info (YAAI) Output File');

 PropertyStringList.Add('visit http://yaai.sourceforge.net/ for more information');

 PropertyStringList.Add('AVI Information');

 PropertyStringList.Add(' None [Loaded from Stream]');

 PropertyStringList.Add(IntToStr(Size) + ' Bytes (' +

   Floattostrf(Size / 1048576, ffFixed, 15, 2) + ' MB)');

 PropertyStringList.Add(IntToStr(AviHeader.dwStreams));

 If HasVideo Then

   Begin

     PropertyStringList.Add('Video Stream');

     PropertyStringList.Add(fcc2String(VideoStreamHeader.fccHandler) + ' - ' +

       CodecIdent2String(VideostreamHeader.fccHandler));

     PropertyStringList.Add(FloatToStrF(VideoBitrate, ffFixed, 15, 2) +

       ' kbit/s');

     PropertyStringList.Add(IntToStr(AviHeader.dwWidth) +

       'x' + IntToStr(AviHeader.dwHeight));

     PropertyStringList.Add(IntToStr(VideoStreamFormat.biBitCount) + ' bits');

     PropertyStringList.Add(FloatToStrF(RunningTime,

       ffFixed, 15, 2) + ' s (' + TimeToShortString(RunningTime) + ')');

       // 86400 = Seconds / Day

     PropertyStringList.Add(FloatToStrF(FrameRate,

       ffFixed, 15, 4) + ' fps');

     PropertyStringList.Add(

       IntToStr(AviHeader.dwMicroSecPerFrame) + ' ms');

     PropertyStringList.Add(

       IntToStr(AviHeader.dwTotalFrames));

     PropertyStringList.Add(IntToStr(KeyFrames) +

       ' (Every ' + IntToStr(AviHeader.dwTotalFrames Div Keyframes) + ')');

   End

 Else

   Begin

     PropertyStringList.Add('No Video Stream Found!');

     PropertyStringList.Add('-');

     PropertyStringList.Add('-');

     PropertyStringList.Add('-');

     PropertyStringList.Add('-');

     PropertyStringList.Add('-');

     PropertyStringList.Add('-');

     PropertyStringList.Add('-');

     PropertyStringList.Add('-');

     PropertyStringList.Add('-');

   End;

 If HasAudio Then

   Begin

     PropertyStringList.Add('Audio Stream');

     PropertyStringList.Add(

       IntToStr(AudioStreamFormat.wFormatTag) + ' - ' +

       WaveType2String(AudioStreamFormat.wFormatTag));

     PropertyStringList.Add(FloatToStrF(AudioBitrate,

       ffFixed, 15, 2) + ' kbit/s');

     PropertyStringList.Add(IntToStr(AudioStreamFormat.nSamplesPerSec) +

       ' Hz');

     PropertyStringList.Add(IntToStr(AudioStreamFormat.wBitsPerSample) +

       ' Bits');

     PropertyStringList.Add(IntToStr(AudioStreamFormat.nChannels));

     PropertyStringList.Add(FloatToStrF(AudioDelay,

       ffFixed, 15, 2) + ' s');

   End

 Else

   Begin

     PropertyStringList.Add('No Audio Stream Found!');

     PropertyStringList.Add('-');

     PropertyStringList.Add('-');

     PropertyStringList.Add('-');

     PropertyStringList.Add('-');

     PropertyStringList.Add('-');

     PropertyStringList.Add('-');

   End;

 PropertyStringList.Add('Author Details');

 PropertyStringList.Add(AuthorInfo.Artist);

 PropertyStringList.Add(AuthorInfo.Comment);

 PropertyStringList.Add(AuthorInfo.Copyright);

 PropertyStringList.Add(AuthorInfo.Name);

 PropertyStringList.Add(AuthorInfo.Product);

 PropertyStringList.Add(AuthorInfo.Source);

 PropertyStringList.Add(AuthorInfo.Subject);

 PropertyStringList.Add(AuthorInfo.DisplayName);

 PropertyStringList.Add(AuthorInfo.Genre);

 PropertyStringList.Add(AuthorInfo.Language);

 PropertyStringList.Add(AuthorInfo.Software);

End;

Procedure TAviStreamInfo.FillPropertyString;

Begin

 PropertyStringList.Clear;

 PropertyStringList.Add('Yet Another Avi Info (YAAI) Output File');

 PropertyStringList.Add('visit http://yaai.sourceforge.net/ for more information');

 PropertyStringList.Add('AVI Information');

 PropertyStringList.Add('    Filename: None [Loaded from Stream]');

 PropertyStringList.Add('    Filesize: ' + IntToStr(Size) + ' Bytes (' +

   Floattostrf(Size / 1048576, ffFixed, 15, 2) + ' MB)');

 PropertyStringList.Add('    Streams (i.e. Video, Audio): ' +

   IntToStr(AviHeader.dwStreams));

 If HasVideo Then

   Begin

     PropertyStringList.Add('Video Stream');

     PropertyStringList.Add('    Compression: ' +

       fcc2String(VideoStreamHeader.fccHandler) + ' - ' +

       CodecIdent2String(VideostreamHeader.fccHandler));

     PropertyStringList.Add('    Avg. Bitrate: ' + FloatToStrF(VideoBitrate,

       ffFixed, 15, 2) + ' kbit/s');

     PropertyStringList.Add('    Resolution: ' + IntToStr(AviHeader.dwWidth) +

       'x' + IntToStr(AviHeader.dwHeight));

     PropertyStringList.Add('    Color Depth: ' +

       IntToStr(VideoStreamFormat.biBitCount) + ' bits');

     PropertyStringList.Add('    Running Time: ' + FloatToStrF(RunningTime,

       ffFixed, 15, 2) + ' s (' + TimeToShortString(RunningTime) + ')');

     PropertyStringList.Add('    Framerate: ' + FloatToStrF(FrameRate,

       ffFixed, 15, 4) + ' fps');

     PropertyStringList.Add('    Microseconds Per Frame: ' +

       IntToStr(AviHeader.dwMicroSecPerFrame) + ' ms');

     PropertyStringList.Add('    Frames: ' +

       IntToStr(AviHeader.dwTotalFrames));

     PropertyStringList.Add('    Keyframes: ' + IntToStr(KeyFrames) +

       ' (Every ' + IntToStr(AviHeader.dwTotalFrames Div Keyframes) + ')');

   End

 Else

   Begin

     PropertyStringList.Add('No Video Stream Found!');

     PropertyStringList.Add('    Compression: -');

     PropertyStringList.Add('    Avg. Bitrate: -');

     PropertyStringList.Add('    Resolution: -');

     PropertyStringList.Add('    Color Depth: -');

     PropertyStringList.Add('    Running Time: -');

     PropertyStringList.Add('    Framerate: -');

     PropertyStringList.Add('    Microseconds Per Frame: -');

     PropertyStringList.Add('    Frames: -');

     PropertyStringList.Add('    Keyframes: -');

   End;

 If HasAudio Then

   Begin

     PropertyStringList.Add('Audio Stream');

     PropertyStringList.Add('    Wave Type: ' +

       IntToStr(AudioStreamFormat.wFormatTag) + ' - ' +

       WaveType2String(AudioStreamFormat.wFormatTag));

     PropertyStringList.Add('    Avg. Bitrate: ' + FloatToStrF(AudioBitrate,

       ffFixed, 15, 2) + ' kbit/s');

     PropertyStringList.Add('    Sample Rate: ' +

       IntToStr(AudioStreamFormat.nSamplesPerSec) + ' Hz');

     PropertyStringList.Add('    Bit Depth: ' +

       IntToStr(AudioStreamFormat.wBitsPerSample) + ' Bits');

     PropertyStringList.Add('    Channels: ' +

       IntToStr(AudioStreamFormat.nChannels));

     PropertyStringList.Add('    Audio Delay: ' + FloatToStrF(AudioDelay,

       ffFixed, 15, 2) + ' s');

   End

 Else

   Begin

     PropertyStringList.Add('No Audio Stream Found!');

     PropertyStringList.Add('    Wave Type: -');

     PropertyStringList.Add('    Avg. Bitrate: -');

     PropertyStringList.Add('    Sample Rate: -');

     PropertyStringList.Add('    Bit Depth: -');

     PropertyStringList.Add('    Channels: -');

     PropertyStringList.Add('    Audio Delay: -');

   End;

 PropertyStringList.Add('Author Details');

 PropertyStringList.Add('    Artist: ' + AuthorInfo.Artist);

 PropertyStringList.Add('    Comment: ' + AuthorInfo.Comment);

 PropertyStringList.Add('    Copyright: ' + AuthorInfo.Copyright);

 PropertyStringList.Add('    Name: ' + AuthorInfo.Name);

 PropertyStringList.Add('    Product: ' + AuthorInfo.Product);

 PropertyStringList.Add('    Source: ' + AuthorInfo.Source);

 PropertyStringList.Add('    Subject: ' + AuthorInfo.Subject);

 PropertyStringList.Add('    Display Name: ' + AuthorInfo.DisplayName);

 PropertyStringList.Add('    Genre: ' + AuthorInfo.Genre);

 PropertyStringList.Add('    Language: ' + AuthorInfo.Language);

 PropertyStringList.Add('    Software: ' + AuthorInfo.Software);

End;

Destructor TAviStreamInfo.Destroy;

Begin

 If PropertyStringList <> Nil Then

   PropertyStringList.Free;

 Inherited Destroy;

End;

Function TAviStreamInfo.PropertyString(pr: PropertyStrings): String;

Begin

 Result := trim(PropertyStringList[ord(pr)]);

End;

Procedure TAviStreamInfo.SaveToFile(aFilename: TFileName);

Begin

 PropertyStringList.SaveToFile(aFilename);

End;

Procedure TAviStreamInfo.SaveToStream(AStream: TStream);

Begin

 PropertyStringList.SaveToStream(aStream);

End;

Function TAviStreamInfo.TimeToShortString(myTime: Double): String;

Var

 i: Dword;

Begin

 i := Trunc(myTime);

 Result := IntToStr(i Mod 60) + 's';

 If i >= 60 Then

   Begin

     i := i Div 60;

     Result := IntToStr(i Mod 60) + 'm ' + Result;

     If i >= 60 Then

       Begin

         i := i Div 60;

         Result := IntToStr(i Mod 60) + 'h ' + Result;

       End;

   End;

End;

End.