Note that this document contains some links to the old news2news website which does not work at the moment. This material will be available sometime in the future.
Custom class Tavi plays AVI files on main FoxPro window using API functions from avifil32 and msvfw32 libraries. Audio stream is not processed only to make this example more simple.
See also:
- Scrolling text on the form horizontally (a newsreel)
- Scrolling text on the form vertically (a movie cast)
- Shaking VFP form controls
- Displaying animated images
- Form Magnifier
= PlayAvi("c:\windows\clock.avi", 50, 100, .T.)
* end of main
FUNCTION PlayAvi(cAviFile, nLeft, nTop, lNonstop)
IF VARTYPE(_screen.avi) = "U"
_SCREEN.AddObject("avi", "Tavi")
ENDIF
WITH _SCREEN.avi
IF Not .StartPlay(cAviFile, nLeft, nTop, lNonstop)
= MessageB("Error code: " + LTRIM(STR(.ErrorCode)) +;
" " + Chr(13) + .ErrorMsg, 48, "Error")
ENDIF
ENDWITH
DEFINE CLASS Tavi As Timer
#DEFINE BHDR_SIZE 40 && BITMAPINFOHEADER
#DEFINE OF_SHARE_DENY_WRITE 0x00000020
#DEFINE streamtypeVIDEO "vids"
#DEFINE AVIGETFRAMEF_BESTDISPLAYFMT 1
#DEFINE AVISTREAMREAD_CONVENIENT -1
#DEFINE AVIFILEINFO_SIZE 108
PROTECTED hAviFile, hVideoStream, hGetFrame, hDrawDC, SrcWidth, SrcHeight,;
SrcScale, SrcRate, SrcLength, LeftPos, TopPos, starttime, IsDrawing
Interval=0
ErrorCode=0
ErrorMsg=""
nonstop=.F.
PROCEDURE Init
THIS.decl
= AVIFileInit() && AVI library initialized
PROCEDURE Destroy
THIS.StopPlay
= AVIFileExit() && AVI library released
PROCEDURE Timer
THIS.DrawFrame
PROCEDURE StartPlay(cFilename, nLeft, nTop, lNonstop)
THIS.StopPlay
THIS.nonstop = lNonstop
IF THIS.OpenFile(m.cFilename)
THIS.GetFileInfo
IF THIS.OpenVideoStream()
THIS.hDrawDC = DrawDibOpen() && creates a DrawDib DC
THIS.LeftPos = m.nLeft
THIS.TopPos = m.nTop
THIS.starttime = 0
THIS.IsDrawing = .F.
THIS.DrawFrame && draws first frame and then starts timer
THIS.Interval = Int((THIS.SrcScale/THIS.SrcRate) * 1000)
ENDIF
ENDIF
RETURN (THIS.ErrorCode = 0)
PROCEDURE StopPlay
STORE 0 TO THIS.Interval, THIS.ErrorCode
THIS.ErrorMsg = ""
IF Not EMPTY(THIS.hDrawDC)
= DrawDibClose(THIS.hDrawDC)
THIS.hDrawDC = 0
ENDIF
IF Not EMPTY(THIS.hGetFrame)
= AVIStreamGetFrameClose(THIS.hGetFrame)
THIS.hGetFrame = 0
ENDIF
IF Not EMPTY(THIS.hVideoStream)
= AVIStreamRelease(THIS.hVideoStream) && AVI stream released
THIS.hVideoStream = 0
ENDIF
IF Not EMPTY(THIS.hAviFile)
= AVIFileRelease(THIS.hAviFile) && AVI file released
THIS.hAviFile = 0
ENDIF
PROTECTED FUNCTION OpenFile(cFilename)
LOCAL hFile
hFile = 0
THIS.ErrorCode = AVIFileOpen(@hFile, cFilename, OF_SHARE_DENY_WRITE, 0)
IF THIS.ErrorCode = 0
THIS.hAviFile = m.hFile
RETURN .T.
ELSE
THIS.ErrorMsg="Could not open AVI file"
RETURN .F.
ENDIF
PROTECTED FUNCTION OpenVideoStream
LOCAL hStream, nFormatFlag
hStream = 0
THIS.ErrorCode = AVIFileGetStream(THIS.hAviFile,;
@hStream, buf2dword(streamtypeVIDEO), 0)
IF THIS.ErrorCode = 0
THIS.hVideoStream = m.hStream
nFormatFlag = 0 && or AVIGETFRAMEF_BESTDISPLAYFMT
THIS.hGetFrame = AVIStreamGetFrameOpen(THIS.hVideoStream, nFormatFlag)
IF THIS.hGetFrame = 0
THIS.ErrorCode = GetLastError()
THIS.ErrorMsg="Could not create GetFrame handle"
RETURN .F.
ELSE
RETURN .T.
ENDIF
ELSE
THIS.ErrorCode = GetLastError()
THIS.ErrorMsg="Could not open video stream"
RETURN .F.
ENDIF
PROTECTED PROCEDURE DrawFrame
IF THIS.IsDrawing Or THIS.hVideoStream = 0
RETURN
ENDIF
THIS.IsDrawing = .T.
LOCAL hWindow, hDC, hFrame, cBuffer, nBufsize, nTime, nSample
* calculating next sample to show
IF THIS.starttime = 0
THIS.starttime = GetTickCount()
ENDIF
nTime = GetTickCount() - THIS.starttime
nSample = AVIStreamTimeToSample(THIS.hVideoStream, nTime)
hFrame = AVIStreamGetFrame(THIS.hGetFrame, nSample)
IF hFrame <> 0
hWindow = GetActiveWindow() && main FoxPro window
hDC = GetWindowDC(hWindow)
* calculating offset to bits
nBufsize = BHDR_SIZE
cBuffer = Repli(Chr(0), nBufsize)
= AVIStreamReadFormat(THIS.hVideoStream, 0, @cBuffer, @nBufsize)
* draw frame using default width and height (-1)
= DrawDibDraw(THIS.hDrawDC, hDC, THIS.LeftPos, THIS.TopPos, -1, -1,;
hFrame, hFrame+nBufsize, 0, 0,;
THIS.SrcWidth, THIS.SrcHeight, 0)
= ReleaseDC(hWindow, hDC)
ENDIF
IF nSample >= THIS.SrcLength-1
IF THIS.nonstop
THIS.starttime = 0 && continue playing...
ELSE
THIS.StopPlay
ENDIF
ENDIF
THIS.IsDrawing = .F.
PROTECTED PROCEDURE GetFileInfo && obtains AVI file parameters
LOCAL cBuffer
cBuffer = Repli(Chr(0), AVIFILEINFO_SIZE)
= AVIFileInfo(THIS.hAviFile, @cBuffer, AVIFILEINFO_SIZE)
THIS.SrcWidth = buf2dword(SUBSTR(cBuffer, 21,4))
THIS.SrcHeight = buf2dword(SUBSTR(cBuffer, 25,4))
THIS.SrcScale = buf2dword(SUBSTR(cBuffer, 29,4))
THIS.SrcRate = buf2dword(SUBSTR(cBuffer, 33,4))
IF THIS.SrcRate = 0
THIS.SrcRate = MAX(THIS.SrcScale, 1)
ENDIF
THIS.SrcLength = buf2dword(SUBSTR(cBuffer, 37,4))
PROTECTED PROCEDURE decl
DECLARE AVIFileInit IN avifil32
DECLARE AVIFileExit IN avifil32
DECLARE INTEGER DrawDibOpen IN msvfw32
DECLARE INTEGER DrawDibClose IN msvfw32 INTEGER hdd
DECLARE INTEGER AVIFileRelease IN avifil32 INTEGER pfile
DECLARE INTEGER AVIStreamRelease IN avifil32 INTEGER pavi
DECLARE INTEGER AVIStreamGetFrameClose IN avifil32 INTEGER pget
DECLARE INTEGER AVIStreamGetFrame IN avifil32 INTEGER pgf, LONG lPos
DECLARE INTEGER ReleaseDC IN user32 INTEGER hwnd, INTEGER dc
DECLARE INTEGER GetWindowDC IN user32 INTEGER hwnd
DECLARE INTEGER GetActiveWindow IN user32
DECLARE INTEGER GetTickCount IN kernel32
DECLARE INTEGER GetLastError IN kernel32
DECLARE INTEGER AVIFileOpen IN avifil32;
INTEGER @ppfile, STRING szFile,;
INTEGER mode, INTEGER pclsidHandler
DECLARE INTEGER AVIFileInfo IN avifil32;
INTEGER pfile, STRING @pfi, INTEGER lSize
DECLARE INTEGER AVIFileGetStream IN avifil32;
INTEGER pfile, INTEGER @ppavi,;
INTEGER fccType, INTEGER lParam
DECLARE INTEGER AVIStreamGetFrameOpen IN avifil32;
INTEGER pavi, INTEGER lpbiWanted
DECLARE INTEGER DrawDibDraw IN msvfw32;
INTEGER hdd, INTEGER hdc, INTEGER xDst, INTEGER yDst,;
INTEGER dxDst, INTEGER dyDst, INTEGER lpbi, INTEGER lpBits,;
INTEGER xSrc, INTEGER ySrc, INTEGER dxSrc, INTEGER dySrc,;
INTEGER wFlags
DECLARE RtlMoveMemory IN kernel32 As CopyMemory;
STRING @Destination, INTEGER Source, INTEGER nLength
DECLARE INTEGER AVIStreamReadFormat IN avifil32;
INTEGER pavi, INTEGER lPos, STRING @lpFormat, INTEGER @lpcbFormat
DECLARE INTEGER AVIStreamTimeToSample IN avifil32;
INTEGER pavi, LONG lTime
ENDDEFINE
FUNCTION buf2dword(lcBuffer)
RETURN Asc(SUBSTR(lcBuffer, 1,1)) + ;
BitLShift(Asc(SUBSTR(lcBuffer, 2,1)), 8) +;
BitLShift(Asc(SUBSTR(lcBuffer, 3,1)), 16) +;
BitLShift(Asc(SUBSTR(lcBuffer, 4,1)), 24)
AVIFileExit
AVIFileGetStream
AVIFileInfo
AVIFileInit
AVIFileOpen
AVIFileRelease
AVIStreamGetFrame
AVIStreamGetFrameClose
AVIStreamGetFrameOpen
AVIStreamReadFormat
AVIStreamRelease
AVIStreamTimeToSample
DrawDibClose
DrawDibDraw
DrawDibOpen
GetActiveWindow
GetLastError
GetTickCount
GetWindowDC
ReleaseDC
Any other window can be used as a target (or a silver screen, if you like) for playing AVI files: FoxPro form, Windows desktop etc. Use that window handle instead of the handle obtained through the GetActiveWindow in the code above.
Technically, you can even play AVIs on a MessageBox window, once you know its window handle (examples 424 and 434 explain how to obtain such handle).
Note that on the last sample, the DrawFrame method either stops playing the AVI or resumes it from the first sample, depending on the nonstop property.
The set of DrawDib functions used in this example can be substituted with a call to the StretchDIBits.
Playing multimedia files in .NET code is a snap. Once you download and install DirectX SDK (merely 450 Mb) you are almost done.