1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/mac/carbon/mediactrl.cpp 
   3 // Purpose:     Built-in Media Backends for Mac 
   4 // Author:      Ryan Norton <wxprojects@comcast.net> 
   8 // Copyright:   (c) 2004-2006 Ryan Norton 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 
  13 // OK, a casual overseer of this file may wonder why we don't use 
  14 // either CreateMovieControl or HIMovieView... 
  17 //      1) Need to dispose and create each time a new movie is loaded 
  18 //      2) Not that many real advantages 
  19 //      3) Progressively buggier in higher OSX versions 
  20 //              (see main.c of QTCarbonShell sample for details) 
  22 //      1) Crashes on destruction in ALL cases on quite a few systems! 
  23 //          (With the only real "alternative" is to simply not 
  24 //           dispose of it and let it leak...) 
  25 //      2) Massive refreshing bugs with its movie controller between 
  28 // At one point we had a complete implementation for CreateMovieControl 
  29 // and on my (RN) local copy I had one for HIMovieView - but they 
  30 // were simply deemed to be too buggy/unuseful. HIMovieView could 
  31 // have been useful as well because it uses OpenGL contexts instead 
  32 // of GWorlds. Perhaps someday when someone comes out with some 
  33 // ingenious workarounds :). 
  34 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 
  36 // For compilers that support precompilation, includes "wx.h". 
  37 #include "wx/wxprec.h" 
  41 #include "wx/mediactrl.h" 
  48 // uma is for wxMacFSSpec 
  49 #include "wx/mac/uma.h" 
  55 #include <QuickTimeComponents.h> 
  57 #include <QuickTime/QuickTimeComponents.h> 
  60 #if !defined(__LP64__) 
  61 #define USE_QUICKTIME 1 
  63 #define USE_QUICKTIME 0 
  68 //--------------------------------------------------------------------------- 
  69 // Height and Width of movie controller in the movie control (apple samples) 
  70 //--------------------------------------------------------------------------- 
  74 //=========================================================================== 
  75 //  BACKEND DECLARATIONS 
  76 //=========================================================================== 
  78 //--------------------------------------------------------------------------- 
  80 //--------------------------------------------------------------------------- 
  82 class WXDLLIMPEXP_MEDIA wxQTMediaBackend 
: public wxMediaBackendCommonBase
 
  86     virtual ~wxQTMediaBackend(); 
  88     virtual bool CreateControl(wxControl
* ctrl
, wxWindow
* parent
, 
  93                                      const wxValidator
& validator
, 
  94                                      const wxString
& name
); 
  96     virtual bool Load(const wxString
& fileName
); 
  97     virtual bool Load(const wxURI
& location
); 
 100     virtual bool Pause(); 
 103     virtual wxMediaState 
GetState(); 
 105     virtual bool SetPosition(wxLongLong where
); 
 106     virtual wxLongLong 
GetPosition(); 
 107     virtual wxLongLong 
GetDuration(); 
 109     virtual void Move(int x
, int y
, int w
, int h
); 
 110     wxSize 
GetVideoSize() const; 
 112     virtual double GetPlaybackRate(); 
 113     virtual bool SetPlaybackRate(double dRate
); 
 115     virtual double GetVolume(); 
 116     virtual bool SetVolume(double); 
 121     virtual bool ShowPlayerControls(wxMediaCtrlPlayerControls flags
); 
 123     virtual wxLongLong 
GetDownloadProgress(); 
 124     virtual wxLongLong 
GetDownloadTotal(); 
 126     virtual void MacVisibilityChanged(); 
 129     //  ------  Implementation from now on  -------- 
 134     void DoLoadBestSize(); 
 135     void DoSetControllerVisible(wxMediaCtrlPlayerControls flags
); 
 137     wxLongLong 
GetDataSizeFromStart(TimeValue end
); 
 139     Boolean 
IsQuickTime4Installed(); 
 140     void DoNewMovieController(); 
 142     static pascal void PPRMProc( 
 143         Movie theMovie
, OSErr theErr
, void* theRefCon
); 
 145     //TODO: Last param actually long - does this work on 64bit machines? 
 146     static pascal Boolean 
MCFilterProc(MovieController theController
, 
 147         short action
, void *params
, long refCon
); 
 149     static pascal OSStatus 
WindowEventHandler( 
 150         EventHandlerCallRef inHandlerCallRef
, 
 151         EventRef inEvent
, void *inUserData  
); 
 153     wxSize m_bestSize
;          // Original movie size 
 154     Movie m_movie
;              // Movie instance 
 155     bool m_bPlaying
;            // Whether media is playing or not 
 156     class wxTimer
* m_timer
;     // Timer for streaming the movie 
 157     MovieController m_mc
;       // MovieController instance 
 158     wxMediaCtrlPlayerControls m_interfaceflags
; // Saved interface flags 
 160     // Event handlers and UPPs/Callbacks 
 161     EventHandlerRef             m_windowEventHandler
; 
 162     EventHandlerUPP             m_windowUPP
; 
 164     MoviePrePrerollCompleteUPP  m_preprerollupp
; 
 165     MCActionFilterWithRefConUPP m_mcactionupp
; 
 167     GWorldPtr m_movieWorld
;  //Offscreen movie GWorld 
 169     friend class wxQTMediaEvtHandler
; 
 171     DECLARE_DYNAMIC_CLASS(wxQTMediaBackend
) 
 174 // helper to hijack background erasing for the QT window 
 175 class WXDLLIMPEXP_MEDIA wxQTMediaEvtHandler 
: public wxEvtHandler
 
 178     wxQTMediaEvtHandler(wxQTMediaBackend 
*qtb
) 
 182         qtb
->m_ctrl
->Connect( 
 183             qtb
->m_ctrl
->GetId(), wxEVT_ERASE_BACKGROUND
, 
 184             wxEraseEventHandler(wxQTMediaEvtHandler::OnEraseBackground
), 
 188     void OnEraseBackground(wxEraseEvent
& event
); 
 191     wxQTMediaBackend 
*m_qtb
; 
 193     DECLARE_NO_COPY_CLASS(wxQTMediaEvtHandler
) 
 196 //=========================================================================== 
 198 //=========================================================================== 
 201 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 205 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 207 IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend
, wxMediaBackend
) 
 209 //Time between timer calls - this is the Apple recommondation to the TCL 
 211 #define MOVIE_DELAY 20 
 213 //--------------------------------------------------------------------------- 
 214 //          wxQTMediaLoadTimer 
 216 //  QT, esp. QT for Windows is very picky about how you go about 
 217 //  async loading.  If you were to go through a Windows message loop 
 218 //  or a MoviesTask or both and then check the movie load state 
 219 //  it would still return 1000 (loading)... even (pre)prerolling doesn't 
 220 //  help.  However, making a load timer like this works 
 221 //--------------------------------------------------------------------------- 
 222 class wxQTMediaLoadTimer 
: public wxTimer
 
 225     wxQTMediaLoadTimer(wxQTMediaBackend
* parent
) : 
 230         ::MCIdle(m_parent
->m_mc
); 
 232         // kMovieLoadStatePlayable is not enough on MAC: 
 233         // it plays, but IsMovieDone might return true (!) 
 234         // sure we need to wait until kMovieLoadStatePlaythroughOK 
 235         if (::GetMovieLoadState(m_parent
->m_movie
) >= 20000) 
 237             m_parent
->FinishLoad(); 
 243     wxQTMediaBackend 
*m_parent
;     // Backend pointer 
 246 // -------------------------------------------------------------------------- 
 247 //          wxQTMediaPlayTimer - Handle Asyncronous Playing 
 249 // 1) Checks to see if the movie is done, and if not continues 
 250 //    streaming the movie 
 251 // 2) Sends the wxEVT_MEDIA_STOP event if we have reached the end of 
 253 // -------------------------------------------------------------------------- 
 254 class wxQTMediaPlayTimer 
: public wxTimer
 
 257     wxQTMediaPlayTimer(wxQTMediaBackend
* parent
) : 
 263         //  OK, a little explaining - basically originally 
 264         //  we only called MoviesTask if the movie was actually 
 265         //  playing (not paused or stopped)... this was before 
 266         //  we realized MoviesTask actually handles repainting 
 267         //  of the current frame - so if you were to resize 
 268         //  or something it would previously not redraw that 
 269         //  portion of the movie. 
 271         //  So now we call MoviesTask always so that it repaints 
 274         ::MCIdle(m_parent
->m_mc
); 
 277         //  Handle the stop event - if the movie has reached 
 278         //  the end, notify our handler 
 280         if (::IsMovieDone(m_parent
->m_movie
)) 
 282             if ( m_parent
->SendStopEvent() ) 
 285                     wxASSERT(::GetMoviesError() == noErr
); 
 287                 m_parent
->QueueFinishEvent(); 
 293     wxQTMediaBackend
* m_parent
;     // Backend pointer 
 297 //--------------------------------------------------------------------------- 
 298 // wxQTMediaBackend Constructor 
 300 // Sets m_timer to NULL signifying we havn't loaded anything yet 
 301 //--------------------------------------------------------------------------- 
 302 wxQTMediaBackend::wxQTMediaBackend() 
 303     : m_movie(NULL
), m_bPlaying(false), m_timer(NULL
) 
 304       , m_mc(NULL
), m_interfaceflags(wxMEDIACTRLPLAYERCONTROLS_NONE
) 
 305       , m_preprerollupp(NULL
), m_movieWorld(NULL
) 
 309 //--------------------------------------------------------------------------- 
 310 // wxQTMediaBackend Destructor 
 312 // 1) Cleans up the QuickTime movie instance 
 313 // 2) Decrements the QuickTime reference counter - if this reaches 
 314 //    0, QuickTime shuts down 
 315 // 3) Decrements the QuickTime Windows Media Layer reference counter - 
 316 //    if this reaches 0, QuickTime shuts down the Windows Media Layer 
 317 //--------------------------------------------------------------------------- 
 318 wxQTMediaBackend::~wxQTMediaBackend() 
 323     // Cleanup for moviecontroller 
 326         // destroy wxQTMediaEvtHandler we pushed on it 
 327         m_ctrl
->PopEventHandler(true); 
 328         RemoveEventHandler(m_windowEventHandler
); 
 329         DisposeEventHandlerUPP(m_windowUPP
); 
 331         // Dispose of the movie controller 
 332         ::DisposeMovieController(m_mc
); 
 335         // Dispose of offscreen GWorld 
 336         ::DisposeGWorld(m_movieWorld
); 
 339     // Note that ExitMovies() is not necessary... 
 343 //--------------------------------------------------------------------------- 
 344 // wxQTMediaBackend::CreateControl 
 346 // 1) Intializes QuickTime 
 347 // 2) Creates the control window 
 348 //--------------------------------------------------------------------------- 
 349 bool wxQTMediaBackend::CreateControl( 
 356     const wxValidator
& validator
, 
 357     const wxString
& name
) 
 359     if (!IsQuickTime4Installed()) 
 364     wxMediaCtrl
* mediactrl 
= (wxMediaCtrl
*)ctrl
; 
 368     // By default wxWindow(s) is created with a border - 
 369     // so we need to get rid of those 
 371     // Since we don't have a child window like most other 
 372     // backends, we don't need wxCLIP_CHILDREN 
 374     if ( !mediactrl
->wxControl::Create( 
 375         parent
, id
, pos
, size
, 
 376         wxWindow::MacRemoveBordersFromStyle(style
), 
 383     mediactrl
->SetValidator(validator
); 
 390 //--------------------------------------------------------------------------- 
 391 // wxQTMediaBackend::IsQuickTime4Installed 
 393 // Determines whether version 4 of QT is installed 
 394 // (Pretty much for Classic only) 
 395 //--------------------------------------------------------------------------- 
 396 Boolean 
wxQTMediaBackend::IsQuickTime4Installed() 
 401     error 
= Gestalt(gestaltQuickTime
, &result
); 
 402     return (error 
== noErr
) && (((result 
>> 16) & 0xffff) >= 0x0400); 
 405 //--------------------------------------------------------------------------- 
 406 // wxQTMediaBackend::Load (file version) 
 408 // 1) Get an FSSpec from the Windows path name 
 410 // 3) Obtain the movie instance from the movie resource 
 411 // 4) Close the movie resource 
 413 //--------------------------------------------------------------------------- 
 414 bool wxQTMediaBackend::Load(const wxString
& fileName
) 
 419     ::ClearMoviesStickyError(); // clear previous errors so 
 420                                 // GetMoviesStickyError is useful 
 426     wxMacFilename2FSSpec( fileName
, &sfFile 
); 
 427     if (OpenMovieFile( &sfFile
, &movieResFile
, fsRdPerm 
) != noErr
) 
 430     short movieResID 
= 0; 
 433     err 
= NewMovieFromFile( 
 441     // Do not use ::GetMoviesStickyError() here because it returns -2009 
 442     // a.k.a. invalid track on valid mpegs            
 443     if (err 
== noErr 
&& ::GetMoviesError() == noErr
) 
 445         ::CloseMovieFile(movieResFile
); 
 447         // Create movie controller/control 
 448         DoNewMovieController(); 
 457 //--------------------------------------------------------------------------- 
 458 // wxQTMediaBackend::Load (URL Version) 
 460 // 1) Build an escaped URI from location 
 461 // 2) Create a handle to store the URI string 
 462 // 3) Put the URI string inside the handle 
 463 // 4) Make a QuickTime URL data ref from the handle with the URI in it 
 464 // 5) Clean up the URI string handle 
 465 // 6) Do some prerolling 
 467 //--------------------------------------------------------------------------- 
 468 bool wxQTMediaBackend::Load(const wxURI
& location
) 
 473     ::ClearMoviesStickyError(); // clear previous errors so 
 474                                 // GetMoviesStickyError is useful 
 476     wxString theURI 
= location
.BuildURI(); 
 480     const char* theURIString
; 
 483     wxCharBuffer buf 
= wxConvLocal
.cWC2MB(theURI
.wc_str(), theURI
.length(), &len
); 
 486     theURIString 
= theURI
; 
 487     len 
= theURI
.length(); 
 490     Handle theHandle 
= ::NewHandleClear(len 
+ 1); 
 493     ::BlockMoveData(theURIString
, *theHandle
, len 
+ 1); 
 495     // create the movie from the handle that refers to the URI 
 496     err 
= ::NewMovieFromDataRef( 
 498         newMovieActive 
| newMovieAsyncOK 
/* | newMovieIdleImportOK*/, 
 500         URLDataHandlerSubType
); 
 502     ::DisposeHandle(theHandle
); 
 504     if (err 
== noErr 
&& ::GetMoviesStickyError() == noErr
) 
 506         // Movie controller resets prerolling, so we must create first 
 507         DoNewMovieController(); 
 512         timeNow 
= ::GetMovieTime(m_movie
, NULL
); 
 513         wxASSERT(::GetMoviesError() == noErr
); 
 515         playRate 
= ::GetMoviePreferredRate(m_movie
); 
 516         wxASSERT(::GetMoviesError() == noErr
); 
 519         //  Note that the callback here is optional, 
 520         //  but without it PrePrerollMovie can be buggy 
 521         //  (see Apple ml).  Also, some may wonder 
 522         //  why we need this at all - this is because 
 523         //  Apple docs say QuickTime streamed movies 
 524         //  require it if you don't use a Movie Controller, 
 525         //  which we don't by default. 
 527         m_preprerollupp 
= wxQTMediaBackend::PPRMProc
; 
 528         ::PrePrerollMovie( m_movie
, timeNow
, playRate
, 
 529                            m_preprerollupp
, (void*)this); 
 537 //--------------------------------------------------------------------------- 
 538 // wxQTMediaBackend::DoNewMovieController 
 540 // Attaches movie to moviecontroller or creates moviecontroller 
 541 // if not created yet 
 542 //--------------------------------------------------------------------------- 
 543 void wxQTMediaBackend::DoNewMovieController() 
 547         // Get top level window ref for some mac functions 
 548         WindowRef wrTLW 
= (WindowRef
) m_ctrl
->MacGetTopLevelWindowRef(); 
 550         // MovieController not set up yet, so we need to create a new one. 
 551         // You have to pass a valid movie to NewMovieController, evidently 
 552         ::SetMovieGWorld(m_movie
, 
 553                        (CGrafPtr
) GetWindowPort(wrTLW
), 
 555         wxASSERT(::GetMoviesError() == noErr
); 
 557         Rect bounds 
= wxMacGetBoundsForControl( 
 559             m_ctrl
->GetPosition(), 
 562         m_mc 
= ::NewMovieController( 
 564             mcTopLeftMovie 
| mcNotVisible 
/* | mcWithFrame */ ); 
 565         wxASSERT(::GetMoviesError() == noErr
); 
 567         ::MCDoAction(m_mc
, 32, (void*)true); // mcActionSetKeysEnabled 
 568         wxASSERT(::GetMoviesError() == noErr
); 
 570         // Setup a callback so we can tell when the user presses 
 571         // play on the player controls 
 572         m_mcactionupp 
= wxQTMediaBackend::MCFilterProc
; 
 573         ::MCSetActionFilterWithRefCon( m_mc
, m_mcactionupp
, (long)this ); 
 574         wxASSERT(::GetMoviesError() == noErr
); 
 576         // Part of a suggestion from Greg Hazel to repaint movie when idle 
 577         m_ctrl
->PushEventHandler(new wxQTMediaEvtHandler(this)); 
 579         // Create offscreen GWorld for where to "show" when window is hidden 
 581         worldRect
.left 
= worldRect
.top 
= 0; 
 582         worldRect
.right 
= worldRect
.bottom 
= 1; 
 583         ::NewGWorld(&m_movieWorld
, 0, &worldRect
, NULL
, NULL
, 0); 
 585         // Catch window messages: 
 586         // if we do not do this and if the user clicks the play 
 587         // button on the controller, for instance, nothing will happen... 
 588         EventTypeSpec theWindowEventTypes
[] = 
 590             { kEventClassMouse
,     kEventMouseDown 
}, 
 591             { kEventClassMouse
,     kEventMouseUp 
}, 
 592             { kEventClassMouse
,     kEventMouseDragged 
}, 
 593             { kEventClassKeyboard
,  kEventRawKeyDown 
}, 
 594             { kEventClassKeyboard
,  kEventRawKeyRepeat 
}, 
 595             { kEventClassKeyboard
,  kEventRawKeyUp 
}, 
 596             { kEventClassWindow
,    kEventWindowUpdate 
}, 
 597             { kEventClassWindow
,    kEventWindowActivated 
}, 
 598             { kEventClassWindow
,    kEventWindowDeactivated 
} 
 601             NewEventHandlerUPP( wxQTMediaBackend::WindowEventHandler 
); 
 602         InstallWindowEventHandler( 
 605             GetEventTypeCount( theWindowEventTypes 
), theWindowEventTypes
, 
 607             &m_windowEventHandler 
); 
 611         // MovieController already created: 
 612         // Just change the movie in it and we're good to go 
 614         thePoint
.h 
= thePoint
.v 
= 0; 
 615         ::MCSetMovie(m_mc
, m_movie
, 
 616               (WindowRef
)m_ctrl
->MacGetTopLevelWindowRef(), 
 618         wxASSERT(::GetMoviesError() == noErr
); 
 622 //--------------------------------------------------------------------------- 
 623 // wxQTMediaBackend::FinishLoad 
 625 // Performs operations after a movie ready to play/loaded. 
 626 //--------------------------------------------------------------------------- 
 627 void wxQTMediaBackend::FinishLoad() 
 629     // get the real size of the movie 
 632     // show the player controls if the user wants to 
 633     if (m_interfaceflags
) 
 634         DoSetControllerVisible(m_interfaceflags
); 
 636     // we want millisecond precision 
 637     ::SetMovieTimeScale(m_movie
, 1000); 
 638     wxASSERT(::GetMoviesError() == noErr
); 
 640     // start movie progress timer 
 641     m_timer 
= new wxQTMediaPlayTimer(this); 
 643     m_timer
->Start(MOVIE_DELAY
, wxTIMER_CONTINUOUS
); 
 645     // send loaded event and refresh size 
 649 //--------------------------------------------------------------------------- 
 650 // wxQTMediaBackend::DoLoadBestSize 
 652 // Sets the best size of the control from the real size of the movie 
 653 //--------------------------------------------------------------------------- 
 654 void wxQTMediaBackend::DoLoadBestSize() 
 656     // get the real size of the movie 
 658     ::GetMovieNaturalBoundsRect(m_movie
, &outRect
); 
 659     wxASSERT(::GetMoviesError() == noErr
); 
 661     // determine best size 
 662     m_bestSize
.x 
= outRect
.right 
- outRect
.left
; 
 663     m_bestSize
.y 
= outRect
.bottom 
- outRect
.top
; 
 666 //--------------------------------------------------------------------------- 
 667 // wxQTMediaBackend::Play 
 669 // Start the QT movie 
 670 // (Apple recommends mcActionPrerollAndPlay but that's QT 4.1+) 
 671 //--------------------------------------------------------------------------- 
 672 bool wxQTMediaBackend::Play() 
 674     Fixed fixRate 
= (Fixed
) (wxQTMediaBackend::GetPlaybackRate() * 0x10000); 
 676         fixRate 
= ::GetMoviePreferredRate(m_movie
); 
 678     wxASSERT(fixRate 
!= 0); 
 681         ::MCDoAction( m_mc
, 8 /* mcActionPlay */, (void*) fixRate
); 
 683     bool result 
= (::GetMoviesError() == noErr
); 
 693 //--------------------------------------------------------------------------- 
 694 // wxQTMediaBackend::Pause 
 697 //--------------------------------------------------------------------------- 
 698 bool wxQTMediaBackend::DoPause() 
 700     // Stop the movie A.K.A. ::StopMovie(m_movie); 
 703         ::MCDoAction( m_mc
, 8 /*mcActionPlay*/,  (void *) 0); 
 705         return ::GetMoviesError() == noErr
; 
 712 bool wxQTMediaBackend::Pause() 
 714     bool bSuccess 
= DoPause(); 
 716         this->QueuePauseEvent(); 
 721 //--------------------------------------------------------------------------- 
 722 // wxQTMediaBackend::Stop 
 725 // 2) Seek to the beginning of the movie 
 726 //--------------------------------------------------------------------------- 
 727 bool wxQTMediaBackend::DoStop() 
 729     if (!wxQTMediaBackend::DoPause()) 
 732     ::GoToBeginningOfMovie(m_movie
); 
 733     return ::GetMoviesError() == noErr
; 
 736 bool wxQTMediaBackend::Stop() 
 738     bool bSuccess 
= DoStop(); 
 745 //--------------------------------------------------------------------------- 
 746 // wxQTMediaBackend::GetPlaybackRate 
 748 // 1) Get the movie playback rate from ::GetMovieRate 
 749 //--------------------------------------------------------------------------- 
 750 double wxQTMediaBackend::GetPlaybackRate() 
 752     return ( ((double)::GetMovieRate(m_movie
)) / 0x10000); 
 755 //--------------------------------------------------------------------------- 
 756 // wxQTMediaBackend::SetPlaybackRate 
 758 // 1) Convert dRate to Fixed and Set the movie rate through SetMovieRate 
 759 //--------------------------------------------------------------------------- 
 760 bool wxQTMediaBackend::SetPlaybackRate(double dRate
) 
 762     ::SetMovieRate(m_movie
, (Fixed
) (dRate 
* 0x10000)); 
 763     return ::GetMoviesError() == noErr
; 
 766 //--------------------------------------------------------------------------- 
 767 // wxQTMediaBackend::SetPosition 
 769 // 1) Create a time record struct (TimeRecord) with appropriate values 
 770 // 2) Pass struct to SetMovieTime 
 771 //--------------------------------------------------------------------------- 
 772 bool wxQTMediaBackend::SetPosition(wxLongLong where
) 
 774     TimeRecord theTimeRecord
; 
 775     memset(&theTimeRecord
, 0, sizeof(TimeRecord
)); 
 776     theTimeRecord
.value
.lo 
= where
.GetValue(); 
 777     theTimeRecord
.scale 
= ::GetMovieTimeScale(m_movie
); 
 778     theTimeRecord
.base 
= ::GetMovieTimeBase(m_movie
); 
 779     ::SetMovieTime(m_movie
, &theTimeRecord
); 
 781     if (::GetMoviesError() != noErr
) 
 787 //--------------------------------------------------------------------------- 
 788 // wxQTMediaBackend::GetPosition 
 790 // Calls GetMovieTime 
 791 //--------------------------------------------------------------------------- 
 792 wxLongLong 
wxQTMediaBackend::GetPosition() 
 794     return ::GetMovieTime(m_movie
, NULL
); 
 797 //--------------------------------------------------------------------------- 
 798 // wxQTMediaBackend::GetVolume 
 800 // Gets the volume through GetMovieVolume - which returns a 16 bit short - 
 802 // +--------+--------+ 
 804 // +--------+--------+ 
 806 // (1) first 8 bits are value before decimal 
 807 // (2) second 8 bits are value after decimal 
 809 // Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to 
 810 // 1 (full gain and sound) 
 811 //--------------------------------------------------------------------------- 
 812 double wxQTMediaBackend::GetVolume() 
 814     short sVolume 
= ::GetMovieVolume(m_movie
); 
 816     if (sVolume 
& (128 << 8)) //negative - no sound 
 819     return sVolume 
/ 256.0; 
 822 //--------------------------------------------------------------------------- 
 823 // wxQTMediaBackend::SetVolume 
 825 // Sets the volume through SetMovieVolume - which takes a 16 bit short - 
 827 // +--------+--------+ 
 829 // +--------+--------+ 
 831 // (1) first 8 bits are value before decimal 
 832 // (2) second 8 bits are value after decimal 
 834 // Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to 
 835 // 1 (full gain and sound) 
 836 //--------------------------------------------------------------------------- 
 837 bool wxQTMediaBackend::SetVolume(double dVolume
) 
 839     ::SetMovieVolume(m_movie
, (short) (dVolume 
* 256)); 
 843 //--------------------------------------------------------------------------- 
 844 // wxQTMediaBackend::GetDuration 
 846 // Calls GetMovieDuration 
 847 //--------------------------------------------------------------------------- 
 848 wxLongLong 
wxQTMediaBackend::GetDuration() 
 850     return ::GetMovieDuration(m_movie
); 
 853 //--------------------------------------------------------------------------- 
 854 // wxQTMediaBackend::GetState 
 856 // Determines the current state - the timer keeps track of whether or not 
 857 // we are paused or stopped (if the timer is running we are playing) 
 858 //--------------------------------------------------------------------------- 
 859 wxMediaState 
wxQTMediaBackend::GetState() 
 862     // GetMovieActive/IsMovieDone/SetMovieActive 
 863     // combo if implemented that way 
 865         return wxMEDIASTATE_PLAYING
; 
 866     else if (!m_movie 
|| wxQTMediaBackend::GetPosition() == 0) 
 867         return wxMEDIASTATE_STOPPED
; 
 869         return wxMEDIASTATE_PAUSED
; 
 872 //--------------------------------------------------------------------------- 
 873 // wxQTMediaBackend::Cleanup 
 875 // Diposes of the movie timer, Control if native, and stops and disposes 
 877 //--------------------------------------------------------------------------- 
 878 void wxQTMediaBackend::Cleanup() 
 888     // Apple samples with CreateMovieControl typically 
 889     // install a event handler and do this on the dispose 
 890     // event, but we do it here for simplicity 
 891     // (It might keep playing for several seconds after 
 892     // control destruction if not) 
 893     wxQTMediaBackend::Pause(); 
 895     // Dispose of control or remove movie from MovieController 
 897     thePoint
.h 
= thePoint
.v 
= 0; 
 898     ::MCSetVisible(m_mc
, false); 
 899     ::MCSetMovie(m_mc
, NULL
, NULL
, thePoint
); 
 901     ::DisposeMovie(m_movie
); 
 905 //--------------------------------------------------------------------------- 
 906 // wxQTMediaBackend::GetVideoSize 
 908 // Returns the actual size of the QT movie 
 909 //--------------------------------------------------------------------------- 
 910 wxSize 
wxQTMediaBackend::GetVideoSize() const 
 915 //--------------------------------------------------------------------------- 
 916 // wxQTMediaBackend::Move 
 918 // Move the movie controller or movie control 
 919 // (we need to actually move the movie control manually...) 
 920 // Top 10 things to do with quicktime in March 93's issue 
 921 // of DEVELOP - very useful 
 922 // http:// www.mactech.com/articles/develop/issue_13/031-033_QuickTime_column.html 
 923 // OLD NOTE: Calling MCSetControllerBoundsRect without detaching 
 924 //          supposively resulted in a crash back then. Current code even 
 925 //          with CFM classic runs fine. If there is ever a problem, 
 926 //          take out the if 0 lines below 
 927 //--------------------------------------------------------------------------- 
 928 void wxQTMediaBackend::Move(int x
, int y
, int w
, int h
) 
 932         m_ctrl
->GetParent()->MacWindowToRootWindow(&x
, &y
); 
 933         Rect theRect 
= {y
, x
, y 
+ h
, x 
+ w
}; 
 935 #if 0 // see note above 
 936         ::MCSetControllerAttached(m_mc
, false); 
 937          wxASSERT(::GetMoviesError() == noErr
); 
 940         ::MCSetControllerBoundsRect(m_mc
, &theRect
); 
 941         wxASSERT(::GetMoviesError() == noErr
); 
 943 #if 0 // see note above 
 944         if (m_interfaceflags
) 
 946             ::MCSetVisible(m_mc
, true); 
 947             wxASSERT(::GetMoviesError() == noErr
); 
 953 //--------------------------------------------------------------------------- 
 954 // wxQTMediaBackend::DoSetControllerVisible 
 956 // Utility function that takes care of showing the moviecontroller 
 957 // and showing/hiding the particular controls on it 
 958 //--------------------------------------------------------------------------- 
 959 void wxQTMediaBackend::DoSetControllerVisible( 
 960                         wxMediaCtrlPlayerControls flags
) 
 962     ::MCSetVisible(m_mc
, true); 
 964     // Take care of subcontrols 
 965     if (::GetMoviesError() == noErr
) 
 968         ::MCDoAction(m_mc
, 39/*mcActionGetFlags*/, (void*)&mcFlags
); 
 970         if (::GetMoviesError() == noErr
) 
 972              mcFlags 
|= (  //(1<<0)/*mcFlagSuppressMovieFrame*/ | 
 973                      (1 << 3)/*mcFlagsUseWindowPalette*/ 
 974                        | ((flags 
& wxMEDIACTRLPLAYERCONTROLS_STEP
) 
 975                           ? 0 : (1 << 1)/*mcFlagSuppressStepButtons*/) 
 976                        | ((flags 
& wxMEDIACTRLPLAYERCONTROLS_VOLUME
) 
 977                           ? 0 : (1 << 2)/*mcFlagSuppressSpeakerButton*/) 
 978                           //if we take care of repainting ourselves 
 979          //              | (1 << 4) /*mcFlagDontInvalidate*/ 
 982             ::MCDoAction(m_mc
, 38/*mcActionSetFlags*/, (void*)mcFlags
); 
 986     // Adjust height and width of best size for movie controller 
 987     // if the user wants it shown 
 988     m_bestSize
.x 
= m_bestSize
.x 
> wxMCWIDTH 
? m_bestSize
.x 
: wxMCWIDTH
; 
 989     m_bestSize
.y 
+= wxMCHEIGHT
; 
 992 //--------------------------------------------------------------------------- 
 993 // wxQTMediaBackend::ShowPlayerControls 
 995 // Shows/Hides subcontrols on the media control 
 996 //--------------------------------------------------------------------------- 
 997 bool wxQTMediaBackend::ShowPlayerControls(wxMediaCtrlPlayerControls flags
) 
1000         return false; // no movie controller... 
1002     bool bSizeChanged 
= false; 
1004     // if the controller is visible and we want to hide it do so 
1005     if (m_interfaceflags 
&& !flags
) 
1007         bSizeChanged 
= true; 
1009         ::MCSetVisible(m_mc
, false); 
1011     else if (!m_interfaceflags 
&& flags
) // show controller if hidden 
1013         bSizeChanged 
= true; 
1014         DoSetControllerVisible(flags
); 
1017     // readjust parent sizers 
1020         NotifyMovieSizeChanged(); 
1022         // remember state in case of loading new media 
1023         m_interfaceflags 
= flags
; 
1026     return ::GetMoviesError() == noErr
; 
1029 //--------------------------------------------------------------------------- 
1030 // wxQTMediaBackend::GetDataSizeFromStart 
1032 // Calls either GetMovieDataSize or GetMovieDataSize64 with a value 
1033 // of 0 for the starting value 
1034 //--------------------------------------------------------------------------- 
1035 wxLongLong 
wxQTMediaBackend::GetDataSizeFromStart(TimeValue end
) 
1037 #if 0 // old pre-qt4 way 
1038     return ::GetMovieDataSize(m_movie
, 0, end
) 
1041     ::GetMovieDataSize64(m_movie
, 0, end
, &llDataSize
); 
1042     return wxLongLong(llDataSize
.hi
, llDataSize
.lo
); 
1046 //--------------------------------------------------------------------------- 
1047 // wxQTMediaBackend::GetDownloadProgress 
1048 //--------------------------------------------------------------------------- 
1049 wxLongLong 
wxQTMediaBackend::GetDownloadProgress() 
1051 #if 0 // hackish and slow 
1052     Handle hMovie 
= NewHandle(0); 
1053     PutMovieIntoHandle(m_movie
, hMovie
); 
1054     long lSize 
= GetHandleSize(hMovie
); 
1055     DisposeHandle(hMovie
); 
1060     if (::GetMaxLoadedTimeInMovie(m_movie
, &tv
) != noErr
) 
1062         wxLogDebug(wxT("GetMaxLoadedTimeInMovie failed")); 
1066     return wxQTMediaBackend::GetDataSizeFromStart(tv
); 
1070 //--------------------------------------------------------------------------- 
1071 // wxQTMediaBackend::GetDownloadTotal 
1072 //--------------------------------------------------------------------------- 
1073 wxLongLong 
wxQTMediaBackend::GetDownloadTotal() 
1075     return wxQTMediaBackend::GetDataSizeFromStart( 
1076                     ::GetMovieDuration(m_movie
) 
1080 //--------------------------------------------------------------------------- 
1081 // wxQTMediaBackend::MacVisibilityChanged 
1083 // The main problem here is that Windows quicktime, for example, 
1084 // renders more directly to a HWND. Mac quicktime does not do this 
1085 // and instead renders to the port of the WindowRef/WindowPtr on top 
1086 // of everything else/all other windows. 
1088 // So, for example, if you were to have a CreateTabsControl/wxNotebook 
1089 // and change pages, even if you called HIViewSetVisible/SetControlVisibility 
1090 // directly the movie will still continue playing on top of everything else 
1091 // if you went to a different tab. 
1093 // Note that another issue, and why we call MCSetControllerPort instead 
1094 // of SetMovieGWorld directly, is that in addition to rendering on 
1095 // top of everything else the last created controller steals mouse and 
1096 // other input from everything else in the window, including other 
1097 // controllers. Setting the port of it releases this behaviour. 
1098 //--------------------------------------------------------------------------- 
1099 void wxQTMediaBackend::MacVisibilityChanged() 
1101     if(!m_mc 
|| !m_ctrl
->m_bLoaded
) 
1102         return; //not initialized yet 
1104     if(m_ctrl
->IsShownOnScreen()) 
1106         //The window is being shown again, so set the GWorld of the 
1107         //controller back to the port of the parent WindowRef 
1109             (WindowRef
) m_ctrl
->MacGetTopLevelWindowRef(); 
1111         ::MCSetControllerPort(m_mc
, (CGrafPtr
) GetWindowPort(wrTLW
)); 
1112         wxASSERT(::GetMoviesError() == noErr
); 
1116         //We are being hidden - set the GWorld of the controller 
1117         //to the offscreen GWorld 
1118         ::MCSetControllerPort(m_mc
, m_movieWorld
); 
1119         wxASSERT(::GetMoviesError() == noErr
); 
1123 //--------------------------------------------------------------------------- 
1124 // wxQTMediaBackend::OnEraseBackground 
1126 // Suggestion from Greg Hazel to repaint the movie when idle 
1128 //--------------------------------------------------------------------------- 
1129 void wxQTMediaEvtHandler::OnEraseBackground(wxEraseEvent
& WXUNUSED(evt
)) 
1131     // Work around Nasty OSX drawing bug: 
1132     // http://lists.apple.com/archives/QuickTime-API/2002/Feb/msg00311.html 
1133     WindowRef wrTLW 
= (WindowRef
) m_qtb
->m_ctrl
->MacGetTopLevelWindowRef(); 
1135     RgnHandle region 
= ::MCGetControllerBoundsRgn(m_qtb
->m_mc
); 
1136     ::MCInvalidate(m_qtb
->m_mc
, wrTLW
, region
); 
1137     ::MCIdle(m_qtb
->m_mc
); 
1140 //--------------------------------------------------------------------------- 
1141 // wxQTMediaBackend::PPRMProc (static) 
1143 // Called when done PrePrerolling the movie. 
1144 // Note that in 99% of the cases this does nothing... 
1145 // Anyway we set up the loading timer here to tell us when the movie is done 
1146 //--------------------------------------------------------------------------- 
1147 pascal void wxQTMediaBackend::PPRMProc( 
1149     OSErr 
WXUNUSED_UNLESS_DEBUG(theErr
), 
1152     wxASSERT( theMovie 
); 
1153     wxASSERT( theRefCon 
); 
1154     wxASSERT( theErr 
== noErr 
); 
1156     wxQTMediaBackend
* pBE 
= (wxQTMediaBackend
*) theRefCon
; 
1158     long lTime 
= ::GetMovieTime(theMovie
,NULL
); 
1159     Fixed rate 
= ::GetMoviePreferredRate(theMovie
); 
1160     ::PrerollMovie(theMovie
,lTime
,rate
); 
1161     pBE
->m_timer 
= new wxQTMediaLoadTimer(pBE
); 
1162     pBE
->m_timer
->Start(MOVIE_DELAY
); 
1165 //--------------------------------------------------------------------------- 
1166 // wxQTMediaBackend::MCFilterProc (static) 
1168 // Callback for when the movie controller recieves a message 
1169 //--------------------------------------------------------------------------- 
1170 pascal Boolean 
wxQTMediaBackend::MCFilterProc( 
1171     MovieController 
WXUNUSED(theController
), 
1173     void * WXUNUSED(params
), 
1176     wxQTMediaBackend
* pThis 
= (wxQTMediaBackend
*)refCon
; 
1181         // don't process idle events 
1185         // play button triggered - MC will set movie to opposite state 
1186         // of current - playing ? paused : playing 
1187         pThis
->m_bPlaying 
= !(pThis
->m_bPlaying
); 
1197 //--------------------------------------------------------------------------- 
1198 // wxQTMediaBackend::WindowEventHandler [static] 
1200 // Event callback for the top level window of our control that passes 
1201 // messages to our moviecontroller so it can receive mouse clicks etc. 
1202 //--------------------------------------------------------------------------- 
1203 pascal OSStatus 
wxQTMediaBackend::WindowEventHandler( 
1204     EventHandlerCallRef 
WXUNUSED(inHandlerCallRef
), 
1208     wxQTMediaBackend
* be 
= (wxQTMediaBackend
*) inUserData
; 
1210     // Only process keyboard messages on this window if it actually 
1211     // has focus, otherwise it will steal keystrokes from other windows! 
1212     // As well as when it is not loaded properly as it 
1213     // will crash in MCIsPlayerEvent 
1214     if((GetEventClass(inEvent
) == kEventClassKeyboard 
&& 
1215         wxWindow::FindFocus() != be
->m_ctrl
) 
1216         || !be
->m_ctrl
->m_bLoaded
) 
1217             return eventNotHandledErr
; 
1219     // Pass the event onto the movie controller 
1220     EventRecord theEvent
; 
1221     ConvertEventRefToEventRecord( inEvent
, &theEvent 
); 
1224     // TODO: Apple says MCIsPlayerEvent is depreciated and 
1225     // MCClick, MCKey, MCIdle etc. should be used 
1226     // (RN: Of course that's what they say about 
1227     //  CreateMovieControl and HIMovieView as well, LOL!) 
1228     err 
= ::MCIsPlayerEvent( be
->m_mc
, &theEvent 
); 
1230     // Pass on to other event handlers if not handled- i.e. wx 
1234         return eventNotHandledErr
; 
1239 // in source file that contains stuff you don't directly use 
1240 #include "wx/html/forcelnk.h" 
1241 FORCE_LINK_ME(basewxmediabackends
) 
1243 #endif // wxUSE_MEDIACTRL