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 //--------------------------------------------------------------------------- 
  61 // Height and Width of movie controller in the movie control (apple samples) 
  62 //--------------------------------------------------------------------------- 
  66 //=========================================================================== 
  67 //  BACKEND DECLARATIONS 
  68 //=========================================================================== 
  70 //--------------------------------------------------------------------------- 
  72 //--------------------------------------------------------------------------- 
  74 class WXDLLIMPEXP_MEDIA wxQTMediaBackend 
: public wxMediaBackendCommonBase
 
  78     virtual ~wxQTMediaBackend(); 
  80     virtual bool CreateControl(wxControl
* ctrl
, wxWindow
* parent
, 
  85                                      const wxValidator
& validator
, 
  86                                      const wxString
& name
); 
  88     virtual bool Load(const wxString
& fileName
); 
  89     virtual bool Load(const wxURI
& location
); 
  95     virtual wxMediaState 
GetState(); 
  97     virtual bool SetPosition(wxLongLong where
); 
  98     virtual wxLongLong 
GetPosition(); 
  99     virtual wxLongLong 
GetDuration(); 
 101     virtual void Move(int x
, int y
, int w
, int h
); 
 102     wxSize 
GetVideoSize() const; 
 104     virtual double GetPlaybackRate(); 
 105     virtual bool SetPlaybackRate(double dRate
); 
 107     virtual double GetVolume(); 
 108     virtual bool SetVolume(double); 
 113     virtual bool ShowPlayerControls(wxMediaCtrlPlayerControls flags
); 
 115     virtual wxLongLong 
GetDownloadProgress(); 
 116     virtual wxLongLong 
GetDownloadTotal(); 
 118     virtual void MacVisibilityChanged(); 
 121     //  ------  Implementation from now on  -------- 
 126     void DoLoadBestSize(); 
 127     void DoSetControllerVisible(wxMediaCtrlPlayerControls flags
); 
 129     wxLongLong 
GetDataSizeFromStart(TimeValue end
); 
 131     Boolean 
IsQuickTime4Installed(); 
 132     void DoNewMovieController(); 
 134     static pascal void PPRMProc( 
 135         Movie theMovie
, OSErr theErr
, void* theRefCon
); 
 137     //TODO: Last param actually long - does this work on 64bit machines? 
 138     static pascal Boolean 
MCFilterProc(MovieController theController
, 
 139         short action
, void *params
, long refCon
); 
 141     static pascal OSStatus 
WindowEventHandler( 
 142         EventHandlerCallRef inHandlerCallRef
, 
 143         EventRef inEvent
, void *inUserData  
); 
 145     wxSize m_bestSize
;          // Original movie size 
 146     Movie m_movie
;              // Movie instance 
 147     bool m_bPlaying
;            // Whether media is playing or not 
 148     class wxTimer
* m_timer
;     // Timer for streaming the movie 
 149     MovieController m_mc
;       // MovieController instance 
 150     wxMediaCtrlPlayerControls m_interfaceflags
; // Saved interface flags 
 152     // Event handlers and UPPs/Callbacks 
 153     EventHandlerRef             m_windowEventHandler
; 
 154     EventHandlerUPP             m_windowUPP
; 
 156     MoviePrePrerollCompleteUPP  m_preprerollupp
; 
 157     MCActionFilterWithRefConUPP m_mcactionupp
; 
 159     GWorldPtr m_movieWorld
;  //Offscreen movie GWorld 
 161     friend class wxQTMediaEvtHandler
; 
 163     DECLARE_DYNAMIC_CLASS(wxQTMediaBackend
) 
 166 // helper to hijack background erasing for the QT window 
 167 class WXDLLIMPEXP_MEDIA wxQTMediaEvtHandler 
: public wxEvtHandler
 
 170     wxQTMediaEvtHandler(wxQTMediaBackend 
*qtb
) 
 174         qtb
->m_ctrl
->Connect( 
 175             qtb
->m_ctrl
->GetId(), wxEVT_ERASE_BACKGROUND
, 
 176             wxEraseEventHandler(wxQTMediaEvtHandler::OnEraseBackground
), 
 180     void OnEraseBackground(wxEraseEvent
& event
); 
 183     wxQTMediaBackend 
*m_qtb
; 
 185     DECLARE_NO_COPY_CLASS(wxQTMediaEvtHandler
) 
 188 //=========================================================================== 
 190 //=========================================================================== 
 193 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 197 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 199 IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend
, wxMediaBackend
) 
 201 //Time between timer calls - this is the Apple recommondation to the TCL 
 203 #define MOVIE_DELAY 20 
 205 //--------------------------------------------------------------------------- 
 206 //          wxQTMediaLoadTimer 
 208 //  QT, esp. QT for Windows is very picky about how you go about 
 209 //  async loading.  If you were to go through a Windows message loop 
 210 //  or a MoviesTask or both and then check the movie load state 
 211 //  it would still return 1000 (loading)... even (pre)prerolling doesn't 
 212 //  help.  However, making a load timer like this works 
 213 //--------------------------------------------------------------------------- 
 214 class wxQTMediaLoadTimer 
: public wxTimer
 
 217     wxQTMediaLoadTimer(wxQTMediaBackend
* parent
) : 
 222         ::MCIdle(m_parent
->m_mc
); 
 224         // kMovieLoadStatePlayable is not enough on MAC: 
 225         // it plays, but IsMovieDone might return true (!) 
 226         // sure we need to wait until kMovieLoadStatePlaythroughOK 
 227         if (::GetMovieLoadState(m_parent
->m_movie
) >= 20000) 
 229             m_parent
->FinishLoad(); 
 235     wxQTMediaBackend 
*m_parent
;     // Backend pointer 
 238 // -------------------------------------------------------------------------- 
 239 //          wxQTMediaPlayTimer - Handle Asyncronous Playing 
 241 // 1) Checks to see if the movie is done, and if not continues 
 242 //    streaming the movie 
 243 // 2) Sends the wxEVT_MEDIA_STOP event if we have reached the end of 
 245 // -------------------------------------------------------------------------- 
 246 class wxQTMediaPlayTimer 
: public wxTimer
 
 249     wxQTMediaPlayTimer(wxQTMediaBackend
* parent
) : 
 255         //  OK, a little explaining - basically originally 
 256         //  we only called MoviesTask if the movie was actually 
 257         //  playing (not paused or stopped)... this was before 
 258         //  we realized MoviesTask actually handles repainting 
 259         //  of the current frame - so if you were to resize 
 260         //  or something it would previously not redraw that 
 261         //  portion of the movie. 
 263         //  So now we call MoviesTask always so that it repaints 
 266         ::MCIdle(m_parent
->m_mc
); 
 269         //  Handle the stop event - if the movie has reached 
 270         //  the end, notify our handler 
 272         if (::IsMovieDone(m_parent
->m_movie
)) 
 274             if ( m_parent
->SendStopEvent() ) 
 277                     wxASSERT(::GetMoviesError() == noErr
); 
 279                 m_parent
->QueueFinishEvent(); 
 285     wxQTMediaBackend
* m_parent
;     // Backend pointer 
 289 //--------------------------------------------------------------------------- 
 290 // wxQTMediaBackend Constructor 
 292 // Sets m_timer to NULL signifying we havn't loaded anything yet 
 293 //--------------------------------------------------------------------------- 
 294 wxQTMediaBackend::wxQTMediaBackend() 
 295     : m_movie(NULL
), m_bPlaying(false), m_timer(NULL
) 
 296       , m_mc(NULL
), m_interfaceflags(wxMEDIACTRLPLAYERCONTROLS_NONE
) 
 297       , m_preprerollupp(NULL
), m_movieWorld(NULL
) 
 301 //--------------------------------------------------------------------------- 
 302 // wxQTMediaBackend Destructor 
 304 // 1) Cleans up the QuickTime movie instance 
 305 // 2) Decrements the QuickTime reference counter - if this reaches 
 306 //    0, QuickTime shuts down 
 307 // 3) Decrements the QuickTime Windows Media Layer reference counter - 
 308 //    if this reaches 0, QuickTime shuts down the Windows Media Layer 
 309 //--------------------------------------------------------------------------- 
 310 wxQTMediaBackend::~wxQTMediaBackend() 
 315     // Cleanup for moviecontroller 
 318         // destroy wxQTMediaEvtHandler we pushed on it 
 319         m_ctrl
->PopEventHandler(true); 
 320         RemoveEventHandler(m_windowEventHandler
); 
 321         DisposeEventHandlerUPP(m_windowUPP
); 
 323         // Dispose of the movie controller 
 324         ::DisposeMovieController(m_mc
); 
 327         // Dispose of offscreen GWorld 
 328         ::DisposeGWorld(m_movieWorld
); 
 331     // Note that ExitMovies() is not necessary... 
 335 //--------------------------------------------------------------------------- 
 336 // wxQTMediaBackend::CreateControl 
 338 // 1) Intializes QuickTime 
 339 // 2) Creates the control window 
 340 //--------------------------------------------------------------------------- 
 341 bool wxQTMediaBackend::CreateControl( 
 348     const wxValidator
& validator
, 
 349     const wxString
& name
) 
 351     if (!IsQuickTime4Installed()) 
 356     wxMediaCtrl
* mediactrl 
= (wxMediaCtrl
*)ctrl
; 
 360     // By default wxWindow(s) is created with a border - 
 361     // so we need to get rid of those 
 363     // Since we don't have a child window like most other 
 364     // backends, we don't need wxCLIP_CHILDREN 
 366     if ( !mediactrl
->wxControl::Create( 
 367         parent
, id
, pos
, size
, 
 368         wxWindow::MacRemoveBordersFromStyle(style
), 
 375     mediactrl
->SetValidator(validator
); 
 382 //--------------------------------------------------------------------------- 
 383 // wxQTMediaBackend::IsQuickTime4Installed 
 385 // Determines whether version 4 of QT is installed 
 386 // (Pretty much for Classic only) 
 387 //--------------------------------------------------------------------------- 
 388 Boolean 
wxQTMediaBackend::IsQuickTime4Installed() 
 393     error 
= Gestalt(gestaltQuickTime
, &result
); 
 394     return (error 
== noErr
) && (((result 
>> 16) & 0xffff) >= 0x0400); 
 397 //--------------------------------------------------------------------------- 
 398 // wxQTMediaBackend::Load (file version) 
 400 // 1) Get an FSSpec from the Windows path name 
 402 // 3) Obtain the movie instance from the movie resource 
 403 // 4) Close the movie resource 
 405 //--------------------------------------------------------------------------- 
 406 bool wxQTMediaBackend::Load(const wxString
& fileName
) 
 411     ::ClearMoviesStickyError(); // clear previous errors so 
 412                                 // GetMoviesStickyError is useful 
 418     wxMacFilename2FSSpec( fileName
, &sfFile 
); 
 419     if (OpenMovieFile( &sfFile
, &movieResFile
, fsRdPerm 
) != noErr
) 
 422     short movieResID 
= 0; 
 425     err 
= NewMovieFromFile( 
 434     // check GetMoviesStickyError() because it may not find the 
 435     // proper codec and play black video and other strange effects, 
 436     // not to mention mess up the dynamic backend loading scheme 
 437     // of wxMediaCtrl - so it just does what the QuickTime player does 
 439     if (err 
== noErr 
&& ::GetMoviesStickyError() == noErr
) 
 441         ::CloseMovieFile(movieResFile
); 
 443         // Create movie controller/control 
 444         DoNewMovieController(); 
 453 //--------------------------------------------------------------------------- 
 454 // wxQTMediaBackend::Load (URL Version) 
 456 // 1) Build an escaped URI from location 
 457 // 2) Create a handle to store the URI string 
 458 // 3) Put the URI string inside the handle 
 459 // 4) Make a QuickTime URL data ref from the handle with the URI in it 
 460 // 5) Clean up the URI string handle 
 461 // 6) Do some prerolling 
 463 //--------------------------------------------------------------------------- 
 464 bool wxQTMediaBackend::Load(const wxURI
& location
) 
 469     ::ClearMoviesStickyError(); // clear previous errors so 
 470                                 // GetMoviesStickyError is useful 
 472     wxString theURI 
= location
.BuildURI(); 
 476     const char* theURIString
; 
 479     wxCharBuffer buf 
= wxConvLocal
.cWC2MB(theURI
, theURI
.length(), &len
); 
 482     theURIString 
= theURI
; 
 483     len 
= theURI
.length(); 
 486     Handle theHandle 
= ::NewHandleClear(len 
+ 1); 
 489     ::BlockMoveData(theURIString
, *theHandle
, len 
+ 1); 
 491     // create the movie from the handle that refers to the URI 
 492     err 
= ::NewMovieFromDataRef( 
 494         newMovieActive 
| newMovieAsyncOK 
/* | newMovieIdleImportOK*/, 
 496         URLDataHandlerSubType
); 
 498     ::DisposeHandle(theHandle
); 
 500     if (err 
== noErr 
&& ::GetMoviesStickyError() == noErr
) 
 502         // Movie controller resets prerolling, so we must create first 
 503         DoNewMovieController(); 
 508         timeNow 
= ::GetMovieTime(m_movie
, NULL
); 
 509         wxASSERT(::GetMoviesError() == noErr
); 
 511         playRate 
= ::GetMoviePreferredRate(m_movie
); 
 512         wxASSERT(::GetMoviesError() == noErr
); 
 515         //  Note that the callback here is optional, 
 516         //  but without it PrePrerollMovie can be buggy 
 517         //  (see Apple ml).  Also, some may wonder 
 518         //  why we need this at all - this is because 
 519         //  Apple docs say QuickTime streamed movies 
 520         //  require it if you don't use a Movie Controller, 
 521         //  which we don't by default. 
 523         m_preprerollupp 
= wxQTMediaBackend::PPRMProc
; 
 524         ::PrePrerollMovie( m_movie
, timeNow
, playRate
, 
 525                            m_preprerollupp
, (void*)this); 
 533 //--------------------------------------------------------------------------- 
 534 // wxQTMediaBackend::DoNewMovieController 
 536 // Attaches movie to moviecontroller or creates moviecontroller 
 537 // if not created yet 
 538 //--------------------------------------------------------------------------- 
 539 void wxQTMediaBackend::DoNewMovieController() 
 543         // Get top level window ref for some mac functions 
 544         WindowRef wrTLW 
= (WindowRef
) m_ctrl
->MacGetTopLevelWindowRef(); 
 546         // MovieController not set up yet, so we need to create a new one. 
 547         // You have to pass a valid movie to NewMovieController, evidently 
 548         ::SetMovieGWorld(m_movie
, 
 549                        (CGrafPtr
) GetWindowPort(wrTLW
), 
 551         wxASSERT(::GetMoviesError() == noErr
); 
 553         Rect bounds 
= wxMacGetBoundsForControl( 
 555             m_ctrl
->GetPosition(), 
 558         m_mc 
= ::NewMovieController( 
 560             mcTopLeftMovie 
| mcNotVisible 
/* | mcWithFrame */ ); 
 561         wxASSERT(::GetMoviesError() == noErr
); 
 563         ::MCDoAction(m_mc
, 32, (void*)true); // mcActionSetKeysEnabled 
 564         wxASSERT(::GetMoviesError() == noErr
); 
 566         // Setup a callback so we can tell when the user presses 
 567         // play on the player controls 
 568         m_mcactionupp 
= wxQTMediaBackend::MCFilterProc
; 
 569         ::MCSetActionFilterWithRefCon( m_mc
, m_mcactionupp
, (long)this ); 
 570         wxASSERT(::GetMoviesError() == noErr
); 
 572         // Part of a suggestion from Greg Hazel to repaint movie when idle 
 573         m_ctrl
->PushEventHandler(new wxQTMediaEvtHandler(this)); 
 575         // Create offscreen GWorld for where to "show" when window is hidden 
 577         worldRect
.left 
= worldRect
.top 
= 0; 
 578         worldRect
.right 
= worldRect
.bottom 
= 1; 
 579         ::NewGWorld(&m_movieWorld
, 0, &worldRect
, NULL
, NULL
, 0); 
 581         // Catch window messages: 
 582         // if we do not do this and if the user clicks the play 
 583         // button on the controller, for instance, nothing will happen... 
 584         EventTypeSpec theWindowEventTypes
[] = 
 586             { kEventClassMouse
,     kEventMouseDown 
}, 
 587             { kEventClassMouse
,     kEventMouseUp 
}, 
 588             { kEventClassMouse
,     kEventMouseDragged 
}, 
 589             { kEventClassKeyboard
,  kEventRawKeyDown 
}, 
 590             { kEventClassKeyboard
,  kEventRawKeyRepeat 
}, 
 591             { kEventClassKeyboard
,  kEventRawKeyUp 
}, 
 592             { kEventClassWindow
,    kEventWindowUpdate 
}, 
 593             { kEventClassWindow
,    kEventWindowActivated 
}, 
 594             { kEventClassWindow
,    kEventWindowDeactivated 
} 
 597             NewEventHandlerUPP( wxQTMediaBackend::WindowEventHandler 
); 
 598         InstallWindowEventHandler( 
 601             GetEventTypeCount( theWindowEventTypes 
), theWindowEventTypes
, 
 603             &m_windowEventHandler 
); 
 607         // MovieController already created: 
 608         // Just change the movie in it and we're good to go 
 610         thePoint
.h 
= thePoint
.v 
= 0; 
 611         ::MCSetMovie(m_mc
, m_movie
, 
 612               (WindowRef
)m_ctrl
->MacGetTopLevelWindowRef(), 
 614         wxASSERT(::GetMoviesError() == noErr
); 
 618 //--------------------------------------------------------------------------- 
 619 // wxQTMediaBackend::FinishLoad 
 621 // Performs operations after a movie ready to play/loaded. 
 622 //--------------------------------------------------------------------------- 
 623 void wxQTMediaBackend::FinishLoad() 
 625     // get the real size of the movie 
 628     // show the player controls if the user wants to 
 629     if (m_interfaceflags
) 
 630         DoSetControllerVisible(m_interfaceflags
); 
 632     // we want millisecond precision 
 633     ::SetMovieTimeScale(m_movie
, 1000); 
 634     wxASSERT(::GetMoviesError() == noErr
); 
 636     // start movie progress timer 
 637     m_timer 
= new wxQTMediaPlayTimer(this); 
 639     m_timer
->Start(MOVIE_DELAY
, wxTIMER_CONTINUOUS
); 
 641     // send loaded event and refresh size 
 645 //--------------------------------------------------------------------------- 
 646 // wxQTMediaBackend::DoLoadBestSize 
 648 // Sets the best size of the control from the real size of the movie 
 649 //--------------------------------------------------------------------------- 
 650 void wxQTMediaBackend::DoLoadBestSize() 
 652     // get the real size of the movie 
 654     ::GetMovieNaturalBoundsRect(m_movie
, &outRect
); 
 655     wxASSERT(::GetMoviesError() == noErr
); 
 657     // determine best size 
 658     m_bestSize
.x 
= outRect
.right 
- outRect
.left
; 
 659     m_bestSize
.y 
= outRect
.bottom 
- outRect
.top
; 
 662 //--------------------------------------------------------------------------- 
 663 // wxQTMediaBackend::Play 
 665 // Start the QT movie 
 666 // (Apple recommends mcActionPrerollAndPlay but that's QT 4.1+) 
 667 //--------------------------------------------------------------------------- 
 668 bool wxQTMediaBackend::Play() 
 670     Fixed fixRate 
= (Fixed
) (wxQTMediaBackend::GetPlaybackRate() * 0x10000); 
 672         fixRate 
= ::GetMoviePreferredRate(m_movie
); 
 674     wxASSERT(fixRate 
!= 0); 
 677         ::MCDoAction( m_mc
, 8 /* mcActionPlay */, (void*) fixRate
); 
 679     bool result 
= (::GetMoviesError() == noErr
); 
 689 //--------------------------------------------------------------------------- 
 690 // wxQTMediaBackend::Pause 
 693 //--------------------------------------------------------------------------- 
 694 bool wxQTMediaBackend::DoPause() 
 696     // Stop the movie A.K.A. ::StopMovie(m_movie); 
 699         ::MCDoAction( m_mc
, 8 /*mcActionPlay*/,  (void *) 0); 
 701         return ::GetMoviesError() == noErr
; 
 708 bool wxQTMediaBackend::Pause() 
 710     bool bSuccess 
= DoPause(); 
 712         this->QueuePauseEvent(); 
 717 //--------------------------------------------------------------------------- 
 718 // wxQTMediaBackend::Stop 
 721 // 2) Seek to the beginning of the movie 
 722 //--------------------------------------------------------------------------- 
 723 bool wxQTMediaBackend::DoStop() 
 725     if (!wxQTMediaBackend::DoPause()) 
 728     ::GoToBeginningOfMovie(m_movie
); 
 729     return ::GetMoviesError() == noErr
; 
 732 bool wxQTMediaBackend::Stop() 
 734     bool bSuccess 
= DoStop(); 
 741 //--------------------------------------------------------------------------- 
 742 // wxQTMediaBackend::GetPlaybackRate 
 744 // 1) Get the movie playback rate from ::GetMovieRate 
 745 //--------------------------------------------------------------------------- 
 746 double wxQTMediaBackend::GetPlaybackRate() 
 748     return ( ((double)::GetMovieRate(m_movie
)) / 0x10000); 
 751 //--------------------------------------------------------------------------- 
 752 // wxQTMediaBackend::SetPlaybackRate 
 754 // 1) Convert dRate to Fixed and Set the movie rate through SetMovieRate 
 755 //--------------------------------------------------------------------------- 
 756 bool wxQTMediaBackend::SetPlaybackRate(double dRate
) 
 758     ::SetMovieRate(m_movie
, (Fixed
) (dRate 
* 0x10000)); 
 759     return ::GetMoviesError() == noErr
; 
 762 //--------------------------------------------------------------------------- 
 763 // wxQTMediaBackend::SetPosition 
 765 // 1) Create a time record struct (TimeRecord) with appropriate values 
 766 // 2) Pass struct to SetMovieTime 
 767 //--------------------------------------------------------------------------- 
 768 bool wxQTMediaBackend::SetPosition(wxLongLong where
) 
 770     TimeRecord theTimeRecord
; 
 771     memset(&theTimeRecord
, 0, sizeof(TimeRecord
)); 
 772     theTimeRecord
.value
.lo 
= where
.GetValue(); 
 773     theTimeRecord
.scale 
= ::GetMovieTimeScale(m_movie
); 
 774     theTimeRecord
.base 
= ::GetMovieTimeBase(m_movie
); 
 775     ::SetMovieTime(m_movie
, &theTimeRecord
); 
 777     if (::GetMoviesError() != noErr
) 
 783 //--------------------------------------------------------------------------- 
 784 // wxQTMediaBackend::GetPosition 
 786 // Calls GetMovieTime 
 787 //--------------------------------------------------------------------------- 
 788 wxLongLong 
wxQTMediaBackend::GetPosition() 
 790     return ::GetMovieTime(m_movie
, NULL
); 
 793 //--------------------------------------------------------------------------- 
 794 // wxQTMediaBackend::GetVolume 
 796 // Gets the volume through GetMovieVolume - which returns a 16 bit short - 
 798 // +--------+--------+ 
 800 // +--------+--------+ 
 802 // (1) first 8 bits are value before decimal 
 803 // (2) second 8 bits are value after decimal 
 805 // Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to 
 806 // 1 (full gain and sound) 
 807 //--------------------------------------------------------------------------- 
 808 double wxQTMediaBackend::GetVolume() 
 810     short sVolume 
= ::GetMovieVolume(m_movie
); 
 812     if (sVolume 
& (128 << 8)) //negative - no sound 
 815     return sVolume 
/ 256.0; 
 818 //--------------------------------------------------------------------------- 
 819 // wxQTMediaBackend::SetVolume 
 821 // Sets the volume through SetMovieVolume - which takes a 16 bit short - 
 823 // +--------+--------+ 
 825 // +--------+--------+ 
 827 // (1) first 8 bits are value before decimal 
 828 // (2) second 8 bits are value after decimal 
 830 // Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to 
 831 // 1 (full gain and sound) 
 832 //--------------------------------------------------------------------------- 
 833 bool wxQTMediaBackend::SetVolume(double dVolume
) 
 835     ::SetMovieVolume(m_movie
, (short) (dVolume 
* 256)); 
 839 //--------------------------------------------------------------------------- 
 840 // wxQTMediaBackend::GetDuration 
 842 // Calls GetMovieDuration 
 843 //--------------------------------------------------------------------------- 
 844 wxLongLong 
wxQTMediaBackend::GetDuration() 
 846     return ::GetMovieDuration(m_movie
); 
 849 //--------------------------------------------------------------------------- 
 850 // wxQTMediaBackend::GetState 
 852 // Determines the current state - the timer keeps track of whether or not 
 853 // we are paused or stopped (if the timer is running we are playing) 
 854 //--------------------------------------------------------------------------- 
 855 wxMediaState 
wxQTMediaBackend::GetState() 
 858     // GetMovieActive/IsMovieDone/SetMovieActive 
 859     // combo if implemented that way 
 861         return wxMEDIASTATE_PLAYING
; 
 862     else if (!m_movie 
|| wxQTMediaBackend::GetPosition() == 0) 
 863         return wxMEDIASTATE_STOPPED
; 
 865         return wxMEDIASTATE_PAUSED
; 
 868 //--------------------------------------------------------------------------- 
 869 // wxQTMediaBackend::Cleanup 
 871 // Diposes of the movie timer, Control if native, and stops and disposes 
 873 //--------------------------------------------------------------------------- 
 874 void wxQTMediaBackend::Cleanup() 
 884     // Apple samples with CreateMovieControl typically 
 885     // install a event handler and do this on the dispose 
 886     // event, but we do it here for simplicity 
 887     // (It might keep playing for several seconds after 
 888     // control destruction if not) 
 889     wxQTMediaBackend::Pause(); 
 891     // Dispose of control or remove movie from MovieController 
 893     thePoint
.h 
= thePoint
.v 
= 0; 
 894     ::MCSetVisible(m_mc
, false); 
 895     ::MCSetMovie(m_mc
, NULL
, NULL
, thePoint
); 
 897     ::DisposeMovie(m_movie
); 
 901 //--------------------------------------------------------------------------- 
 902 // wxQTMediaBackend::GetVideoSize 
 904 // Returns the actual size of the QT movie 
 905 //--------------------------------------------------------------------------- 
 906 wxSize 
wxQTMediaBackend::GetVideoSize() const 
 911 //--------------------------------------------------------------------------- 
 912 // wxQTMediaBackend::Move 
 914 // Move the movie controller or movie control 
 915 // (we need to actually move the movie control manually...) 
 916 // Top 10 things to do with quicktime in March 93's issue 
 917 // of DEVELOP - very useful 
 918 // http:// www.mactech.com/articles/develop/issue_13/031-033_QuickTime_column.html 
 919 // OLD NOTE: Calling MCSetControllerBoundsRect without detaching 
 920 //          supposively resulted in a crash back then. Current code even 
 921 //          with CFM classic runs fine. If there is ever a problem, 
 922 //          take out the if 0 lines below 
 923 //--------------------------------------------------------------------------- 
 924 void wxQTMediaBackend::Move(int x
, int y
, int w
, int h
) 
 928         m_ctrl
->GetParent()->MacWindowToRootWindow(&x
, &y
); 
 929         Rect theRect 
= {y
, x
, y 
+ h
, x 
+ w
}; 
 931 #if 0 // see note above 
 932         ::MCSetControllerAttached(m_mc
, false); 
 933          wxASSERT(::GetMoviesError() == noErr
); 
 936         ::MCSetControllerBoundsRect(m_mc
, &theRect
); 
 937         wxASSERT(::GetMoviesError() == noErr
); 
 939 #if 0 // see note above 
 940         if (m_interfaceflags
) 
 942             ::MCSetVisible(m_mc
, true); 
 943             wxASSERT(::GetMoviesError() == noErr
); 
 949 //--------------------------------------------------------------------------- 
 950 // wxQTMediaBackend::DoSetControllerVisible 
 952 // Utility function that takes care of showing the moviecontroller 
 953 // and showing/hiding the particular controls on it 
 954 //--------------------------------------------------------------------------- 
 955 void wxQTMediaBackend::DoSetControllerVisible( 
 956                         wxMediaCtrlPlayerControls flags
) 
 958     ::MCSetVisible(m_mc
, true); 
 960     // Take care of subcontrols 
 961     if (::GetMoviesError() == noErr
) 
 964         ::MCDoAction(m_mc
, 39/*mcActionGetFlags*/, (void*)&mcFlags
); 
 966         if (::GetMoviesError() == noErr
) 
 968              mcFlags 
|= (  //(1<<0)/*mcFlagSuppressMovieFrame*/ | 
 969                      (1 << 3)/*mcFlagsUseWindowPalette*/ 
 970                        | ((flags 
& wxMEDIACTRLPLAYERCONTROLS_STEP
) 
 971                           ? 0 : (1 << 1)/*mcFlagSuppressStepButtons*/) 
 972                        | ((flags 
& wxMEDIACTRLPLAYERCONTROLS_VOLUME
) 
 973                           ? 0 : (1 << 2)/*mcFlagSuppressSpeakerButton*/) 
 974                           //if we take care of repainting ourselves 
 975          //              | (1 << 4) /*mcFlagDontInvalidate*/ 
 978             ::MCDoAction(m_mc
, 38/*mcActionSetFlags*/, (void*)mcFlags
); 
 982     // Adjust height and width of best size for movie controller 
 983     // if the user wants it shown 
 984     m_bestSize
.x 
= m_bestSize
.x 
> wxMCWIDTH 
? m_bestSize
.x 
: wxMCWIDTH
; 
 985     m_bestSize
.y 
+= wxMCHEIGHT
; 
 988 //--------------------------------------------------------------------------- 
 989 // wxQTMediaBackend::ShowPlayerControls 
 991 // Shows/Hides subcontrols on the media control 
 992 //--------------------------------------------------------------------------- 
 993 bool wxQTMediaBackend::ShowPlayerControls(wxMediaCtrlPlayerControls flags
) 
 996         return false; // no movie controller... 
 998     bool bSizeChanged 
= false; 
1000     // if the controller is visible and we want to hide it do so 
1001     if (m_interfaceflags 
&& !flags
) 
1003         bSizeChanged 
= true; 
1005         ::MCSetVisible(m_mc
, false); 
1007     else if (!m_interfaceflags 
&& flags
) // show controller if hidden 
1009         bSizeChanged 
= true; 
1010         DoSetControllerVisible(flags
); 
1013     // readjust parent sizers 
1016         NotifyMovieSizeChanged(); 
1018         // remember state in case of loading new media 
1019         m_interfaceflags 
= flags
; 
1022     return ::GetMoviesError() == noErr
; 
1025 //--------------------------------------------------------------------------- 
1026 // wxQTMediaBackend::GetDataSizeFromStart 
1028 // Calls either GetMovieDataSize or GetMovieDataSize64 with a value 
1029 // of 0 for the starting value 
1030 //--------------------------------------------------------------------------- 
1031 wxLongLong 
wxQTMediaBackend::GetDataSizeFromStart(TimeValue end
) 
1033 #if 0 // old pre-qt4 way 
1034     return ::GetMovieDataSize(m_movie
, 0, end
) 
1037     ::GetMovieDataSize64(m_movie
, 0, end
, &llDataSize
); 
1038     return wxLongLong(llDataSize
.hi
, llDataSize
.lo
); 
1042 //--------------------------------------------------------------------------- 
1043 // wxQTMediaBackend::GetDownloadProgress 
1044 //--------------------------------------------------------------------------- 
1045 wxLongLong 
wxQTMediaBackend::GetDownloadProgress() 
1047 #if 0 // hackish and slow 
1048     Handle hMovie 
= NewHandle(0); 
1049     PutMovieIntoHandle(m_movie
, hMovie
); 
1050     long lSize 
= GetHandleSize(hMovie
); 
1051     DisposeHandle(hMovie
); 
1056     if (::GetMaxLoadedTimeInMovie(m_movie
, &tv
) != noErr
) 
1058         wxLogDebug(wxT("GetMaxLoadedTimeInMovie failed")); 
1062     return wxQTMediaBackend::GetDataSizeFromStart(tv
); 
1066 //--------------------------------------------------------------------------- 
1067 // wxQTMediaBackend::GetDownloadTotal 
1068 //--------------------------------------------------------------------------- 
1069 wxLongLong 
wxQTMediaBackend::GetDownloadTotal() 
1071     return wxQTMediaBackend::GetDataSizeFromStart( 
1072                     ::GetMovieDuration(m_movie
) 
1076 //--------------------------------------------------------------------------- 
1077 // wxQTMediaBackend::MacVisibilityChanged 
1079 // The main problem here is that Windows quicktime, for example, 
1080 // renders more directly to a HWND. Mac quicktime does not do this 
1081 // and instead renders to the port of the WindowRef/WindowPtr on top 
1082 // of everything else/all other windows. 
1084 // So, for example, if you were to have a CreateTabsControl/wxNotebook 
1085 // and change pages, even if you called HIViewSetVisible/SetControlVisibility 
1086 // directly the movie will still continue playing on top of everything else 
1087 // if you went to a different tab. 
1089 // Note that another issue, and why we call MCSetControllerPort instead 
1090 // of SetMovieGWorld directly, is that in addition to rendering on 
1091 // top of everything else the last created controller steals mouse and 
1092 // other input from everything else in the window, including other 
1093 // controllers. Setting the port of it releases this behaviour. 
1094 //--------------------------------------------------------------------------- 
1095 void wxQTMediaBackend::MacVisibilityChanged() 
1097     if(!m_mc 
|| !m_ctrl
->m_bLoaded
) 
1098         return; //not initialized yet 
1100     if(m_ctrl
->MacIsReallyShown()) 
1102         //The window is being shown again, so set the GWorld of the 
1103         //controller back to the port of the parent WindowRef 
1105             (WindowRef
) m_ctrl
->MacGetTopLevelWindowRef(); 
1107         ::MCSetControllerPort(m_mc
, (CGrafPtr
) GetWindowPort(wrTLW
)); 
1108         wxASSERT(::GetMoviesError() == noErr
); 
1112         //We are being hidden - set the GWorld of the controller 
1113         //to the offscreen GWorld 
1114         ::MCSetControllerPort(m_mc
, m_movieWorld
); 
1115         wxASSERT(::GetMoviesError() == noErr
); 
1119 //--------------------------------------------------------------------------- 
1120 // wxQTMediaBackend::OnEraseBackground 
1122 // Suggestion from Greg Hazel to repaint the movie when idle 
1124 //--------------------------------------------------------------------------- 
1125 void wxQTMediaEvtHandler::OnEraseBackground(wxEraseEvent
& evt
) 
1127     // Work around Nasty OSX drawing bug: 
1128     // http://lists.apple.com/archives/QuickTime-API/2002/Feb/msg00311.html 
1129     WindowRef wrTLW 
= (WindowRef
) m_qtb
->m_ctrl
->MacGetTopLevelWindowRef(); 
1131     RgnHandle region 
= ::MCGetControllerBoundsRgn(m_qtb
->m_mc
); 
1132     ::MCInvalidate(m_qtb
->m_mc
, wrTLW
, region
); 
1133     ::MCIdle(m_qtb
->m_mc
); 
1136 //--------------------------------------------------------------------------- 
1137 // wxQTMediaBackend::PPRMProc (static) 
1139 // Called when done PrePrerolling the movie. 
1140 // Note that in 99% of the cases this does nothing... 
1141 // Anyway we set up the loading timer here to tell us when the movie is done 
1142 //--------------------------------------------------------------------------- 
1143 pascal void wxQTMediaBackend::PPRMProc( 
1145     OSErr 
WXUNUSED_UNLESS_DEBUG(theErr
), 
1148     wxASSERT( theMovie 
); 
1149     wxASSERT( theRefCon 
); 
1150     wxASSERT( theErr 
== noErr 
); 
1152     wxQTMediaBackend
* pBE 
= (wxQTMediaBackend
*) theRefCon
; 
1154     long lTime 
= ::GetMovieTime(theMovie
,NULL
); 
1155     Fixed rate 
= ::GetMoviePreferredRate(theMovie
); 
1156     ::PrerollMovie(theMovie
,lTime
,rate
); 
1157     pBE
->m_timer 
= new wxQTMediaLoadTimer(pBE
); 
1158     pBE
->m_timer
->Start(MOVIE_DELAY
); 
1161 //--------------------------------------------------------------------------- 
1162 // wxQTMediaBackend::MCFilterProc (static) 
1164 // Callback for when the movie controller recieves a message 
1165 //--------------------------------------------------------------------------- 
1166 pascal Boolean 
wxQTMediaBackend::MCFilterProc( 
1167     MovieController 
WXUNUSED(theController
), 
1169     void * WXUNUSED(params
), 
1172     wxQTMediaBackend
* pThis 
= (wxQTMediaBackend
*)refCon
; 
1177         // don't process idle events 
1181         // play button triggered - MC will set movie to opposite state 
1182         // of current - playing ? paused : playing 
1183         pThis
->m_bPlaying 
= !(pThis
->m_bPlaying
); 
1193 //--------------------------------------------------------------------------- 
1194 // wxQTMediaBackend::WindowEventHandler [static] 
1196 // Event callback for the top level window of our control that passes 
1197 // messages to our moviecontroller so it can receive mouse clicks etc. 
1198 //--------------------------------------------------------------------------- 
1199 pascal OSStatus 
wxQTMediaBackend::WindowEventHandler( 
1200     EventHandlerCallRef inHandlerCallRef
, 
1204     wxQTMediaBackend
* be 
= (wxQTMediaBackend
*) inUserData
; 
1206     // Only process keyboard messages on this window if it actually 
1207     // has focus, otherwise it will steal keystrokes from other windows! 
1208     // As well as when it is not loaded properly as it 
1209     // will crash in MCIsPlayerEvent 
1210     if((GetEventClass(inEvent
) == kEventClassKeyboard 
&& 
1211         wxWindow::FindFocus() != be
->m_ctrl
) 
1212         || !be
->m_ctrl
->m_bLoaded
) 
1213             return eventNotHandledErr
; 
1215     // Pass the event onto the movie controller 
1216     EventRecord theEvent
; 
1217     ConvertEventRefToEventRecord( inEvent
, &theEvent 
); 
1220     // TODO: Apple says MCIsPlayerEvent is depreciated and 
1221     // MCClick, MCKey, MCIdle etc. should be used 
1222     // (RN: Of course that's what they say about 
1223     //  CreateMovieControl and HIMovieView as well, LOL!) 
1224     err 
= ::MCIsPlayerEvent( be
->m_mc
, &theEvent 
); 
1226     // Pass on to other event handlers if not handled- i.e. wx 
1230         return eventNotHandledErr
; 
1233 // in source file that contains stuff you don't directly use 
1234 #include "wx/html/forcelnk.h" 
1235 FORCE_LINK_ME(basewxmediabackends
) 
1237 #endif // wxUSE_MEDIACTRL