1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/mediactrl_qt.cpp
3 // Purpose: QuickTime Media Backend for Windows
4 // Author: Ryan Norton <wxprojects@comcast.net>
5 // Modified by: Robin Dunn (moved QT code from mediactrl.cpp)
9 // Copyright: (c) Ryan Norton
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
14 //===========================================================================
16 //===========================================================================
18 //---------------------------------------------------------------------------
19 // Pre-compiled header stuff
20 //---------------------------------------------------------------------------
22 // For compilers that support precompilation, includes "wx.h".
23 #include "wx/wxprec.h"
31 #include "wx/mediactrl.h"
35 #include "wx/dcclient.h"
37 #include "wx/math.h" // log10 & pow
40 #include "wx/msw/private.h" // user info and wndproc setting/getting
41 #include "wx/dynlib.h"
43 //---------------------------------------------------------------------------
44 // Externals (somewhere in src/msw/app.cpp and src/msw/window.cpp)
45 //---------------------------------------------------------------------------
46 extern "C" WXDLLIMPEXP_BASE HINSTANCE
wxGetInstance(void);
48 extern WXDLLIMPEXP_CORE wxChar
*wxCanvasClassName
;
50 extern WXDLLIMPEXP_CORE
const wxChar
*wxCanvasClassName
;
53 LRESULT WXDLLIMPEXP_CORE APIENTRY _EXPORT
wxWndProc(HWND hWnd
, UINT message
,
54 WPARAM wParam
, LPARAM lParam
);
56 //---------------------------------------------------------------------------
57 // Killed MSVC warnings
58 //---------------------------------------------------------------------------
59 //disable "cast truncates constant value" for VARIANT_BOOL values
60 //passed as parameters in VC5 and up
62 #pragma warning (disable:4310)
66 //---------------------------------------------------------------------------
69 // We don't include Quicktime headers here and define all the types
70 // ourselves because looking for the quicktime libaries etc. would
71 // be tricky to do and making this a dependency for the MSVC projects
72 // would be unrealistic.
74 // Thanks to Robert Roebling for the wxDL macro/library idea
75 //---------------------------------------------------------------------------
77 //---------------------------------------------------------------------------
79 //---------------------------------------------------------------------------
80 //#include <qtml.h> // Windoze QT include
81 //#include <QuickTimeComponents.h> // Standard QT stuff
82 #include "wx/dynlib.h"
84 //---------------------------------------------------------------------------
86 //---------------------------------------------------------------------------
87 typedef struct MovieRecord
* Movie
;
88 typedef wxInt16 OSErr
;
89 typedef wxInt32 OSStatus
;
92 typedef unsigned char Str255
[256];
93 #define StringPtr unsigned char*
94 #define newMovieActive 1
95 #define newMovieAsyncOK (1 << 8)
99 #define OSType unsigned long
100 #define CGrafPtr struct GrafPort *
101 #define TimeScale long
102 #define TimeBase struct TimeBaseRecord *
103 typedef struct ComponentInstanceRecord
* ComponentInstance
;
104 #define kMovieLoadStatePlayable 10000
106 #define MovieController ComponentInstance
108 #ifndef URLDataHandlerSubType
109 #if defined(__WATCOMC__) || defined(__MINGW32__)
110 // use magic numbers for compilers which complain about multicharacter integers
111 const OSType URLDataHandlerSubType
= 1970433056;
112 const OSType VisualMediaCharacteristic
= 1702454643;
114 const OSType URLDataHandlerSubType
= 'url ';
115 const OSType VisualMediaCharacteristic
= 'eyes';
123 Str255 name
; // Str63 on mac, Str255 on msw
143 TimeScale scale
; // units per second
165 mcScaleMovieToFit
= 2,
171 //---------------------------------------------------------------------------
173 //---------------------------------------------------------------------------
174 #define wxDL_METHOD_DEFINE( rettype, name, args, shortargs, defret ) \
175 typedef rettype (* name ## Type) args ; \
176 name ## Type pfn_ ## name; \
178 { if (m_ok) return pfn_ ## name shortargs ; return defret; }
180 #define wxDL_VOIDMETHOD_DEFINE( name, args, shortargs ) \
181 typedef void (* name ## Type) args ; \
182 name ## Type pfn_ ## name; \
184 { if (m_ok) pfn_ ## name shortargs ; }
186 #define wxDL_METHOD_LOAD( lib, name, success ) \
187 pfn_ ## name = (name ## Type) lib.GetSymbol( wxT(#name), &success ); \
188 if (!success) return false
191 class WXDLLIMPEXP_MEDIA wxQuickTimeLibrary
194 ~wxQuickTimeLibrary()
196 if (m_dll
.IsLoaded())
201 bool IsOk() const {return m_ok
;}
204 wxDynamicLibrary m_dll
;
208 wxDL_VOIDMETHOD_DEFINE( StartMovie
, (Movie m
), (m
) )
209 wxDL_VOIDMETHOD_DEFINE( StopMovie
, (Movie m
), (m
) )
210 wxDL_METHOD_DEFINE( bool, IsMovieDone
, (Movie m
), (m
), false)
211 wxDL_VOIDMETHOD_DEFINE( GoToBeginningOfMovie
, (Movie m
), (m
) )
212 wxDL_METHOD_DEFINE( OSErr
, GetMoviesError
, (), (), -1)
213 wxDL_METHOD_DEFINE( OSErr
, EnterMovies
, (), (), -1)
214 wxDL_VOIDMETHOD_DEFINE( ExitMovies
, (), () )
215 wxDL_METHOD_DEFINE( OSErr
, InitializeQTML
, (long flags
), (flags
), -1)
216 wxDL_VOIDMETHOD_DEFINE( TerminateQTML
, (), () )
218 wxDL_METHOD_DEFINE( OSErr
, NativePathNameToFSSpec
,
219 (char* inName
, FSSpec
* outFile
, long flags
),
220 (inName
, outFile
, flags
), -1)
222 wxDL_METHOD_DEFINE( OSErr
, OpenMovieFile
,
223 (const FSSpec
* fileSpec
, short * resRefNum
, wxInt8 permission
),
224 (fileSpec
, resRefNum
, permission
), -1 )
226 wxDL_METHOD_DEFINE( OSErr
, CloseMovieFile
,
227 (short resRefNum
), (resRefNum
), -1)
229 wxDL_METHOD_DEFINE( OSErr
, NewMovieFromFile
,
230 (Movie
* theMovie
, short resRefNum
, short * resId
,
231 StringPtr resName
, short newMovieFlags
,
232 bool * dataRefWasChanged
),
233 (theMovie
, resRefNum
, resId
, resName
, newMovieFlags
,
234 dataRefWasChanged
), -1)
236 wxDL_VOIDMETHOD_DEFINE( SetMovieRate
, (Movie m
, Fixed rate
), (m
, rate
) )
237 wxDL_METHOD_DEFINE( Fixed
, GetMovieRate
, (Movie m
), (m
), 0)
238 wxDL_VOIDMETHOD_DEFINE( MoviesTask
, (Movie m
, long maxms
), (m
, maxms
) )
239 wxDL_VOIDMETHOD_DEFINE( BlockMove
,
240 (const char* p1
, const char* p2
, long s
), (p1
,p2
,s
) )
241 wxDL_METHOD_DEFINE( Handle
, NewHandleClear
, (long s
), (s
), NULL
)
243 wxDL_METHOD_DEFINE( OSErr
, NewMovieFromDataRef
,
244 (Movie
* m
, short flags
, short * id
,
245 Handle dataRef
, OSType dataRefType
),
246 (m
,flags
,id
,dataRef
,dataRefType
), -1 )
248 wxDL_VOIDMETHOD_DEFINE( DisposeHandle
, (Handle h
), (h
) )
249 wxDL_VOIDMETHOD_DEFINE( GetMovieNaturalBoundsRect
, (Movie m
, Rect
* r
), (m
,r
) )
250 wxDL_METHOD_DEFINE( void*, GetMovieIndTrackType
,
251 (Movie m
, long index
, OSType type
, long flags
),
252 (m
,index
,type
,flags
), NULL
)
253 wxDL_VOIDMETHOD_DEFINE( CreatePortAssociation
,
254 (void* hWnd
, void* junk
, long morejunk
), (hWnd
, junk
, morejunk
) )
255 wxDL_METHOD_DEFINE(void*, GetNativeWindowPort
, (void* hWnd
), (hWnd
), NULL
)
256 wxDL_VOIDMETHOD_DEFINE(SetMovieGWorld
, (Movie m
, CGrafPtr port
, void* whatever
),
257 (m
, port
, whatever
) )
258 wxDL_VOIDMETHOD_DEFINE(DisposeMovie
, (Movie m
), (m
) )
259 wxDL_VOIDMETHOD_DEFINE(SetMovieBox
, (Movie m
, Rect
* r
), (m
,r
))
260 wxDL_VOIDMETHOD_DEFINE(SetMovieTimeScale
, (Movie m
, long s
), (m
,s
))
261 wxDL_METHOD_DEFINE(long, GetMovieDuration
, (Movie m
), (m
), 0)
262 wxDL_METHOD_DEFINE(TimeBase
, GetMovieTimeBase
, (Movie m
), (m
), 0)
263 wxDL_METHOD_DEFINE(TimeScale
, GetMovieTimeScale
, (Movie m
), (m
), 0)
264 wxDL_METHOD_DEFINE(long, GetMovieTime
, (Movie m
, void* cruft
), (m
,cruft
), 0)
265 wxDL_VOIDMETHOD_DEFINE(SetMovieTime
, (Movie m
, TimeRecord
* tr
), (m
,tr
) )
266 wxDL_METHOD_DEFINE(short, GetMovieVolume
, (Movie m
), (m
), 0)
267 wxDL_VOIDMETHOD_DEFINE(SetMovieVolume
, (Movie m
, short sVolume
), (m
,sVolume
) )
268 wxDL_VOIDMETHOD_DEFINE(SetMovieTimeValue
, (Movie m
, long s
), (m
,s
))
269 wxDL_METHOD_DEFINE(ComponentInstance
, NewMovieController
, (Movie m
, const Rect
* mr
, long fl
), (m
,mr
,fl
), 0)
270 wxDL_VOIDMETHOD_DEFINE(DisposeMovieController
, (ComponentInstance ci
), (ci
))
271 wxDL_METHOD_DEFINE(int, MCSetVisible
, (ComponentInstance m
, int b
), (m
, b
), 0)
273 wxDL_VOIDMETHOD_DEFINE(PrePrerollMovie
, (Movie m
, long t
, Fixed r
, WXFARPROC p1
, void* p2
), (m
,t
,r
,p1
,p2
) )
274 wxDL_VOIDMETHOD_DEFINE(PrerollMovie
, (Movie m
, long t
, Fixed r
), (m
,t
,r
) )
275 wxDL_METHOD_DEFINE(Fixed
, GetMoviePreferredRate
, (Movie m
), (m
), 0)
276 wxDL_METHOD_DEFINE(long, GetMovieLoadState
, (Movie m
), (m
), 0)
277 wxDL_METHOD_DEFINE(void*, NewRoutineDescriptor
, (WXFARPROC f
, int l
, void* junk
), (f
, l
, junk
), 0)
278 wxDL_VOIDMETHOD_DEFINE(DisposeRoutineDescriptor
, (void* f
), (f
))
279 wxDL_METHOD_DEFINE(void*, GetCurrentArchitecture
, (), (), 0)
280 wxDL_METHOD_DEFINE(int, MCDoAction
, (ComponentInstance ci
, long f
, void* p
), (ci
,f
,p
), 0)
281 wxDL_VOIDMETHOD_DEFINE(MCSetControllerBoundsRect
, (ComponentInstance ci
, Rect
* r
), (ci
,r
))
282 wxDL_VOIDMETHOD_DEFINE(DestroyPortAssociation
, (CGrafPtr g
), (g
))
283 wxDL_VOIDMETHOD_DEFINE(NativeEventToMacEvent
, (MSG
* p1
, EventRecord
* p2
), (p1
,p2
))
284 wxDL_VOIDMETHOD_DEFINE(MCIsPlayerEvent
, (ComponentInstance ci
, EventRecord
* p2
), (ci
, p2
))
285 wxDL_METHOD_DEFINE(int, MCSetMovie
, (ComponentInstance ci
, Movie m
, void* p1
, Point w
),
287 wxDL_VOIDMETHOD_DEFINE(MCPositionController
,
288 (ComponentInstance ci
, Rect
* r
, void* junk
, void* morejunk
), (ci
,r
,junk
,morejunk
))
289 wxDL_VOIDMETHOD_DEFINE(MCSetActionFilterWithRefCon
,
290 (ComponentInstance ci
, WXFARPROC cb
, void* ref
), (ci
,cb
,ref
))
291 wxDL_VOIDMETHOD_DEFINE(MCGetControllerInfo
, (MovieController mc
, long* flags
), (mc
,flags
))
292 wxDL_VOIDMETHOD_DEFINE(BeginUpdate
, (CGrafPtr port
), (port
))
293 wxDL_VOIDMETHOD_DEFINE(UpdateMovie
, (Movie m
), (m
))
294 wxDL_VOIDMETHOD_DEFINE(EndUpdate
, (CGrafPtr port
), (port
))
295 wxDL_METHOD_DEFINE( OSErr
, GetMoviesStickyError
, (), (), -1)
298 bool wxQuickTimeLibrary::Initialize()
302 // Turn off the wxDynamicLibrary logging as we're prepared to handle the
306 if (!m_dll
.Load(wxT("qtmlClient.dll")))
311 wxDL_METHOD_LOAD( m_dll
, StartMovie
, m_ok
);
312 wxDL_METHOD_LOAD( m_dll
, StopMovie
, m_ok
);
313 wxDL_METHOD_LOAD( m_dll
, IsMovieDone
, m_ok
);
314 wxDL_METHOD_LOAD( m_dll
, GoToBeginningOfMovie
, m_ok
);
315 wxDL_METHOD_LOAD( m_dll
, GetMoviesError
, m_ok
);
316 wxDL_METHOD_LOAD( m_dll
, EnterMovies
, m_ok
);
317 wxDL_METHOD_LOAD( m_dll
, ExitMovies
, m_ok
);
318 wxDL_METHOD_LOAD( m_dll
, InitializeQTML
, m_ok
);
319 wxDL_METHOD_LOAD( m_dll
, TerminateQTML
, m_ok
);
320 wxDL_METHOD_LOAD( m_dll
, NativePathNameToFSSpec
, m_ok
);
321 wxDL_METHOD_LOAD( m_dll
, OpenMovieFile
, m_ok
);
322 wxDL_METHOD_LOAD( m_dll
, CloseMovieFile
, m_ok
);
323 wxDL_METHOD_LOAD( m_dll
, NewMovieFromFile
, m_ok
);
324 wxDL_METHOD_LOAD( m_dll
, GetMovieRate
, m_ok
);
325 wxDL_METHOD_LOAD( m_dll
, SetMovieRate
, m_ok
);
326 wxDL_METHOD_LOAD( m_dll
, MoviesTask
, m_ok
);
327 wxDL_METHOD_LOAD( m_dll
, BlockMove
, m_ok
);
328 wxDL_METHOD_LOAD( m_dll
, NewHandleClear
, m_ok
);
329 wxDL_METHOD_LOAD( m_dll
, NewMovieFromDataRef
, m_ok
);
330 wxDL_METHOD_LOAD( m_dll
, DisposeHandle
, m_ok
);
331 wxDL_METHOD_LOAD( m_dll
, GetMovieNaturalBoundsRect
, m_ok
);
332 wxDL_METHOD_LOAD( m_dll
, GetMovieIndTrackType
, m_ok
);
333 wxDL_METHOD_LOAD( m_dll
, CreatePortAssociation
, m_ok
);
334 wxDL_METHOD_LOAD( m_dll
, DestroyPortAssociation
, m_ok
);
335 wxDL_METHOD_LOAD( m_dll
, GetNativeWindowPort
, m_ok
);
336 wxDL_METHOD_LOAD( m_dll
, SetMovieGWorld
, m_ok
);
337 wxDL_METHOD_LOAD( m_dll
, DisposeMovie
, m_ok
);
338 wxDL_METHOD_LOAD( m_dll
, SetMovieBox
, m_ok
);
339 wxDL_METHOD_LOAD( m_dll
, SetMovieTimeScale
, m_ok
);
340 wxDL_METHOD_LOAD( m_dll
, GetMovieDuration
, m_ok
);
341 wxDL_METHOD_LOAD( m_dll
, GetMovieTimeBase
, m_ok
);
342 wxDL_METHOD_LOAD( m_dll
, GetMovieTimeScale
, m_ok
);
343 wxDL_METHOD_LOAD( m_dll
, GetMovieTime
, m_ok
);
344 wxDL_METHOD_LOAD( m_dll
, SetMovieTime
, m_ok
);
345 wxDL_METHOD_LOAD( m_dll
, GetMovieVolume
, m_ok
);
346 wxDL_METHOD_LOAD( m_dll
, SetMovieVolume
, m_ok
);
347 wxDL_METHOD_LOAD( m_dll
, SetMovieTimeValue
, m_ok
);
348 wxDL_METHOD_LOAD( m_dll
, NewMovieController
, m_ok
);
349 wxDL_METHOD_LOAD( m_dll
, DisposeMovieController
, m_ok
);
350 wxDL_METHOD_LOAD( m_dll
, MCSetVisible
, m_ok
);
351 wxDL_METHOD_LOAD( m_dll
, PrePrerollMovie
, m_ok
);
352 wxDL_METHOD_LOAD( m_dll
, PrerollMovie
, m_ok
);
353 wxDL_METHOD_LOAD( m_dll
, GetMoviePreferredRate
, m_ok
);
354 wxDL_METHOD_LOAD( m_dll
, GetMovieLoadState
, m_ok
);
355 wxDL_METHOD_LOAD( m_dll
, MCDoAction
, m_ok
);
356 wxDL_METHOD_LOAD( m_dll
, MCSetControllerBoundsRect
, m_ok
);
357 wxDL_METHOD_LOAD( m_dll
, NativeEventToMacEvent
, m_ok
);
358 wxDL_METHOD_LOAD( m_dll
, MCIsPlayerEvent
, m_ok
);
359 wxDL_METHOD_LOAD( m_dll
, MCSetMovie
, m_ok
);
360 wxDL_METHOD_LOAD( m_dll
, MCSetActionFilterWithRefCon
, m_ok
);
361 wxDL_METHOD_LOAD( m_dll
, MCGetControllerInfo
, m_ok
);
362 wxDL_METHOD_LOAD( m_dll
, BeginUpdate
, m_ok
);
363 wxDL_METHOD_LOAD( m_dll
, UpdateMovie
, m_ok
);
364 wxDL_METHOD_LOAD( m_dll
, EndUpdate
, m_ok
);
365 wxDL_METHOD_LOAD( m_dll
, GetMoviesStickyError
, m_ok
);
372 class WXDLLIMPEXP_MEDIA wxQTMediaBackend
: public wxMediaBackendCommonBase
376 virtual ~wxQTMediaBackend();
378 virtual bool CreateControl(wxControl
* ctrl
, wxWindow
* parent
,
383 const wxValidator
& validator
,
384 const wxString
& name
);
387 virtual bool Pause();
390 virtual bool Load(const wxURI
& location
,
392 { return wxMediaBackend::Load(location
, proxy
); }
394 virtual bool Load(const wxString
& fileName
);
395 virtual bool Load(const wxURI
& location
);
397 virtual wxMediaState
GetState();
399 virtual bool SetPosition(wxLongLong where
);
400 virtual wxLongLong
GetPosition();
401 virtual wxLongLong
GetDuration();
403 virtual void Move(int x
, int y
, int w
, int h
);
404 wxSize
GetVideoSize() const;
406 virtual double GetPlaybackRate();
407 virtual bool SetPlaybackRate(double dRate
);
409 virtual double GetVolume();
410 virtual bool SetVolume(double);
415 static void PPRMProc (Movie theMovie
, OSErr theErr
, void* theRefCon
);
417 // TODO: Last param actually long - does this work on 64bit machines?
418 static Boolean
MCFilterProc(MovieController theController
,
419 short action
, void *params
, LONG_PTR refCon
);
421 static LRESULT CALLBACK
QTWndProc(HWND
, UINT
, WPARAM
, LPARAM
);
423 virtual bool ShowPlayerControls(wxMediaCtrlPlayerControls flags
);
425 wxSize m_bestSize
; // Original movie size
426 Movie m_movie
; // QT Movie handle/instance
427 bool m_bVideo
; // Whether or not we have video
428 bool m_bPlaying
; // Whether or not movie is playing
429 wxTimer
* m_timer
; // Load or Play timer
430 wxQuickTimeLibrary m_lib
; // DLL to load functions from
431 ComponentInstance m_pMC
; // Movie Controller
432 wxEvtHandler
* m_evthandler
;
434 friend class wxQTMediaEvtHandler
;
436 DECLARE_DYNAMIC_CLASS(wxQTMediaBackend
)
439 // helper to hijack background erasing for the QT window
440 class WXDLLIMPEXP_MEDIA wxQTMediaEvtHandler
: public wxEvtHandler
443 wxQTMediaEvtHandler(wxQTMediaBackend
*qtb
, WXHWND hwnd
)
448 m_qtb
->m_ctrl
->Connect(m_qtb
->m_ctrl
->GetId(),
449 wxEVT_ERASE_BACKGROUND
,
450 wxEraseEventHandler(wxQTMediaEvtHandler::OnEraseBackground
),
454 void OnEraseBackground(wxEraseEvent
& event
);
457 wxQTMediaBackend
*m_qtb
;
460 DECLARE_NO_COPY_CLASS(wxQTMediaEvtHandler
)
464 //===========================================================================
466 //===========================================================================
469 //---------------------------------------------------------------------------
472 // TODO: Use a less kludgy way to pause/get state/set state
473 // FIXME: Greg Hazel reports that sometimes files that cannot be played
474 // with this backend are treated as playable anyway - not verified though.
475 //---------------------------------------------------------------------------
477 IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend
, wxMediaBackend
)
479 // Time between timer calls - this is the Apple recommendation to the TCL
481 #define MOVIE_DELAY 20
483 //---------------------------------------------------------------------------
486 // QT, esp. QT for Windows is very picky about how you go about
487 // async loading. If you were to go through a Windows message loop
488 // or a MoviesTask or both and then check the movie load state
489 // it would still return 1000 (loading)... even (pre)prerolling doesn't
490 // help. However, making a load timer like this works
491 //---------------------------------------------------------------------------
492 class wxQTLoadTimer
: public wxTimer
495 wxQTLoadTimer(Movie movie
, wxQTMediaBackend
* parent
, wxQuickTimeLibrary
* pLib
) :
496 m_movie(movie
), m_parent(parent
), m_pLib(pLib
) {}
500 m_pLib
->MoviesTask(m_movie
, 0);
501 // kMovieLoadStatePlayable
502 if (m_pLib
->GetMovieLoadState(m_movie
) >= 10000)
504 m_parent
->FinishLoad();
510 Movie m_movie
; //Our movie instance
511 wxQTMediaBackend
* m_parent
; //Backend pointer
512 wxQuickTimeLibrary
* m_pLib
; //Interfaces
516 // --------------------------------------------------------------------------
517 // wxQTPlayTimer - Handle Asyncronous Playing
519 // 1) Checks to see if the movie is done, and if not continues
520 // streaming the movie
521 // 2) Sends the wxEVT_MEDIA_STOP event if we have reached the end of
523 // --------------------------------------------------------------------------
524 class wxQTPlayTimer
: public wxTimer
527 wxQTPlayTimer(Movie movie
, wxQTMediaBackend
* parent
,
528 wxQuickTimeLibrary
* pLib
) :
529 m_movie(movie
), m_parent(parent
), m_pLib(pLib
) {}
534 // OK, a little explaining - basically originally
535 // we only called MoviesTask if the movie was actually
536 // playing (not paused or stopped)... this was before
537 // we realized MoviesTask actually handles repainting
538 // of the current frame - so if you were to resize
539 // or something it would previously not redraw that
540 // portion of the movie.
542 // So now we call MoviesTask always so that it repaints
545 m_pLib
->MoviesTask(m_movie
, 0);
548 // Handle the stop event - if the movie has reached
549 // the end, notify our handler
551 // m_bPlaying == !(Stopped | Paused)
553 if (m_parent
->m_bPlaying
)
555 if (m_pLib
->IsMovieDone(m_movie
))
557 if ( m_parent
->SendStopEvent() )
560 wxASSERT(m_pLib
->GetMoviesError() == noErr
);
562 m_parent
->QueueFinishEvent();
569 Movie m_movie
; // Our movie instance
570 wxQTMediaBackend
* m_parent
; //Backend pointer
571 wxQuickTimeLibrary
* m_pLib
; //Interfaces
575 //---------------------------------------------------------------------------
576 // wxQTMediaBackend::QTWndProc
578 // Forwards events to the Movie Controller so that it can
579 // redraw itself/process messages etc..
580 //---------------------------------------------------------------------------
581 LRESULT CALLBACK
wxQTMediaBackend::QTWndProc(HWND hWnd
, UINT nMsg
,
582 WPARAM wParam
, LPARAM lParam
)
584 wxQTMediaBackend
* pThis
= (wxQTMediaBackend
*)wxGetWindowUserData(hWnd
);
594 EventRecord theEvent
;
595 pThis
->m_lib
.NativeEventToMacEvent(&msg
, &theEvent
);
596 pThis
->m_lib
.MCIsPlayerEvent(pThis
->m_pMC
, &theEvent
);
598 return pThis
->m_ctrl
->MSWWindowProc(nMsg
, wParam
, lParam
);
601 //---------------------------------------------------------------------------
602 // wxQTMediaBackend Destructor
604 // Sets m_timer to NULL signifying we havn't loaded anything yet
605 //---------------------------------------------------------------------------
606 wxQTMediaBackend::wxQTMediaBackend()
607 : m_movie(NULL
), m_bPlaying(false), m_timer(NULL
), m_pMC(NULL
)
612 //---------------------------------------------------------------------------
613 // wxQTMediaBackend Destructor
615 // 1) Cleans up the QuickTime movie instance
616 // 2) Decrements the QuickTime reference counter - if this reaches
617 // 0, QuickTime shuts down
618 // 3) Decrements the QuickTime Windows Media Layer reference counter -
619 // if this reaches 0, QuickTime shuts down the Windows Media Layer
620 //---------------------------------------------------------------------------
621 wxQTMediaBackend::~wxQTMediaBackend()
630 m_lib
.DisposeMovieController(m_pMC
);
634 // destroy wxQTMediaEvtHandler we pushed on it
637 m_ctrl
->RemoveEventHandler(m_evthandler
);
641 m_lib
.DestroyPortAssociation(
642 (CGrafPtr
)m_lib
.GetNativeWindowPort(m_ctrl
->GetHWND()));
644 //Note that ExitMovies() is not necessary, but
645 //the docs are fuzzy on whether or not TerminateQTML is
647 m_lib
.TerminateQTML();
651 //---------------------------------------------------------------------------
652 // wxQTMediaBackend::CreateControl
654 // 1) Intializes QuickTime
655 // 2) Creates the control window
656 //---------------------------------------------------------------------------
657 bool wxQTMediaBackend::CreateControl(wxControl
* ctrl
, wxWindow
* parent
,
662 const wxValidator
& validator
,
663 const wxString
& name
)
665 if (!m_lib
.Initialize())
668 int nError
= m_lib
.InitializeQTML(0);
669 if (nError
!= noErr
) //-2093 no dll
671 wxFAIL_MSG(wxString::Format(wxT("Couldn't Initialize Quicktime-%i"), nError
));
678 // By default wxWindow(s) is created with a border -
679 // so we need to get rid of those
681 // Since we don't have a child window like most other
682 // backends, we don't need wxCLIP_CHILDREN
683 if ( !ctrl
->wxControl::Create(parent
, id
, pos
, size
,
684 (style
& ~wxBORDER_MASK
) | wxBORDER_NONE
,
690 m_ctrl
= wxStaticCast(ctrl
, wxMediaCtrl
);
692 // Create a port association for our window so we
693 // can use it as a WindowRef
694 m_lib
.CreatePortAssociation(m_ctrl
->GetHWND(), NULL
, 0L);
696 // Part of a suggestion from Greg Hazel
697 // to repaint movie when idle
698 m_evthandler
= new wxQTMediaEvtHandler(this, m_ctrl
->GetHWND());
699 m_ctrl
->PushEventHandler(m_evthandler
);
705 //---------------------------------------------------------------------------
706 // wxQTMediaBackend::Load (file version)
708 // 1) Get an FSSpec from the Windows path name
710 // 3) Obtain the movie instance from the movie resource
711 // 4) Close the movie resource
713 //---------------------------------------------------------------------------
714 bool wxQTMediaBackend::Load(const wxString
& fileName
)
721 short movieResFile
= 0; //= 0 because of annoying VC6 warning
724 err
= m_lib
.NativePathNameToFSSpec(
725 (char*) (const char*) fileName
.mb_str(),
727 result
= (err
== noErr
);
731 err
= m_lib
.OpenMovieFile(&sfFile
, &movieResFile
, fsRdPerm
);
732 result
= (err
== noErr
);
737 short movieResID
= 0;
740 err
= m_lib
.NewMovieFromFile(
746 NULL
); // wasChanged
747 result
= (err
== noErr
/*&& m_lib.GetMoviesStickyError() == noErr*/);
749 // check m_lib.GetMoviesStickyError() because it may not find the
750 // proper codec and play black video and other strange effects,
751 // not to mention mess up the dynamic backend loading scheme
752 // of wxMediaCtrl - so it just does what the QuickTime player does
755 m_lib
.CloseMovieFile(movieResFile
);
763 //---------------------------------------------------------------------------
764 // wxQTMediaBackend::PPRMProc (static)
766 // Called when done PrePrerolling the movie.
767 // Note that in 99% of the cases this does nothing...
768 // Anyway we set up the loading timer here to tell us when the movie is done
769 //---------------------------------------------------------------------------
770 void wxQTMediaBackend::PPRMProc (Movie theMovie
,
771 OSErr
WXUNUSED_UNLESS_DEBUG(theErr
),
774 wxASSERT( theMovie
);
775 wxASSERT( theRefCon
);
776 wxASSERT( theErr
== noErr
);
778 wxQTMediaBackend
* pBE
= (wxQTMediaBackend
*) theRefCon
;
780 long lTime
= pBE
->m_lib
.GetMovieTime(theMovie
,NULL
);
781 Fixed rate
= pBE
->m_lib
.GetMoviePreferredRate(theMovie
);
782 pBE
->m_lib
.PrerollMovie(theMovie
, lTime
, rate
);
783 pBE
->m_timer
= new wxQTLoadTimer(pBE
->m_movie
, pBE
, &pBE
->m_lib
);
784 pBE
->m_timer
->Start(MOVIE_DELAY
);
787 //---------------------------------------------------------------------------
788 // wxQTMediaBackend::Load (URL Version)
790 // 1) Build an escaped URI from location
791 // 2) Create a handle to store the URI string
792 // 3) Put the URI string inside the handle
793 // 4) Make a QuickTime URL data ref from the handle with the URI in it
794 // 5) Clean up the URI string handle
795 // 6) Do some prerolling
797 //---------------------------------------------------------------------------
798 bool wxQTMediaBackend::Load(const wxURI
& location
)
803 wxString theURI
= location
.BuildURI();
805 Handle theHandle
= m_lib
.NewHandleClear(theURI
.length() + 1);
808 m_lib
.BlockMove(theURI
.mb_str(), *theHandle
, theURI
.length() + 1);
810 // create the movie from the handle that refers to the URI
811 OSErr err
= m_lib
.NewMovieFromDataRef(&m_movie
, newMovieActive
|
813 /* | newMovieIdleImportOK */,
815 URLDataHandlerSubType
);
817 m_lib
.DisposeHandle(theHandle
);
824 timeNow
= m_lib
.GetMovieTime(m_movie
, NULL
);
825 wxASSERT(m_lib
.GetMoviesError() == noErr
);
827 playRate
= m_lib
.GetMoviePreferredRate(m_movie
);
828 wxASSERT(m_lib
.GetMoviesError() == noErr
);
830 // Note that the callback here is optional,
831 // but without it PrePrerollMovie can be buggy
832 // (see Apple ml). Also, some may wonder
833 // why we need this at all - this is because
834 // Apple docs say QuickTime streamed movies
835 // require it if you don't use a Movie Controller,
836 // which we don't by default.
838 m_lib
.PrePrerollMovie(m_movie
, timeNow
, playRate
,
839 (WXFARPROC
)wxQTMediaBackend::PPRMProc
,
848 //---------------------------------------------------------------------------
849 // wxQTMediaBackend::FinishLoad
851 // 1) Create the movie timer
852 // 2) Get real size of movie for GetBestSize/sizers
853 // 3) Set the movie time scale to something usable so that seeking
854 // etc. will work correctly
855 // 4) Set our Movie Controller to display the movie if it exists,
856 // otherwise set the bounds of the Movie
857 // 5) Refresh parent window
858 //---------------------------------------------------------------------------
859 void wxQTMediaBackend::FinishLoad()
861 // Create the playing/streaming timer
862 m_timer
= new wxQTPlayTimer(m_movie
, (wxQTMediaBackend
*) this, &m_lib
);
865 m_timer
->Start(MOVIE_DELAY
, wxTIMER_CONTINUOUS
);
867 // get the real size of the movie
869 memset(&outRect
, 0, sizeof(Rect
)); // suppress annoying VC6 warning
870 m_lib
.GetMovieNaturalBoundsRect (m_movie
, &outRect
);
871 wxASSERT(m_lib
.GetMoviesError() == noErr
);
873 m_bestSize
.x
= outRect
.right
- outRect
.left
;
874 m_bestSize
.y
= outRect
.bottom
- outRect
.top
;
876 // Handle the movie GWorld
880 thePoint
.h
= thePoint
.v
= 0;
881 m_lib
.MCSetMovie(m_pMC
, m_movie
,
882 m_lib
.GetNativeWindowPort(m_ctrl
->GetHandle()),
884 m_lib
.MCSetVisible(m_pMC
, true);
889 m_lib
.SetMovieGWorld(m_movie
,
890 (CGrafPtr
) m_lib
.GetNativeWindowPort(m_ctrl
->GetHWND()),
894 // Set the movie to millisecond precision
895 m_lib
.SetMovieTimeScale(m_movie
, 1000);
896 wxASSERT(m_lib
.GetMoviesError() == noErr
);
901 //---------------------------------------------------------------------------
902 // wxQTMediaBackend::Play
904 // 1) Start the QT movie
905 // 2) Start the movie loading timer
907 // NOTE: This will still return success even when
908 // the movie is still loading, and as mentioned in wxQTLoadTimer
909 // I don't know of a way to force this to be sync - so if its
910 // still loading the function will return true but the movie will
911 // still be in the stopped state
912 //---------------------------------------------------------------------------
913 bool wxQTMediaBackend::Play()
915 m_lib
.StartMovie(m_movie
);
918 return m_lib
.GetMoviesError() == noErr
;
921 //---------------------------------------------------------------------------
922 // wxQTMediaBackend::Pause
925 // 2) Stop the movie timer
926 //---------------------------------------------------------------------------
927 bool wxQTMediaBackend::Pause()
930 m_lib
.StopMovie(m_movie
);
932 return m_lib
.GetMoviesError() == noErr
;
935 //---------------------------------------------------------------------------
936 // wxQTMediaBackend::Stop
939 // 2) Stop the movie timer
940 // 3) Seek to the beginning of the movie
941 //---------------------------------------------------------------------------
942 bool wxQTMediaBackend::Stop()
946 m_lib
.StopMovie(m_movie
);
947 if (m_lib
.GetMoviesError() == noErr
)
948 m_lib
.GoToBeginningOfMovie(m_movie
);
950 return m_lib
.GetMoviesError() == noErr
;
953 //---------------------------------------------------------------------------
954 // wxQTMediaBackend::GetPlaybackRate
956 // Get the movie playback rate from ::GetMovieRate
957 //---------------------------------------------------------------------------
958 double wxQTMediaBackend::GetPlaybackRate()
960 return ( ((double)m_lib
.GetMovieRate(m_movie
)) / 0x10000);
963 //---------------------------------------------------------------------------
964 // wxQTMediaBackend::SetPlaybackRate
966 // Convert dRate to Fixed and Set the movie rate through SetMovieRate
967 //---------------------------------------------------------------------------
968 bool wxQTMediaBackend::SetPlaybackRate(double dRate
)
970 m_lib
.SetMovieRate(m_movie
, (Fixed
) (dRate
* 0x10000));
972 return m_lib
.GetMoviesError() == noErr
;
975 //---------------------------------------------------------------------------
976 // wxQTMediaBackend::SetPosition
978 // 1) Create a time record struct (TimeRecord) with appropriate values
979 // 2) Pass struct to SetMovieTime
980 //---------------------------------------------------------------------------
981 bool wxQTMediaBackend::SetPosition(wxLongLong where
)
983 // NB: For some reason SetMovieTime does not work
984 // correctly with the Quicktime Windows SDK (6)
985 // From Muskelkatermann at the wxForum
986 // http://www.solidsteel.nl/users/wxwidgets/viewtopic.php?t=2957
987 // RN - note that I have not verified this but there
988 // is no harm in calling SetMovieTimeValue instead
990 TimeRecord theTimeRecord
;
991 memset(&theTimeRecord
, 0, sizeof(TimeRecord
));
992 theTimeRecord
.value
.lo
= where
.GetLo();
993 theTimeRecord
.scale
= m_lib
.GetMovieTimeScale(m_movie
);
994 theTimeRecord
.base
= m_lib
.GetMovieTimeBase(m_movie
);
995 m_lib
.SetMovieTime(m_movie
, &theTimeRecord
);
997 m_lib
.SetMovieTimeValue(m_movie
, where
.GetLo());
1000 return (m_lib
.GetMoviesError() == noErr
);
1003 //---------------------------------------------------------------------------
1004 // wxQTMediaBackend::GetPosition
1006 // 1) Calls GetMovieTime to get the position we are in in the movie
1007 // in milliseconds (we called
1008 //---------------------------------------------------------------------------
1009 wxLongLong
wxQTMediaBackend::GetPosition()
1011 return m_lib
.GetMovieTime(m_movie
, NULL
);
1014 //---------------------------------------------------------------------------
1015 // wxQTMediaBackend::GetVolume
1017 // Gets the volume through GetMovieVolume - which returns a 16 bit short -
1019 // +--------+--------+
1021 // +--------+--------+
1023 // (1) first 8 bits are value before decimal
1024 // (2) second 8 bits are value after decimal
1026 // Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to
1027 // 1 (full gain and sound)
1028 //---------------------------------------------------------------------------
1029 double wxQTMediaBackend::GetVolume()
1031 short sVolume
= m_lib
.GetMovieVolume(m_movie
);
1032 wxASSERT(m_lib
.GetMoviesError() == noErr
);
1034 if (sVolume
& (128 << 8)) //negative - no sound
1037 return sVolume
/ 256.0;
1040 //---------------------------------------------------------------------------
1041 // wxQTMediaBackend::SetVolume
1043 // Sets the volume through SetMovieVolume - which takes a 16 bit short -
1045 // +--------+--------+
1047 // +--------+--------+
1049 // (1) first 8 bits are value before decimal
1050 // (2) second 8 bits are value after decimal
1052 // Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to
1053 // 1 (full gain and sound)
1054 //---------------------------------------------------------------------------
1055 bool wxQTMediaBackend::SetVolume(double dVolume
)
1057 m_lib
.SetMovieVolume(m_movie
, (short) (dVolume
* 256));
1058 return m_lib
.GetMoviesError() == noErr
;
1061 //---------------------------------------------------------------------------
1062 // wxQTMediaBackend::GetDuration
1064 // Calls GetMovieDuration
1065 //---------------------------------------------------------------------------
1066 wxLongLong
wxQTMediaBackend::GetDuration()
1068 return m_lib
.GetMovieDuration(m_movie
);
1071 //---------------------------------------------------------------------------
1072 // wxQTMediaBackend::GetState
1074 // Determines the current state:
1075 // if we are at the beginning, then we are stopped
1076 //---------------------------------------------------------------------------
1077 wxMediaState
wxQTMediaBackend::GetState()
1080 return wxMEDIASTATE_PLAYING
;
1081 else if ( !m_movie
|| wxQTMediaBackend::GetPosition() == 0 )
1082 return wxMEDIASTATE_STOPPED
;
1084 return wxMEDIASTATE_PAUSED
;
1087 //---------------------------------------------------------------------------
1088 // wxQTMediaBackend::Cleanup
1090 // Diposes of the movie timer, Disassociates the Movie Controller with
1091 // movie and hides it if it exists, and stops and disposes
1093 //---------------------------------------------------------------------------
1094 void wxQTMediaBackend::Cleanup()
1104 m_lib
.StopMovie(m_movie
);
1109 thePoint
.h
= thePoint
.v
= 0;
1110 m_lib
.MCSetVisible(m_pMC
, false);
1111 m_lib
.MCSetMovie(m_pMC
, NULL
, NULL
, thePoint
);
1114 m_lib
.DisposeMovie(m_movie
);
1118 //---------------------------------------------------------------------------
1119 // wxQTMediaBackend::ShowPlayerControls
1121 // Creates a movie controller for the Movie if the user wants it
1122 //---------------------------------------------------------------------------
1123 bool wxQTMediaBackend::ShowPlayerControls(wxMediaCtrlPlayerControls flags
)
1127 // restore old wndproc
1128 wxSetWindowProc((HWND
)m_ctrl
->GetHWND(), wxWndProc
);
1129 m_lib
.DisposeMovieController(m_pMC
);
1132 // movie controller height
1136 if (flags
&& m_movie
)
1139 wxRect wxrect
= m_ctrl
->GetClientRect();
1141 // make room for controller
1142 if (wxrect
.width
< 320)
1145 rect
.top
= (short)wxrect
.y
;
1146 rect
.left
= (short)wxrect
.x
;
1147 rect
.right
= (short)(rect
.left
+ wxrect
.width
);
1148 rect
.bottom
= (short)(rect
.top
+ wxrect
.height
);
1152 m_pMC
= m_lib
.NewMovieController(m_movie
, &rect
, mcTopLeftMovie
|
1153 // mcScaleMovieToFit |
1156 m_lib
.MCDoAction(m_pMC
, 32, (void*)true); // mcActionSetKeysEnabled
1157 m_lib
.MCSetActionFilterWithRefCon(m_pMC
,
1158 (WXFARPROC
)wxQTMediaBackend::MCFilterProc
, (void*)this);
1159 m_bestSize
.y
+= 16; // movie controller height
1161 // By default the movie controller uses its own colour palette
1162 // for the movie which can be bad on some files, so turn it off.
1163 // Also turn off its frame / border for the movie
1164 // Also take care of a couple of the interface flags here
1166 m_lib
.MCDoAction(m_pMC
, 39/*mcActionGetFlags*/, (void*)&mcFlags
);
1169 // (1<< 0) /*mcFlagSuppressMovieFrame*/ |
1170 (1<< 3) /*mcFlagsUseWindowPalette*/
1171 | ((flags
& wxMEDIACTRLPLAYERCONTROLS_STEP
)
1172 ? 0 : (1<< 1) /*mcFlagSuppressStepButtons*/)
1173 | ((flags
& wxMEDIACTRLPLAYERCONTROLS_VOLUME
)
1174 ? 0 : (1<< 2) /*mcFlagSuppressSpeakerButton*/)
1175 // | (1<< 4) /*mcFlagDontInvalidate*/ // if we take care of repainting ourselves
1178 m_lib
.MCDoAction(m_pMC
, 38/*mcActionSetFlags*/, (void*)mcFlags
);
1180 // intercept the wndproc of our control window
1181 wxSetWindowProc((HWND
)m_ctrl
->GetHWND(), wxQTMediaBackend::QTWndProc
);
1183 // set the user data of our window
1184 wxSetWindowUserData((HWND
)m_ctrl
->GetHWND(), this);
1188 NotifyMovieSizeChanged();
1190 return m_lib
.GetMoviesError() == noErr
;
1193 //---------------------------------------------------------------------------
1194 // wxQTMediaBackend::MCFilterProc (static)
1196 // Callback for when the movie controller recieves a message
1197 //---------------------------------------------------------------------------
1198 Boolean
wxQTMediaBackend::MCFilterProc(MovieController
WXUNUSED(theController
),
1200 void * WXUNUSED(params
),
1203 // NB: potential optimisation
1207 wxQTMediaBackend
* pThis
= (wxQTMediaBackend
*)refCon
;
1212 // don't process idle events
1216 // play button triggered - MC will set movie to opposite state
1217 // of current - playing ? paused : playing
1219 pThis
->m_bPlaying
= !(pThis
->m_bPlaying
);
1221 // NB: Sometimes it doesn't redraw properly -
1222 // if you click on the button but don't move the mouse
1223 // the button will not change its state until you move
1224 // mcActionDraw and Refresh/Update combo do nothing
1225 // to help this unfortunately
1235 //---------------------------------------------------------------------------
1236 // wxQTMediaBackend::GetVideoSize
1238 // Returns the actual size of the QT movie
1239 //---------------------------------------------------------------------------
1240 wxSize
wxQTMediaBackend::GetVideoSize() const
1245 //---------------------------------------------------------------------------
1246 // wxQTMediaBackend::Move
1248 // Sets the bounds of either the Movie or Movie Controller
1249 //---------------------------------------------------------------------------
1250 void wxQTMediaBackend::Move(int WXUNUSED(x
), int WXUNUSED(y
), int w
, int h
)
1254 // make room for controller
1260 Rect theRect
= {0, 0, (short)h
, (short)w
};
1261 m_lib
.MCSetControllerBoundsRect(m_pMC
, &theRect
);
1265 Rect theRect
= {0, 0, (short)h
, (short)w
};
1266 m_lib
.SetMovieBox(m_movie
, &theRect
);
1269 wxASSERT(m_lib
.GetMoviesError() == noErr
);
1273 //---------------------------------------------------------------------------
1274 // wxQTMediaBackend::OnEraseBackground
1276 // Suggestion from Greg Hazel to repaint the movie when idle
1279 // TODO: We may be repainting too much here - under what exact circumstances
1280 // do we need this? I think Move also repaints correctly for the Movie
1281 // Controller, so in that instance we don't need this either
1282 //---------------------------------------------------------------------------
1283 void wxQTMediaEvtHandler::OnEraseBackground(wxEraseEvent
& evt
)
1285 wxQuickTimeLibrary
& m_pLib
= m_qtb
->m_lib
;
1289 // repaint movie controller
1290 m_pLib
.MCDoAction(m_qtb
->m_pMC
, 2 /*mcActionDraw*/,
1291 m_pLib
.GetNativeWindowPort(m_hwnd
));
1293 else if ( m_qtb
->m_movie
)
1295 // no movie controller
1296 CGrafPtr port
= (CGrafPtr
)m_pLib
.GetNativeWindowPort(m_hwnd
);
1298 m_pLib
.BeginUpdate(port
);
1299 m_pLib
.UpdateMovie(m_qtb
->m_movie
);
1300 wxASSERT(m_pLib
.GetMoviesError() == noErr
);
1301 m_pLib
.EndUpdate(port
);
1306 // let the system repaint the window
1311 //---------------------------------------------------------------------------
1313 //---------------------------------------------------------------------------
1315 // in source file that contains stuff you don't directly use
1316 #include "wx/html/forcelnk.h"
1317 FORCE_LINK_ME(wxmediabackend_qt
)
1319 #endif // wxUSE_MEDIACTRL && wxUSE_ACTIVEX