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 // it's 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 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
27 #include "wx/mediactrl.h"
29 // uma is for wxMacFSSpec
30 #include "wx/mac/uma.h"
37 #include <QuickTimeComponents.h>
39 #include <QuickTime/QuickTimeComponents.h>
44 //---------------------------------------------------------------------------
45 // Whether or not to use OSX 10.2's CreateMovieControl for native QuickTime
46 // control - i.e. native positioning and event handling etc..
47 //---------------------------------------------------------------------------
48 #ifndef wxUSE_CREATEMOVIECONTROL
49 # if defined( __WXMAC_OSX__ ) && \
50 ( MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_2 )
51 # define wxUSE_CREATEMOVIECONTROL 1
53 # define wxUSE_CREATEMOVIECONTROL 0
57 //---------------------------------------------------------------------------
58 // Height and Width of movie controller in the movie control
59 //---------------------------------------------------------------------------
63 //===========================================================================
64 // BACKEND DECLARATIONS
65 //===========================================================================
67 //---------------------------------------------------------------------------
69 //---------------------------------------------------------------------------
72 class WXDLLIMPEXP_MEDIA wxQTMediaBackend
: public wxMediaBackendCommonBase
78 virtual bool CreateControl(wxControl
* ctrl
, wxWindow
* parent
,
83 const wxValidator
& validator
,
84 const wxString
& name
);
90 virtual bool Load(const wxString
& fileName
);
91 virtual bool Load(const wxURI
& location
);
93 virtual wxMediaState
GetState();
95 virtual bool SetPosition(wxLongLong where
);
96 virtual wxLongLong
GetPosition();
97 virtual wxLongLong
GetDuration();
99 virtual void Move(int x
, int y
, int w
, int h
);
100 wxSize
GetVideoSize() const;
102 virtual double GetPlaybackRate();
103 virtual bool SetPlaybackRate(double dRate
);
105 virtual double GetVolume();
106 virtual bool SetVolume(double);
111 virtual bool ShowPlayerControls(wxMediaCtrlPlayerControls flags
);
113 virtual wxLongLong
GetDownloadProgress();
114 virtual wxLongLong
GetDownloadTotal();
117 // ------ Implementation from now on --------
122 void DoLoadBestSize();
123 void DoSetControllerVisible(wxMediaCtrlPlayerControls flags
);
125 wxLongLong
GetDataSizeFromStart(TimeValue end
);
127 //TODO: Last param actually long - does this work on 64bit machines?
128 static pascal Boolean
MCFilterProc(MovieController theController
,
129 short action
, void *params
, long refCon
);
131 #if wxUSE_CREATEMOVIECONTROL
132 void DoCreateMovieControl();
134 Boolean
IsQuickTime4Installed();
135 void DoNewMovieController();
136 static pascal void PPRMProc(Movie theMovie
, OSErr theErr
, void* theRefCon
);
139 wxSize m_bestSize
; // Original movie size
142 struct MovieType
** m_movie
; // QT Movie handle/instance
144 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 #if !wxUSE_CREATEMOVIECONTROL
153 EventHandlerRef m_pEventHandlerRef
; // Event handler to cleanup
154 MoviePrePrerollCompleteUPP m_preprerollupp
;
155 EventHandlerUPP m_eventupp
;
156 MCActionFilterWithRefConUPP m_mcactionupp
;
158 friend class wxQTMediaEvtHandler
;
161 DECLARE_DYNAMIC_CLASS(wxQTMediaBackend
)
164 #if !wxUSE_CREATEMOVIECONTROL
165 // helper to hijack background erasing for the QT window
166 class WXDLLIMPEXP_MEDIA wxQTMediaEvtHandler
: public wxEvtHandler
169 wxQTMediaEvtHandler(wxQTMediaBackend
*qtb
)
173 qtb
->m_ctrl
->Connect(
174 qtb
->m_ctrl
->GetId(), wxEVT_ERASE_BACKGROUND
,
175 wxEraseEventHandler(wxQTMediaEvtHandler::OnEraseBackground
),
179 void OnEraseBackground(wxEraseEvent
& event
);
182 wxQTMediaBackend
*m_qtb
;
184 DECLARE_NO_COPY_CLASS(wxQTMediaEvtHandler
)
187 // Window event handler
188 static pascal OSStatus
wxQTMediaWindowEventHandler(
189 EventHandlerCallRef inHandlerCallRef
,
190 EventRef inEvent
, void *inUserData
);
194 //===========================================================================
196 //===========================================================================
199 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
201 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
203 IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend
, wxMediaBackend
)
205 //Time between timer calls - this is the Apple recommondation to the TCL
207 #define MOVIE_DELAY 20
209 //---------------------------------------------------------------------------
210 // wxQTMediaLoadTimer
212 // QT, esp. QT for Windows is very picky about how you go about
213 // async loading. If you were to go through a Windows message loop
214 // or a MoviesTask or both and then check the movie load state
215 // it would still return 1000 (loading)... even (pre)prerolling doesn't
216 // help. However, making a load timer like this works
217 //---------------------------------------------------------------------------
218 class wxQTMediaLoadTimer
: public wxTimer
221 wxQTMediaLoadTimer(Movie movie
, wxQTMediaBackend
* parent
) :
222 m_movie(movie
), m_parent(parent
) {}
226 // Note that the CreateMovieControl variety performs
227 // its own custom idling
228 #if !wxUSE_CREATEMOVIECONTROL
229 ::MCIdle(m_parent
->m_mc
);
232 // kMovieLoadStatePlayable is not enough on MAC:
233 // it plays, but IsMovieDone might return true (!)
234 // sure we need to wait until kMovieLoadStatePlaythroughOK
235 if (::GetMovieLoadState(m_movie
) >= 20000)
237 m_parent
->FinishLoad();
243 Movie m_movie
; // Our movie instance
244 wxQTMediaBackend
*m_parent
; // Backend pointer
247 // --------------------------------------------------------------------------
248 // wxQTMediaPlayTimer - Handle Asyncronous Playing
250 // 1) Checks to see if the movie is done, and if not continues
251 // streaming the movie
252 // 2) Sends the wxEVT_MEDIA_STOP event if we have reached the end of
254 // --------------------------------------------------------------------------
255 class wxQTMediaPlayTimer
: public wxTimer
258 wxQTMediaPlayTimer(Movie movie
, wxQTMediaBackend
* parent
) :
259 m_movie(movie
), m_parent(parent
) {}
263 //Note that CreateMovieControl performs its own idleing
264 #if !wxUSE_CREATEMOVIECONTROL
266 // OK, a little explaining - basically originally
267 // we only called MoviesTask if the movie was actually
268 // playing (not paused or stopped)... this was before
269 // we realized MoviesTask actually handles repainting
270 // of the current frame - so if you were to resize
271 // or something it would previously not redraw that
272 // portion of the movie.
274 // So now we call MoviesTask always so that it repaints
277 ::MCIdle(m_parent
->m_mc
);
280 // Handle the stop event - if the movie has reached
281 // the end, notify our handler
282 if (::IsMovieDone(m_movie
))
284 if ( m_parent
->SendStopEvent() )
287 wxASSERT(::GetMoviesError() == noErr
);
289 m_parent
->QueueFinishEvent();
295 Movie m_movie
; // Our movie instance
296 wxQTMediaBackend
* m_parent
; // Backend pointer
300 //---------------------------------------------------------------------------
301 // wxQTMediaBackend Constructor
303 // Sets m_timer to NULL signifying we havn't loaded anything yet
304 //---------------------------------------------------------------------------
305 wxQTMediaBackend::wxQTMediaBackend()
306 : m_movie(NULL
), m_bPlaying(false), m_timer(NULL
)
307 , m_mc(NULL
), m_interfaceflags(wxMEDIACTRLPLAYERCONTROLS_NONE
)
308 #if !wxUSE_CREATEMOVIECONTROL
309 , m_preprerollupp(NULL
)
314 //---------------------------------------------------------------------------
315 // wxQTMediaBackend Destructor
317 // 1) Cleans up the QuickTime movie instance
318 // 2) Decrements the QuickTime reference counter - if this reaches
319 // 0, QuickTime shuts down
320 // 3) Decrements the QuickTime Windows Media Layer reference counter -
321 // if this reaches 0, QuickTime shuts down the Windows Media Layer
322 //---------------------------------------------------------------------------
323 wxQTMediaBackend::~wxQTMediaBackend()
328 #if !wxUSE_CREATEMOVIECONTROL
329 // Cleanup for moviecontroller
332 // destroy wxQTMediaEvtHandler we pushed on it
333 m_ctrl
->PopEventHandler(true);
334 RemoveEventHandler((EventHandlerRef
&)m_pEventHandlerRef
);
335 DisposeEventHandlerUPP(m_eventupp
);
337 // Dispose of the movie controller
338 ::DisposeMovieController(m_mc
);
339 DisposeMCActionFilterWithRefConUPP(m_mcactionupp
);
343 // Note that ExitMovies() is not necessary...
347 //---------------------------------------------------------------------------
348 // wxQTMediaBackend::CreateControl
350 // 1) Intializes QuickTime
351 // 2) Creates the control window
352 //---------------------------------------------------------------------------
353 bool wxQTMediaBackend::CreateControl(
360 const wxValidator
& validator
,
361 const wxString
& name
)
363 // Don't bother in Native control mode
364 #if !wxUSE_CREATEMOVIECONTROL
365 if (!IsQuickTime4Installed())
372 // By default wxWindow(s) is created with a border -
373 // so we need to get rid of those
375 // Since we don't have a child window like most other
376 // backends, we don't need wxCLIP_CHILDREN
377 if ( !ctrl
->wxControl::Create(
378 parent
, id
, pos
, size
,
379 wxWindow::MacRemoveBordersFromStyle(style
),
386 ctrl
->SetValidator(validator
);
389 m_ctrl
= (wxMediaCtrl
*)ctrl
;
393 //---------------------------------------------------------------------------
394 // wxQTMediaBackend::IsQuickTime4Installed
396 // Determines whether version 4 of QT is installed
397 // (Pretty much for Classic only)
398 //---------------------------------------------------------------------------
399 #if !wxUSE_CREATEMOVIECONTROL
400 Boolean
wxQTMediaBackend::IsQuickTime4Installed()
405 error
= Gestalt(gestaltQuickTime
, &result
);
406 return (error
== noErr
) && (((result
>> 16) & 0xffff) >= 0x0400);
410 //---------------------------------------------------------------------------
411 // wxQTMediaBackend::Load (file version)
413 // 1) Get an FSSpec from the Windows path name
415 // 3) Obtain the movie instance from the movie resource
416 // 4) Close the movie resource
418 //---------------------------------------------------------------------------
419 bool wxQTMediaBackend::Load(const wxString
& fileName
)
428 // FIXME:wxMacFilename2FSSpec crashes on empty string -
429 // does it crash on other strings too and should this
430 // "fix" be put in the Carbon wxSound?
431 if (fileName
.empty())
434 wxMacFilename2FSSpec( fileName
, &sfFile
);
435 if (OpenMovieFile( &sfFile
, &movieResFile
, fsRdPerm
) != noErr
)
438 short movieResID
= 0;
442 err
= NewMovieFromFile(
450 // No ::GetMoviesStickyError() here because it returns -2009
451 // a.k.a. invalid track on valid mpegs
452 result
= (err
== noErr
);
455 ::CloseMovieFile(movieResFile
);
457 // Create movie controller/control
458 #if wxUSE_CREATEMOVIECONTROL
459 DoCreateMovieControl();
461 DoNewMovieController();
470 //---------------------------------------------------------------------------
471 // wxQTMediaBackend::PPRMProc (static)
473 // Called when done PrePrerolling the movie.
474 // Note that in 99% of the cases this does nothing...
475 // Anyway we set up the loading timer here to tell us when the movie is done
476 //---------------------------------------------------------------------------
477 #if !wxUSE_CREATEMOVIECONTROL
478 pascal void wxQTMediaBackend::PPRMProc(
480 OSErr
WXUNUSED_UNLESS_DEBUG(theErr
),
483 wxASSERT( theMovie
);
484 wxASSERT( theRefCon
);
485 wxASSERT( theErr
== noErr
);
487 wxQTMediaBackend
* pBE
= (wxQTMediaBackend
*) theRefCon
;
489 long lTime
= ::GetMovieTime(theMovie
,NULL
);
490 Fixed rate
= ::GetMoviePreferredRate(theMovie
);
491 ::PrerollMovie(theMovie
,lTime
,rate
);
492 pBE
->m_timer
= new wxQTMediaLoadTimer(pBE
->m_movie
, pBE
);
493 pBE
->m_timer
->Start(MOVIE_DELAY
);
497 //---------------------------------------------------------------------------
498 // wxQTMediaBackend::Load (URL Version)
500 // 1) Build an escaped URI from location
501 // 2) Create a handle to store the URI string
502 // 3) Put the URI string inside the handle
503 // 4) Make a QuickTime URL data ref from the handle with the URI in it
504 // 5) Clean up the URI string handle
505 // 6) Do some prerolling
507 //---------------------------------------------------------------------------
508 bool wxQTMediaBackend::Load(const wxURI
& location
)
513 wxString theURI
= location
.BuildURI();
518 // FIXME: lurking Unicode problem here
519 Handle theHandle
= ::NewHandleClear(theURI
.length() + 1);
522 ::BlockMoveData(theURI
.mb_str(), *theHandle
, theURI
.length() + 1);
524 // create the movie from the handle that refers to the URI
525 err
= ::NewMovieFromDataRef(
527 newMovieActive
| newMovieAsyncOK
/* | newMovieIdleImportOK*/,
529 URLDataHandlerSubType
);
531 ::DisposeHandle(theHandle
);
533 result
= (err
== noErr
);
536 #if wxUSE_CREATEMOVIECONTROL
537 // Movie control does its own "(pre)prerolling"
538 // but we still need to buffer the movie for the URL
539 DoCreateMovieControl();
541 // Setup timer to catch load event
542 m_timer
= new wxQTMediaLoadTimer(m_movie
, this);
543 m_timer
->Start(MOVIE_DELAY
);
545 // Movie controller resets prerolling, so we must create first
546 DoNewMovieController();
551 timeNow
= ::GetMovieTime(m_movie
, NULL
);
552 wxASSERT(::GetMoviesError() == noErr
);
554 playRate
= ::GetMoviePreferredRate(m_movie
);
555 wxASSERT(::GetMoviesError() == noErr
);
557 // Note that the callback here is optional,
558 // but without it PrePrerollMovie can be buggy
559 // (see Apple ml). Also, some may wonder
560 // why we need this at all - this is because
561 // Apple docs say QuickTime streamed movies
562 // require it if you don't use a Movie Controller,
563 // which we don't by default.
565 m_preprerollupp
= NewMoviePrePrerollCompleteUPP( wxQTMediaBackend::PPRMProc
);
566 ::PrePrerollMovie( m_movie
, timeNow
, playRate
, m_preprerollupp
, (void*)this);
573 //---------------------------------------------------------------------------
574 // wxQTMediaBackend::DoCreateMovieControl
576 // Calls CreateMovieControl and performs setup related to it
578 // Note that we always hide the controller initially becuase when loading
579 // from a url it displays about a 40x40 box with the word loading... in it,
580 // but the box is outside the range of the control, which is bad (0,0
581 // i believe), so we need to wait until finishload to actually display
582 // the movie controller in this instance
583 //---------------------------------------------------------------------------
584 #if wxUSE_CREATEMOVIECONTROL
585 void wxQTMediaBackend::DoCreateMovieControl()
587 // Native CreateMovieControl QT control (Thanks to Kevin Olliver's
588 // wxQTMovie for some of this).
589 Rect bounds
= wxMacGetBoundsForControl(
591 m_ctrl
->GetPosition(),
594 // Dispose of old control for new one
595 if (m_ctrl
->m_peer
&& m_ctrl
->m_peer
->Ok() )
596 m_ctrl
->m_peer
->Dispose();
599 // kMovieControlOptionXXX
600 // HideController - hide the movie controller
601 // LocateTopLeft - movie is pinned to top left rather than centered in the control
602 // EnableEditing - Allows programmatic editing and dragn'drop
603 // HandleEditingHI- Installs event stuff for edit menu - forces EnableEditing also
604 // SetKeysEnabled - Allows keyboard input
605 // ManuallyIdled - app handles movie idling rather than internal timer event loop
606 ::CreateMovieControl(
607 (WindowRef
) m_ctrl
->MacGetTopLevelWindowRef(), //parent
608 &bounds
, // control bounds
609 m_movie
, // movie handle
610 kMovieControlOptionHideController
// flags
611 // | kMovieControlOptionManuallyIdled
612 | kMovieControlOptionLocateTopLeft
613 | kMovieControlOptionSetKeysEnabled
,
614 m_ctrl
->m_peer
->GetControlRefAddr() );
617 m_ctrl
->m_peer
->GetControlRef(),
618 (ControlRef
)m_ctrl
->GetParent()->GetHandle());
620 // set up MovieController for the new movie
623 // Get movie controller from our control
625 m_ctrl
->m_peer
->GetControlRef(), 0,
626 kMovieControlDataMovieController
,
627 sizeof(MovieController
), (Ptr
)&m_mc
, &dataSize
);
629 // Setup a callback so we can tell when the user presses
630 // play on the player controls
631 ::MCSetActionFilterWithRefCon(m_mc
,
632 (MCActionFilterWithRefConUPP
)wxQTMediaBackend::MCFilterProc
,
637 //---------------------------------------------------------------------------
638 // wxQTMediaBackend::DoNewMovieController
640 // Attaches movie to moviecontroller or creates moviecontroller
641 // if not created yet
642 //---------------------------------------------------------------------------
643 #if !wxUSE_CREATEMOVIECONTROL
644 void wxQTMediaBackend::DoNewMovieController()
648 // Get top level window ref for some mac functions
649 WindowRef wrTLW
= (WindowRef
) m_ctrl
->MacGetTopLevelWindowRef();
651 // MovieController not set up yet, so we need to create a new one.
652 // You have to pass a valid movie to NewMovieController, evidently
653 ::SetMovieGWorld(m_movie
,
654 (CGrafPtr
) GetWindowPort(wrTLW
),
656 wxASSERT(::GetMoviesError() == noErr
);
658 Rect bounds
= wxMacGetBoundsForControl(
660 m_ctrl
->GetPosition(),
663 m_mc
= ::NewMovieController(
665 mcTopLeftMovie
| mcNotVisible
/* | mcWithFrame */ );
666 wxASSERT(::GetMoviesError() == noErr
);
668 ::MCDoAction(m_mc
, 32, (void*)true); // mcActionSetKeysEnabled
669 wxASSERT(::GetMoviesError() == noErr
);
671 // Setup a callback so we can tell when the user presses
672 // play on the player controls
673 m_mcactionupp
= NewMCActionFilterWithRefConUPP( wxQTMediaBackend::MCFilterProc
);
674 ::MCSetActionFilterWithRefCon( m_mc
, m_mcactionupp
, (long)this );
675 wxASSERT(::GetMoviesError() == noErr
);
677 // Part of a suggestion from Greg Hazel to repaint movie when idle
678 m_ctrl
->PushEventHandler(new wxQTMediaEvtHandler(this));
680 // Event types to catch from the TLW
681 // for the moviecontroller
682 EventTypeSpec theEventTypes
[] =
684 { kEventClassMouse
, kEventMouseDown
},
685 { kEventClassMouse
, kEventMouseUp
},
686 { kEventClassMouse
, kEventMouseDragged
},
687 { kEventClassKeyboard
, kEventRawKeyDown
},
688 { kEventClassKeyboard
, kEventRawKeyRepeat
},
689 { kEventClassKeyboard
, kEventRawKeyUp
},
690 { kEventClassWindow
, kEventWindowUpdate
},
691 { kEventClassWindow
, kEventWindowActivated
},
692 { kEventClassWindow
, kEventWindowDeactivated
}
695 // Catch window messages:
696 // if we do not do this and if the user clicks the play
697 // button on the controller, for instance, nothing will happen...
698 m_eventupp
= NewEventHandlerUPP( wxQTMediaWindowEventHandler
);
699 InstallWindowEventHandler(
702 GetEventTypeCount( theEventTypes
), theEventTypes
,
703 m_mc
, (&(EventHandlerRef
&)m_pEventHandlerRef
) );
707 // MovieController already created:
708 // Just change the movie in it and we're good to go
710 thePoint
.h
= thePoint
.v
= 0;
711 ::MCSetMovie(m_mc
, m_movie
,
712 (WindowRef
)m_ctrl
->MacGetTopLevelWindowRef(),
714 wxASSERT(::GetMoviesError() == noErr
);
719 //---------------------------------------------------------------------------
720 // wxQTMediaBackend::FinishLoad
722 // Performs operations after a movie ready to play/loaded.
723 //---------------------------------------------------------------------------
724 void wxQTMediaBackend::FinishLoad()
726 // Dispose of the PrePrerollMovieUPP if we used it
727 #if !wxUSE_CREATEMOVIECONTROL
728 DisposeMoviePrePrerollCompleteUPP(m_preprerollupp
);
731 // get the real size of the movie
734 // show the player controls if the user wants to
735 if (m_interfaceflags
)
736 DoSetControllerVisible(m_interfaceflags
);
738 // we want millisecond precision
739 ::SetMovieTimeScale(m_movie
, 1000);
740 wxASSERT(::GetMoviesError() == noErr
);
742 // start movie progress timer
743 m_timer
= new wxQTMediaPlayTimer(m_movie
, (wxQTMediaBackend
*) this);
745 m_timer
->Start(MOVIE_DELAY
, wxTIMER_CONTINUOUS
);
747 // send loaded event and refresh size
751 //---------------------------------------------------------------------------
752 // wxQTMediaBackend::DoLoadBestSize
754 // Sets the best size of the control from the real size of the movie
755 //---------------------------------------------------------------------------
756 void wxQTMediaBackend::DoLoadBestSize()
758 // get the real size of the movie
760 ::GetMovieNaturalBoundsRect(m_movie
, &outRect
);
761 wxASSERT(::GetMoviesError() == noErr
);
763 // determine best size
764 m_bestSize
.x
= outRect
.right
- outRect
.left
;
765 m_bestSize
.y
= outRect
.bottom
- outRect
.top
;
768 //---------------------------------------------------------------------------
769 // wxQTMediaBackend::Play
771 // Start the QT movie
772 // (Apple recommends mcActionPrerollAndPlay but that's QT 4.1+)
773 //---------------------------------------------------------------------------
774 bool wxQTMediaBackend::Play()
776 Fixed fixRate
= (Fixed
) (wxQTMediaBackend::GetPlaybackRate() * 0x10000);
778 fixRate
= ::GetMoviePreferredRate(m_movie
);
780 wxASSERT(fixRate
!= 0);
783 ::MCDoAction( m_mc
, 8 /* mcActionPlay */, (void*) fixRate
);
785 bool result
= (::GetMoviesError() == noErr
);
795 //---------------------------------------------------------------------------
796 // wxQTMediaBackend::Pause
799 //---------------------------------------------------------------------------
800 bool wxQTMediaBackend::DoPause()
802 // Stop the movie A.K.A. ::StopMovie(m_movie);
805 ::MCDoAction( m_mc
, 8 /*mcActionPlay*/, (void *) 0);
807 return ::GetMoviesError() == noErr
;
814 bool wxQTMediaBackend::Pause()
816 bool bSuccess
= DoPause();
818 this->QueuePauseEvent();
823 //---------------------------------------------------------------------------
824 // wxQTMediaBackend::Stop
827 // 2) Seek to the beginning of the movie
828 //---------------------------------------------------------------------------
829 bool wxQTMediaBackend::DoStop()
831 if (!wxQTMediaBackend::DoPause())
834 ::GoToBeginningOfMovie(m_movie
);
835 return ::GetMoviesError() == noErr
;
838 bool wxQTMediaBackend::Stop()
840 bool bSuccess
= DoStop();
847 //---------------------------------------------------------------------------
848 // wxQTMediaBackend::GetPlaybackRate
850 // 1) Get the movie playback rate from ::GetMovieRate
851 //---------------------------------------------------------------------------
852 double wxQTMediaBackend::GetPlaybackRate()
854 return ( ((double)::GetMovieRate(m_movie
)) / 0x10000);
857 //---------------------------------------------------------------------------
858 // wxQTMediaBackend::SetPlaybackRate
860 // 1) Convert dRate to Fixed and Set the movie rate through SetMovieRate
861 //---------------------------------------------------------------------------
862 bool wxQTMediaBackend::SetPlaybackRate(double dRate
)
864 ::SetMovieRate(m_movie
, (Fixed
) (dRate
* 0x10000));
865 return ::GetMoviesError() == noErr
;
868 //---------------------------------------------------------------------------
869 // wxQTMediaBackend::SetPosition
871 // 1) Create a time record struct (TimeRecord) with appropriate values
872 // 2) Pass struct to SetMovieTime
873 //---------------------------------------------------------------------------
874 bool wxQTMediaBackend::SetPosition(wxLongLong where
)
876 TimeRecord theTimeRecord
;
877 memset(&theTimeRecord
, 0, sizeof(TimeRecord
));
878 theTimeRecord
.value
.lo
= where
.GetValue();
879 theTimeRecord
.scale
= ::GetMovieTimeScale(m_movie
);
880 theTimeRecord
.base
= ::GetMovieTimeBase(m_movie
);
881 ::SetMovieTime(m_movie
, &theTimeRecord
);
883 if (::GetMoviesError() != noErr
)
889 //---------------------------------------------------------------------------
890 // wxQTMediaBackend::GetPosition
892 // Calls GetMovieTime
893 //---------------------------------------------------------------------------
894 wxLongLong
wxQTMediaBackend::GetPosition()
896 return ::GetMovieTime(m_movie
, NULL
);
899 //---------------------------------------------------------------------------
900 // wxQTMediaBackend::GetVolume
902 // Gets the volume through GetMovieVolume - which returns a 16 bit short -
904 // +--------+--------+
906 // +--------+--------+
908 // (1) first 8 bits are value before decimal
909 // (2) second 8 bits are value after decimal
911 // Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to
912 // 1 (full gain and sound)
913 //---------------------------------------------------------------------------
914 double wxQTMediaBackend::GetVolume()
916 short sVolume
= ::GetMovieVolume(m_movie
);
918 if (sVolume
& (128 << 8)) //negative - no sound
921 return sVolume
/ 256.0;
924 //---------------------------------------------------------------------------
925 // wxQTMediaBackend::SetVolume
927 // Sets the volume through SetMovieVolume - which takes a 16 bit short -
929 // +--------+--------+
931 // +--------+--------+
933 // (1) first 8 bits are value before decimal
934 // (2) second 8 bits are value after decimal
936 // Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to
937 // 1 (full gain and sound)
938 //---------------------------------------------------------------------------
939 bool wxQTMediaBackend::SetVolume(double dVolume
)
941 ::SetMovieVolume(m_movie
, (short) (dVolume
* 256));
945 //---------------------------------------------------------------------------
946 // wxQTMediaBackend::GetDuration
948 // Calls GetMovieDuration
949 //---------------------------------------------------------------------------
950 wxLongLong
wxQTMediaBackend::GetDuration()
952 return ::GetMovieDuration(m_movie
);
955 //---------------------------------------------------------------------------
956 // wxQTMediaBackend::GetState
958 // Determines the current state - the timer keeps track of whether or not
959 // we are paused or stopped (if the timer is running we are playing)
960 //---------------------------------------------------------------------------
961 wxMediaState
wxQTMediaBackend::GetState()
964 // GetMovieActive/IsMovieDone/SetMovieActive
965 // combo if implemented that way
967 return wxMEDIASTATE_PLAYING
;
968 else if ( !m_movie
|| wxQTMediaBackend::GetPosition() == 0)
969 return wxMEDIASTATE_STOPPED
;
971 return wxMEDIASTATE_PAUSED
;
974 //---------------------------------------------------------------------------
975 // wxQTMediaBackend::Cleanup
977 // Diposes of the movie timer, Control if native, and stops and disposes
979 //---------------------------------------------------------------------------
980 void wxQTMediaBackend::Cleanup()
990 // Apple samples with CreateMovieControl typically
991 // install a event handler and do this on the dispose
992 // event, but we do it here for simplicity
993 // (It might keep playing for several seconds after
994 // control destruction if not)
995 wxQTMediaBackend::Pause();
997 // Dispose of control or remove movie from MovieController
998 #if wxUSE_CREATEMOVIECONTROL
999 if (m_ctrl
->m_peer
&& m_ctrl
->m_peer
->Ok() )
1000 m_ctrl
->m_peer
->Dispose();
1003 thePoint
.h
= thePoint
.v
= 0;
1004 ::MCSetVisible(m_mc
, false);
1005 ::MCSetMovie(m_mc
, NULL
, NULL
, thePoint
);
1008 ::DisposeMovie(m_movie
);
1012 //---------------------------------------------------------------------------
1013 // wxQTMediaBackend::MCFilterProc (static)
1015 // Callback for when the movie controller recieves a message
1016 //---------------------------------------------------------------------------
1017 pascal Boolean
wxQTMediaBackend::MCFilterProc(
1018 MovieController
WXUNUSED(theController
),
1020 void * WXUNUSED(params
),
1023 wxQTMediaBackend
* pThis
= (wxQTMediaBackend
*)refCon
;
1028 // don't process idle events
1032 // play button triggered - MC will set movie to opposite state
1033 // of current - playing ? paused : playing
1034 pThis
->m_bPlaying
= !(pThis
->m_bPlaying
);
1044 //---------------------------------------------------------------------------
1045 // wxQTMediaBackend::GetVideoSize
1047 // Returns the actual size of the QT movie
1048 //---------------------------------------------------------------------------
1049 wxSize
wxQTMediaBackend::GetVideoSize() const
1054 //---------------------------------------------------------------------------
1055 // wxQTMediaBackend::Move
1057 // Move the movie controller or movie control
1058 // (we need to actually move the movie control manually...)
1059 // Top 10 things to do with quicktime in March 93's issue
1060 // of DEVELOP - very useful
1061 // http:// www.mactech.com/articles/develop/issue_13/031-033_QuickTime_column.html
1062 // OLD NOTE: Calling MCSetControllerBoundsRect without detaching
1063 // supposively resulted in a crash back then. Current code even
1064 // with CFM classic runs fine. If there is ever a problem,
1065 // take out the if 0 lines below
1066 //---------------------------------------------------------------------------
1067 void wxQTMediaBackend::Move(int x
, int y
, int w
, int h
)
1069 #if !wxUSE_CREATEMOVIECONTROL
1072 m_ctrl
->GetParent()->MacWindowToRootWindow(&x
, &y
);
1073 Rect theRect
= {y
, x
, y
+ h
, x
+ w
};
1077 ::MCSetControllerAttached(m_mc
, false);
1078 wxASSERT(::GetMoviesError() == noErr
);
1081 ::MCSetControllerBoundsRect(m_mc
, &theRect
);
1082 wxASSERT(::GetMoviesError() == noErr
);
1084 #if 0 // see note above
1085 if (m_interfaceflags
)
1087 ::MCSetVisible(m_mc
, true);
1088 wxASSERT(::GetMoviesError() == noErr
);
1094 if (m_timer
&& m_ctrl
)
1096 m_ctrl
->GetParent()->MacWindowToRootWindow( &x
, &y
);
1097 ::MoveControl( (ControlRef
) m_ctrl
->GetHandle(), x
, y
);
1098 m_ctrl
->GetParent()->Refresh();
1099 m_ctrl
->GetParent()->Update();
1104 //---------------------------------------------------------------------------
1105 // wxQTMediaBackend::DoSetControllerVisible
1107 // Utility function that takes care of showing the moviecontroller
1108 // and showing/hiding the particular controls on it
1109 //---------------------------------------------------------------------------
1110 void wxQTMediaBackend::DoSetControllerVisible(wxMediaCtrlPlayerControls flags
)
1112 ::MCSetVisible(m_mc
, true);
1114 // Take care of subcontrols
1115 if (::GetMoviesError() == noErr
)
1118 ::MCDoAction(m_mc
, 39/*mcActionGetFlags*/, (void*)&mcFlags
);
1120 if (::GetMoviesError() == noErr
)
1122 mcFlags
|= ( //(1<<0)/*mcFlagSuppressMovieFrame*/ |
1123 (1 << 3)/*mcFlagsUseWindowPalette*/
1124 | ((flags
& wxMEDIACTRLPLAYERCONTROLS_STEP
)
1125 ? 0 : (1 << 1)/*mcFlagSuppressStepButtons*/)
1126 | ((flags
& wxMEDIACTRLPLAYERCONTROLS_VOLUME
)
1127 ? 0 : (1 << 2)/*mcFlagSuppressSpeakerButton*/)
1128 // | (1 << 4) /*mcFlagDontInvalidate*/ //if we take care of repainting ourselves
1131 ::MCDoAction(m_mc
, 38/*mcActionSetFlags*/, (void*)mcFlags
);
1135 // Adjust height and width of best size for movie controller
1136 // if the user wants it shown
1137 m_bestSize
.x
= m_bestSize
.x
> wxMCWIDTH
? m_bestSize
.x
: wxMCWIDTH
;
1138 m_bestSize
.y
+= wxMCHEIGHT
;
1141 //---------------------------------------------------------------------------
1142 // wxQTMediaBackend::ShowPlayerControls
1144 // Shows/Hides subcontrols on the media control
1145 //---------------------------------------------------------------------------
1146 bool wxQTMediaBackend::ShowPlayerControls(wxMediaCtrlPlayerControls flags
)
1149 return false; // no movie controller...
1151 bool bSizeChanged
= false;
1153 // if the controller is visible and we want to hide it do so
1154 if (m_interfaceflags
&& !flags
)
1156 bSizeChanged
= true;
1158 ::MCSetVisible(m_mc
, false);
1160 else if (!m_interfaceflags
&& flags
) // show controller if hidden
1162 bSizeChanged
= true;
1163 DoSetControllerVisible(flags
);
1166 // readjust parent sizers
1169 NotifyMovieSizeChanged();
1171 // remember state in case of loading new media
1172 m_interfaceflags
= flags
;
1175 return ::GetMoviesError() == noErr
;
1178 //---------------------------------------------------------------------------
1179 // wxQTMediaBackend::GetDataSizeFromStart
1181 // Calls either GetMovieDataSize or GetMovieDataSize64 with a value
1182 // of 0 for the starting value
1183 //---------------------------------------------------------------------------
1184 wxLongLong
wxQTMediaBackend::GetDataSizeFromStart(TimeValue end
)
1186 #if 0 // old pre-qt4 way
1187 return ::GetMovieDataSize(m_movie
, 0, end
)
1190 ::GetMovieDataSize64(m_movie
, 0, end
, &llDataSize
);
1191 return wxLongLong(llDataSize
.hi
, llDataSize
.lo
);
1195 //---------------------------------------------------------------------------
1196 // wxQTMediaBackend::GetDownloadProgress
1197 //---------------------------------------------------------------------------
1198 wxLongLong
wxQTMediaBackend::GetDownloadProgress()
1200 #if 0 // hackish and slow
1201 Handle hMovie
= NewHandle(0);
1202 PutMovieIntoHandle(m_movie
, hMovie
);
1203 long lSize
= GetHandleSize(hMovie
);
1204 DisposeHandle(hMovie
);
1209 if (::GetMaxLoadedTimeInMovie(m_movie
, &tv
) != noErr
)
1211 wxLogDebug(wxT("GetMaxLoadedTimeInMovie failed"));
1215 return wxQTMediaBackend::GetDataSizeFromStart(tv
);
1219 //---------------------------------------------------------------------------
1220 // wxQTMediaBackend::GetDownloadTotal
1221 //---------------------------------------------------------------------------
1222 wxLongLong
wxQTMediaBackend::GetDownloadTotal()
1224 return wxQTMediaBackend::GetDataSizeFromStart( ::GetMovieDuration(m_movie
) );
1227 //---------------------------------------------------------------------------
1228 // wxQTMediaBackend::OnEraseBackground
1230 // Suggestion from Greg Hazel to repaint the movie when idle
1232 //---------------------------------------------------------------------------
1233 #if !wxUSE_CREATEMOVIECONTROL
1234 void wxQTMediaEvtHandler::OnEraseBackground(wxEraseEvent
& evt
)
1236 // Work around Nasty OSX drawing bug:
1237 // http://lists.apple.com/archives/QuickTime-API/2002/Feb/msg00311.html
1238 WindowRef wrTLW
= (WindowRef
) m_qtb
->m_ctrl
->MacGetTopLevelWindowRef();
1240 RgnHandle region
= MCGetControllerBoundsRgn(m_qtb
->m_mc
);
1241 MCInvalidate(m_qtb
->m_mc
, wrTLW
, region
);
1242 MCIdle(m_qtb
->m_mc
);
1246 //---------------------------------------------------------------------------
1247 // wxQTMediaWindowEventHandler
1249 // Event callback for the top level window of our control that passes
1250 // messages to our moviecontroller so it can receive mouse clicks etc.
1251 //---------------------------------------------------------------------------
1252 #if !wxUSE_CREATEMOVIECONTROL
1253 static pascal OSStatus
wxQTMediaWindowEventHandler(
1254 EventHandlerCallRef inHandlerCallRef
,
1258 // for the overly paranoid....
1260 UInt32 eventClass
= GetEventClass( eventRef
);
1261 UInt32 eventKind
= GetEventKind( inEvent
);
1263 if (eventKind
!= kEventMouseDown
&&
1264 eventKind
!= kEventMouseUp
&&
1265 eventKind
!= kEventMouseDragged
&&
1266 eventKind
!= kEventRawKeyDown
&&
1267 eventKind
!= kEventRawKeyRepeat
&&
1268 eventKind
!= kEventRawKeyUp
&&
1269 eventKind
!= kEventWindowUpdate
&&
1270 eventKind
!= kEventWindowActivated
&&
1271 eventKind
!= kEventWindowDeactivated
)
1272 return eventNotHandledErr
;
1275 EventRecord theEvent
;
1276 ConvertEventRefToEventRecord( inEvent
, &theEvent
);
1279 err
= ::MCIsPlayerEvent( (MovieController
) inUserData
, &theEvent
);
1281 // pass on to other event handlers if not handled- i.e. wx
1285 return eventNotHandledErr
;
1289 // in source file that contains stuff you don't directly use
1290 #include "wx/html/forcelnk.h"
1291 FORCE_LINK_ME(basewxmediabackends
)
1293 #endif // wxUSE_MEDIACTRL