MPEG / AVI Header-Reader

Share your advanced PureBasic knowledge/code with the community.
Froggerprogger
Enthusiast
Enthusiast
Posts: 423
Joined: Fri Apr 25, 2003 5:22 pm
Contact:

MPEG / AVI Header-Reader

Post by Froggerprogger »

Code updated for 5.20+

There was a rumour in the German forum, that MovieInfo() doesn't return correct values for the fps.
Therefore here are two procedures, one for MPEG, and one for AVI-files to get Width, Height, Fps etc. of the movie.

Remark the information given by GetAspectRatioStr and GetPicRateStr, it was damn hard to find any information about this coding scheme. At wotsit there's even cursing wrong information.

Code: Select all

;-----------------------------------------------------------------------------------------------------
;- MPEG-Header-Reader 1.0
;-----------------------------------------------------------------------------------------------------
;-
;- Reads in the video-related data
;- from the sequence header of an MPEG-video
;-
;- Seems to work with .mpg .mpeg .m1v .vob (MPEG I+II)
;-
;- by Froggerprogger 02.06.04
;-
;-----------------------------------------------------------------------------------------------------
;- USAGE:
;-----------------------------------------------------------------------------------------------------
;- GetMPEGHeaderInformation(  p_filename.s,
;-                            *p_struct.MPEG_SeqHeader_Video,
;-                            p_forceHeaderSearch.l)
;-
;- p_filename.s             :   the filename of the file in which to search the mpeg-info
;- *p_struct.MPEG_MainInfo  :   pointer to an empty MPEG_SeqHeader_Video-Structure to be filled
;- p_forceHeaderSearch.l    :   a flag (#True|#False) to force searching for a sequence header
;-                               bytealigned (slow), if there's noone found in the typical (fast) way.
;-
;- RETURNS #True, if the file could be opened and a sequence-header could be found,
;- or #False otherwise. (you might try another call with p_forceHeaderSearch = #True then e.g.)
;-
;-
;-----------------------------------------------------------------------------------------------------
;- GetAspectRatioStr(p_aspectRatioID.l)
;- REQUESTS the ID that is filled into MPEG_SeqHeader_Video\aspectRatio by GetMPEGHeaderInformation()
;- RETURNS a string identifiing the aspect ratio. You might use ValF() on the result to get it's
;- numerical float-value or 0.0 if it was no valid ID.
;-
;-
;-----------------------------------------------------------------------------------------------------
;- GetPicRateStr(p_picRate.l)
;- REQUESTS the ID that is filled into MPEG_SeqHeader_Video\picRate by GetMPEGHeaderInformation()
;- RETURNS a string identifiing the picture rate. You might use ValF() on the result to get it's
;- numerical float-value or 0.0 if it was no valid ID.
;-
;-


#File_Temp = 0

Structure MPEG_SeqHeader_Video
  width.l         ; 12 bits in sequence header
  height.l        ; 12 bits in sequence header
  aspectRatio.l   ; 4 bits in sequence header
  picRate.l       ; 4 bits in sequence header
EndStructure

Declare.s GetAspectRatioStr(p_aspectRatioID.l)
Declare.s GetPicRateStr(p_picRate.l)
Declare.l GetMPEGHeaderInformation(p_filename.s, *p_struct.MPEG_SeqHeader_Video, p_forceHeaderSearch.l)

Procedure.s GetAspectRatioStr(p_aspectRatioID.l)
  Select p_aspectRatioID
    Case 01   :   ProcedureReturn "1.0000  VGA etc."
    Case 02   :   ProcedureReturn "0.6735"
    Case 03   :   ProcedureReturn "0.7031  16:9 - 625 lines"
    Case 04   :   ProcedureReturn "0.7615"
    Case 05   :   ProcedureReturn "0.8055"
    Case 06   :   ProcedureReturn "0.8437  16:9 - 525 lines"
    Case 07   :   ProcedureReturn "0.8935"
    Case 08   :   ProcedureReturn "0.9375  CCIR 601 - 625 lines"
    Case 09   :   ProcedureReturn "0.9815"
    Case 10   :   ProcedureReturn "1.0255"
    Case 11   :   ProcedureReturn "1.0695"
    Case 12   :   ProcedureReturn "1.1250  CCIR 601 - 525 lines"
    Case 13   :   ProcedureReturn "1.1575"
    Case 14   :   ProcedureReturn "1.2015"
    Case 15   :   ProcedureReturn "reserved"
    Default   :   ProcedureReturn "error - no valid ID for aspect ratio" 
  EndSelect
EndProcedure

Procedure.s GetPicRateStr(p_picRate.l)
  Select p_picRate
    Case 01   :   ProcedureReturn "23.976"
    Case 02   :   ProcedureReturn "24"
    Case 03   :   ProcedureReturn "25"
    Case 04   :   ProcedureReturn "29.97"
    Case 05   :   ProcedureReturn "30"
    Case 06   :   ProcedureReturn "50"
    Case 07   :   ProcedureReturn "59.94"
    Case 08   :   ProcedureReturn "60"
    Default   :   ProcedureReturn "error - no valid ID for picture rate"
  EndSelect
EndProcedure

Procedure.l GetMPEGHeaderInformation(p_filename.s, *p_struct.MPEG_SeqHeader_Video, p_forceHeaderSearch.l)
  Protected tempL.l
  If ReadFile(#File_Temp, p_filename) ; open file for read-only

    tempL = ReadLong(#File_Temp) ; read in the first 32 bit
    If p_forceHeaderSearch = #False And tempL <> $B3010000 And tempL <> $BA010000
      ProcedureReturn #False ; return if not a common MPEG-file and no forceHeaderSearch is set
    ElseIf tempL <> $B3010000
      Repeat ; search bytealigned for a (possible) sequence-header (simply straight forward, no chunk-jumps here)
        tempL = ReadLong(#File_Temp)
        FileSeek(#File_Temp, Loc(#File_Temp)-3)
      Until Eof(#File_Temp) Or tempL = $B3010000 ; until fileend reached or sequenceheader found
      If tempL = $B3010000
        FileSeek(#File_Temp, Loc(#File_Temp) + 3) ; set the filepointer back
      Else
        ProcedureReturn #False ; there's no sequence header in this file
      EndIf
    EndIf
   
    If tempL = $B3010000  ; in fact, at this point tempL is $B3010000 in ALL cases
      tempL = ReadLong(#File_Temp) ; copy the next 32 bit into tempL
        ; mirror the order of the bytes (MSB/LSB) :
        tempL = ((tempL & $FF000000) >> 24 & $FF) | (tempL & $FF) << 24 | (tempL & $FF00) << 8 | (tempL & $FF0000) >> 8

        *p_struct\width         = (tempL & $FFF00000) >> 20   ; width is in the first 12 bit
        *p_struct\height        = (tempL & $000FFF00) >> 8    ; height in the next 12 bit
        *p_struct\aspectRatio   = (tempL & $000000F0) >> 4    ; aspectRatio in the next 4 bit
        *p_struct\picRate       = (tempL & $0000000F)         ; picRate in the last 4 bit
     
      CloseFile(#File_Temp)
      ProcedureReturn #True
    EndIf
  Else
    ProcedureReturn #False
  EndIf
EndProcedure


;-
;- an example
;-

Global myMPEG_Data.MPEG_SeqHeader_Video
Global resultstr.s

filename.s = OpenFileRequester("Choose an MPG:", "G:\Videos\", "*.mpg;*.mpeg;*.m1v;*.vob|*.mpg;*.mpeg;*.m1v;*.vob|*.mpg|*.mpg|*.mpeg|*.mpeg|*.m1v|*.m1v|*.vob|*.vob|*.*|*.*", 0)
If filename <> ""
  If GetMPEGHeaderInformation(filename, @myMPEG_Data, #False) = #False
    If MessageRequester("MPEG-Header-Reader", "Error: The file doesn't seem to be a valid mpg-file - search for the MPEG-Header anyway ?", #MB_ICONERROR|#MB_YESNO) = #IDNO
      End
    EndIf
  EndIf
 
  If GetMPEGHeaderInformation(filename, @myMPEG_Data, #True) = #False ; enable 'force searching in any file'
    MessageRequester("MPEG-Header-Reader", "Error: There's no MPEG-Header in this file", #MB_ICONERROR)
    End
  Else
    ; prepare the output
    resultstr + "File:  " + filename + Chr(13) + Chr(10)
    resultstr + "Width:  " + Str(myMPEG_Data\width) + Chr(13) + Chr(10)
    resultstr + "Height:  " + Str(myMPEG_Data\height) + Chr(13) + Chr(10)
    resultstr + "aspect ratio:  " + Str(myMPEG_Data\aspectRatio) + "  ( " + GetAspectRatioStr(myMPEG_Data\aspectRatio) + " )" + Chr(13) + Chr(10)
    resultstr + "picture rate:  " + Str(myMPEG_Data\picRate) + "  ( " + GetPicRateStr(myMPEG_Data\picRate) + " fps )" + Chr(13) + Chr(10)
   
    MessageRequester("MPEG-Header-Reader", resultstr, #MB_ICONINFORMATION)
  EndIf
EndIf 



Code: Select all

;-----------------------------------------------------------------------------------------------------
;- AVI-Header-Reader 1.0
;-----------------------------------------------------------------------------------------------------
;- 
;-
;- simply streams an AVI for the header and reads out it's data
;- REQUESTS a filename and an empty MainAVIHeader-structure to be filled
;- RETURN #True if all is OK, or #False if not
;-
;- see the comments and the example on how to interpretate the data
;-
;- by Froggerprogger 02.06.04 
;- 
#File_Temp = 0 

Structure MainAVIHeader 
  dwMicroSecPerFrame.l ; use this to calculate the AVIs framerate (fps = 1.000.000 / dwMicroSecPerFrame)
  dwMaxBytesPerSec.l 
  dwReserved1.l 
  dwFlags.l 
  dwTotalFrames.l ; use this one to calculate the film's length (secs = dwTotalFrames / fps)
  dwInitialFrames.l 
  dwStreams.l 
  dwSuggestedBufferSize.l 
  dwWidth.l 
  dwHeight.l 
  dwScale.l 
  dwRate.l  ; a lot of AVIs let this value at 0, so use dwMicroSecPerFrame instead
  dwStart.l ; this is also set to 0 very often
  dwLength.l ; this is also set to 0 very often
EndStructure 

Procedure.l GetAVIHeaderInformation(p_filename.s, *p_struct.MainAVIHeader) 
  Protected tempL.l 
  If ReadFile(#File_Temp, p_filename) ; open the file
    
    If ReadLong(#File_Temp) <> 'FFIR'   ; "RIFF" ? 
      ProcedureReturn #False 
    EndIf 
    ReadLong(#File_Temp)                ; (not interesting) size of RIFF-chunk 
    If ReadLong(#File_Temp) <> ' IVA'   ; "AVI " ? 
      ProcedureReturn #False 
    EndIf 
    
    Repeat 
      tempL = ReadLong(#File_Temp) ; read in the actual Long-value (4 Bytes) 
      FileSeek(#File_Temp, Loc(#File_Temp)-3) ; go back 3 Bytes, because perhaps "avih" isn't 4-Byte-aligned
    Until Eof(#File_Temp) Or tempL = 'hiva' ; "avih" ? 
    
    If tempL = 'hiva' 
      FileSeek(#File_Temp, Loc(#File_Temp) + 7) ; go on 3 because the above and further 4 because of the not intersting chunksize
      ReadData(#File_Temp, *p_struct, SizeOf(MainAVIHeader)) ; copy the whole header
      CloseFile(#File_Temp)
      ProcedureReturn #True 
    Else 
      CloseFile(#File_Temp)
      ProcedureReturn #False 
    EndIf 

  Else 
    ProcedureReturn #False 
  EndIf 
EndProcedure 


;- an example
myAVI_Data.MainAVIHeader 

filename.s = OpenFileRequester("Choose an AVI:", "G:\Videos", "*.avi|*.avi", 0) 
If filename <> "" 
  If GetAVIHeaderInformation(filename, @myAVI_Data) = #False 
    MessageRequester("AVI-Header-Reader:", "Error during GetAVIHeaderInformation!", #MB_ICONERROR) 
  Else 
    result.s = filename + ":" + Chr(13) + Chr(10)
    result + "(W/H)  " +  Str(myAVI_Data\dwWidth) + " / " + Str(myAVI_Data\dwHeight) + Chr(13) + Chr(10)
    result + StrF(1000000.0 / myAVI_Data\dwMicroSecPerFrame, 2) + " fps" + Chr(13) + Chr(10)
    result + Str(myAVI_Data\dwTotalFrames) + " frames total" + Chr(13) + Chr(10)
    MessageRequester("AVI-Header-Reader:", result, #MB_ICONINFORMATION) 
  EndIf 
EndIf 
%1>>1+1*1/1-1!1|1&1<<$1=1
spongehammer
User
User
Posts: 84
Joined: Sat Jul 19, 2003 6:45 pm
Location: UK

Post by spongehammer »

hi Froggerprogger

i noticed the avi code allows retrieval of the length of the AVI. Is this possible for mpgs too?. I need the fastest method possible of getting the playing time of an mpg clip. The only way i know of is to query each clip using the MCI commands which is very long winded for a lot of clips :?

Spongehammer
I looked up 'paranoid' in the dictionary this morning.
It said, 'what do you want to know that for?'
Justin
Addict
Addict
Posts: 948
Joined: Sat Apr 26, 2003 2:49 pm

Post by Justin »

You could use Directshow, like in this example i did. if someone is interested in this i recommend to download the directx sdk. the IMediaDet Interface retrieves all info of a media file.

btw the lenght is converted from double to ulong, so it will be a limit in this.

Code: Select all

;Direct show example IMediaDet Interface
;Gets the lenght(secs) of the 1st video stream found in a media type, avi, mpg, etc..
;No error checking, choosing a non media type probably will crash
;Justin

Structure DOUBLE
	l1.l
	l2.l
EndStructure

Structure AM_MEDIA_TYPE
	majortype.GUID
	subtype.GUID
	bFixedSizeSamples.b
	bTemporalCompression.b
	lSampleSize.l
	formattype.GUID
	pUnk.l
	cbFormat.l
	pbFormat.l
EndStructure  

;String to BSTR
Procedure BSTR(st$)
	blen = Len(st$)*2 + 10
	buf = AllocateMemory(blen)
	MultiByteToWideChar_(#CP_ACP, 0, st$, -1, buf, blen)
	bstr = sysallocstring_(buf)
	FreeMemory(buf)
	ProcedureReturn bstr	
EndProcedure

#CLSCTX_INPROC_SERVER = 1

;CODE START

;vars
mt.AM_MEDIA_TYPE
fvideoFound.b
nstreams.l
dvlen.DOUBLE ;video len
lvlen.l ;video len

CoInitialize_(#NULL)

r = CoCreateInstance_(?CLSID_MediaDet, #NULL, #CLSCTX_INPROC_SERVER, ?IID_IMediaDet, @pMDet.IMediaDet)
If r<>0 : End : EndIf

file$ = OpenFileRequester("Choose video file", "", "", 0)
If file$="" : End : EndIf 

bfile = BSTR(file$)

pMDet\put_Filename(bfile)
sysfreestring_(bfile)

pMDet\get_OutputStreams(@nstreams)

fvideoFound = 0
For s=0 To nstreams-1
	pMDet\put_CurrentStream(s)
	pMDet\get_StreamMediaType(@mt.AM_MEDIA_TYPE)
	
	If CompareMemory(mt\majortype, ?MEDIATYPE_Video, 16) ;is video
		pMDet\get_StreamLength(@dvlen)
		
		VarI4FromR8_(dvlen\l1, dvlen\l2, @lvlen) ;double to unsigned long..
		
		Debug "Video seconds:"
		Debug lvlen
		
		fvideoFound = 1		
		break
	EndIf 
Next 

If fvideoFound = 0 
	Debug "Video stream not found"
EndIf 

pMDet\Release()

CoUnInitialize_()
End    

DataSection
	CLSID_MediaDet:
	Data.l $65BD0711
	Data.w $24D2, $4ff7
	Data.b $93, $24, $ed, $2e, $5d, $3a, $ba, $fa
	
	IID_IMediaDet:
	Data.l $65BD0710
	Data.w $24D2, $4ff7
	Data.b $93, $24, $ed, $2e, $5d, $3a, $ba, $fa
	
	MEDIATYPE_Video:
	Data.l $73646976
	Data.w $0000, $0010
	Data.b $80, $00, $00, $aa, $00, $38, $9b, $71	
	
	MEDIATYPE_Audio:
	Data.l $73647561
	Data.w $0000, $0010
	Data.b $80, $00, $00, $aa, $00, $38, $9b, $71	
EndDataSection
Froggerprogger
Enthusiast
Enthusiast
Posts: 423
Joined: Fri Apr 25, 2003 5:22 pm
Contact:

Post by Froggerprogger »

@spongehammer
There's no information on the total length of the MPEG-video in it's header and I googled around a bit, but found no information on how to get it. I suppose MPEG is so strongly designed as a stream that there's no time-overview inside. We would have to add the number of described frames inside the file manually, but I don't know how.

Perhaps it is possible to get the other data with DShow, too ? Or does it only work for the length ?

[edit]
the IMediaDet Interface retrieves all info of a media file.
Seems to be so :)
[/edit]
%1>>1+1*1/1-1!1|1&1<<$1=1
spongehammer
User
User
Posts: 84
Joined: Sat Jul 19, 2003 6:45 pm
Location: UK

Post by spongehammer »

Hi Justin,

that seems to work ok :D . I just need to get my head around whats happening in the code. Seems a bit complex for my tiny brain :D

Spongehammer
I looked up 'paranoid' in the dictionary this morning.
It said, 'what do you want to know that for?'
Justin
Addict
Addict
Posts: 948
Joined: Sat Apr 26, 2003 2:49 pm

Post by Justin »

yes it's a little complex, you'll need to read a lilltle of the interface in the sdk. but the principle is simple loop through all streams, get the type of the stream and get the data you want from the stream.

there is a typo in the comment, it's converting to signed long, not unsigned.
kwag
User
User
Posts: 35
Joined: Thu Jul 17, 2003 10:03 pm
Contact:

Post by kwag »

Here's a small program that will return the correct frame rate and the horizontal and vertical resolution of the material:

Code: Select all

If InitMovie() = 0
  MessageRequester("Error", "Can't initialize movie", 0) 
  End
EndIf

MovieName$ = OpenFileRequester("Please choose your video", "", "AVI files|*.avi|All Files|*.*", 0)
If MovieName$
  If LoadMovie(0, MovieName$)
  ; Valid AVI. Now release the movie.
   FreeMovie(0)
   
   ReadFile( 0, MovieName$ )
 
   FileSeek( 32  ) ; Jump to time delay between frames field
   timedelay.l = ReadLong()
   FileSeek( 32 + 32 ) ; Jump to width field
   width.l = ReadLong() ; Get width
   height.l = ReadLong() ;Get height
    
   OpenWindow(0, 400, 10, 400, 100, #PB_Window_SystemMenu, "AVI Properties - KW -")
    
    framerate.f = (1/timedelay) * 1000000 
    SetWindowTitle( 0, "Frames per second: " + StrF( framerate, #Long ) + ", Width=" + StrF( width, #Long ) + ", Height=" + StrF( height, #Long ) )
      
    Repeat
    Until WaitWindowEvent() = #PB_EventCloseWindow
  Else
    MessageRequester("Error", "Can't load video...", 0)
  EndIf
EndIf
-Karl
Post Reply