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( 
 433     // Do not use ::GetMoviesStickyError() here because it returns -2009 
 434     // a.k.a. invalid track on valid mpegs            
 435     if (err 
== noErr 
&& ::GetMoviesError() == noErr
) 
 437         ::CloseMovieFile(movieResFile
); 
 439         // Create movie controller/control 
 440         DoNewMovieController(); 
 449 //--------------------------------------------------------------------------- 
 450 // wxQTMediaBackend::Load (URL Version) 
 452 // 1) Build an escaped URI from location 
 453 // 2) Create a handle to store the URI string 
 454 // 3) Put the URI string inside the handle 
 455 // 4) Make a QuickTime URL data ref from the handle with the URI in it 
 456 // 5) Clean up the URI string handle 
 457 // 6) Do some prerolling 
 459 //--------------------------------------------------------------------------- 
 460 bool wxQTMediaBackend::Load(const wxURI
& location
) 
 465     ::ClearMoviesStickyError(); // clear previous errors so 
 466                                 // GetMoviesStickyError is useful 
 468     wxString theURI 
= location
.BuildURI(); 
 472     const char* theURIString
; 
 475     wxCharBuffer buf 
= wxConvLocal
.cWC2MB(theURI
, theURI
.length(), &len
); 
 478     theURIString 
= theURI
; 
 479     len 
= theURI
.length(); 
 482     Handle theHandle 
= ::NewHandleClear(len 
+ 1); 
 485     ::BlockMoveData(theURIString
, *theHandle
, len 
+ 1); 
 487     // create the movie from the handle that refers to the URI 
 488     err 
= ::NewMovieFromDataRef( 
 490         newMovieActive 
| newMovieAsyncOK 
/* | newMovieIdleImportOK*/, 
 492         URLDataHandlerSubType
); 
 494     ::DisposeHandle(theHandle
); 
 496     if (err 
== noErr 
&& ::GetMoviesStickyError() == noErr
) 
 498         // Movie controller resets prerolling, so we must create first 
 499         DoNewMovieController(); 
 504         timeNow 
= ::GetMovieTime(m_movie
, NULL
); 
 505         wxASSERT(::GetMoviesError() == noErr
); 
 507         playRate 
= ::GetMoviePreferredRate(m_movie
); 
 508         wxASSERT(::GetMoviesError() == noErr
); 
 511         //  Note that the callback here is optional, 
 512         //  but without it PrePrerollMovie can be buggy 
 513         //  (see Apple ml).  Also, some may wonder 
 514         //  why we need this at all - this is because 
 515         //  Apple docs say QuickTime streamed movies 
 516         //  require it if you don't use a Movie Controller, 
 517         //  which we don't by default. 
 519         m_preprerollupp 
= wxQTMediaBackend::PPRMProc
; 
 520         ::PrePrerollMovie( m_movie
, timeNow
, playRate
, 
 521                            m_preprerollupp
, (void*)this); 
 529 //--------------------------------------------------------------------------- 
 530 // wxQTMediaBackend::DoNewMovieController 
 532 // Attaches movie to moviecontroller or creates moviecontroller 
 533 // if not created yet 
 534 //--------------------------------------------------------------------------- 
 535 void wxQTMediaBackend::DoNewMovieController() 
 539         // Get top level window ref for some mac functions 
 540         WindowRef wrTLW 
= (WindowRef
) m_ctrl
->MacGetTopLevelWindowRef(); 
 542         // MovieController not set up yet, so we need to create a new one. 
 543         // You have to pass a valid movie to NewMovieController, evidently 
 544         ::SetMovieGWorld(m_movie
, 
 545                        (CGrafPtr
) GetWindowPort(wrTLW
), 
 547         wxASSERT(::GetMoviesError() == noErr
); 
 549         Rect bounds 
= wxMacGetBoundsForControl( 
 551             m_ctrl
->GetPosition(), 
 554         m_mc 
= ::NewMovieController( 
 556             mcTopLeftMovie 
| mcNotVisible 
/* | mcWithFrame */ ); 
 557         wxASSERT(::GetMoviesError() == noErr
); 
 559         ::MCDoAction(m_mc
, 32, (void*)true); // mcActionSetKeysEnabled 
 560         wxASSERT(::GetMoviesError() == noErr
); 
 562         // Setup a callback so we can tell when the user presses 
 563         // play on the player controls 
 564         m_mcactionupp 
= wxQTMediaBackend::MCFilterProc
; 
 565         ::MCSetActionFilterWithRefCon( m_mc
, m_mcactionupp
, (long)this ); 
 566         wxASSERT(::GetMoviesError() == noErr
); 
 568         // Part of a suggestion from Greg Hazel to repaint movie when idle 
 569         m_ctrl
->PushEventHandler(new wxQTMediaEvtHandler(this)); 
 571         // Create offscreen GWorld for where to "show" when window is hidden 
 573         worldRect
.left 
= worldRect
.top 
= 0; 
 574         worldRect
.right 
= worldRect
.bottom 
= 1; 
 575         ::NewGWorld(&m_movieWorld
, 0, &worldRect
, NULL
, NULL
, 0); 
 577         // Catch window messages: 
 578         // if we do not do this and if the user clicks the play 
 579         // button on the controller, for instance, nothing will happen... 
 580         EventTypeSpec theWindowEventTypes
[] = 
 582             { kEventClassMouse
,     kEventMouseDown 
}, 
 583             { kEventClassMouse
,     kEventMouseUp 
}, 
 584             { kEventClassMouse
,     kEventMouseDragged 
}, 
 585             { kEventClassKeyboard
,  kEventRawKeyDown 
}, 
 586             { kEventClassKeyboard
,  kEventRawKeyRepeat 
}, 
 587             { kEventClassKeyboard
,  kEventRawKeyUp 
}, 
 588             { kEventClassWindow
,    kEventWindowUpdate 
}, 
 589             { kEventClassWindow
,    kEventWindowActivated 
}, 
 590             { kEventClassWindow
,    kEventWindowDeactivated 
} 
 593             NewEventHandlerUPP( wxQTMediaBackend::WindowEventHandler 
); 
 594         InstallWindowEventHandler( 
 597             GetEventTypeCount( theWindowEventTypes 
), theWindowEventTypes
, 
 599             &m_windowEventHandler 
); 
 603         // MovieController already created: 
 604         // Just change the movie in it and we're good to go 
 606         thePoint
.h 
= thePoint
.v 
= 0; 
 607         ::MCSetMovie(m_mc
, m_movie
, 
 608               (WindowRef
)m_ctrl
->MacGetTopLevelWindowRef(), 
 610         wxASSERT(::GetMoviesError() == noErr
); 
 614 //--------------------------------------------------------------------------- 
 615 // wxQTMediaBackend::FinishLoad 
 617 // Performs operations after a movie ready to play/loaded. 
 618 //--------------------------------------------------------------------------- 
 619 void wxQTMediaBackend::FinishLoad() 
 621     // get the real size of the movie 
 624     // show the player controls if the user wants to 
 625     if (m_interfaceflags
) 
 626         DoSetControllerVisible(m_interfaceflags
); 
 628     // we want millisecond precision 
 629     ::SetMovieTimeScale(m_movie
, 1000); 
 630     wxASSERT(::GetMoviesError() == noErr
); 
 632     // start movie progress timer 
 633     m_timer 
= new wxQTMediaPlayTimer(this); 
 635     m_timer
->Start(MOVIE_DELAY
, wxTIMER_CONTINUOUS
); 
 637     // send loaded event and refresh size 
 641 //--------------------------------------------------------------------------- 
 642 // wxQTMediaBackend::DoLoadBestSize 
 644 // Sets the best size of the control from the real size of the movie 
 645 //--------------------------------------------------------------------------- 
 646 void wxQTMediaBackend::DoLoadBestSize() 
 648     // get the real size of the movie 
 650     ::GetMovieNaturalBoundsRect(m_movie
, &outRect
); 
 651     wxASSERT(::GetMoviesError() == noErr
); 
 653     // determine best size 
 654     m_bestSize
.x 
= outRect
.right 
- outRect
.left
; 
 655     m_bestSize
.y 
= outRect
.bottom 
- outRect
.top
; 
 658 //--------------------------------------------------------------------------- 
 659 // wxQTMediaBackend::Play 
 661 // Start the QT movie 
 662 // (Apple recommends mcActionPrerollAndPlay but that's QT 4.1+) 
 663 //--------------------------------------------------------------------------- 
 664 bool wxQTMediaBackend::Play() 
 666     Fixed fixRate 
= (Fixed
) (wxQTMediaBackend::GetPlaybackRate() * 0x10000); 
 668         fixRate 
= ::GetMoviePreferredRate(m_movie
); 
 670     wxASSERT(fixRate 
!= 0); 
 673         ::MCDoAction( m_mc
, 8 /* mcActionPlay */, (void*) fixRate
); 
 675     bool result 
= (::GetMoviesError() == noErr
); 
 685 //--------------------------------------------------------------------------- 
 686 // wxQTMediaBackend::Pause 
 689 //--------------------------------------------------------------------------- 
 690 bool wxQTMediaBackend::DoPause() 
 692     // Stop the movie A.K.A. ::StopMovie(m_movie); 
 695         ::MCDoAction( m_mc
, 8 /*mcActionPlay*/,  (void *) 0); 
 697         return ::GetMoviesError() == noErr
; 
 704 bool wxQTMediaBackend::Pause() 
 706     bool bSuccess 
= DoPause(); 
 708         this->QueuePauseEvent(); 
 713 //--------------------------------------------------------------------------- 
 714 // wxQTMediaBackend::Stop 
 717 // 2) Seek to the beginning of the movie 
 718 //--------------------------------------------------------------------------- 
 719 bool wxQTMediaBackend::DoStop() 
 721     if (!wxQTMediaBackend::DoPause()) 
 724     ::GoToBeginningOfMovie(m_movie
); 
 725     return ::GetMoviesError() == noErr
; 
 728 bool wxQTMediaBackend::Stop() 
 730     bool bSuccess 
= DoStop(); 
 737 //--------------------------------------------------------------------------- 
 738 // wxQTMediaBackend::GetPlaybackRate 
 740 // 1) Get the movie playback rate from ::GetMovieRate 
 741 //--------------------------------------------------------------------------- 
 742 double wxQTMediaBackend::GetPlaybackRate() 
 744     return ( ((double)::GetMovieRate(m_movie
)) / 0x10000); 
 747 //--------------------------------------------------------------------------- 
 748 // wxQTMediaBackend::SetPlaybackRate 
 750 // 1) Convert dRate to Fixed and Set the movie rate through SetMovieRate 
 751 //--------------------------------------------------------------------------- 
 752 bool wxQTMediaBackend::SetPlaybackRate(double dRate
) 
 754     ::SetMovieRate(m_movie
, (Fixed
) (dRate 
* 0x10000)); 
 755     return ::GetMoviesError() == noErr
; 
 758 //--------------------------------------------------------------------------- 
 759 // wxQTMediaBackend::SetPosition 
 761 // 1) Create a time record struct (TimeRecord) with appropriate values 
 762 // 2) Pass struct to SetMovieTime 
 763 //--------------------------------------------------------------------------- 
 764 bool wxQTMediaBackend::SetPosition(wxLongLong where
) 
 766     TimeRecord theTimeRecord
; 
 767     memset(&theTimeRecord
, 0, sizeof(TimeRecord
)); 
 768     theTimeRecord
.value
.lo 
= where
.GetValue(); 
 769     theTimeRecord
.scale 
= ::GetMovieTimeScale(m_movie
); 
 770     theTimeRecord
.base 
= ::GetMovieTimeBase(m_movie
); 
 771     ::SetMovieTime(m_movie
, &theTimeRecord
); 
 773     if (::GetMoviesError() != noErr
) 
 779 //--------------------------------------------------------------------------- 
 780 // wxQTMediaBackend::GetPosition 
 782 // Calls GetMovieTime 
 783 //--------------------------------------------------------------------------- 
 784 wxLongLong 
wxQTMediaBackend::GetPosition() 
 786     return ::GetMovieTime(m_movie
, NULL
); 
 789 //--------------------------------------------------------------------------- 
 790 // wxQTMediaBackend::GetVolume 
 792 // Gets the volume through GetMovieVolume - which returns a 16 bit short - 
 794 // +--------+--------+ 
 796 // +--------+--------+ 
 798 // (1) first 8 bits are value before decimal 
 799 // (2) second 8 bits are value after decimal 
 801 // Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to 
 802 // 1 (full gain and sound) 
 803 //--------------------------------------------------------------------------- 
 804 double wxQTMediaBackend::GetVolume() 
 806     short sVolume 
= ::GetMovieVolume(m_movie
); 
 808     if (sVolume 
& (128 << 8)) //negative - no sound 
 811     return sVolume 
/ 256.0; 
 814 //--------------------------------------------------------------------------- 
 815 // wxQTMediaBackend::SetVolume 
 817 // Sets the volume through SetMovieVolume - which takes a 16 bit short - 
 819 // +--------+--------+ 
 821 // +--------+--------+ 
 823 // (1) first 8 bits are value before decimal 
 824 // (2) second 8 bits are value after decimal 
 826 // Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to 
 827 // 1 (full gain and sound) 
 828 //--------------------------------------------------------------------------- 
 829 bool wxQTMediaBackend::SetVolume(double dVolume
) 
 831     ::SetMovieVolume(m_movie
, (short) (dVolume 
* 256)); 
 835 //--------------------------------------------------------------------------- 
 836 // wxQTMediaBackend::GetDuration 
 838 // Calls GetMovieDuration 
 839 //--------------------------------------------------------------------------- 
 840 wxLongLong 
wxQTMediaBackend::GetDuration() 
 842     return ::GetMovieDuration(m_movie
); 
 845 //--------------------------------------------------------------------------- 
 846 // wxQTMediaBackend::GetState 
 848 // Determines the current state - the timer keeps track of whether or not 
 849 // we are paused or stopped (if the timer is running we are playing) 
 850 //--------------------------------------------------------------------------- 
 851 wxMediaState 
wxQTMediaBackend::GetState() 
 854     // GetMovieActive/IsMovieDone/SetMovieActive 
 855     // combo if implemented that way 
 857         return wxMEDIASTATE_PLAYING
; 
 858     else if (!m_movie 
|| wxQTMediaBackend::GetPosition() == 0) 
 859         return wxMEDIASTATE_STOPPED
; 
 861         return wxMEDIASTATE_PAUSED
; 
 864 //--------------------------------------------------------------------------- 
 865 // wxQTMediaBackend::Cleanup 
 867 // Diposes of the movie timer, Control if native, and stops and disposes 
 869 //--------------------------------------------------------------------------- 
 870 void wxQTMediaBackend::Cleanup() 
 880     // Apple samples with CreateMovieControl typically 
 881     // install a event handler and do this on the dispose 
 882     // event, but we do it here for simplicity 
 883     // (It might keep playing for several seconds after 
 884     // control destruction if not) 
 885     wxQTMediaBackend::Pause(); 
 887     // Dispose of control or remove movie from MovieController 
 889     thePoint
.h 
= thePoint
.v 
= 0; 
 890     ::MCSetVisible(m_mc
, false); 
 891     ::MCSetMovie(m_mc
, NULL
, NULL
, thePoint
); 
 893     ::DisposeMovie(m_movie
); 
 897 //--------------------------------------------------------------------------- 
 898 // wxQTMediaBackend::GetVideoSize 
 900 // Returns the actual size of the QT movie 
 901 //--------------------------------------------------------------------------- 
 902 wxSize 
wxQTMediaBackend::GetVideoSize() const 
 907 //--------------------------------------------------------------------------- 
 908 // wxQTMediaBackend::Move 
 910 // Move the movie controller or movie control 
 911 // (we need to actually move the movie control manually...) 
 912 // Top 10 things to do with quicktime in March 93's issue 
 913 // of DEVELOP - very useful 
 914 // http:// www.mactech.com/articles/develop/issue_13/031-033_QuickTime_column.html 
 915 // OLD NOTE: Calling MCSetControllerBoundsRect without detaching 
 916 //          supposively resulted in a crash back then. Current code even 
 917 //          with CFM classic runs fine. If there is ever a problem, 
 918 //          take out the if 0 lines below 
 919 //--------------------------------------------------------------------------- 
 920 void wxQTMediaBackend::Move(int x
, int y
, int w
, int h
) 
 924         m_ctrl
->GetParent()->MacWindowToRootWindow(&x
, &y
); 
 925         Rect theRect 
= {y
, x
, y 
+ h
, x 
+ w
}; 
 927 #if 0 // see note above 
 928         ::MCSetControllerAttached(m_mc
, false); 
 929          wxASSERT(::GetMoviesError() == noErr
); 
 932         ::MCSetControllerBoundsRect(m_mc
, &theRect
); 
 933         wxASSERT(::GetMoviesError() == noErr
); 
 935 #if 0 // see note above 
 936         if (m_interfaceflags
) 
 938             ::MCSetVisible(m_mc
, true); 
 939             wxASSERT(::GetMoviesError() == noErr
); 
 945 //--------------------------------------------------------------------------- 
 946 // wxQTMediaBackend::DoSetControllerVisible 
 948 // Utility function that takes care of showing the moviecontroller 
 949 // and showing/hiding the particular controls on it 
 950 //--------------------------------------------------------------------------- 
 951 void wxQTMediaBackend::DoSetControllerVisible( 
 952                         wxMediaCtrlPlayerControls flags
) 
 954     ::MCSetVisible(m_mc
, true); 
 956     // Take care of subcontrols 
 957     if (::GetMoviesError() == noErr
) 
 960         ::MCDoAction(m_mc
, 39/*mcActionGetFlags*/, (void*)&mcFlags
); 
 962         if (::GetMoviesError() == noErr
) 
 964              mcFlags 
|= (  //(1<<0)/*mcFlagSuppressMovieFrame*/ | 
 965                      (1 << 3)/*mcFlagsUseWindowPalette*/ 
 966                        | ((flags 
& wxMEDIACTRLPLAYERCONTROLS_STEP
) 
 967                           ? 0 : (1 << 1)/*mcFlagSuppressStepButtons*/) 
 968                        | ((flags 
& wxMEDIACTRLPLAYERCONTROLS_VOLUME
) 
 969                           ? 0 : (1 << 2)/*mcFlagSuppressSpeakerButton*/) 
 970                           //if we take care of repainting ourselves 
 971          //              | (1 << 4) /*mcFlagDontInvalidate*/ 
 974             ::MCDoAction(m_mc
, 38/*mcActionSetFlags*/, (void*)mcFlags
); 
 978     // Adjust height and width of best size for movie controller 
 979     // if the user wants it shown 
 980     m_bestSize
.x 
= m_bestSize
.x 
> wxMCWIDTH 
? m_bestSize
.x 
: wxMCWIDTH
; 
 981     m_bestSize
.y 
+= wxMCHEIGHT
; 
 984 //--------------------------------------------------------------------------- 
 985 // wxQTMediaBackend::ShowPlayerControls 
 987 // Shows/Hides subcontrols on the media control 
 988 //--------------------------------------------------------------------------- 
 989 bool wxQTMediaBackend::ShowPlayerControls(wxMediaCtrlPlayerControls flags
) 
 992         return false; // no movie controller... 
 994     bool bSizeChanged 
= false; 
 996     // if the controller is visible and we want to hide it do so 
 997     if (m_interfaceflags 
&& !flags
) 
1001         ::MCSetVisible(m_mc
, false); 
1003     else if (!m_interfaceflags 
&& flags
) // show controller if hidden 
1005         bSizeChanged 
= true; 
1006         DoSetControllerVisible(flags
); 
1009     // readjust parent sizers 
1012         NotifyMovieSizeChanged(); 
1014         // remember state in case of loading new media 
1015         m_interfaceflags 
= flags
; 
1018     return ::GetMoviesError() == noErr
; 
1021 //--------------------------------------------------------------------------- 
1022 // wxQTMediaBackend::GetDataSizeFromStart 
1024 // Calls either GetMovieDataSize or GetMovieDataSize64 with a value 
1025 // of 0 for the starting value 
1026 //--------------------------------------------------------------------------- 
1027 wxLongLong 
wxQTMediaBackend::GetDataSizeFromStart(TimeValue end
) 
1029 #if 0 // old pre-qt4 way 
1030     return ::GetMovieDataSize(m_movie
, 0, end
) 
1033     ::GetMovieDataSize64(m_movie
, 0, end
, &llDataSize
); 
1034     return wxLongLong(llDataSize
.hi
, llDataSize
.lo
); 
1038 //--------------------------------------------------------------------------- 
1039 // wxQTMediaBackend::GetDownloadProgress 
1040 //--------------------------------------------------------------------------- 
1041 wxLongLong 
wxQTMediaBackend::GetDownloadProgress() 
1043 #if 0 // hackish and slow 
1044     Handle hMovie 
= NewHandle(0); 
1045     PutMovieIntoHandle(m_movie
, hMovie
); 
1046     long lSize 
= GetHandleSize(hMovie
); 
1047     DisposeHandle(hMovie
); 
1052     if (::GetMaxLoadedTimeInMovie(m_movie
, &tv
) != noErr
) 
1054         wxLogDebug(wxT("GetMaxLoadedTimeInMovie failed")); 
1058     return wxQTMediaBackend::GetDataSizeFromStart(tv
); 
1062 //--------------------------------------------------------------------------- 
1063 // wxQTMediaBackend::GetDownloadTotal 
1064 //--------------------------------------------------------------------------- 
1065 wxLongLong 
wxQTMediaBackend::GetDownloadTotal() 
1067     return wxQTMediaBackend::GetDataSizeFromStart( 
1068                     ::GetMovieDuration(m_movie
) 
1072 //--------------------------------------------------------------------------- 
1073 // wxQTMediaBackend::MacVisibilityChanged 
1075 // The main problem here is that Windows quicktime, for example, 
1076 // renders more directly to a HWND. Mac quicktime does not do this 
1077 // and instead renders to the port of the WindowRef/WindowPtr on top 
1078 // of everything else/all other windows. 
1080 // So, for example, if you were to have a CreateTabsControl/wxNotebook 
1081 // and change pages, even if you called HIViewSetVisible/SetControlVisibility 
1082 // directly the movie will still continue playing on top of everything else 
1083 // if you went to a different tab. 
1085 // Note that another issue, and why we call MCSetControllerPort instead 
1086 // of SetMovieGWorld directly, is that in addition to rendering on 
1087 // top of everything else the last created controller steals mouse and 
1088 // other input from everything else in the window, including other 
1089 // controllers. Setting the port of it releases this behaviour. 
1090 //--------------------------------------------------------------------------- 
1091 void wxQTMediaBackend::MacVisibilityChanged() 
1093     if(!m_mc 
|| !m_ctrl
->m_bLoaded
) 
1094         return; //not initialized yet 
1096     if(m_ctrl
->MacIsReallyShown()) 
1098         //The window is being shown again, so set the GWorld of the 
1099         //controller back to the port of the parent WindowRef 
1101             (WindowRef
) m_ctrl
->MacGetTopLevelWindowRef(); 
1103         ::MCSetControllerPort(m_mc
, (CGrafPtr
) GetWindowPort(wrTLW
)); 
1104         wxASSERT(::GetMoviesError() == noErr
); 
1108         //We are being hidden - set the GWorld of the controller 
1109         //to the offscreen GWorld 
1110         ::MCSetControllerPort(m_mc
, m_movieWorld
); 
1111         wxASSERT(::GetMoviesError() == noErr
); 
1115 //--------------------------------------------------------------------------- 
1116 // wxQTMediaBackend::OnEraseBackground 
1118 // Suggestion from Greg Hazel to repaint the movie when idle 
1120 //--------------------------------------------------------------------------- 
1121 void wxQTMediaEvtHandler::OnEraseBackground(wxEraseEvent
& evt
) 
1123     // Work around Nasty OSX drawing bug: 
1124     // http://lists.apple.com/archives/QuickTime-API/2002/Feb/msg00311.html 
1125     WindowRef wrTLW 
= (WindowRef
) m_qtb
->m_ctrl
->MacGetTopLevelWindowRef(); 
1127     RgnHandle region 
= ::MCGetControllerBoundsRgn(m_qtb
->m_mc
); 
1128     ::MCInvalidate(m_qtb
->m_mc
, wrTLW
, region
); 
1129     ::MCIdle(m_qtb
->m_mc
); 
1132 //--------------------------------------------------------------------------- 
1133 // wxQTMediaBackend::PPRMProc (static) 
1135 // Called when done PrePrerolling the movie. 
1136 // Note that in 99% of the cases this does nothing... 
1137 // Anyway we set up the loading timer here to tell us when the movie is done 
1138 //--------------------------------------------------------------------------- 
1139 pascal void wxQTMediaBackend::PPRMProc( 
1141     OSErr 
WXUNUSED_UNLESS_DEBUG(theErr
), 
1144     wxASSERT( theMovie 
); 
1145     wxASSERT( theRefCon 
); 
1146     wxASSERT( theErr 
== noErr 
); 
1148     wxQTMediaBackend
* pBE 
= (wxQTMediaBackend
*) theRefCon
; 
1150     long lTime 
= ::GetMovieTime(theMovie
,NULL
); 
1151     Fixed rate 
= ::GetMoviePreferredRate(theMovie
); 
1152     ::PrerollMovie(theMovie
,lTime
,rate
); 
1153     pBE
->m_timer 
= new wxQTMediaLoadTimer(pBE
); 
1154     pBE
->m_timer
->Start(MOVIE_DELAY
); 
1157 //--------------------------------------------------------------------------- 
1158 // wxQTMediaBackend::MCFilterProc (static) 
1160 // Callback for when the movie controller recieves a message 
1161 //--------------------------------------------------------------------------- 
1162 pascal Boolean 
wxQTMediaBackend::MCFilterProc( 
1163     MovieController 
WXUNUSED(theController
), 
1165     void * WXUNUSED(params
), 
1168     wxQTMediaBackend
* pThis 
= (wxQTMediaBackend
*)refCon
; 
1173         // don't process idle events 
1177         // play button triggered - MC will set movie to opposite state 
1178         // of current - playing ? paused : playing 
1179         pThis
->m_bPlaying 
= !(pThis
->m_bPlaying
); 
1189 //--------------------------------------------------------------------------- 
1190 // wxQTMediaBackend::WindowEventHandler [static] 
1192 // Event callback for the top level window of our control that passes 
1193 // messages to our moviecontroller so it can receive mouse clicks etc. 
1194 //--------------------------------------------------------------------------- 
1195 pascal OSStatus 
wxQTMediaBackend::WindowEventHandler( 
1196     EventHandlerCallRef inHandlerCallRef
, 
1200     wxQTMediaBackend
* be 
= (wxQTMediaBackend
*) inUserData
; 
1202     // Only process keyboard messages on this window if it actually 
1203     // has focus, otherwise it will steal keystrokes from other windows! 
1204     // As well as when it is not loaded properly as it 
1205     // will crash in MCIsPlayerEvent 
1206     if((GetEventClass(inEvent
) == kEventClassKeyboard 
&& 
1207         wxWindow::FindFocus() != be
->m_ctrl
) 
1208         || !be
->m_ctrl
->m_bLoaded
) 
1209             return eventNotHandledErr
; 
1211     // Pass the event onto the movie controller 
1212     EventRecord theEvent
; 
1213     ConvertEventRefToEventRecord( inEvent
, &theEvent 
); 
1216     // TODO: Apple says MCIsPlayerEvent is depreciated and 
1217     // MCClick, MCKey, MCIdle etc. should be used 
1218     // (RN: Of course that's what they say about 
1219     //  CreateMovieControl and HIMovieView as well, LOL!) 
1220     err 
= ::MCIsPlayerEvent( be
->m_mc
, &theEvent 
); 
1222     // Pass on to other event handlers if not handled- i.e. wx 
1226         return eventNotHandledErr
; 
1229 // in source file that contains stuff you don't directly use 
1230 #include "wx/html/forcelnk.h" 
1231 FORCE_LINK_ME(basewxmediabackends
) 
1233 #endif // wxUSE_MEDIACTRL