1 /////////////////////////////////////////////////////////////////////////////
2 // Name: mac/carbon/mediactrl.cpp
3 // Purpose: Built-in Media Backends for Mac
4 // Author: Ryan Norton <wxprojects@comcast.net>
8 // Copyright: (c) 2004-2005 Ryan Norton
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 // There are several known bugs with CreateMovieControl
14 // on systems > 10.2 - see main.c of QTCarbonShell sample for details
16 // Also, with either version it will overdraw anything below its TLW - so
17 // its relatively useless on a notebook page (this happens in Opera too).
19 // Even though though the CreateMovieControl version is the default
20 // for OSX, the MovieController version is heavily tested and works
22 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
24 //===========================================================================
26 //===========================================================================
28 //---------------------------------------------------------------------------
29 // Pre-compiled header stuff
30 //---------------------------------------------------------------------------
32 // For compilers that support precompilation, includes "wx.h".
33 #include "wx/wxprec.h"
35 //---------------------------------------------------------------------------
37 //---------------------------------------------------------------------------
38 #include "wx/mediactrl.h"
40 //---------------------------------------------------------------------------
42 //---------------------------------------------------------------------------
45 //---------------------------------------------------------------------------
46 // Whether or not to use OSX 10.2's CreateMovieControl for native QuickTime
47 // control - i.e. native positioning and event handling etc..
48 //---------------------------------------------------------------------------
49 #ifndef wxUSE_CREATEMOVIECONTROL
50 # if defined( __WXMAC_OSX__ ) && \
51 ( MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_2 )
52 # define wxUSE_CREATEMOVIECONTROL 1
54 # define wxUSE_CREATEMOVIECONTROL 0
58 //---------------------------------------------------------------------------
59 // Height and Width of movie controller in the movie control
60 //---------------------------------------------------------------------------
64 //===========================================================================
65 // BACKEND DECLARATIONS
66 //===========================================================================
68 //---------------------------------------------------------------------------
72 //---------------------------------------------------------------------------
74 //---------------------------------------------------------------------------
76 //---------------------------------------------------------------------------
77 //uma is for wxMacFSSpec
78 #include "wx/mac/uma.h"
83 #include <QuickTimeComponents.h> //Standard QT stuff
85 #include <QuickTime/QuickTimeComponents.h>
88 class WXDLLIMPEXP_MEDIA wxQTMediaBackend
: public wxMediaBackendCommonBase
94 virtual bool CreateControl(wxControl
* ctrl
, wxWindow
* parent
,
99 const wxValidator
& validator
,
100 const wxString
& name
);
103 virtual bool Pause();
106 virtual bool Load(const wxString
& fileName
);
107 virtual bool Load(const wxURI
& location
);
109 virtual wxMediaState
GetState();
111 virtual bool SetPosition(wxLongLong where
);
112 virtual wxLongLong
GetPosition();
113 virtual wxLongLong
GetDuration();
115 virtual void Move(int x
, int y
, int w
, int h
);
116 wxSize
GetVideoSize() const;
118 virtual double GetPlaybackRate();
119 virtual bool SetPlaybackRate(double dRate
);
121 virtual double GetVolume();
122 virtual bool SetVolume(double);
127 virtual bool ShowPlayerControls(wxMediaCtrlPlayerControls flags
);
129 virtual wxLongLong
GetDownloadProgress();
130 virtual wxLongLong
GetDownloadTotal();
133 // ------ Implementation from now on --------
138 void DoLoadBestSize();
139 void DoSetControllerVisible(wxMediaCtrlPlayerControls flags
);
141 wxLongLong
GetDataSizeFromStart(TimeValue end
);
143 //TODO: Last param actually long - does this work on 64bit machines?
144 static pascal Boolean
MCFilterProc (MovieController theController
,
145 short action
, void *params
, long refCon
);
147 #if wxUSE_CREATEMOVIECONTROL
148 void DoCreateMovieControl();
150 Boolean
IsQuickTime4Installed();
151 void DoNewMovieController();
152 static pascal void PPRMProc (Movie theMovie
,
153 OSErr theErr
, void* theRefCon
);
156 wxSize m_bestSize
; // Original movie size
158 struct MovieType
** m_movie
; // QT Movie handle/instance
160 Movie m_movie
; // Movie instance
162 bool m_bPlaying
; // Whether media is playing or not
163 class wxTimer
* m_timer
; // Timer for streaming the movie
164 MovieController m_mc
; // MovieController instance
165 wxMediaCtrlPlayerControls m_interfaceflags
; // Saved interface flags
166 #if !wxUSE_CREATEMOVIECONTROL
167 EventHandlerRef m_pEventHandlerRef
; // Event handler to cleanup
168 MoviePrePrerollCompleteUPP m_preprerollupp
;
169 EventHandlerUPP m_eventupp
;
170 MCActionFilterWithRefConUPP m_mcactionupp
;
172 friend class wxQTMediaEvtHandler
;
174 DECLARE_DYNAMIC_CLASS(wxQTMediaBackend
)
177 #if !wxUSE_CREATEMOVIECONTROL
178 // helper to hijack background erasing for the QT window
179 class WXDLLIMPEXP_MEDIA wxQTMediaEvtHandler
: public wxEvtHandler
182 wxQTMediaEvtHandler(wxQTMediaBackend
*qtb
)
186 qtb
->m_ctrl
->Connect(qtb
->m_ctrl
->GetId(), wxEVT_ERASE_BACKGROUND
,
187 wxEraseEventHandler(wxQTMediaEvtHandler::OnEraseBackground
),
191 void OnEraseBackground(wxEraseEvent
& event
);
194 wxQTMediaBackend
*m_qtb
;
196 DECLARE_NO_COPY_CLASS(wxQTMediaEvtHandler
)
199 // Window event handler
200 static pascal OSStatus
wxQTMediaWindowEventHandler(
201 EventHandlerCallRef inHandlerCallRef
,
202 EventRef inEvent
, void *inUserData
);
206 //===========================================================================
208 //===========================================================================
211 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
215 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
217 IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend
, wxMediaBackend
)
219 //Time between timer calls - this is the Apple recommondation to the TCL
221 #define MOVIE_DELAY 20
223 //---------------------------------------------------------------------------
224 // wxQTMediaLoadTimer
226 // QT, esp. QT for Windows is very picky about how you go about
227 // async loading. If you were to go through a Windows message loop
228 // or a MoviesTask or both and then check the movie load state
229 // it would still return 1000 (loading)... even (pre)prerolling doesn't
230 // help. However, making a load timer like this works
231 //---------------------------------------------------------------------------
232 class wxQTMediaLoadTimer
: public wxTimer
235 wxQTMediaLoadTimer(Movie movie
, wxQTMediaBackend
* parent
) :
236 m_movie(movie
), m_parent(parent
) {}
240 //Note that the CreateMovieControl variety performs
241 //its own custom idleing
242 #if !wxUSE_CREATEMOVIECONTROL
243 ::MCIdle(m_parent
->m_mc
);
245 //kMovieLoadStatePlayable is not enough on MAC
246 //- it plays, but IsMovieDone might return true (!)
247 //sure we need to wait until kMovieLoadStatePlaythroughOK
248 if(::GetMovieLoadState(m_movie
) >= 20000)
250 m_parent
->FinishLoad();
256 Movie m_movie
; //Our movie instance
257 wxQTMediaBackend
* m_parent
; //Backend pointer
260 // --------------------------------------------------------------------------
261 // wxQTMediaPlayTimer - Handle Asyncronous Playing
263 // 1) Checks to see if the movie is done, and if not continues
264 // streaming the movie
265 // 2) Sends the wxEVT_MEDIA_STOP event if we have reached the end of
267 // --------------------------------------------------------------------------
268 class wxQTMediaPlayTimer
: public wxTimer
271 wxQTMediaPlayTimer(Movie movie
, wxQTMediaBackend
* parent
) :
272 m_movie(movie
), m_parent(parent
) {}
276 //Note that CreateMovieControl performs its own idleing
277 #if !wxUSE_CREATEMOVIECONTROL
279 // OK, a little explaining - basically originally
280 // we only called MoviesTask if the movie was actually
281 // playing (not paused or stopped)... this was before
282 // we realized MoviesTask actually handles repainting
283 // of the current frame - so if you were to resize
284 // or something it would previously not redraw that
285 // portion of the movie.
287 // So now we call MoviesTask always so that it repaints
290 ::MCIdle(m_parent
->m_mc
);
294 // Handle the stop event - if the movie has reached
295 // the end, notify our handler
297 if(::IsMovieDone(m_movie
))
299 if ( m_parent
->SendStopEvent() )
302 wxASSERT(::GetMoviesError() == noErr
);
304 m_parent
->QueueFinishEvent();
310 Movie m_movie
; //Our movie instance
311 wxQTMediaBackend
* m_parent
; //Backend pointer
315 //---------------------------------------------------------------------------
316 // wxQTMediaBackend Constructor
318 // Sets m_timer to NULL signifying we havn't loaded anything yet
319 //---------------------------------------------------------------------------
320 wxQTMediaBackend::wxQTMediaBackend()
321 : m_movie(NULL
), m_bPlaying(false), m_timer(NULL
)
322 , m_mc(NULL
), m_interfaceflags(wxMEDIACTRLPLAYERCONTROLS_NONE
)
323 #if !wxUSE_CREATEMOVIECONTROL
324 , m_preprerollupp(NULL
)
329 //---------------------------------------------------------------------------
330 // wxQTMediaBackend Destructor
332 // 1) Cleans up the QuickTime movie instance
333 // 2) Decrements the QuickTime reference counter - if this reaches
334 // 0, QuickTime shuts down
335 // 3) Decrements the QuickTime Windows Media Layer reference counter -
336 // if this reaches 0, QuickTime shuts down the Windows Media Layer
337 //---------------------------------------------------------------------------
338 wxQTMediaBackend::~wxQTMediaBackend()
343 #if !wxUSE_CREATEMOVIECONTROL
344 // Cleanup for moviecontroller
347 // destroy wxQTMediaEvtHandler we pushed on it
348 m_ctrl
->PopEventHandler(true);
349 RemoveEventHandler((EventHandlerRef
&)m_pEventHandlerRef
);
350 DisposeEventHandlerUPP(m_eventupp
);
352 // Dispose of the movie controller
353 ::DisposeMovieController(m_mc
);
354 DisposeMCActionFilterWithRefConUPP(m_mcactionupp
);
358 //Note that ExitMovies() is not necessary...
362 //---------------------------------------------------------------------------
363 // wxQTMediaBackend::CreateControl
365 // 1) Intializes QuickTime
366 // 2) Creates the control window
367 //---------------------------------------------------------------------------
368 bool wxQTMediaBackend::CreateControl(wxControl
* ctrl
, wxWindow
* parent
,
373 const wxValidator
& validator
,
374 const wxString
& name
)
376 //Don't bother in Native control mode
377 #if !wxUSE_CREATEMOVIECONTROL
378 if (!IsQuickTime4Installed())
386 // By default wxWindow(s) is created with a border -
387 // so we need to get rid of those
389 // Since we don't have a child window like most other
390 // backends, we don't need wxCLIP_CHILDREN
392 if ( !ctrl
->wxControl::Create(parent
, id
, pos
, size
,
393 wxWindow::MacRemoveBordersFromStyle(style
),
399 ctrl
->SetValidator(validator
);
402 m_ctrl
= (wxMediaCtrl
*)ctrl
;
406 //---------------------------------------------------------------------------
407 // wxQTMediaBackend::IsQuickTime4Installed
409 // Determines whether version 4 of QT is installed
410 // (Pretty much for classic only)
411 //---------------------------------------------------------------------------
412 #if !wxUSE_CREATEMOVIECONTROL
413 Boolean
wxQTMediaBackend::IsQuickTime4Installed()
418 error
= Gestalt (gestaltQuickTime
, &result
);
419 return (error
== noErr
) && (((result
>> 16) & 0xffff) >= 0x0400);
423 //---------------------------------------------------------------------------
424 // wxQTMediaBackend::Load (file version)
426 // 1) Get an FSSpec from the Windows path name
428 // 3) Obtain the movie instance from the movie resource
429 // 4) Close the movie resource
431 //---------------------------------------------------------------------------
432 bool wxQTMediaBackend::Load(const wxString
& fileName
)
441 //FIXME:wxMacFilename2FSSpec crashes on empty string -
442 //does it crash on other strings too and should this
443 //"fix" be put in the carbon wxSound?
444 if (fileName
.empty())
447 wxMacFilename2FSSpec( fileName
, &sfFile
);
449 if (OpenMovieFile (&sfFile
, &movieResFile
, fsRdPerm
) != noErr
)
452 short movieResID
= 0;
455 err
= NewMovieFromFile (
463 //No ::GetMoviesStickyError() here because it returns -2009
464 // a.k.a. invalid track on valid mpegs
467 ::CloseMovieFile (movieResFile
);
469 // Create movie controller/control
470 #if wxUSE_CREATEMOVIECONTROL
471 DoCreateMovieControl();
473 DoNewMovieController();
484 //---------------------------------------------------------------------------
485 // wxQTMediaBackend::PPRMProc (static)
487 // Called when done PrePrerolling the movie.
488 // Note that in 99% of the cases this does nothing...
489 // Anyway we set up the loading timer here to tell us when the movie is done
490 //---------------------------------------------------------------------------
491 #if !wxUSE_CREATEMOVIECONTROL
492 pascal void wxQTMediaBackend::PPRMProc (Movie theMovie
,
493 OSErr
WXUNUSED_UNLESS_DEBUG(theErr
),
496 wxASSERT( theMovie
);
497 wxASSERT( theRefCon
);
498 wxASSERT( theErr
== noErr
);
500 wxQTMediaBackend
* pBE
= (wxQTMediaBackend
*) theRefCon
;
502 long lTime
= ::GetMovieTime(theMovie
,NULL
);
503 Fixed rate
= ::GetMoviePreferredRate(theMovie
);
504 ::PrerollMovie(theMovie
,lTime
,rate
);
505 pBE
->m_timer
= new wxQTMediaLoadTimer(pBE
->m_movie
, pBE
);
506 pBE
->m_timer
->Start(MOVIE_DELAY
);
510 //---------------------------------------------------------------------------
511 // wxQTMediaBackend::Load (URL Version)
513 // 1) Build an escaped URI from location
514 // 2) Create a handle to store the URI string
515 // 3) Put the URI string inside the handle
516 // 4) Make a QuickTime URL data ref from the handle with the URI in it
517 // 5) Clean up the URI string handle
518 // 6) Do some prerolling
520 //---------------------------------------------------------------------------
521 bool wxQTMediaBackend::Load(const wxURI
& location
)
526 wxString theURI
= location
.BuildURI();
530 Handle theHandle
= ::NewHandleClear(theURI
.length() + 1);
533 ::BlockMove(theURI
.mb_str(), *theHandle
, theURI
.length() + 1);
535 //create the movie from the handle that refers to the URI
536 err
= ::NewMovieFromDataRef(&m_movie
, newMovieActive
|
538 /*|newMovieIdleImportOK*/,
540 URLDataHandlerSubType
);
542 ::DisposeHandle(theHandle
);
546 #if wxUSE_CREATEMOVIECONTROL
547 // Movie control does its own "(pre)prerolling"
548 // but we still need to buffer the movie for the url
549 DoCreateMovieControl();
551 // Setup timer to catch load event
552 m_timer
= new wxQTMediaLoadTimer(m_movie
, this);
553 m_timer
->Start(MOVIE_DELAY
);
555 // Movie controller resets prerolling, so we must create first
556 DoNewMovieController();
561 timeNow
= ::GetMovieTime(m_movie
, NULL
);
562 wxASSERT(::GetMoviesError() == noErr
);
564 playRate
= ::GetMoviePreferredRate(m_movie
);
565 wxASSERT(::GetMoviesError() == noErr
);
568 // Note that the callback here is optional,
569 // but without it PrePrerollMovie can be buggy
570 // (see Apple ml). Also, some may wonder
571 // why we need this at all - this is because
572 // Apple docs say QuickTime streamed movies
573 // require it if you don't use a Movie Controller,
574 // which we don't by default.
577 NewMoviePrePrerollCompleteUPP(
578 wxQTMediaBackend::PPRMProc
581 ::PrePrerollMovie(m_movie
, timeNow
, playRate
,
591 //---------------------------------------------------------------------------
592 // wxQTMediaBackend::DoCreateMovieControl
594 // Calls CreateMovieControl and performs setup related to it
596 // Note that we always hide the controller initially becuase when loading
597 // from a url it displays about a 40x40 box with the word loading... in it,
598 // but the box is outside the range of the control, which is bad (0,0
599 // i believe), so we need to wait until finishload to actually display
600 // the movie controller in this instance
601 //---------------------------------------------------------------------------
602 #if wxUSE_CREATEMOVIECONTROL
603 void wxQTMediaBackend::DoCreateMovieControl()
606 //Native CreateMovieControl QT control (Thanks to Kevin Olliver's
607 // wxQTMovie for how to do some of this).
609 Rect bounds
= wxMacGetBoundsForControl(m_ctrl
,
610 m_ctrl
->GetPosition(),
613 //Dispose of old control for new one
614 if (m_ctrl
->m_peer
&& m_ctrl
->m_peer
->Ok() )
615 m_ctrl
->m_peer
->Dispose();
618 //kMovieControlOptionXXX
619 //HideController - hide the movie controller
620 //LocateTopLeft - movie is pinned to top left rather than centered in the control
621 //EnableEditing - Allows programmatic editing and dragn'drop
622 //HandleEditingHI- Installs event stuff for edit menu - forces EnableEditing also
623 //SetKeysEnabled - Allows keyboard input
624 //ManuallyIdled - app handles movie idling rather than internal timer event loop
625 ::CreateMovieControl(
627 m_ctrl
->MacGetTopLevelWindowRef(), //parent
628 &bounds
, //control bounds
629 m_movie
, //movie handle
630 kMovieControlOptionHideController
631 | kMovieControlOptionLocateTopLeft
632 | kMovieControlOptionSetKeysEnabled
633 // | kMovieControlOptionManuallyIdled
635 m_ctrl
->m_peer
->GetControlRefAddr() );
637 ::EmbedControl(m_ctrl
->m_peer
->GetControlRef(),
638 (ControlRef
)m_ctrl
->GetParent()->GetHandle());
641 // Setup MovieController for the new movie
645 //Get movie controller from our control
646 ::GetControlData( m_ctrl
->m_peer
->GetControlRef(), 0,
647 kMovieControlDataMovieController
,
648 sizeof(MovieController
), (Ptr
)&m_mc
, &dataSize
);
650 // Setup a callback so we can tell when the user presses
651 // play on the player controls
652 ::MCSetActionFilterWithRefCon(m_mc
,
653 (MCActionFilterWithRefConUPP
)wxQTMediaBackend::MCFilterProc
,
658 //---------------------------------------------------------------------------
659 // wxQTMediaBackend::DoNewMovieController
661 // Attaches movie to moviecontroller or creates moviecontroller
662 // if not created yet
663 //---------------------------------------------------------------------------
664 #if !wxUSE_CREATEMOVIECONTROL
665 void wxQTMediaBackend::DoNewMovieController()
669 // Get top level window ref for some mac functions
670 WindowRef wrTLW
= (WindowRef
) m_ctrl
->MacGetTopLevelWindowRef();
672 // MovieController not setup yet -
673 // so we need to create a new one.
674 // You have to pass a valid movie to
675 // NewMovieController, evidently
676 ::SetMovieGWorld(m_movie
,
677 (CGrafPtr
) GetWindowPort(wrTLW
),
679 wxASSERT(::GetMoviesError() == noErr
);
681 Rect bounds
= wxMacGetBoundsForControl(m_ctrl
,
682 m_ctrl
->GetPosition(),
685 m_mc
= ::NewMovieController(m_movie
, &bounds
, mcTopLeftMovie
|
688 wxASSERT(::GetMoviesError() == noErr
);
689 ::MCDoAction(m_mc
, 32, (void*)true); //mcActionSetKeysEnabled
690 wxASSERT(::GetMoviesError() == noErr
);
692 // Setup a callback so we can tell when the user presses
693 // play on the player controls
695 NewMCActionFilterWithRefConUPP(
696 wxQTMediaBackend::MCFilterProc
698 ::MCSetActionFilterWithRefCon(m_mc
,
701 wxASSERT(::GetMoviesError() == noErr
);
703 //Part of a suggestion from Greg Hazel to repaint
705 m_ctrl
->PushEventHandler(new wxQTMediaEvtHandler(this));
707 // Event types to catch from the TLW
708 // for the moviecontroller
709 EventTypeSpec theEventTypes
[] = {
710 { kEventClassMouse
, kEventMouseDown
},
711 { kEventClassMouse
, kEventMouseUp
},
712 { kEventClassMouse
, kEventMouseDragged
},
713 { kEventClassKeyboard
, kEventRawKeyDown
},
714 { kEventClassKeyboard
, kEventRawKeyRepeat
},
715 { kEventClassKeyboard
, kEventRawKeyUp
},
716 { kEventClassWindow
, kEventWindowUpdate
},
717 { kEventClassWindow
, kEventWindowActivated
},
718 { kEventClassWindow
, kEventWindowDeactivated
}
721 // Catch window messages -
722 // if we do not do this and if the user clicks the play
723 // button on the controller, for instance, nothing will happen...
724 m_eventupp
= NewEventHandlerUPP(
725 wxQTMediaWindowEventHandler
727 InstallWindowEventHandler( wrTLW
,
729 GetEventTypeCount( theEventTypes
), theEventTypes
,
730 m_mc
, (&(EventHandlerRef
&)m_pEventHandlerRef
) );
734 // MovieController already created -
735 // Just change the movie in it and we're good to go
737 thePoint
.h
= thePoint
.v
= 0;
738 ::MCSetMovie(m_mc
, m_movie
,
739 (WindowRef
)m_ctrl
->MacGetTopLevelWindowRef(),
741 wxASSERT(::GetMoviesError() == noErr
);
746 //---------------------------------------------------------------------------
747 // wxQTMediaBackend::FinishLoad
749 // Performs operations after a movie ready to play/loaded.
750 //---------------------------------------------------------------------------
751 void wxQTMediaBackend::FinishLoad()
753 // Dispose of the Preprerollmovieupp if we used it
754 #if !wxUSE_CREATEMOVIECONTROL
755 DisposeMoviePrePrerollCompleteUPP(m_preprerollupp
);
757 // get the real size of the movie
760 // Show the player controls if the user wants to
762 DoSetControllerVisible(m_interfaceflags
);
764 //we want millisecond precision
765 ::SetMovieTimeScale(m_movie
, 1000);
766 wxASSERT(::GetMoviesError() == noErr
);
768 // Start movie progress timer
769 m_timer
= new wxQTMediaPlayTimer(m_movie
, (wxQTMediaBackend
*) this);
771 m_timer
->Start(MOVIE_DELAY
, wxTIMER_CONTINUOUS
);
773 //send loaded event & refresh size
777 //---------------------------------------------------------------------------
778 // wxQTMediaBackend::DoLoadBestSize
780 // Sets the best size of the control from the real size of the movie
781 //---------------------------------------------------------------------------
782 void wxQTMediaBackend::DoLoadBestSize()
784 //get the real size of the movie
786 ::GetMovieNaturalBoundsRect (m_movie
, &outRect
);
787 wxASSERT(::GetMoviesError() == noErr
);
789 //determine best size
790 m_bestSize
.x
= outRect
.right
- outRect
.left
;
791 m_bestSize
.y
= outRect
.bottom
- outRect
.top
;
794 //---------------------------------------------------------------------------
795 // wxQTMediaBackend::Play
797 // Start the QT movie
798 // (Apple recommends mcActionPrerollAndPlay but that's QT 4.1+)
799 //---------------------------------------------------------------------------
800 bool wxQTMediaBackend::Play()
802 Fixed fixRate
= (Fixed
) (wxQTMediaBackend::GetPlaybackRate() * 0x10000);
804 fixRate
= ::GetMoviePreferredRate(m_movie
);
806 wxASSERT(fixRate
!= 0);
809 ::MCDoAction( m_mc
, 8, // mcActionPlay
812 if(::GetMoviesError() == noErr
)
823 //---------------------------------------------------------------------------
824 // wxQTMediaBackend::Pause
827 //---------------------------------------------------------------------------
828 bool wxQTMediaBackend::DoPause()
830 //Stop the movie A.K.A. ::StopMovie(m_movie);
833 ::MCDoAction( m_mc
, 8 /*mcActionPlay*/,
836 return ::GetMoviesError() == noErr
;
838 return true; //already paused
841 bool wxQTMediaBackend::Pause()
843 bool bSuccess
= DoPause();
846 this->QueuePauseEvent();
853 //---------------------------------------------------------------------------
854 // wxQTMediaBackend::Stop
857 // 2) Seek to the beginning of the movie
858 //---------------------------------------------------------------------------
859 bool wxQTMediaBackend::DoStop()
861 if(!wxQTMediaBackend::DoPause())
864 ::GoToBeginningOfMovie(m_movie
);
865 return ::GetMoviesError() == noErr
;
868 bool wxQTMediaBackend::Stop()
870 bool bSuccess
= DoStop();
880 //---------------------------------------------------------------------------
881 // wxQTMediaBackend::GetPlaybackRate
883 // 1) Get the movie playback rate from ::GetMovieRate
884 //---------------------------------------------------------------------------
885 double wxQTMediaBackend::GetPlaybackRate()
887 return ( ((double)::GetMovieRate(m_movie
)) / 0x10000);
890 //---------------------------------------------------------------------------
891 // wxQTMediaBackend::SetPlaybackRate
893 // 1) Convert dRate to Fixed and Set the movie rate through SetMovieRate
894 //---------------------------------------------------------------------------
895 bool wxQTMediaBackend::SetPlaybackRate(double dRate
)
897 ::SetMovieRate(m_movie
, (Fixed
) (dRate
* 0x10000));
898 return ::GetMoviesError() == noErr
;
901 //---------------------------------------------------------------------------
902 // wxQTMediaBackend::SetPosition
904 // 1) Create a time record struct (TimeRecord) with appropriate values
905 // 2) Pass struct to SetMovieTime
906 //---------------------------------------------------------------------------
907 bool wxQTMediaBackend::SetPosition(wxLongLong where
)
909 TimeRecord theTimeRecord
;
910 memset(&theTimeRecord
, 0, sizeof(TimeRecord
));
911 theTimeRecord
.value
.lo
= where
.GetValue();
912 theTimeRecord
.scale
= ::GetMovieTimeScale(m_movie
);
913 theTimeRecord
.base
= ::GetMovieTimeBase(m_movie
);
914 ::SetMovieTime(m_movie
, &theTimeRecord
);
916 if (::GetMoviesError() != noErr
)
922 //---------------------------------------------------------------------------
923 // wxQTMediaBackend::GetPosition
925 // Calls GetMovieTime
926 //---------------------------------------------------------------------------
927 wxLongLong
wxQTMediaBackend::GetPosition()
929 return ::GetMovieTime(m_movie
, NULL
);
932 //---------------------------------------------------------------------------
933 // wxQTMediaBackend::GetVolume
935 // Gets the volume through GetMovieVolume - which returns a 16 bit short -
937 // +--------+--------+
939 // +--------+--------+
941 // (1) first 8 bits are value before decimal
942 // (2) second 8 bits are value after decimal
944 // Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to
945 // 1 (full gain and sound)
946 //---------------------------------------------------------------------------
947 double wxQTMediaBackend::GetVolume()
949 short sVolume
= ::GetMovieVolume(m_movie
);
951 if(sVolume
& (128 << 8)) //negative - no sound
954 return sVolume
/256.0;
957 //---------------------------------------------------------------------------
958 // wxQTMediaBackend::SetVolume
960 // Sets the volume through SetMovieVolume - which takes a 16 bit short -
962 // +--------+--------+
964 // +--------+--------+
966 // (1) first 8 bits are value before decimal
967 // (2) second 8 bits are value after decimal
969 // Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to
970 // 1 (full gain and sound)
971 //---------------------------------------------------------------------------
972 bool wxQTMediaBackend::SetVolume(double dVolume
)
974 ::SetMovieVolume(m_movie
, (short) (dVolume
* 256));
978 //---------------------------------------------------------------------------
979 // wxQTMediaBackend::GetDuration
981 // Calls GetMovieDuration
982 //---------------------------------------------------------------------------
983 wxLongLong
wxQTMediaBackend::GetDuration()
985 return ::GetMovieDuration(m_movie
);
988 //---------------------------------------------------------------------------
989 // wxQTMediaBackend::GetState
991 // Determines the current state - the timer keeps track of whether or not
992 // we are paused or stopped (if the timer is running we are playing)
993 //---------------------------------------------------------------------------
994 wxMediaState
wxQTMediaBackend::GetState()
997 // GetMovieActive/IsMovieDone/SetMovieActive
998 // combo if implemented that way
999 if (m_bPlaying
== true)
1000 return wxMEDIASTATE_PLAYING
;
1001 else if ( !m_movie
|| wxQTMediaBackend::GetPosition() == 0)
1002 return wxMEDIASTATE_STOPPED
;
1004 return wxMEDIASTATE_PAUSED
;
1007 //---------------------------------------------------------------------------
1008 // wxQTMediaBackend::Cleanup
1010 // Diposes of the movie timer, Control if native, and stops and disposes
1012 //---------------------------------------------------------------------------
1013 void wxQTMediaBackend::Cleanup()
1023 // Apple samples with CreateMovieControl typically
1024 // install a event handler and do this on the dispose
1025 // event, but we do it here for simplicity
1026 // (It might keep playing for several seconds after
1027 // control destruction if not)
1028 wxQTMediaBackend::Pause();
1031 // Dispose of control or remove movie from MovieController
1033 #if wxUSE_CREATEMOVIECONTROL
1034 if (m_ctrl
->m_peer
&& m_ctrl
->m_peer
->Ok() )
1035 m_ctrl
->m_peer
->Dispose();
1038 thePoint
.h
= thePoint
.v
= 0;
1039 ::MCSetVisible(m_mc
, false);
1040 ::MCSetMovie(m_mc
, NULL
, NULL
, thePoint
);
1043 ::DisposeMovie(m_movie
);
1046 //---------------------------------------------------------------------------
1047 // wxQTMediaBackend::MCFilterProc (static)
1049 // Callback for when the movie controller recieves a message
1050 //---------------------------------------------------------------------------
1051 pascal Boolean
wxQTMediaBackend::MCFilterProc(
1052 MovieController
WXUNUSED(theController
),
1054 void * WXUNUSED(params
),
1057 if(action
!= 1) //don't process idle events
1059 wxQTMediaBackend
* pThis
= (wxQTMediaBackend
*)refCon
;
1063 case 8: //play button triggered - MC will set movie to opposite state
1064 //of current - playing ? paused : playing
1065 pThis
->m_bPlaying
= !(pThis
->m_bPlaying
);
1074 //---------------------------------------------------------------------------
1075 // wxQTMediaBackend::GetVideoSize
1077 // Returns the actual size of the QT movie
1078 //---------------------------------------------------------------------------
1079 wxSize
wxQTMediaBackend::GetVideoSize() const
1084 //---------------------------------------------------------------------------
1085 // wxQTMediaBackend::Move
1087 // Move the movie controller or movie control
1088 // (we need to actually move the movie control manually...)
1089 // Top 10 things to do with quicktime in March 93's issue
1090 // of DEVELOP - very useful
1091 // http:// www.mactech.com/articles/develop/issue_13/031-033_QuickTime_column.html
1092 // OLD NOTE: Calling MCSetControllerBoundsRect without detaching
1093 // supposively resulted in a crash back then. Current code even
1094 // with CFM classic runs fine. If there is ever a problem,
1095 // take out the if 0 lines below
1096 //---------------------------------------------------------------------------
1097 void wxQTMediaBackend::Move(int x
, int y
, int w
, int h
)
1099 #if !wxUSE_CREATEMOVIECONTROL
1102 m_ctrl
->GetParent()->MacWindowToRootWindow(&x
, &y
);
1103 Rect theRect
= {y
, x
, y
+h
, x
+w
};
1104 #if 0 // see note above
1105 ::MCSetControllerAttached(m_mc
, FALSE
);
1106 wxASSERT(::GetMoviesError() == noErr
);
1108 ::MCSetControllerBoundsRect(m_mc
, &theRect
);
1109 wxASSERT(::GetMoviesError() == noErr
);
1111 #if 0 // see note above
1112 if(m_interfaceflags
)
1114 ::MCSetVisible(m_mc
, TRUE
);
1115 wxASSERT(::GetMoviesError() == noErr
);
1120 if(m_timer
&& m_ctrl
)
1122 m_ctrl
->GetParent()->MacWindowToRootWindow(&x
, &y
);
1124 ::MoveControl( (ControlRef
) m_ctrl
->GetHandle(), x
, y
);
1125 m_ctrl
->GetParent()->Refresh();
1126 m_ctrl
->GetParent()->Update();
1131 //---------------------------------------------------------------------------
1132 // wxQTMediaBackend::DoSetControllerVisible
1134 // Utility function that takes care of showing the moviecontroller
1135 // and showing/hiding the particular controls on it
1136 //---------------------------------------------------------------------------
1137 void wxQTMediaBackend::DoSetControllerVisible(wxMediaCtrlPlayerControls flags
)
1139 ::MCSetVisible(m_mc
, TRUE
);
1142 // Take care of subcontrols
1144 if(::GetMoviesError() == noErr
)
1147 ::MCDoAction(m_mc
, 39/*mcActionGetFlags*/, (void*)&mcFlags
);
1149 if(::GetMoviesError() == noErr
)
1151 mcFlags
|= ( //(1<<0)/*mcFlagSuppressMovieFrame*/ |
1152 (1<<3)/*mcFlagsUseWindowPalette*/
1153 | ((flags
& wxMEDIACTRLPLAYERCONTROLS_STEP
)
1154 ? 0 : (1<<1)/*mcFlagSuppressStepButtons*/)
1155 | ((flags
& wxMEDIACTRLPLAYERCONTROLS_VOLUME
)
1156 ? 0 : (1<<2)/*mcFlagSuppressSpeakerButton*/)
1157 // | (1<<4) /*mcFlagDontInvalidate*/ //if we take care of repainting ourselves
1159 ::MCDoAction(m_mc
, 38/*mcActionSetFlags*/, (void*)mcFlags
);
1164 //Adjust height and width of best size for movie controller
1165 //if the user wants it shown
1167 m_bestSize
.x
= m_bestSize
.x
> wxMCWIDTH
? m_bestSize
.x
: wxMCWIDTH
;
1168 m_bestSize
.y
+= wxMCHEIGHT
;
1171 //---------------------------------------------------------------------------
1172 // wxQTMediaBackend::ShowPlayerControls
1174 // Shows/Hides subcontrols on the media control
1175 //---------------------------------------------------------------------------
1176 bool wxQTMediaBackend::ShowPlayerControls(wxMediaCtrlPlayerControls flags
)
1179 return false; //no movie controller...
1181 bool bSizeChanged
= false;
1183 //if the controller is visible and we want to hide it do so
1184 if(m_interfaceflags
&& !flags
)
1186 bSizeChanged
= true;
1188 ::MCSetVisible(m_mc
, FALSE
);
1190 else if(!m_interfaceflags
&& flags
) //show controller if hidden
1192 bSizeChanged
= true;
1193 DoSetControllerVisible(flags
);
1196 //readjust parent sizers
1199 NotifyMovieSizeChanged();
1201 //remember state in case of loading new media
1202 m_interfaceflags
= flags
;
1204 return ::GetMoviesError() == noErr
;
1207 //---------------------------------------------------------------------------
1208 // wxQTMediaBackend::GetDataSizeFromStart
1210 // Calls either GetMovieDataSize or GetMovieDataSize64 with a value
1211 // of 0 for the starting value
1212 //---------------------------------------------------------------------------
1213 wxLongLong
wxQTMediaBackend::GetDataSizeFromStart(TimeValue end
)
1215 #if 0 // old pre-qt4 way
1216 return ::GetMovieDataSize(m_movie
, 0, end
)
1219 ::GetMovieDataSize64(m_movie
, 0, end
, &llDataSize
);
1220 return wxLongLong(llDataSize
.hi
, llDataSize
.lo
);
1224 //---------------------------------------------------------------------------
1225 // wxQTMediaBackend::GetDownloadProgress
1226 //---------------------------------------------------------------------------
1227 wxLongLong
wxQTMediaBackend::GetDownloadProgress()
1229 #if 0 // hackish and slow
1230 Handle hMovie
= NewHandle(0);
1231 PutMovieIntoHandle(m_movie
, hMovie
);
1232 long lSize
= GetHandleSize(hMovie
);
1233 DisposeHandle(hMovie
);
1237 if(::GetMaxLoadedTimeInMovie(m_movie
, &tv
) != noErr
)
1239 wxLogDebug(wxT("GetMaxLoadedTimeInMovie failed"));
1242 return wxQTMediaBackend::GetDataSizeFromStart(tv
);
1246 //---------------------------------------------------------------------------
1247 // wxQTMediaBackend::GetDownloadTotal
1248 //---------------------------------------------------------------------------
1249 wxLongLong
wxQTMediaBackend::GetDownloadTotal()
1251 return wxQTMediaBackend::GetDataSizeFromStart(
1252 ::GetMovieDuration(m_movie
)
1257 //---------------------------------------------------------------------------
1258 // wxQTMediaBackend::OnEraseBackground
1260 // Suggestion from Greg Hazel to repaint the movie when idle
1262 //---------------------------------------------------------------------------
1263 #if !wxUSE_CREATEMOVIECONTROL
1264 void wxQTMediaEvtHandler::OnEraseBackground(wxEraseEvent
& evt
)
1266 // Work around Nasty OSX drawing bug -
1267 // http://lists.apple.com/archives/QuickTime-API/2002/Feb/msg00311.html
1269 (WindowRef
) m_qtb
->m_ctrl
->MacGetTopLevelWindowRef();
1271 RgnHandle region
= ::MCGetControllerBoundsRgn(m_qtb
->m_mc
);
1272 ::MCInvalidate(m_qtb
->m_mc
, wrTLW
, region
);
1273 ::MCIdle(m_qtb
->m_mc
);
1277 //---------------------------------------------------------------------------
1278 // wxQTMediaWindowEventHandler
1280 // Event callback for the top level window of our control that passes
1281 // messages to our moviecontroller so it can recieve mouse clicks etc.
1282 //---------------------------------------------------------------------------
1283 #if !wxUSE_CREATEMOVIECONTROL
1284 static pascal OSStatus
wxQTMediaWindowEventHandler(
1285 EventHandlerCallRef inHandlerCallRef
,
1286 EventRef inEvent
, void *inUserData
)
1288 // for the overly paranoid....
1290 UInt32 eventClass
= GetEventClass( eventRef
);
1291 UInt32 eventKind
= GetEventKind( inEvent
);
1293 if(eventKind
!= kEventMouseDown
&&
1294 eventKind
!= kEventMouseUp
&&
1295 eventKind
!= kEventMouseDragged
&&
1296 eventKind
!= kEventRawKeyDown
&&
1297 eventKind
!= kEventRawKeyRepeat
&&
1298 eventKind
!= kEventRawKeyUp
&&
1299 eventKind
!= kEventWindowUpdate
&&
1300 eventKind
!= kEventWindowActivated
&&
1301 eventKind
!= kEventWindowDeactivated
)
1302 return eventNotHandledErr
;
1304 EventRecord theEvent
;
1305 ConvertEventRefToEventRecord( inEvent
, &theEvent
);
1308 err
= ::MCIsPlayerEvent( (MovieController
) inUserData
, &theEvent
);
1310 // pass on to other event handlers if not handled- i.e. wx
1314 return eventNotHandledErr
;
1318 //in source file that contains stuff you don't directly use
1319 #include "wx/html/forcelnk.h"
1320 FORCE_LINK_ME(basewxmediabackends
)
1322 #endif //wxUSE_MEDIACTRL