]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/mediactrl.cpp
fixed crash introduced in v1.41
[wxWidgets.git] / src / mac / carbon / mediactrl.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: mac/carbon/mediactrl.cpp
3 // Purpose: Built-in Media Backends for Mac
4 // Author: Ryan Norton <wxprojects@comcast.net>
5 // Modified by:
6 // Created: 11/07/04
7 // RCS-ID: $Id$
8 // Copyright: (c) 2004-2005 Ryan Norton
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 // There are several known bugs with CreateMovieControl
14 // on systems > 10.2 - see main.c of QTCarbonShell sample for details
15 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16
17 //===========================================================================
18 // DECLARATIONS
19 //===========================================================================
20
21 //---------------------------------------------------------------------------
22 // Pre-compiled header stuff
23 //---------------------------------------------------------------------------
24
25 // For compilers that support precompilation, includes "wx.h".
26 #include "wx/wxprec.h"
27
28 #ifdef __BORLANDC__
29 #pragma hdrstop
30 #endif
31
32 //---------------------------------------------------------------------------
33 // Includes
34 //---------------------------------------------------------------------------
35 #include "wx/mediactrl.h"
36
37 //---------------------------------------------------------------------------
38 // Compilation guard
39 //---------------------------------------------------------------------------
40 #if wxUSE_MEDIACTRL
41
42 //---------------------------------------------------------------------------
43 // Whether or not to use OSX 10.2's CreateMovieControl for native QuickTime
44 // control - i.e. native positioning and event handling etc..
45 //---------------------------------------------------------------------------
46 #ifndef wxUSE_CREATEMOVIECONTROL
47 # if defined( __WXMAC_OSX__ ) && \
48 ( MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_2 )
49 # define wxUSE_CREATEMOVIECONTROL 1
50 # else
51 # define wxUSE_CREATEMOVIECONTROL 0
52 # endif
53 #endif
54
55 //---------------------------------------------------------------------------
56 // Height and Width of movie controller in the movie control
57 //---------------------------------------------------------------------------
58 #define wxMCWIDTH 320
59 #define wxMCHEIGHT 16
60
61 //===========================================================================
62 // BACKEND DECLARATIONS
63 //===========================================================================
64
65 //---------------------------------------------------------------------------
66 //
67 // wxQTMediaBackend
68 //
69 //---------------------------------------------------------------------------
70
71 //---------------------------------------------------------------------------
72 // QT Includes
73 //---------------------------------------------------------------------------
74 //uma is for wxMacFSSpec
75 #include "wx/mac/uma.h"
76 #include "wx/timer.h"
77 #ifndef __DARWIN__
78 #include <Movies.h>
79 #include <Gestalt.h>
80 #include <QuickTimeComponents.h> //Standard QT stuff
81 #else
82 #include <QuickTime/QuickTimeComponents.h>
83 #endif
84
85 class WXDLLIMPEXP_MEDIA wxQTMediaBackend : public wxMediaBackendCommonBase
86 {
87 public:
88 wxQTMediaBackend();
89 ~wxQTMediaBackend();
90
91 virtual bool CreateControl(wxControl* ctrl, wxWindow* parent,
92 wxWindowID id,
93 const wxPoint& pos,
94 const wxSize& size,
95 long style,
96 const wxValidator& validator,
97 const wxString& name);
98
99 virtual bool Play();
100 virtual bool Pause();
101 virtual bool Stop();
102
103 virtual bool Load(const wxString& fileName);
104 virtual bool Load(const wxURI& location);
105
106 virtual wxMediaState GetState();
107
108 virtual bool SetPosition(wxLongLong where);
109 virtual wxLongLong GetPosition();
110 virtual wxLongLong GetDuration();
111
112 virtual void Move(int x, int y, int w, int h);
113 wxSize GetVideoSize() const;
114
115 virtual double GetPlaybackRate();
116 virtual bool SetPlaybackRate(double dRate);
117
118 virtual double GetVolume();
119 virtual bool SetVolume(double);
120
121 void Cleanup();
122 void FinishLoad();
123
124 virtual bool ShowPlayerControls(wxMediaCtrlPlayerControls flags);
125
126
127 //
128 // ------ Implementation from now on --------
129 //
130 void DoLoadBestSize();
131 void DoSetControllerVisible(wxMediaCtrlPlayerControls flags);
132
133 //TODO: Last param actually long - does this work on 64bit machines?
134 static Boolean MCFilterProc (MovieController theController,
135 short action, void *params, long refCon);
136
137 #if wxUSE_CREATEMOVIECONTROL
138 void DoCreateMovieControl();
139 #else
140 Boolean IsQuickTime4Installed();
141 void DoNewMovieController();
142 static void PPRMProc (Movie theMovie, OSErr theErr, void* theRefCon);
143 #endif
144
145 wxSize m_bestSize; // Original movie size
146 #ifdef __WXMAC_OSX__
147 struct MovieType** m_movie; // QT Movie handle/instance
148 #else
149 Movie m_movie; // Movie instance
150 #endif
151 bool m_bPlaying; // Whether media is playing or not
152 class wxTimer* m_timer; // Timer for streaming the movie
153 MovieController m_mc; // MovieController instance
154 wxMediaCtrlPlayerControls m_interfaceflags; // Saved interface flags
155 #if !wxUSE_CREATEMOVIECONTROL
156 EventHandlerRef m_pEventHandlerRef; // Event handler to cleanup
157
158 friend class wxQTMediaEvtHandler;
159 #endif
160 DECLARE_DYNAMIC_CLASS(wxQTMediaBackend)
161 };
162
163 #if !wxUSE_CREATEMOVIECONTROL
164 // helper to hijack background erasing for the QT window
165 class WXDLLIMPEXP_MEDIA wxQTMediaEvtHandler : public wxEvtHandler
166 {
167 public:
168 wxQTMediaEvtHandler(wxQTMediaBackend *qtb)
169 {
170 m_qtb = qtb;
171
172 qtb->m_ctrl->Connect(qtb->m_ctrl->GetId(), wxEVT_ERASE_BACKGROUND,
173 wxEraseEventHandler(wxQTMediaEvtHandler::OnEraseBackground),
174 NULL, this);
175 }
176
177 void OnEraseBackground(wxEraseEvent& event);
178
179 private:
180 wxQTMediaBackend *m_qtb;
181
182 DECLARE_NO_COPY_CLASS(wxQTMediaEvtHandler)
183 };
184
185 // Window event handler
186 static pascal OSStatus wxQTMediaWindowEventHandler(
187 EventHandlerCallRef inHandlerCallRef,
188 EventRef inEvent, void *inUserData);
189 DEFINE_ONE_SHOT_HANDLER_GETTER( wxQTMediaWindowEventHandler );
190
191 #endif
192
193 //===========================================================================
194 // IMPLEMENTATION
195 //===========================================================================
196
197
198 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
199 //
200 // wxQTMediaBackend
201 //
202 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
203
204 IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend, wxMediaBackend)
205
206 //Time between timer calls - this is the Apple recommondation to the TCL
207 //team I believe
208 #define MOVIE_DELAY 20
209
210 //---------------------------------------------------------------------------
211 // wxQTMediaLoadTimer
212 //
213 // QT, esp. QT for Windows is very picky about how you go about
214 // async loading. If you were to go through a Windows message loop
215 // or a MoviesTask or both and then check the movie load state
216 // it would still return 1000 (loading)... even (pre)prerolling doesn't
217 // help. However, making a load timer like this works
218 //---------------------------------------------------------------------------
219 class wxQTMediaLoadTimer : public wxTimer
220 {
221 public:
222 wxQTMediaLoadTimer(Movie movie, wxQTMediaBackend* parent) :
223 m_movie(movie), m_parent(parent) {}
224
225 void Notify()
226 {
227 //Note that the CreateMovieControl variety performs
228 //its own custom idleing
229 #if !wxUSE_CREATEMOVIECONTROL
230 ::MCIdle(m_parent->m_mc);
231 #endif
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)
236 {
237 m_parent->FinishLoad();
238 delete this;
239 }
240 }
241
242 protected:
243 Movie m_movie; //Our movie instance
244 wxQTMediaBackend* m_parent; //Backend pointer
245 };
246
247 // --------------------------------------------------------------------------
248 // wxQTMediaPlayTimer - Handle Asyncronous Playing
249 //
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
253 // the movie.
254 // --------------------------------------------------------------------------
255 class wxQTMediaPlayTimer : public wxTimer
256 {
257 public:
258 wxQTMediaPlayTimer(Movie movie, wxQTMediaBackend* parent) :
259 m_movie(movie), m_parent(parent) {}
260
261 void Notify()
262 {
263 //Note that CreateMovieControl performs its own idleing
264 #if !wxUSE_CREATEMOVIECONTROL
265 //
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.
273 //
274 // So now we call MoviesTask always so that it repaints
275 // correctly.
276 //
277 ::MCIdle(m_parent->m_mc);
278 #endif
279
280 //
281 // Handle the stop event - if the movie has reached
282 // the end, notify our handler
283 //
284 if(::IsMovieDone(m_movie))
285 {
286 if ( m_parent->SendStopEvent() )
287 {
288 m_parent->Stop();
289 wxASSERT(::GetMoviesError() == noErr);
290
291 m_parent->QueueFinishEvent();
292 }
293 }
294 }
295
296 protected:
297 Movie m_movie; //Our movie instance
298 wxQTMediaBackend* m_parent; //Backend pointer
299 };
300
301
302 //---------------------------------------------------------------------------
303 // wxQTMediaBackend Constructor
304 //
305 // Sets m_timer to NULL signifying we havn't loaded anything yet
306 //---------------------------------------------------------------------------
307 wxQTMediaBackend::wxQTMediaBackend()
308 : m_movie(NULL), m_bPlaying(false), m_timer(NULL)
309 , m_mc(NULL), m_interfaceflags(wxMEDIACTRLPLAYERCONTROLS_NONE)
310 {
311 }
312
313 //---------------------------------------------------------------------------
314 // wxQTMediaBackend Destructor
315 //
316 // 1) Cleans up the QuickTime movie instance
317 // 2) Decrements the QuickTime reference counter - if this reaches
318 // 0, QuickTime shuts down
319 // 3) Decrements the QuickTime Windows Media Layer reference counter -
320 // if this reaches 0, QuickTime shuts down the Windows Media Layer
321 //---------------------------------------------------------------------------
322 wxQTMediaBackend::~wxQTMediaBackend()
323 {
324 if(m_movie)
325 Cleanup();
326
327 #if !wxUSE_CREATEMOVIECONTROL
328 // Cleanup for moviecontroller
329 if(m_mc)
330 {
331 // destroy wxQTMediaEvtHandler we pushed on it
332 m_ctrl->PopEventHandler(true);
333 RemoveEventHandler((EventHandlerRef&)m_pEventHandlerRef);
334 ::DisposeMovieController(m_mc);
335 }
336 #endif
337
338 //Note that ExitMovies() is not necessary...
339 ExitMovies();
340 }
341
342 //---------------------------------------------------------------------------
343 // wxQTMediaBackend::CreateControl
344 //
345 // 1) Intializes QuickTime
346 // 2) Creates the control window
347 //---------------------------------------------------------------------------
348 bool wxQTMediaBackend::CreateControl(wxControl* ctrl, wxWindow* parent,
349 wxWindowID id,
350 const wxPoint& pos,
351 const wxSize& size,
352 long style,
353 const wxValidator& validator,
354 const wxString& name)
355 {
356 //Don't bother in Native control mode
357 #if !wxUSE_CREATEMOVIECONTROL
358 if (!IsQuickTime4Installed())
359 return false;
360 #endif
361
362 EnterMovies();
363
364 //
365 // Create window
366 // By default wxWindow(s) is created with a border -
367 // so we need to get rid of those
368 //
369 // Since we don't have a child window like most other
370 // backends, we don't need wxCLIP_CHILDREN
371 //
372 if ( !ctrl->wxControl::Create(parent, id, pos, size,
373 wxWindow::MacRemoveBordersFromStyle(style),
374 validator, name)
375 )
376 return false;
377
378 #if wxUSE_VALIDATORS
379 ctrl->SetValidator(validator);
380 #endif
381
382 m_ctrl = (wxMediaCtrl*)ctrl;
383 return true;
384 }
385
386 //---------------------------------------------------------------------------
387 // wxQTMediaBackend::IsQuickTime4Installed
388 //
389 // Determines whether version 4 of QT is installed
390 // (Pretty much for classic only)
391 //---------------------------------------------------------------------------
392 #if !wxUSE_CREATEMOVIECONTROL
393 Boolean wxQTMediaBackend::IsQuickTime4Installed()
394 {
395 short error;
396 long result;
397
398 error = Gestalt (gestaltQuickTime, &result);
399 return (error == noErr) && (((result >> 16) & 0xffff) >= 0x0400);
400 }
401 #endif
402
403 //---------------------------------------------------------------------------
404 // wxQTMediaBackend::Load (file version)
405 //
406 // 1) Get an FSSpec from the Windows path name
407 // 2) Open the movie
408 // 3) Obtain the movie instance from the movie resource
409 // 4) Close the movie resource
410 // 5) Finish loading
411 //---------------------------------------------------------------------------
412 bool wxQTMediaBackend::Load(const wxString& fileName)
413 {
414 if(m_movie)
415 Cleanup();
416
417 OSErr err = noErr;
418 short movieResFile;
419 FSSpec sfFile;
420
421 //FIXME:wxMacFilename2FSSpec crashes on empty string -
422 //does it crash on other strings too and should this
423 //"fix" be put in the carbon wxSound?
424 if (fileName.empty())
425 return false;
426
427 wxMacFilename2FSSpec( fileName , &sfFile );
428
429 if (OpenMovieFile (&sfFile, &movieResFile, fsRdPerm) != noErr)
430 return false;
431
432 short movieResID = 0;
433 Str255 movieName;
434
435 err = NewMovieFromFile (
436 &m_movie,
437 movieResFile,
438 &movieResID,
439 movieName,
440 newMovieActive,
441 NULL); //wasChanged
442
443 //No ::GetMoviesStickyError() here because it returns -2009
444 // a.k.a. invalid track on valid mpegs
445 if(err == noErr)
446 {
447 ::CloseMovieFile (movieResFile);
448
449 // Create movie controller/control
450 #if wxUSE_CREATEMOVIECONTROL
451 DoCreateMovieControl();
452 #else
453 DoNewMovieController();
454 #endif
455 FinishLoad();
456 return true;
457 }
458 else
459 {
460 return false;
461 }
462 }
463
464 //---------------------------------------------------------------------------
465 // wxQTMediaBackend::PPRMProc (static)
466 //
467 // Called when done PrePrerolling the movie.
468 // Note that in 99% of the cases this does nothing...
469 // Anyway we set up the loading timer here to tell us when the movie is done
470 //---------------------------------------------------------------------------
471 #if !wxUSE_CREATEMOVIECONTROL
472 void wxQTMediaBackend::PPRMProc (Movie theMovie,
473 OSErr WXUNUSED_UNLESS_DEBUG(theErr),
474 void* theRefCon)
475 {
476 wxASSERT( theMovie );
477 wxASSERT( theRefCon );
478 wxASSERT( theErr == noErr );
479
480 wxQTMediaBackend* pBE = (wxQTMediaBackend*) theRefCon;
481
482 long lTime = ::GetMovieTime(theMovie,NULL);
483 Fixed rate = ::GetMoviePreferredRate(theMovie);
484 ::PrerollMovie(theMovie,lTime,rate);
485 pBE->m_timer = new wxQTMediaLoadTimer(pBE->m_movie, pBE);
486 pBE->m_timer->Start(MOVIE_DELAY);
487 }
488 #endif
489
490 //---------------------------------------------------------------------------
491 // wxQTMediaBackend::Load (URL Version)
492 //
493 // 1) Build an escaped URI from location
494 // 2) Create a handle to store the URI string
495 // 3) Put the URI string inside the handle
496 // 4) Make a QuickTime URL data ref from the handle with the URI in it
497 // 5) Clean up the URI string handle
498 // 6) Do some prerolling
499 // 7) Finish Loading
500 //---------------------------------------------------------------------------
501 bool wxQTMediaBackend::Load(const wxURI& location)
502 {
503 if(m_movie)
504 Cleanup();
505
506 wxString theURI = location.BuildURI();
507
508 OSErr err = noErr;
509
510 Handle theHandle = ::NewHandleClear(theURI.length() + 1);
511 wxASSERT(theHandle);
512
513 ::BlockMove(theURI.mb_str(), *theHandle, theURI.length() + 1);
514
515 //create the movie from the handle that refers to the URI
516 err = ::NewMovieFromDataRef(&m_movie, newMovieActive |
517 newMovieAsyncOK
518 /*|newMovieIdleImportOK*/,
519 NULL, theHandle,
520 URLDataHandlerSubType);
521
522 ::DisposeHandle(theHandle);
523
524 if (err == noErr)
525 {
526 #if wxUSE_CREATEMOVIECONTROL
527 // Movie control resets prerolling, so we must create first
528 DoCreateMovieControl();
529
530 // Setup timer to catch load event
531 m_timer = new wxQTMediaLoadTimer(m_movie, this);
532 m_timer->Start(MOVIE_DELAY);
533 #else
534 // Movie controller resets prerolling, so we must create first
535 DoNewMovieController();
536
537 long timeNow;
538 Fixed playRate;
539
540 timeNow = ::GetMovieTime(m_movie, NULL);
541 wxASSERT(::GetMoviesError() == noErr);
542
543 playRate = ::GetMoviePreferredRate(m_movie);
544 wxASSERT(::GetMoviesError() == noErr);
545
546 //
547 // Note that the callback here is optional,
548 // but without it PrePrerollMovie can be buggy
549 // (see Apple ml). Also, some may wonder
550 // why we need this at all - this is because
551 // Apple docs say QuickTime streamed movies
552 // require it if you don't use a Movie Controller,
553 // which we don't by default.
554 //
555 ::PrePrerollMovie(m_movie, timeNow, playRate,
556 wxQTMediaBackend::PPRMProc,
557 (void*)this);
558 #endif
559 return true;
560 }
561 else
562 return false;
563 }
564
565 //---------------------------------------------------------------------------
566 // wxQTMediaBackend::DoCreateMovieControl
567 //
568 // Calls CreateMovieControl and performs setup related to it
569 //
570 // Note that we always hide the controller initially becuase when loading
571 // from a url it displays about a 40x40 box with the word loading... in it,
572 // but the box is outside the range of the control, which is bad (0,0
573 // i believe), so we need to wait until finishload to actually display
574 // the movie controller in this instance
575 //---------------------------------------------------------------------------
576 #if wxUSE_CREATEMOVIECONTROL
577 void wxQTMediaBackend::DoCreateMovieControl()
578 {
579 //
580 //Native CreateMovieControl QT control (Thanks to Kevin Olliver's
581 //wxQTMovie for some of this).
582 //
583 Rect bounds = wxMacGetBoundsForControl(m_ctrl,
584 m_ctrl->GetPosition(),
585 m_ctrl->GetSize());
586
587 //Dispose of old control for new one
588 if (m_ctrl->m_peer && m_ctrl->m_peer->Ok() )
589 m_ctrl->m_peer->Dispose();
590
591 //Options-
592 //kMovieControlOptionXXX
593 //HideController - hide the movie controller
594 //LocateTopLeft - movie is pinned to top left rather than centered in the control
595 //EnableEditing - Allows programmatic editing and dragn'drop
596 //HandleEditingHI- Installs event stuff for edit menu - forces EnableEditing also
597 //SetKeysEnabled - Allows keyboard input
598 //ManuallyIdled - app handles movie idling rather than internal timer event loop
599 ::CreateMovieControl(
600 (WindowRef)
601 m_ctrl->MacGetTopLevelWindowRef(), //parent
602 &bounds, //control bounds
603 m_movie, //movie handle
604 kMovieControlOptionHideController
605 | kMovieControlOptionLocateTopLeft
606 | kMovieControlOptionSetKeysEnabled
607 // | kMovieControlOptionManuallyIdled
608 , //flags
609 m_ctrl->m_peer->GetControlRefAddr() );
610
611 ::EmbedControl(m_ctrl->m_peer->GetControlRef(),
612 (ControlRef)m_ctrl->GetParent()->GetHandle());
613
614 //
615 // Setup MovieController for the new movie
616 //
617 long dataSize;
618
619 //Get movie controller from our control
620 ::GetControlData( m_ctrl->m_peer->GetControlRef(), 0,
621 kMovieControlDataMovieController,
622 sizeof(MovieController), (Ptr)&m_mc, &dataSize );
623
624 // Setup a callback so we can tell when the user presses
625 // play on the player controls
626 ::MCSetActionFilterWithRefCon(m_mc,
627 wxQTMediaBackend::MCFilterProc, (long)this);
628 }
629 #endif
630
631 //---------------------------------------------------------------------------
632 // wxQTMediaBackend::DoNewMovieController
633 //
634 // Attaches movie to moviecontroller or creates moviecontroller
635 // if not created yet
636 //---------------------------------------------------------------------------
637 #if !wxUSE_CREATEMOVIECONTROL
638 void wxQTMediaBackend::DoNewMovieController()
639 {
640 if(!m_mc)
641 {
642 // Get top level window ref for some mac functions
643 WindowRef wrTLW = (WindowRef) m_ctrl->MacGetTopLevelWindowRef();
644
645 // MovieController not setup yet -
646 // so we need to create a new one.
647 // You have to pass a valid movie to
648 // NewMovieController, evidently
649 ::SetMovieGWorld(m_movie,
650 (CGrafPtr) GetWindowPort(wrTLW),
651 NULL);
652 wxASSERT(::GetMoviesError() == noErr);
653
654 Rect bounds = wxMacGetBoundsForControl(m_ctrl,
655 m_ctrl->GetPosition(),
656 m_ctrl->GetSize());
657
658 m_mc = ::NewMovieController(m_movie, &bounds, mcTopLeftMovie |
659 //mcWithFrame |
660 mcNotVisible);
661 wxASSERT(::GetMoviesError() == noErr);
662 ::MCDoAction(m_mc, 32, (void*)true); //mcActionSetKeysEnabled
663 wxASSERT(::GetMoviesError() == noErr);
664
665 // Setup a callback so we can tell when the user presses
666 // play on the player controls
667 ::MCSetActionFilterWithRefCon(m_mc,
668 wxQTMediaBackend::MCFilterProc, (long)this);
669 wxASSERT(::GetMoviesError() == noErr);
670
671 //Part of a suggestion from Greg Hazel to repaint
672 //movie when idle
673 m_ctrl->PushEventHandler(new wxQTMediaEvtHandler(this));
674
675 // Event types to catch from the TLW
676 // for the moviecontroller
677 EventTypeSpec theEventTypes[] = {
678 { kEventClassMouse, kEventMouseDown },
679 { kEventClassMouse, kEventMouseUp },
680 { kEventClassKeyboard, kEventRawKeyDown },
681 { kEventClassKeyboard, kEventRawKeyRepeat },
682 { kEventClassKeyboard, kEventRawKeyUp },
683 { kEventClassWindow, kEventWindowUpdate },
684 { kEventClassWindow, kEventWindowActivated },
685 { kEventClassWindow, kEventWindowDeactivated }
686 };
687
688 // Catch window messages -
689 // if we do not do this and if the user clicks the play
690 // button on the controller, for instance, nothing will happen...
691 InstallWindowEventHandler( wrTLW,
692 GetwxQTMediaWindowEventHandlerUPP(),
693 GetEventTypeCount( theEventTypes ), theEventTypes,
694 m_mc, (&(EventHandlerRef&)m_pEventHandlerRef) );
695 }
696 else
697 {
698 // MovieController already created -
699 // Just change the movie in it and we're good to go
700 Point thePoint;
701 thePoint.h = thePoint.v = 0;
702 ::MCSetMovie(m_mc, m_movie,
703 (WindowRef)m_ctrl->MacGetTopLevelWindowRef(),
704 thePoint);
705 wxASSERT(::GetMoviesError() == noErr);
706 }
707 }
708 #endif
709
710 //---------------------------------------------------------------------------
711 // wxQTMediaBackend::FinishLoad
712 //
713 // Performs operations after a movie ready to play/loaded.
714 //---------------------------------------------------------------------------
715 void wxQTMediaBackend::FinishLoad()
716 {
717 // get the real size of the movie
718 DoLoadBestSize();
719
720 // Show the player controls if the user wants to
721 if(m_interfaceflags)
722 DoSetControllerVisible(m_interfaceflags);
723
724 //we want millisecond precision
725 ::SetMovieTimeScale(m_movie, 1000);
726 wxASSERT(::GetMoviesError() == noErr);
727
728 // Start movie progress timer
729 m_timer = new wxQTMediaPlayTimer(m_movie, (wxQTMediaBackend*) this);
730 wxASSERT(m_timer);
731 m_timer->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS);
732
733 //send loaded event & refresh size
734 NotifyMovieLoaded();
735 }
736
737 //---------------------------------------------------------------------------
738 // wxQTMediaBackend::DoLoadBestSize
739 //
740 // Sets the best size of the control from the real size of the movie
741 //---------------------------------------------------------------------------
742 void wxQTMediaBackend::DoLoadBestSize()
743 {
744 //get the real size of the movie
745 Rect outRect;
746 ::GetMovieNaturalBoundsRect (m_movie, &outRect);
747 wxASSERT(::GetMoviesError() == noErr);
748
749 //determine best size
750 m_bestSize.x = outRect.right - outRect.left;
751 m_bestSize.y = outRect.bottom - outRect.top;
752 }
753
754 //---------------------------------------------------------------------------
755 // wxQTMediaBackend::Play
756 //
757 // Start the QT movie
758 //---------------------------------------------------------------------------
759 bool wxQTMediaBackend::Play()
760 {
761 Fixed fixRate = (Fixed) (wxQTMediaBackend::GetPlaybackRate() * 0x10000);
762 if(!fixRate)
763 fixRate = ::GetMoviePreferredRate(m_movie);
764
765 wxASSERT(fixRate != 0);
766
767 if(!m_bPlaying)
768 ::MCDoAction( m_mc, 8, // mcActionPlay
769 (void *) fixRate);
770
771 m_bPlaying = true;
772 return ::GetMoviesError() == noErr;
773 }
774
775 //---------------------------------------------------------------------------
776 // wxQTMediaBackend::Pause
777 //
778 // Stop the movie
779 //---------------------------------------------------------------------------
780 bool wxQTMediaBackend::Pause()
781 {
782 //Stop the movie A.K.A. ::StopMovie(m_movie);
783 if(m_bPlaying)
784 {
785 ::MCDoAction( m_mc, 8 /*mcActionPlay*/,
786 (void *) 0);
787 m_bPlaying = false;
788 return ::GetMoviesError() == noErr;
789 }
790 return true; //already paused
791 }
792
793 //---------------------------------------------------------------------------
794 // wxQTMediaBackend::Stop
795 //
796 // 1) Stop the movie
797 // 2) Seek to the beginning of the movie
798 //---------------------------------------------------------------------------
799 bool wxQTMediaBackend::Stop()
800 {
801 if(!wxQTMediaBackend::Pause())
802 return false;
803
804 ::GoToBeginningOfMovie(m_movie);
805 return ::GetMoviesError() == noErr;
806 }
807
808 //---------------------------------------------------------------------------
809 // wxQTMediaBackend::GetPlaybackRate
810 //
811 // 1) Get the movie playback rate from ::GetMovieRate
812 //---------------------------------------------------------------------------
813 double wxQTMediaBackend::GetPlaybackRate()
814 {
815 return ( ((double)::GetMovieRate(m_movie)) / 0x10000);
816 }
817
818 //---------------------------------------------------------------------------
819 // wxQTMediaBackend::SetPlaybackRate
820 //
821 // 1) Convert dRate to Fixed and Set the movie rate through SetMovieRate
822 //---------------------------------------------------------------------------
823 bool wxQTMediaBackend::SetPlaybackRate(double dRate)
824 {
825 ::SetMovieRate(m_movie, (Fixed) (dRate * 0x10000));
826 return ::GetMoviesError() == noErr;
827 }
828
829 //---------------------------------------------------------------------------
830 // wxQTMediaBackend::SetPosition
831 //
832 // 1) Create a time record struct (TimeRecord) with appropriate values
833 // 2) Pass struct to SetMovieTime
834 //---------------------------------------------------------------------------
835 bool wxQTMediaBackend::SetPosition(wxLongLong where)
836 {
837 TimeRecord theTimeRecord;
838 memset(&theTimeRecord, 0, sizeof(TimeRecord));
839 theTimeRecord.value.lo = where.GetValue();
840 theTimeRecord.scale = ::GetMovieTimeScale(m_movie);
841 theTimeRecord.base = ::GetMovieTimeBase(m_movie);
842 ::SetMovieTime(m_movie, &theTimeRecord);
843
844 if (::GetMoviesError() != noErr)
845 return false;
846
847 return true;
848 }
849
850 //---------------------------------------------------------------------------
851 // wxQTMediaBackend::GetPosition
852 //
853 // Calls GetMovieTime
854 //---------------------------------------------------------------------------
855 wxLongLong wxQTMediaBackend::GetPosition()
856 {
857 return ::GetMovieTime(m_movie, NULL);
858 }
859
860 //---------------------------------------------------------------------------
861 // wxQTMediaBackend::GetVolume
862 //
863 // Gets the volume through GetMovieVolume - which returns a 16 bit short -
864 //
865 // +--------+--------+
866 // + (1) + (2) +
867 // +--------+--------+
868 //
869 // (1) first 8 bits are value before decimal
870 // (2) second 8 bits are value after decimal
871 //
872 // Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to
873 // 1 (full gain and sound)
874 //---------------------------------------------------------------------------
875 double wxQTMediaBackend::GetVolume()
876 {
877 short sVolume = ::GetMovieVolume(m_movie);
878
879 if(sVolume & (128 << 8)) //negative - no sound
880 return 0.0;
881
882 return sVolume/256.0;
883 }
884
885 //---------------------------------------------------------------------------
886 // wxQTMediaBackend::SetVolume
887 //
888 // Sets the volume through SetMovieVolume - which takes a 16 bit short -
889 //
890 // +--------+--------+
891 // + (1) + (2) +
892 // +--------+--------+
893 //
894 // (1) first 8 bits are value before decimal
895 // (2) second 8 bits are value after decimal
896 //
897 // Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to
898 // 1 (full gain and sound)
899 //---------------------------------------------------------------------------
900 bool wxQTMediaBackend::SetVolume(double dVolume)
901 {
902 ::SetMovieVolume(m_movie, (short) (dVolume * 256));
903 return true;
904 }
905
906 //---------------------------------------------------------------------------
907 // wxQTMediaBackend::GetDuration
908 //
909 // Calls GetMovieDuration
910 //---------------------------------------------------------------------------
911 wxLongLong wxQTMediaBackend::GetDuration()
912 {
913 return ::GetMovieDuration(m_movie);
914 }
915
916 //---------------------------------------------------------------------------
917 // wxQTMediaBackend::GetState
918 //
919 // Determines the current state - the timer keeps track of whether or not
920 // we are paused or stopped (if the timer is running we are playing)
921 //---------------------------------------------------------------------------
922 wxMediaState wxQTMediaBackend::GetState()
923 {
924 // Could use
925 // GetMovieActive/IsMovieDone/SetMovieActive
926 // combo if implemented that way
927 if (m_bPlaying == true)
928 return wxMEDIASTATE_PLAYING;
929 else if ( !m_movie || wxQTMediaBackend::GetPosition() == 0)
930 return wxMEDIASTATE_STOPPED;
931 else
932 return wxMEDIASTATE_PAUSED;
933 }
934
935 //---------------------------------------------------------------------------
936 // wxQTMediaBackend::Cleanup
937 //
938 // Diposes of the movie timer, Control if native, and stops and disposes
939 // of the QT movie
940 //---------------------------------------------------------------------------
941 void wxQTMediaBackend::Cleanup()
942 {
943 m_bPlaying = false;
944 if(m_timer)
945 {
946 delete m_timer;
947 m_timer = NULL;
948 }
949
950 // Stop the movie
951 // Apple samples with CreateMovieControl typically
952 // install a event handler and do this on the dispose
953 // event, but we do it here for simplicity
954 // (It might keep playing for several seconds after
955 // control destruction if not)
956 wxQTMediaBackend::Pause();
957
958 //
959 // Dispose of control or remove movie from MovieController
960 //
961 #if wxUSE_CREATEMOVIECONTROL
962 if (m_ctrl->m_peer && m_ctrl->m_peer->Ok() )
963 m_ctrl->m_peer->Dispose();
964 #else
965 Point thePoint;
966 thePoint.h = thePoint.v = 0;
967 ::MCSetVisible(m_mc, false);
968 ::MCSetMovie(m_mc, NULL, NULL, thePoint);
969 #endif
970
971 ::DisposeMovie(m_movie);
972 }
973
974 //---------------------------------------------------------------------------
975 // wxQTMediaBackend::MCFilterProc (static)
976 //
977 // Callback for when the movie controller recieves a message
978 //---------------------------------------------------------------------------
979 Boolean wxQTMediaBackend::MCFilterProc(
980 MovieController WXUNUSED(theController),
981 short action,
982 void * WXUNUSED(params),
983 long refCon)
984 {
985 if(action != 1) //don't process idle events
986 {
987 wxQTMediaBackend* pThis = (wxQTMediaBackend*)refCon;
988
989 switch(action)
990 {
991 case 8: //play button triggered - MC will set movie to opposite state
992 //of current - playing ? paused : playing
993 pThis->m_bPlaying = !(pThis->m_bPlaying);
994 break;
995 default:
996 break;
997 }
998 }
999 return 0;
1000 }
1001
1002 //---------------------------------------------------------------------------
1003 // wxQTMediaBackend::GetVideoSize
1004 //
1005 // Returns the actual size of the QT movie
1006 //---------------------------------------------------------------------------
1007 wxSize wxQTMediaBackend::GetVideoSize() const
1008 {
1009 return m_bestSize;
1010 }
1011
1012 //---------------------------------------------------------------------------
1013 // wxQTMediaBackend::Move
1014 //
1015 // We need to do this even when using native qt control because
1016 // CreateMovieControl is broken in this regard...
1017 //---------------------------------------------------------------------------
1018 void wxQTMediaBackend::Move(int x, int y, int w, int h)
1019 {
1020 #if !wxUSE_CREATEMOVIECONTROL
1021 if(m_timer)
1022 {
1023 m_ctrl->GetParent()->MacWindowToRootWindow(&x, &y);
1024 Rect theRect = {y, x, y+h, x+w};
1025
1026 ::MCSetControllerBoundsRect(m_mc, &theRect);
1027 wxASSERT(::GetMoviesError() == noErr);
1028 }
1029 #else
1030 if(m_timer && m_ctrl)
1031 {
1032 m_ctrl->GetParent()->MacWindowToRootWindow(&x, &y);
1033
1034 ::MoveControl( (ControlRef) m_ctrl->GetHandle(), x, y );
1035 m_ctrl->GetParent()->Refresh();
1036 m_ctrl->GetParent()->Update();
1037 }
1038 #endif
1039 }
1040
1041 //---------------------------------------------------------------------------
1042 // wxQTMediaBackend::DoSetControllerVisible
1043 //
1044 // Utility function that takes care of showing the moviecontroller
1045 // and showing/hiding the particular controls on it
1046 //---------------------------------------------------------------------------
1047 void wxQTMediaBackend::DoSetControllerVisible(wxMediaCtrlPlayerControls flags)
1048 {
1049 ::MCSetVisible(m_mc, TRUE);
1050
1051 //
1052 // Take care of subcontrols
1053 //
1054 if(::GetMoviesError() == noErr)
1055 {
1056 long mcFlags = 0;
1057 ::MCDoAction(m_mc, 39/*mcActionGetFlags*/, (void*)&mcFlags);
1058
1059 if(::GetMoviesError() == noErr)
1060 {
1061 mcFlags |= ( //(1<<0)/*mcFlagSuppressMovieFrame*/ |
1062 (1<<3)/*mcFlagsUseWindowPalette*/
1063 | ((flags & wxMEDIACTRLPLAYERCONTROLS_STEP)
1064 ? 0 : (1<<1)/*mcFlagSuppressStepButtons*/)
1065 | ((flags & wxMEDIACTRLPLAYERCONTROLS_VOLUME)
1066 ? 0 : (1<<2)/*mcFlagSuppressSpeakerButton*/)
1067 // | (1<<4) /*mcFlagDontInvalidate*/ //if we take care of repainting ourselves
1068 );
1069 ::MCDoAction(m_mc, 38/*mcActionSetFlags*/, (void*)mcFlags);
1070 }
1071 }
1072
1073 //
1074 //Adjust height and width of best size for movie controller
1075 //if the user wants it shown
1076 //
1077 m_bestSize.x = m_bestSize.x > wxMCWIDTH ? m_bestSize.x : wxMCWIDTH;
1078 m_bestSize.y += wxMCHEIGHT;
1079 }
1080
1081 //---------------------------------------------------------------------------
1082 // wxQTMediaBackend::ShowPlayerControls
1083 //
1084 // Shows/Hides subcontrols on the media control
1085 //---------------------------------------------------------------------------
1086 bool wxQTMediaBackend::ShowPlayerControls(wxMediaCtrlPlayerControls flags)
1087 {
1088 if(!m_mc)
1089 return false; //no movie controller...
1090
1091 bool bSizeChanged = false;
1092
1093 //if the controller is visible and we want to hide it do so
1094 if(m_interfaceflags && !flags)
1095 {
1096 bSizeChanged = true;
1097 DoLoadBestSize();
1098 ::MCSetVisible(m_mc, FALSE);
1099 }
1100 else if(!m_interfaceflags && flags) //show controller if hidden
1101 {
1102 bSizeChanged = true;
1103 DoSetControllerVisible(flags);
1104 }
1105
1106 //readjust parent sizers
1107 if(bSizeChanged)
1108 {
1109 NotifyMovieSizeChanged();
1110
1111 //remember state in case of loading new media
1112 m_interfaceflags = flags;
1113 }
1114
1115 return ::GetMoviesError() == noErr;
1116 }
1117
1118 //---------------------------------------------------------------------------
1119 // wxQTMediaBackend::OnEraseBackground
1120 //
1121 // Suggestion from Greg Hazel to repaint the movie when idle
1122 // (on pause also)
1123 //---------------------------------------------------------------------------
1124 #if !wxUSE_CREATEMOVIECONTROL
1125 void wxQTMediaEvtHandler::OnEraseBackground(wxEraseEvent& evt)
1126 {
1127 // Work around Nasty OSX drawing bug -
1128 // http://lists.apple.com/archives/QuickTime-API/2002/Feb/msg00311.html
1129 WindowRef wrTLW =
1130 (WindowRef) m_qtb->m_ctrl->MacGetTopLevelWindowRef();
1131
1132 RgnHandle region = MCGetControllerBoundsRgn(m_qtb->m_mc);
1133 MCInvalidate(m_qtb->m_mc, wrTLW, region);
1134 MCIdle(m_qtb->m_mc);
1135 }
1136 #endif
1137
1138 //---------------------------------------------------------------------------
1139 // wxQTMediaWindowEventHandler
1140 //
1141 // Event callback for the top level window of our control that passes
1142 // messages to our moviecontroller so it can recieve mouse clicks etc.
1143 //---------------------------------------------------------------------------
1144 #if !wxUSE_CREATEMOVIECONTROL
1145 OSStatus wxQTMediaWindowEventHandler(EventHandlerCallRef inHandlerCallRef,
1146 EventRef inEvent, void *inUserData)
1147 {
1148 EventRecord theEvent;
1149 ConvertEventRefToEventRecord( inEvent, &theEvent );
1150 OSStatus err;
1151 err = ::MCIsPlayerEvent( (MovieController) inUserData, &theEvent );
1152
1153 // pass on to other event handlers if not handled- i.e. wx
1154 if(err)
1155 return noErr;
1156 else
1157 return eventNotHandledErr;
1158 }
1159 #endif
1160
1161 //in source file that contains stuff you don't directly use
1162 #include "wx/html/forcelnk.h"
1163 FORCE_LINK_ME(basewxmediabackends)
1164
1165 #endif //wxUSE_MEDIACTRL