Code archives/Graphics/Quicktime for Blitzmax
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
| Here is my implementation for Quicktime for Blitzmax. It currently only runs under Windows and MacOSX. Updated: 2011 Dec 27 - improved playback speed with DrawMovieImage() TO USE: I have this module living under the "blitzmax/mods/addons.mod" directory. MACOSX: You will have the framework installed with XCode. No need to install additional libraries. WINDOWS: In order for this to work, you will need to register and download Apple's Quicktime SDK for Windows (its free). Copy these files into the module's folder. Here is a sample folder layout: +blitzmax ++mod +++addons.mod ++++quicktime.mod +++++maxquicktime_glue.cpp +++++quicktime.bmx +++++QuicktimeSDK ++++++CIncludes ++++++Libraries ++++++RIncludes ++++++ComponentIncludes ++++++Tools You will also need to modify a couple lines in the "Math64.h" file (to compile under MINGW) to read as follows:
#if TYPE_LONGLONG && TARGET_OS_WIN32 && !defined(__MINGW32__)
#define S64Max() 9223372036854775807i64
..and...
#if TYPE_LONGLONG && TARGET_OS_WIN32 && !defined(__MINGW32__)
#define U64Max() 0xffffffffffffffffui64
WINDOWS & MACOSX You will then need the "maxquicktime_glue.cpp" file, and place it in the quicktime.mod folder. maxquicktime_glue.cpp The code is not foolproof, but it works. I've tested under win vista, win7, macosx 10.5.6 with QT 7. quicktime.bmx: | |||||
''
''Quicktime Module for MSWin & MacOSX
''by AdamRedwoods 2011
''
Module addons.quicktime
?Win32
ModuleInfo "CC_OPTS: -IQuickTimeSDK/CIncludes/"
ModuleInfo "CC_OPTS: -IQuickTimeSDK/CIncludes/CoreFoundation"
ModuleInfo "CC_OPTS: -IQuickTimeSDK/CIncludes/GNUCompatibility"
Import "maxquicktime_glue.cpp"
Import "QuickTimeSDK/Libraries/QTMLClient.lib"
Import "QuickTimeSDK/Libraries/CVClient.lib"
?MacOS
'ModuleInfo "LD_OPTS: -framework/System/Library/Frameworks/QuickTime.framework"
ModuleInfo "LD_OPTS: -framework QuickTime"
Import "maxquicktime_glue.cpp"
?
Import brl.Pixmap
Import brl.glmax2d
Import brl.standardio
Import brl.GLGraphics
Extern
Function bmx_CreateQT:Byte Ptr() ''cpp class
Function EnterMovies() ' Initialize QuickTime
Function ExitMovies() ' Terminate QuickTime
?Win32
Function TerminateQTML() 'terminate QTML
Function InitializeQTML:Int(i:Int) ' Initialize QTML
?
Function bmx_OpenMovieFile:Int( qtclass:Byte Ptr, permission:Int) ''return -1 if error
Function bmx_CloseMovieFile(qtclass:Byte Ptr)
Function bmx_NewMovieFromFile:Int( qtclass:Byte Ptr, filename:Byte Ptr)
Function bmx_NewMovieFromURL:Int( qtclass:Byte Ptr, filename:Byte Ptr)
Function bmx_NewMovieFromFileRef:Int(qtclass:Byte Ptr, flags:Int)
Function bmx_GetMovieLoadState:Int(qtclass:Byte Ptr)
'''EXTERN_API( ComponentInstance ) NewMovieController( Movie theMovie, Const Rect * movieRect, Long someFlags);
Function bmx_NewMovieController:Int( qtclass:Byte Ptr, flags:Int=0) '' create new movie controller
Function bmx_SetMovieActive(qtclass:Byte Ptr , boolean:Int)
Function bmx_StartMovie(qtclass:Byte Ptr )
Function bmx_MoviesTask( qtclass:Byte Ptr, flags:Int=0)
Function bmx_UpdateMovie:Int(qtclass:Byte Ptr )
Function bmx_StopMovie(qtclass:Byte Ptr)
Function bmx_GoToBeginningOfMovie(qtclass:Byte Ptr )
Function bmx_IsMovieDone(qtclass:Byte Ptr )
Function bmx_SetMovieVolume ( qtclass:Byte Ptr, volume:Float) ''volume is signed short: -1.0 to 1.0 ''neg volume is silent
Function bmx_GetMovieNextInterestingTime:Int(qtclass:Byte Ptr, flags:Int)
Function bmx_SetMovieTimeValue(qtclass:Byte Ptr, newtime:Int)
Function bmx_DisposeMovieController (qtclass:Byte Ptr) ' // Close movie controller, if any
Function bmx_DisposeMovie (qtclass:Byte Ptr) ' // Destroy movie Object, If any
Function bmx_FSMakeFSSpec:Int(qtclass:Byte Ptr, fullPath:Byte Ptr) ''in windows, pass full pathname
''' FSMakeFSRefUnicode ( Const FSRef *parentRef, UniCharCount nameLength, Const UniChar *name, TextEncoding textEncodingHint,FSRef *newRef)
''Function FSMakeFSRefUnicode (x:Int=0, filenamelength:Int, name:String, econdingHintConst:Int=0, newf:FSRef)
Function bmx_NativePathNameToFSSpec:Int(qtclass:Byte Ptr, filename:Byte Ptr, flag:Int=0)
Function bmx_QTNewGWorldFromPtr:Int(qtclass:Byte Ptr, pixelformat:Int, w:Int, h:Int, buffer:Byte Ptr, rowbytes:Int, GWorldFlags:Int=0)
Function bmx_NewGWorld:Int(qtclass:Byte Ptr, w:Int, h:Int, flags:Int)
Function bmx_SetMovieGWorld:Int(qtclass:Byte Ptr)
Function bmx_SetGWorld(qtclass:Byte Ptr) ' // Port Or Graphics world To make Current ''gdhandle null if gworld
Function bmx_DisposeGWorld(qtclass:Byte Ptr) ''; //close gworld
Function bmx_GetGWorldDevice:Int(qtclass:Byte Ptr)
Function bmx_LockPixels:Int(qtclass:Byte Ptr) ''return true 1 or false 0
Function bmx_UnlockPixels(qtclass:Byte Ptr)
''Function CopyCStringToPascal(src:String, dst:String)
Function c2pstr:Int(src:Byte Ptr)
Function bmx_SetMoviePlayHints(qtclass:Byte Ptr , hints:Int=0, hint2:Int=0)
'hintsDontUseVideoOverlaySurface
Function GetPixBaseAddr:Byte Ptr(pmhd:Object )
Function GetGWorldPixMap:Byte Ptr(gw:Byte Ptr )
'''EXTERN_API( Boolean ) PixMap32Bit(PixMapHandle pmHandle)
''Function GetPixRowBytes:int(pmhd:Object) ''NOT IN MSWIN
Function bmx_GetMovieBox(qtclass:Byte Ptr)
Function bmx_GetMovieWidth(qtclass:Byte Ptr)
Function bmx_GetMovieHeight(qtclass:Byte Ptr)
Function bmx_GetMovieDuration:Int(qtclass:Byte Ptr)
Function bmx_GetMovieTime:Int(qtclass:Byte Ptr)
Function bmx_GetMovieTimeScale:Int(qtclass:Byte Ptr)
Function bmx_GetMovieTrackCount:Int(qtclass:Byte Ptr)
Function bmx_PreRollMovie( qtclass:Byte Ptr, time:Int)
EndExtern
Const k1MonochromePixelFormat:Int = $00000001', /* 1 bit indexed*/
Const k2IndexedPixelFormat:Int = $00000002', /* 2 bit indexed*/
Const k4IndexedPixelFormat:Int = $00000004', /* 4 bit indexed*/
Const k8IndexedPixelFormat:Int = $00000008', /* 8 bit indexed*/
Const k16BE555PixelFormat:Int = $00000010', /* 16 bit BE rgb 555 (Mac)*/
Const k24RGBPixelFormat:Int = $00000018', /* 24 bit rgb */
Const k32ARGBPixelFormat:Int = $00000020', /* 32 bit argb (Mac)*/
Const k1IndexedGrayPixelFormat:Int = $00000021', /* 1 bit indexed gray*/
Const k2IndexedGrayPixelFormat:Int = $00000022', /* 2 bit indexed gray*/
Const k4IndexedGrayPixelFormat:Int = $00000024', /* 4 bit indexed gray*/
Const k8IndexedGrayPixelFormat:Int = $00000028' /* 8 bit indexed gray*/
Const k32BGRAPixelFormat:Int = 1111970369
Const keepLocal:Int =8
Const NEWMOVIEACTIVE:Int = 1 Shl 0
Const mcTopLeftMovie:Int = 1 Shl 0 ', /* usually centered */
Const mcScaleMovieToFit:Int = 1 Shl 1 ', /* usually only scales down */
Const mcWithBadge:Int = 1 Shl 2 ', /* give me a badge */
Const mcNotVisible:Int = 1 Shl 3 ', /* don't show controller */
Const mcWithFrame :Int = 1 Shl 4
Const hintsDontUseVideoOverlaySurface:Int = 1 Shl 16
Const hintsOffscreen:Int = 1 Shl 12
Const hintsUseScreenBuffer:Int = 1 Shl 5
Const hintsAllowDynamicResize:Int = 1 Shl 19
Const kInitializeQTMLUseGDIFlag:Int = 1 Shl 1
Const deviceIsExternalBuffer:Int = (1 Shl 3)
Const deviceIsGDISurface:Int = 64
Const deviceIsIndirect:Int = 1
''nextInterestingTime flags
Const nextTimeStep:Int = 1 Shl 4
'nextTimeMediaSample ''not recommended since messes up on mpeg
'nextTimeEdgeOK
Const kMovieLoadStateError:Int = -1
Const kMovieLoadStateLoading:Int = 1000
Const kMovieLoadStateLoaded:Int = 2000
Const kMovieLoadStatePlayable:Int = 10000
Const kMovieLoadStatePlaythroughOK:Int = 20000
Const kMovieLoadStateComplete:Int = 100000
Rem
hintsScrubMode = 1 << 0, /* mask == && (if flags == scrub on, flags != scrub off) */
hintsLoop = 1 << 1,
hintsDontPurge = 1 << 2,
hintsUseScreenBuffer = 1 << 5,
hintsAllowInterlace = 1 << 6,
hintsUseSoundInterp = 1 << 7,
hintsHighQuality = 1 << 8, /* slooooow */
hintsPalindrome = 1 << 9,
hintsInactive = 1 << 11,
hintsOffscreen = 1 << 12,
hintsDontDraw = 1 << 13,
hintsAllowBlacklining = 1 << 14,
hintsDontUseVideoOverlaySurface = 1 << 16,
hintsIgnoreBandwidthRestrictions = 1 << 17,
hintsPlayingEveryFrame = 1 << 18,
hintsAllowDynamicResize = 1 << 19,
hintsSingleField = 1 << 20,
hintsNoRenderingTimeOut = 1 << 21,
hintsFlushVideoInsteadOfDirtying = 1 << 22,
hintsEnableSubPixelPositioning = 1L << 23,
hintsRenderingMode = 1L << 24,
hintsAllowIdleSleep = 1L << 25, /* asks media handlers not to call UpdateSystemActivity etc */
hintsDeinterlaceFields = 1L << 26
EndRem
Type TQuickTime
Field isQTInit:Int =0
Field myQTClass:Byte Ptr = Null
Field debug:Int
Field _pixmap:TPixmap
Field texid:Int
Field imageframe:TGLImageFrame
Method Init:Int(flags:Int=0)
If (isQTInit) Then Return True
If StartQT(flags) <> 0 Then Return False
If EnterMovies() <> 0
If debug Then Print "EnterMovies fail"
EndQT()
Return False
EndIf
myQTClass = bmx_CreateQT()
isQTInit =1
If debug Then Print "INIT ok"
Return True
EndMethod
Method StartQT:Int(flags:Int =0)
?Win32
Local r:Int = InitializeQTML(flags) ''the check for QT on system
If r Then Print "Quicktime not found."
Return r
?MacOS
Return 0 ''assume QT is installed
?
Return 0 ''0= success (error code) in QT
EndMethod
Method EndQT()
?Win32
TerminateQTML()
?
Return
EndMethod
Method LoadMovieToPixmap:Int(filename:String, pixmap:TPixmap, flags:Int=0, isURL:Int=0)
Init()
If Not myQTClass Or Not pixmap Then Return False
Local r:Int
Local pixw:Int = pixmap.width
Local pixh:Int = pixmap.height
r = OpenMovie(filename, 0, isURL)'newMovieActive)
If r Then Return False
SetupGWorld(pixmap, flags)
Return True
EndMethod
Method LoadMovieToImage:Int(filename:String, img:TImage, flags:Int=0, isURL:Int=0)
Init()
If Not myQTClass Or Not img Then Return False
Local r:Int
Local pixw:Int = img.width
Local pixh:Int = img.height
r = OpenMovie(filename, 0, isURL)'newMovieActive)
If r Then Return False
If Not img.pixmaps[0] Then img.pixmaps[0] = TPixmap.Create( img.width,img.height,PF_RGB888 )
If img.pixmaps[0].format <> PF_RGB888 Then img.pixmaps[0] = img.pixmaps[0].convert(PF_RGB888) ;Print "convert to rgb888"
SetupGWorld(img.pixmaps[0], flags)
Return True
EndMethod
Method SetupGWorld:Int(pixmap:TPixmap, flags:Int=0)
If Not pixmap Then Return False
Local r:Int
'r=bmx_NewGWorld(myQTClass, 900, 600, 0)
r=bmx_QTNewGWorldFromPtr(myQTClass, 24, pixmap.width, pixmap.height, PixmapPixelPtr(pixmap), pixmap.pitch, deviceIsExternalBuffer)'deviceIsIndirect)
If debug Then Print "Gworld: "+uinttoint(r)
r = bmx_SetGWorld(myQTClass) ''works better than setmoviegworld in osx
r = bmx_SetMovieGWorld(myQTClass) ''need both for windows
If debug Then Print "setGW: "+uinttoint(r)
r = bmx_NewMovieController (myQTClass, mcTopLeftMovie|mcNotVisible|flags)
If debug Then Print "controller: " +uinttoint(r)
bmx_SetMoviePlayHints(myQTClass, hintsOffscreen|hintsAllowDynamicResize, hintsOffscreen|hintsAllowDynamicResize )
If debug Then Print "hints ok"
r=bmx_SetMovieActive(myQTClass,True)
If debug Print "setactivem: "+uinttoint(r)
GotoBeginning()
_pixmap = pixmap
Return True
EndMethod
''Opening a movie does not necessarily load the entirety
Method OpenMovie:Int(filename:String, flags:Int =0, isURL:Int = 0)
Init()
If Not myQTClass Then Return -1
''determine isURL
If Not isURL And filename.StartsWith("http:") Then isURL = 1
Local r:Int
'r = bmx_NativePathNameToFSSpec(myQTClass, filename, 0)
'If debug Then Print "fsspec: " +uinttoint(r)
'r= bmx_OpenMovieFile( myQTClass, 1)
'If debug Then Print "openfile "+uinttoint(r)
'If(r) Then Return -1
''flags = newMovieActive
'r=bmx_NewMovieFromFile(myQTClass, flags)
If isURL Then r = bmx_NewMovieFromURL(myQTClass, filename) Else r = bmx_NewMovieFromFile(myQTClass, filename)
Local myLoadState:Int= kMovieLoadStateLoading
Local tick:Int =MilliSecs()
While Not (myLoadState & kMovieLoadStatePlaythroughOK) 'And tick <> 0
bmx_GetMovieDuration(myQTClass)
myLoadState = bmx_GetMovieLoadState(myQTClass)
Wend
If debug Then Print "newmovie: "+uinttoint(r)
bmx_CloseMovieFile(myQTClass) ''success close file
''get width and height and etc
bmx_GetMovieBox(myQTClass)
Return 0
EndMethod
Method IsMovieReady:Int()
Local k:Int = bmx_GetMovieLoadState(myQTClass)
If k & kMovieLoadStatePlaythroughOK Then Return True
Return False
EndMethod
Method CloseMovie()
If Not myQTClass Then Return
ExitMovies()
bmx_DisposeMovieController (myQTClass)
bmx_DisposeGWorld(myQTClass)
EndQT()
isQtInit =0
If debug Then Print "CloseMovie ok"
EndMethod
Method GetMovieWidth:Int()
Return bmx_GetMovieWidth(myQTClass)
EndMethod
Method GetMovieHeight:Int()
Return bmx_GetMovieHeight(myQTClass)
EndMethod
'' in seconds
Method GetMovieDuration:Float()
Return bmx_GetMovieDuration(myQTClass)/Float(bmx_GetMovieTimeScale(myQTClass))
EndMethod
Method GetMovieTracks:Int()
Return bmx_GetMovieTrackCount(myQTClass)
EndMethod
''in ticks
Method GetMovieTime:Int()
Return bmx_GetMovieTime(myQTClass)
EndMethod
'' in seconds
Method GetMovieTimeSecs:Float()
Return bmx_GetMovieTime(myQTClass)/Float(bmx_GetMovieTimeScale(myQTClass))
EndMethod
'' volume is between 0.0 and 1.0
Method SetMovieVolume(vol:Float)
bmx_SetMovieVolume(myQTClass, vol)
EndMethod
''movie time in ticks
Method SetMovieTime(ms:Int)
bmx_SetMovieTimeValue(myQTClass, ms )
EndMethod
Method StartMovie()
If Not myQTClass Then Return
bmx_StartMovie(myQTClass)
If debug Then Print "startmovie ok"
EndMethod
Method StopMovie()
If Not myQTClass Then Return
bmx_StopMovie(myQTClass)
If debug Then Print "stopmovie ok"
EndMethod
Method UpdateMovie(update:Int=0)
'If update Then bmx_UpdateMovie(myQTClass)
bmx_MoviesTask(myQTClass,0)
'bmx_UpdateMovie(myQTClass)
EndMethod
Method IsMovieDone:Int()
Return bmx_IsMovieDone(myQTClass)
EndMethod
Method DrawMovieImage(img:TImage, x:Int, y:Int)
If Not img Or Not _pixmap Then Return
If Not texid
imageframe = TGLImageFrame.CreateFromPixmap( _pixmap,0 )
texid = imageframe.name
If debug Then Print "draw movie bind"
EndIf
img.pixmaps[0]=_pixmap
img.seqs[0]=0
If texid
glBindTexture(GL_TEXTURE_2D, texid)
glPixelStorei GL_UNPACK_ROW_LENGTH, _pixmap.pitch/BytesPerPixel[_pixmap.format]
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _pixmap.width, _pixmap.height, GL_RGB, GL_UNSIGNED_BYTE, _pixmap.pixels)
glPixelStorei GL_UNPACK_ROW_LENGTH,0
imageframe.Draw( 0,0,_pixmap.width,_pixmap.height, x, y ,0,0,_pixmap.width,_pixmap.height)
EndIf
EndMethod
Method GotoBeginning()
bmx_GoToBeginningOfMovie(myQTClass)
EndMethod
EndType
Function UintToInt:Int(value:Int)
Const OFFSET_2:Int = 65536
Const MAXINT_2:Int = 32767
If Value < 0 Or Value >= OFFSET_2 Then Return value ' Overflow
If Value <= MAXINT_2 Then
Return Value
Else
Return Value - OFFSET_2
End If
End Function |
Comments
| ||
| Here is a sample test program. You will need to grab your own "Ironman2" trailer for the test or use the URLs. Use the mouse to click to pause/play and click-hold with a move left/right to scrub the movie. SEE UPDATED CODE BELOW |
| ||
| I have this module living under Blitzmax's "pub" module directory. Not a good idea -- the pub and brl trees both get erased each time you install a blitzmax update on top of an existing installation. You really should make your own subfolder under the mod folder to prevent that from happening. :-? but an interesting addition nonetheless. Does it do both video and audio, or just video? |
| ||
ModuleInfo "BCC_OPTS: -O2" I don't think there is such an option - unless Mark sneaked something in there when I wasn't looking? The latest version of BlitzMax may already be using -O2, however. |
| ||
| Thanks for the tips! I've updated and revised the source code. Yes, this quicktime module plays audio and video. The sample test program also allows scrubbing through the movie as well. |
| ||
| - Updated module, now works with OSX. - Also added capabilities to load from URL. See example test file for cool trailers to view. |
| ||
| UPDATED 2011 dec 27 - improved playback speed using DrawMovieImage( myimage, x, y)... I was able to play two apple trailers at the same time - allows scaling, coloring, etc. - can only use OpenGL UPDATED sample code: |
| ||
| (deleted) |
| ||
| mar 2012 - possible case issue for win32 CCOPTS in "QuickTime". corrected above. |
| ||
| Recently I tried rebuilding all of BlitzMax's modules using the latest version of OS X & Xcode and received this error from maxquicktime_glue.cpp. Compile Error error: 'NewGWorld' was not declared in this scope |
| ||
| @CB: Same here. |
Code Archives Forum