views:

193

answers:

2

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.

+1  A: 

98 minutes is an hour and 38 minutes. What is "about two hours"?

As for the crash on FreeAndNil(), it's designed to free a TObject descendant and set the variable containing it to nil. Where do you create a TObject descendant in your code? It appears that lFile is an interface, so simply setting the variable to nil should be enough to decrement the reference count:

lFile := nil;

The call to Free inside FreeAndNil() is probably what's causing the crash.

EDIT: Based on the edits to the original question, obviously the above isn't correct. However, I'd suspect that the call to AVIFileRelease() has already freed the interface, and therefore there's nothing left for you to do. lFile is going out of scope anyway, and the reference count will be decremented automatically.

As far as the MSDN quote on AVIFileOpen(), note that it says that it "returns the address of a file interface". This is a COM interface, which in no way, shape or form is a Delphi TObject descendant. The snippet from the JEDI code says so as well, as it says that IAviFile is an interface(IUnknown)

Ken White
IMDB says "Sideways" is 126 minutes long.
Rob Kennedy
The movie is 2hours and 1 minute... BTW: setting lFile to nil also crashes the code...
smok1
@Rob: Thanks. Don't really care what IMDB says, though. If the OP says that the movie length is being returned incorrectly, the OP should also say what the correct value should be. It shouldn't be up to me to research the file, find out where to get info, and find that info myself. (That's presuming, of course, that IMDB is somewhere I can reach from where I'm posting.)
Ken White
Ok, but when I remove whole niling, FreeAndNil, and so on, I get Access Violation simply when lFile goes out of scope...
smok1
mghie found the cause and solution, I believe. I just upvoted his answer.
Ken White
Ken, I wouldn't expect you to look it up. I looked it up and told you, though. Given the choice between 1:38 and 2:06 for the meaning of "about two hours," I think you'd agree the latter is the better choice, so I was merely trying to say that the 98-minute result Smok was getting was indeed a wrong value. (I'd personally never describe 1:38 as "about two hours." It's clearly "about an hour and a half.")
Rob Kennedy
@Rob: I agree 100%. I'd personally never say "about two hours" for 1:38 either. I didn't post the original question, though. <g> I was pointing out that, although it was nice you posted the info from IMDB, the OP should have provided it. (I didn't really look at the filename, because I didn't care what file it was. I'm also not sure I would have recognized it as the movie "Sideways" even though that's in the filename.)
Ken White
@Rob, @Ken - I have tried to write it more meaningful on the second question, since the Access Violation issue has been solved by mghie. And about filename - yes, it is "Sideways". We have some objection for downloading illegal movies (to have more test subjects), because having warez in workplace is asking for trouble (even though our software is intended to detect warez...). So our test subjects are limited.
smok1
+3  A: 

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().

mghie
@mghie: Nice catch!
Ken White