Search code examples
delphiwinapimetadatamultimediaavi

Error on getting AVI file duration


I am using VFW unit from JEDI wrapper on WinAPI.

The code I am writing is intended to search user drives and detect warez. We do MP3, WMA and some graphic file search. Now we want to detect illegal movies. I want to open AVI file, read some details from it and close it. I have the following code:

uses WFV; //from JEDI api wrappers

procedure TForm1.Button1Click(Sender: TObject);
var
  lInfo : TAVIFILEINFO      lFile : IAVIFILE;
  lFileType : string;
  lLenMinutes : integer;
  lFPS : integer;
begin
  {init file}
  AVIFileInit;
  {Open file - note: since we search for warez this is perfely "warezy" file}
  AVIFileOpen(lFile, 'e:\Sideways KLAXXON\Sideways KLAXXON.avi', OF_READ, nil);
  {Get file info}
  AVIFileInfoW(lFile, lInfo, sizeof(lInfo));
  lFPS:=Round(lInfo.dwRate /lInfo.dwScale);
  lLenMinutes := Round(lInfo.dwLength  / lFPS / 60);
  lFileType := lInfo.szFileType;
  {just for show: prepare some memo to see what we get}
  memo1.Lines.Clear;

  memo1.Lines.Add('File lenght [min]: ' + IntToStr(lLenMinutes));
  memo1.Lines.Add('Width: ' + IntToStr(lInfo.dwWidth));
  memo1.Lines.Add('Height: ' + IntToStr(lInfo.dwHeight));
  memo1.Lines.Add('File type: ' + lFileType);

  {Closing the file}
  AVIFileRelease (lFile);
  {and here goes the crash}
  FreeAndNil(lFile);

end;

There are two problems:

  1. The lLenMinutes is something equal to 98 while the movie is about two hours. dwRate is 1 million and dwScale is 40k, so the FPS is perfectly 25. MSDN says: “The units are defined by dwRate and dwScale”.
  2. The code crashes on FreeAndNil line. Why? I assume I am responsible for freeing lFile (and at least I feel supposed to release file). Without line with FreeAndNil, I have Acces Violation on exit from a procedure.

So, do you have any clue how to correctly obtain movie duration from AVI file? And why the crash?

Edit

The movie is 2hours one minute, so the result should be really close to 120.The lFile is declared in Jedi as:

IAVIFile = interface(IUnknown)

the AVIFileOpen is declared in JEDI as:

function AVIFileOpen(var ppfile: IAVIFILE; szFile: LPCWSTR; uMode: UINT; lpHandler: PCLSID): HResult; stdcall; external AVIFILDLL name 'AVIFileOpenW';

and in MSDN:

STDAPI AVIFileOpen( PAVIFILE *ppfile, LPCTSTR szFile, UINT mode, CLSID pclsidHandler );

MSDN says:

"The AVIFileOpen function opens an AVI file and returns the address of a file interface used to access it."

so I assume object is created by this function.

Edit 2

The avi file length has been move to new question, since mghie answered this question.


Solution

  • The functions are paired, AVIFileOpen() and AVIFileRelease() belong together. Before AVIFileOpen() is called the lFile variable is nil, afterwards (if all went well) it contains an interface pointer. It has the reference count 1. After calling AVIFileRelease() the variable should again contain nil, but it doesn't. Now when your method exits the compiler-provided code to release interface pointers will try to decrement the reference count of the already released interface.

    You have basically two ways to fix this:

    • Increment the reference count of the interface pointer after AVIFileOpen().

    • Reset the variable without trying to decrement the reference count. Use a typecast to a pointer:

      pointer(lFile) := nil;

    Also, add a call to AVIFileExit() to match your call to AVIFileInit().