]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/mediactrl.cpp
e77383dd494218a62ff36ab920728612678847d6
[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 // 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).
18 //
19 // Even though though the CreateMovieControl version is the default
20 // for OSX, the MovieController version is heavily tested and works
21 // just as well...
22 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
23
24 //===========================================================================
25 // DECLARATIONS
26 //===========================================================================
27
28 //---------------------------------------------------------------------------
29 // Pre-compiled header stuff
30 //---------------------------------------------------------------------------
31
32 // For compilers that support precompilation, includes "wx.h".
33 #include "wx/wxprec.h"
34
35 //---------------------------------------------------------------------------
36 // Includes
37 //---------------------------------------------------------------------------
38 #include "wx/mediactrl.h"
39
40 //---------------------------------------------------------------------------
41 // Compilation guard
42 //---------------------------------------------------------------------------
43 #if wxUSE_MEDIACTRL
44
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
53 # else
54 # define wxUSE_CREATEMOVIECONTROL 0
55 # endif
56 #endif
57
58 //---------------------------------------------------------------------------
59 // Height and Width of movie controller in the movie control
60 //---------------------------------------------------------------------------
61 #define wxMCWIDTH 320
62 #define wxMCHEIGHT 16
63
64 //===========================================================================
65 // BACKEND DECLARATIONS
66 //===========================================================================
67
68 //---------------------------------------------------------------------------
69 //
70 // wxQTMediaBackend
71 //
72 //---------------------------------------------------------------------------
73
74 //---------------------------------------------------------------------------
75 // QT Includes
76 //---------------------------------------------------------------------------
77 //uma is for wxMacFSSpec
78 #include "wx/mac/uma.h"
79 #include "wx/timer.h"
80 #ifndef __DARWIN__
81 #include <Movies.h>
82 #include <Gestalt.h>
83 #include <QuickTimeComponents.h> //Standard QT stuff
84 #else
85 #include <QuickTime/QuickTimeComponents.h>
86 #endif
87
88 class WXDLLIMPEXP_MEDIA wxQTMediaBackend : public wxMediaBackendCommonBase
89 {
90 public:
91 wxQTMediaBackend();
92 ~wxQTMediaBackend();
93
94 virtual bool CreateControl(wxControl* ctrl, wxWindow* parent,
95 wxWindowID id,
96 const wxPoint& pos,
97 const wxSize& size,
98 long style,
99 const wxValidator& validator,
100 const wxString& name);
101
102 virtual bool Play();
103 virtual bool Pause();
104 virtual bool Stop();
105
106 virtual bool Load(const wxString& fileName);
107 virtual bool Load(const wxURI& location);
108
109 virtual wxMediaState GetState();
110
111 virtual bool SetPosition(wxLongLong where);
112 virtual wxLongLong GetPosition();
113 virtual wxLongLong GetDuration();
114
115 virtual void Move(int x, int y, int w, int h);
116 wxSize GetVideoSize() const;
117
118 virtual double GetPlaybackRate();
119 virtual bool SetPlaybackRate(double dRate);
120
121 virtual double GetVolume();
122 virtual bool SetVolume(double);
123
124 void Cleanup();
125 void FinishLoad();
126
127 virtual bool ShowPlayerControls(wxMediaCtrlPlayerControls flags);
128
129 virtual wxLongLong GetDownloadProgress();
130 virtual wxLongLong GetDownloadTotal();
131
132 //
133 // ------ Implementation from now on --------
134 //
135 bool DoPause();
136 bool DoStop();
137
138 void DoLoadBestSize();
139 void DoSetControllerVisible(wxMediaCtrlPlayerControls flags);
140
141 wxLongLong GetDataSizeFromStart(TimeValue end);
142
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);
146
147 #if wxUSE_CREATEMOVIECONTROL
148 void DoCreateMovieControl();
149 #else
150 Boolean IsQuickTime4Installed();
151 void DoNewMovieController();
152 static pascal void PPRMProc (Movie theMovie,
153 OSErr theErr, void* theRefCon);
154 #endif
155
156 wxSize m_bestSize; // Original movie size
157 #ifdef __WXMAC_OSX__
158 struct MovieType** m_movie; // QT Movie handle/instance
159 #else
160 Movie m_movie; // Movie instance
161 #endif
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;
171
172 friend class wxQTMediaEvtHandler;
173 #endif
174 DECLARE_DYNAMIC_CLASS(wxQTMediaBackend)
175 };
176
177 #if !wxUSE_CREATEMOVIECONTROL
178 // helper to hijack background erasing for the QT window
179 class WXDLLIMPEXP_MEDIA wxQTMediaEvtHandler : public wxEvtHandler
180 {
181 public:
182 wxQTMediaEvtHandler(wxQTMediaBackend *qtb)
183 {
184 m_qtb = qtb;
185
186 qtb->m_ctrl->Connect(qtb->m_ctrl->GetId(), wxEVT_ERASE_BACKGROUND,
187 wxEraseEventHandler(wxQTMediaEvtHandler::OnEraseBackground),
188 NULL, this);
189 }
190
191 void OnEraseBackground(wxEraseEvent& event);
192
193 private:
194 wxQTMediaBackend *m_qtb;
195
196 DECLARE_NO_COPY_CLASS(wxQTMediaEvtHandler)
197 };
198
199 // Window event handler
200 static pascal OSStatus wxQTMediaWindowEventHandler(
201 EventHandlerCallRef inHandlerCallRef,
202 EventRef inEvent, void *inUserData);
203
204 #endif
205
206 //===========================================================================
207 // IMPLEMENTATION
208 //===========================================================================
209
210
211 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
212 //
213 // wxQTMediaBackend
214 //
215 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
216
217 IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend, wxMediaBackend)
218
219 //Time between timer calls - this is the Apple recommondation to the TCL
220 //team I believe
221 #define MOVIE_DELAY 20
222
223 //---------------------------------------------------------------------------
224 // wxQTMediaLoadTimer
225 //
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
233 {
234 public:
235 wxQTMediaLoadTimer(Movie movie, wxQTMediaBackend* parent) :
236 m_movie(movie), m_parent(parent) {}
237
238 void Notify()
239 {
240 //Note that the CreateMovieControl variety performs
241 //its own custom idleing
242 #if !wxUSE_CREATEMOVIECONTROL
243 ::MCIdle(m_parent->m_mc);
244 #endif
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)
249 {
250 m_parent->FinishLoad();
251 delete this;
252 }
253 }
254
255 protected:
256 Movie m_movie; //Our movie instance
257 wxQTMediaBackend* m_parent; //Backend pointer
258 };
259
260 // --------------------------------------------------------------------------
261 // wxQTMediaPlayTimer - Handle Asyncronous Playing
262 //
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
266 // the movie.
267 // --------------------------------------------------------------------------
268 class wxQTMediaPlayTimer : public wxTimer
269 {
270 public:
271 wxQTMediaPlayTimer(Movie movie, wxQTMediaBackend* parent) :
272 m_movie(movie), m_parent(parent) {}
273
274 void Notify()
275 {
276 //Note that CreateMovieControl performs its own idleing
277 #if !wxUSE_CREATEMOVIECONTROL
278 //
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.
286 //
287 // So now we call MoviesTask always so that it repaints
288 // correctly.
289 //
290 ::MCIdle(m_parent->m_mc);
291 #endif
292
293 //
294 // Handle the stop event - if the movie has reached
295 // the end, notify our handler
296 //
297 if(::IsMovieDone(m_movie))
298 {
299 if ( m_parent->SendStopEvent() )
300 {
301 m_parent->Stop();
302 wxASSERT(::GetMoviesError() == noErr);
303
304 m_parent->QueueFinishEvent();
305 }
306 }
307 }
308
309 protected:
310 Movie m_movie; //Our movie instance
311 wxQTMediaBackend* m_parent; //Backend pointer
312 };
313
314
315 //---------------------------------------------------------------------------
316 // wxQTMediaBackend Constructor
317 //
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)
325 #endif
326 {
327 }
328
329 //---------------------------------------------------------------------------
330 // wxQTMediaBackend Destructor
331 //
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()
339 {
340 if(m_movie)
341 Cleanup();
342
343 #if !wxUSE_CREATEMOVIECONTROL
344 // Cleanup for moviecontroller
345 if(m_mc)
346 {
347 // destroy wxQTMediaEvtHandler we pushed on it
348 m_ctrl->PopEventHandler(true);
349 RemoveEventHandler((EventHandlerRef&)m_pEventHandlerRef);
350 DisposeEventHandlerUPP(m_eventupp);
351
352 // Dispose of the movie controller
353 ::DisposeMovieController(m_mc);
354 DisposeMCActionFilterWithRefConUPP(m_mcactionupp);
355 }
356 #endif
357
358 //Note that ExitMovies() is not necessary...
359 ExitMovies();
360 }
361
362 //---------------------------------------------------------------------------
363 // wxQTMediaBackend::CreateControl
364 //
365 // 1) Intializes QuickTime
366 // 2) Creates the control window
367 //---------------------------------------------------------------------------
368 bool wxQTMediaBackend::CreateControl(wxControl* ctrl, wxWindow* parent,
369 wxWindowID id,
370 const wxPoint& pos,
371 const wxSize& size,
372 long style,
373 const wxValidator& validator,
374 const wxString& name)
375 {
376 //Don't bother in Native control mode
377 #if !wxUSE_CREATEMOVIECONTROL
378 if (!IsQuickTime4Installed())
379 return false;
380 #endif
381
382 EnterMovies();
383
384 //
385 // Create window
386 // By default wxWindow(s) is created with a border -
387 // so we need to get rid of those
388 //
389 // Since we don't have a child window like most other
390 // backends, we don't need wxCLIP_CHILDREN
391 //
392 if ( !ctrl->wxControl::Create(parent, id, pos, size,
393 wxWindow::MacRemoveBordersFromStyle(style),
394 validator, name)
395 )
396 return false;
397
398 #if wxUSE_VALIDATORS
399 ctrl->SetValidator(validator);
400 #endif
401
402 m_ctrl = (wxMediaCtrl*)ctrl;
403 return true;
404 }
405
406 //---------------------------------------------------------------------------
407 // wxQTMediaBackend::IsQuickTime4Installed
408 //
409 // Determines whether version 4 of QT is installed
410 // (Pretty much for classic only)
411 //---------------------------------------------------------------------------
412 #if !wxUSE_CREATEMOVIECONTROL
413 Boolean wxQTMediaBackend::IsQuickTime4Installed()
414 {
415 short error;
416 long result;
417
418 error = Gestalt (gestaltQuickTime, &result);
419 return (error == noErr) && (((result >> 16) & 0xffff) >= 0x0400);
420 }
421 #endif
422
423 //---------------------------------------------------------------------------
424 // wxQTMediaBackend::Load (file version)
425 //
426 // 1) Get an FSSpec from the Windows path name
427 // 2) Open the movie
428 // 3) Obtain the movie instance from the movie resource
429 // 4) Close the movie resource
430 // 5) Finish loading
431 //---------------------------------------------------------------------------
432 bool wxQTMediaBackend::Load(const wxString& fileName)
433 {
434 if(m_movie)
435 Cleanup();
436
437 OSErr err = noErr;
438 short movieResFile;
439 FSSpec sfFile;
440
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())
445 return false;
446
447 wxMacFilename2FSSpec( fileName , &sfFile );
448
449 if (OpenMovieFile (&sfFile, &movieResFile, fsRdPerm) != noErr)
450 return false;
451
452 short movieResID = 0;
453 Str255 movieName;
454
455 err = NewMovieFromFile (
456 &m_movie,
457 movieResFile,
458 &movieResID,
459 movieName,
460 newMovieActive,
461 NULL); //wasChanged
462
463 //No ::GetMoviesStickyError() here because it returns -2009
464 // a.k.a. invalid track on valid mpegs
465 if(err == noErr)
466 {
467 ::CloseMovieFile (movieResFile);
468
469 // Create movie controller/control
470 #if wxUSE_CREATEMOVIECONTROL
471 DoCreateMovieControl();
472 #else
473 DoNewMovieController();
474 #endif
475 FinishLoad();
476 return true;
477 }
478 else
479 {
480 return false;
481 }
482 }
483
484 //---------------------------------------------------------------------------
485 // wxQTMediaBackend::PPRMProc (static)
486 //
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),
494 void* theRefCon)
495 {
496 wxASSERT( theMovie );
497 wxASSERT( theRefCon );
498 wxASSERT( theErr == noErr );
499
500 wxQTMediaBackend* pBE = (wxQTMediaBackend*) theRefCon;
501
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);
507 }
508 #endif
509
510 //---------------------------------------------------------------------------
511 // wxQTMediaBackend::Load (URL Version)
512 //
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
519 // 7) Finish Loading
520 //---------------------------------------------------------------------------
521 bool wxQTMediaBackend::Load(const wxURI& location)
522 {
523 if(m_movie)
524 Cleanup();
525
526 wxString theURI = location.BuildURI();
527
528 OSErr err = noErr;
529
530 Handle theHandle = ::NewHandleClear(theURI.length() + 1);
531 wxASSERT(theHandle);
532
533 ::BlockMove(theURI.mb_str(), *theHandle, theURI.length() + 1);
534
535 //create the movie from the handle that refers to the URI
536 err = ::NewMovieFromDataRef(&m_movie, newMovieActive |
537 newMovieAsyncOK
538 /*|newMovieIdleImportOK*/,
539 NULL, theHandle,
540 URLDataHandlerSubType);
541
542 ::DisposeHandle(theHandle);
543
544 if (err == noErr)
545 {
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();
550
551 // Setup timer to catch load event
552 m_timer = new wxQTMediaLoadTimer(m_movie, this);
553 m_timer->Start(MOVIE_DELAY);
554 #else
555 // Movie controller resets prerolling, so we must create first
556 DoNewMovieController();
557
558 long timeNow;
559 Fixed playRate;
560
561 timeNow = ::GetMovieTime(m_movie, NULL);
562 wxASSERT(::GetMoviesError() == noErr);
563
564 playRate = ::GetMoviePreferredRate(m_movie);
565 wxASSERT(::GetMoviesError() == noErr);
566
567 //
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.
575 //
576 m_preprerollupp =
577 NewMoviePrePrerollCompleteUPP(
578 wxQTMediaBackend::PPRMProc
579 );
580
581 ::PrePrerollMovie(m_movie, timeNow, playRate,
582 m_preprerollupp,
583 (void*)this);
584 #endif
585 return true;
586 }
587 else
588 return false;
589 }
590
591 //---------------------------------------------------------------------------
592 // wxQTMediaBackend::DoCreateMovieControl
593 //
594 // Calls CreateMovieControl and performs setup related to it
595 //
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()
604 {
605 //
606 //Native CreateMovieControl QT control (Thanks to Kevin Olliver's
607 // wxQTMovie for how to do some of this).
608 //
609 Rect bounds = wxMacGetBoundsForControl(m_ctrl,
610 m_ctrl->GetPosition(),
611 m_ctrl->GetSize());
612
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();
616
617 //Options-
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(
626 (WindowRef)
627 m_ctrl->MacGetTopLevelWindowRef(), //parent
628 &bounds, //control bounds
629 m_movie, //movie handle
630 kMovieControlOptionHideController
631 | kMovieControlOptionLocateTopLeft
632 | kMovieControlOptionSetKeysEnabled
633 // | kMovieControlOptionManuallyIdled
634 , //flags
635 m_ctrl->m_peer->GetControlRefAddr() );
636
637 ::EmbedControl(m_ctrl->m_peer->GetControlRef(),
638 (ControlRef)m_ctrl->GetParent()->GetHandle());
639
640 //
641 // Setup MovieController for the new movie
642 //
643 long dataSize;
644
645 //Get movie controller from our control
646 ::GetControlData( m_ctrl->m_peer->GetControlRef(), 0,
647 kMovieControlDataMovieController,
648 sizeof(MovieController), (Ptr)&m_mc, &dataSize );
649
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,
654 (long)this);
655 }
656 #endif
657
658 //---------------------------------------------------------------------------
659 // wxQTMediaBackend::DoNewMovieController
660 //
661 // Attaches movie to moviecontroller or creates moviecontroller
662 // if not created yet
663 //---------------------------------------------------------------------------
664 #if !wxUSE_CREATEMOVIECONTROL
665 void wxQTMediaBackend::DoNewMovieController()
666 {
667 if(!m_mc)
668 {
669 // Get top level window ref for some mac functions
670 WindowRef wrTLW = (WindowRef) m_ctrl->MacGetTopLevelWindowRef();
671
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),
678 NULL);
679 wxASSERT(::GetMoviesError() == noErr);
680
681 Rect bounds = wxMacGetBoundsForControl(m_ctrl,
682 m_ctrl->GetPosition(),
683 m_ctrl->GetSize());
684
685 m_mc = ::NewMovieController(m_movie, &bounds, mcTopLeftMovie |
686 //mcWithFrame |
687 mcNotVisible);
688 wxASSERT(::GetMoviesError() == noErr);
689 ::MCDoAction(m_mc, 32, (void*)true); //mcActionSetKeysEnabled
690 wxASSERT(::GetMoviesError() == noErr);
691
692 // Setup a callback so we can tell when the user presses
693 // play on the player controls
694 m_mcactionupp =
695 NewMCActionFilterWithRefConUPP(
696 wxQTMediaBackend::MCFilterProc
697 );
698 ::MCSetActionFilterWithRefCon(m_mc,
699 m_mcactionupp,
700 (long)this);
701 wxASSERT(::GetMoviesError() == noErr);
702
703 //Part of a suggestion from Greg Hazel to repaint
704 //movie when idle
705 m_ctrl->PushEventHandler(new wxQTMediaEvtHandler(this));
706
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 }
719 };
720
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
726 );
727 InstallWindowEventHandler( wrTLW,
728 m_eventupp,
729 GetEventTypeCount( theEventTypes ), theEventTypes,
730 m_mc, (&(EventHandlerRef&)m_pEventHandlerRef) );
731 }
732 else
733 {
734 // MovieController already created -
735 // Just change the movie in it and we're good to go
736 Point thePoint;
737 thePoint.h = thePoint.v = 0;
738 ::MCSetMovie(m_mc, m_movie,
739 (WindowRef)m_ctrl->MacGetTopLevelWindowRef(),
740 thePoint);
741 wxASSERT(::GetMoviesError() == noErr);
742 }
743 }
744 #endif
745
746 //---------------------------------------------------------------------------
747 // wxQTMediaBackend::FinishLoad
748 //
749 // Performs operations after a movie ready to play/loaded.
750 //---------------------------------------------------------------------------
751 void wxQTMediaBackend::FinishLoad()
752 {
753 // Dispose of the Preprerollmovieupp if we used it
754 #if !wxUSE_CREATEMOVIECONTROL
755 DisposeMoviePrePrerollCompleteUPP(m_preprerollupp);
756 #endif
757 // get the real size of the movie
758 DoLoadBestSize();
759
760 // Show the player controls if the user wants to
761 if(m_interfaceflags)
762 DoSetControllerVisible(m_interfaceflags);
763
764 //we want millisecond precision
765 ::SetMovieTimeScale(m_movie, 1000);
766 wxASSERT(::GetMoviesError() == noErr);
767
768 // Start movie progress timer
769 m_timer = new wxQTMediaPlayTimer(m_movie, (wxQTMediaBackend*) this);
770 wxASSERT(m_timer);
771 m_timer->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS);
772
773 //send loaded event & refresh size
774 NotifyMovieLoaded();
775 }
776
777 //---------------------------------------------------------------------------
778 // wxQTMediaBackend::DoLoadBestSize
779 //
780 // Sets the best size of the control from the real size of the movie
781 //---------------------------------------------------------------------------
782 void wxQTMediaBackend::DoLoadBestSize()
783 {
784 //get the real size of the movie
785 Rect outRect;
786 ::GetMovieNaturalBoundsRect (m_movie, &outRect);
787 wxASSERT(::GetMoviesError() == noErr);
788
789 //determine best size
790 m_bestSize.x = outRect.right - outRect.left;
791 m_bestSize.y = outRect.bottom - outRect.top;
792 }
793
794 //---------------------------------------------------------------------------
795 // wxQTMediaBackend::Play
796 //
797 // Start the QT movie
798 // (Apple recommends mcActionPrerollAndPlay but that's QT 4.1+)
799 //---------------------------------------------------------------------------
800 bool wxQTMediaBackend::Play()
801 {
802 Fixed fixRate = (Fixed) (wxQTMediaBackend::GetPlaybackRate() * 0x10000);
803 if(!fixRate)
804 fixRate = ::GetMoviePreferredRate(m_movie);
805
806 wxASSERT(fixRate != 0);
807
808 if(!m_bPlaying)
809 ::MCDoAction( m_mc, 8, // mcActionPlay
810 (void *) fixRate);
811
812 if(::GetMoviesError() == noErr)
813 {
814
815 m_bPlaying = true;
816 QueuePlayEvent();
817 return true;
818 }
819 else
820 return false;
821 }
822
823 //---------------------------------------------------------------------------
824 // wxQTMediaBackend::Pause
825 //
826 // Stop the movie
827 //---------------------------------------------------------------------------
828 bool wxQTMediaBackend::DoPause()
829 {
830 //Stop the movie A.K.A. ::StopMovie(m_movie);
831 if(m_bPlaying)
832 {
833 ::MCDoAction( m_mc, 8 /*mcActionPlay*/,
834 (void *) 0);
835 m_bPlaying = false;
836 return ::GetMoviesError() == noErr;
837 }
838 return true; //already paused
839 }
840
841 bool wxQTMediaBackend::Pause()
842 {
843 bool bSuccess = DoPause();
844 if(bSuccess)
845 {
846 this->QueuePauseEvent();
847 return true;
848 }
849 else
850 return false;
851 }
852
853 //---------------------------------------------------------------------------
854 // wxQTMediaBackend::Stop
855 //
856 // 1) Stop the movie
857 // 2) Seek to the beginning of the movie
858 //---------------------------------------------------------------------------
859 bool wxQTMediaBackend::DoStop()
860 {
861 if(!wxQTMediaBackend::DoPause())
862 return false;
863
864 ::GoToBeginningOfMovie(m_movie);
865 return ::GetMoviesError() == noErr;
866 }
867
868 bool wxQTMediaBackend::Stop()
869 {
870 bool bSuccess = DoStop();
871 if(bSuccess)
872 {
873 QueueStopEvent();
874 return true;
875 }
876 else
877 return false;
878 }
879
880 //---------------------------------------------------------------------------
881 // wxQTMediaBackend::GetPlaybackRate
882 //
883 // 1) Get the movie playback rate from ::GetMovieRate
884 //---------------------------------------------------------------------------
885 double wxQTMediaBackend::GetPlaybackRate()
886 {
887 return ( ((double)::GetMovieRate(m_movie)) / 0x10000);
888 }
889
890 //---------------------------------------------------------------------------
891 // wxQTMediaBackend::SetPlaybackRate
892 //
893 // 1) Convert dRate to Fixed and Set the movie rate through SetMovieRate
894 //---------------------------------------------------------------------------
895 bool wxQTMediaBackend::SetPlaybackRate(double dRate)
896 {
897 ::SetMovieRate(m_movie, (Fixed) (dRate * 0x10000));
898 return ::GetMoviesError() == noErr;
899 }
900
901 //---------------------------------------------------------------------------
902 // wxQTMediaBackend::SetPosition
903 //
904 // 1) Create a time record struct (TimeRecord) with appropriate values
905 // 2) Pass struct to SetMovieTime
906 //---------------------------------------------------------------------------
907 bool wxQTMediaBackend::SetPosition(wxLongLong where)
908 {
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);
915
916 if (::GetMoviesError() != noErr)
917 return false;
918
919 return true;
920 }
921
922 //---------------------------------------------------------------------------
923 // wxQTMediaBackend::GetPosition
924 //
925 // Calls GetMovieTime
926 //---------------------------------------------------------------------------
927 wxLongLong wxQTMediaBackend::GetPosition()
928 {
929 return ::GetMovieTime(m_movie, NULL);
930 }
931
932 //---------------------------------------------------------------------------
933 // wxQTMediaBackend::GetVolume
934 //
935 // Gets the volume through GetMovieVolume - which returns a 16 bit short -
936 //
937 // +--------+--------+
938 // + (1) + (2) +
939 // +--------+--------+
940 //
941 // (1) first 8 bits are value before decimal
942 // (2) second 8 bits are value after decimal
943 //
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()
948 {
949 short sVolume = ::GetMovieVolume(m_movie);
950
951 if(sVolume & (128 << 8)) //negative - no sound
952 return 0.0;
953
954 return sVolume/256.0;
955 }
956
957 //---------------------------------------------------------------------------
958 // wxQTMediaBackend::SetVolume
959 //
960 // Sets the volume through SetMovieVolume - which takes a 16 bit short -
961 //
962 // +--------+--------+
963 // + (1) + (2) +
964 // +--------+--------+
965 //
966 // (1) first 8 bits are value before decimal
967 // (2) second 8 bits are value after decimal
968 //
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)
973 {
974 ::SetMovieVolume(m_movie, (short) (dVolume * 256));
975 return true;
976 }
977
978 //---------------------------------------------------------------------------
979 // wxQTMediaBackend::GetDuration
980 //
981 // Calls GetMovieDuration
982 //---------------------------------------------------------------------------
983 wxLongLong wxQTMediaBackend::GetDuration()
984 {
985 return ::GetMovieDuration(m_movie);
986 }
987
988 //---------------------------------------------------------------------------
989 // wxQTMediaBackend::GetState
990 //
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()
995 {
996 // Could use
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;
1003 else
1004 return wxMEDIASTATE_PAUSED;
1005 }
1006
1007 //---------------------------------------------------------------------------
1008 // wxQTMediaBackend::Cleanup
1009 //
1010 // Diposes of the movie timer, Control if native, and stops and disposes
1011 // of the QT movie
1012 //---------------------------------------------------------------------------
1013 void wxQTMediaBackend::Cleanup()
1014 {
1015 m_bPlaying = false;
1016 if(m_timer)
1017 {
1018 delete m_timer;
1019 m_timer = NULL;
1020 }
1021
1022 // Stop the movie
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();
1029
1030 //
1031 // Dispose of control or remove movie from MovieController
1032 //
1033 #if wxUSE_CREATEMOVIECONTROL
1034 if (m_ctrl->m_peer && m_ctrl->m_peer->Ok() )
1035 m_ctrl->m_peer->Dispose();
1036 #else
1037 Point thePoint;
1038 thePoint.h = thePoint.v = 0;
1039 ::MCSetVisible(m_mc, false);
1040 ::MCSetMovie(m_mc, NULL, NULL, thePoint);
1041 #endif
1042
1043 ::DisposeMovie(m_movie);
1044 }
1045
1046 //---------------------------------------------------------------------------
1047 // wxQTMediaBackend::MCFilterProc (static)
1048 //
1049 // Callback for when the movie controller recieves a message
1050 //---------------------------------------------------------------------------
1051 pascal Boolean wxQTMediaBackend::MCFilterProc(
1052 MovieController WXUNUSED(theController),
1053 short action,
1054 void * WXUNUSED(params),
1055 long refCon)
1056 {
1057 if(action != 1) //don't process idle events
1058 {
1059 wxQTMediaBackend* pThis = (wxQTMediaBackend*)refCon;
1060
1061 switch(action)
1062 {
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);
1066 break;
1067 default:
1068 break;
1069 }
1070 }
1071 return 0;
1072 }
1073
1074 //---------------------------------------------------------------------------
1075 // wxQTMediaBackend::GetVideoSize
1076 //
1077 // Returns the actual size of the QT movie
1078 //---------------------------------------------------------------------------
1079 wxSize wxQTMediaBackend::GetVideoSize() const
1080 {
1081 return m_bestSize;
1082 }
1083
1084 //---------------------------------------------------------------------------
1085 // wxQTMediaBackend::Move
1086 //
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)
1098 {
1099 #if !wxUSE_CREATEMOVIECONTROL
1100 if(m_timer)
1101 {
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);
1107 #endif
1108 ::MCSetControllerBoundsRect(m_mc, &theRect);
1109 wxASSERT(::GetMoviesError() == noErr);
1110
1111 #if 0 // see note above
1112 if(m_interfaceflags)
1113 {
1114 ::MCSetVisible(m_mc, TRUE);
1115 wxASSERT(::GetMoviesError() == noErr);
1116 }
1117 #endif
1118 }
1119 #else
1120 if(m_timer && m_ctrl)
1121 {
1122 m_ctrl->GetParent()->MacWindowToRootWindow(&x, &y);
1123
1124 ::MoveControl( (ControlRef) m_ctrl->GetHandle(), x, y );
1125 m_ctrl->GetParent()->Refresh();
1126 m_ctrl->GetParent()->Update();
1127 }
1128 #endif
1129 }
1130
1131 //---------------------------------------------------------------------------
1132 // wxQTMediaBackend::DoSetControllerVisible
1133 //
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)
1138 {
1139 ::MCSetVisible(m_mc, TRUE);
1140
1141 //
1142 // Take care of subcontrols
1143 //
1144 if(::GetMoviesError() == noErr)
1145 {
1146 long mcFlags = 0;
1147 ::MCDoAction(m_mc, 39/*mcActionGetFlags*/, (void*)&mcFlags);
1148
1149 if(::GetMoviesError() == noErr)
1150 {
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
1158 );
1159 ::MCDoAction(m_mc, 38/*mcActionSetFlags*/, (void*)mcFlags);
1160 }
1161 }
1162
1163 //
1164 //Adjust height and width of best size for movie controller
1165 //if the user wants it shown
1166 //
1167 m_bestSize.x = m_bestSize.x > wxMCWIDTH ? m_bestSize.x : wxMCWIDTH;
1168 m_bestSize.y += wxMCHEIGHT;
1169 }
1170
1171 //---------------------------------------------------------------------------
1172 // wxQTMediaBackend::ShowPlayerControls
1173 //
1174 // Shows/Hides subcontrols on the media control
1175 //---------------------------------------------------------------------------
1176 bool wxQTMediaBackend::ShowPlayerControls(wxMediaCtrlPlayerControls flags)
1177 {
1178 if(!m_mc)
1179 return false; //no movie controller...
1180
1181 bool bSizeChanged = false;
1182
1183 //if the controller is visible and we want to hide it do so
1184 if(m_interfaceflags && !flags)
1185 {
1186 bSizeChanged = true;
1187 DoLoadBestSize();
1188 ::MCSetVisible(m_mc, FALSE);
1189 }
1190 else if(!m_interfaceflags && flags) //show controller if hidden
1191 {
1192 bSizeChanged = true;
1193 DoSetControllerVisible(flags);
1194 }
1195
1196 //readjust parent sizers
1197 if(bSizeChanged)
1198 {
1199 NotifyMovieSizeChanged();
1200
1201 //remember state in case of loading new media
1202 m_interfaceflags = flags;
1203 }
1204 return ::GetMoviesError() == noErr;
1205 }
1206
1207 //---------------------------------------------------------------------------
1208 // wxQTMediaBackend::GetDataSizeFromStart
1209 //
1210 // Calls either GetMovieDataSize or GetMovieDataSize64 with a value
1211 // of 0 for the starting value
1212 //---------------------------------------------------------------------------
1213 wxLongLong wxQTMediaBackend::GetDataSizeFromStart(TimeValue end)
1214 {
1215 #if 0 // old pre-qt4 way
1216 return ::GetMovieDataSize(m_movie, 0, end)
1217 #else // qt4 way
1218 wide llDataSize;
1219 ::GetMovieDataSize64(m_movie, 0, end, &llDataSize);
1220 return wxLongLong(llDataSize.hi, llDataSize.lo);
1221 #endif
1222 }
1223
1224 //---------------------------------------------------------------------------
1225 // wxQTMediaBackend::GetDownloadProgress
1226 //---------------------------------------------------------------------------
1227 wxLongLong wxQTMediaBackend::GetDownloadProgress()
1228 {
1229 #if 0 // hackish and slow
1230 Handle hMovie = NewHandle(0);
1231 PutMovieIntoHandle(m_movie, hMovie);
1232 long lSize = GetHandleSize(hMovie);
1233 DisposeHandle(hMovie);
1234 return lSize;
1235 #else
1236 TimeValue tv;
1237 if(::GetMaxLoadedTimeInMovie(m_movie, &tv) != noErr)
1238 {
1239 wxLogDebug(wxT("GetMaxLoadedTimeInMovie failed"));
1240 return 0;
1241 }
1242 return wxQTMediaBackend::GetDataSizeFromStart(tv);
1243 #endif
1244 }
1245
1246 //---------------------------------------------------------------------------
1247 // wxQTMediaBackend::GetDownloadTotal
1248 //---------------------------------------------------------------------------
1249 wxLongLong wxQTMediaBackend::GetDownloadTotal()
1250 {
1251 return wxQTMediaBackend::GetDataSizeFromStart(
1252 ::GetMovieDuration(m_movie)
1253 );
1254 }
1255
1256
1257 //---------------------------------------------------------------------------
1258 // wxQTMediaBackend::OnEraseBackground
1259 //
1260 // Suggestion from Greg Hazel to repaint the movie when idle
1261 // (on pause also)
1262 //---------------------------------------------------------------------------
1263 #if !wxUSE_CREATEMOVIECONTROL
1264 void wxQTMediaEvtHandler::OnEraseBackground(wxEraseEvent& evt)
1265 {
1266 // Work around Nasty OSX drawing bug -
1267 // http://lists.apple.com/archives/QuickTime-API/2002/Feb/msg00311.html
1268 WindowRef wrTLW =
1269 (WindowRef) m_qtb->m_ctrl->MacGetTopLevelWindowRef();
1270
1271 RgnHandle region = ::MCGetControllerBoundsRgn(m_qtb->m_mc);
1272 ::MCInvalidate(m_qtb->m_mc, wrTLW, region);
1273 ::MCIdle(m_qtb->m_mc);
1274 }
1275 #endif
1276
1277 //---------------------------------------------------------------------------
1278 // wxQTMediaWindowEventHandler
1279 //
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)
1287 {
1288 // for the overly paranoid....
1289 #if 0
1290 UInt32 eventClass = GetEventClass( eventRef );
1291 UInt32 eventKind = GetEventKind( inEvent );
1292
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;
1303 #endif
1304 EventRecord theEvent;
1305 ConvertEventRefToEventRecord( inEvent, &theEvent );
1306 OSStatus err;
1307
1308 err = ::MCIsPlayerEvent( (MovieController) inUserData, &theEvent );
1309
1310 // pass on to other event handlers if not handled- i.e. wx
1311 if(err)
1312 return noErr;
1313 else
1314 return eventNotHandledErr;
1315 }
1316 #endif
1317
1318 //in source file that contains stuff you don't directly use
1319 #include "wx/html/forcelnk.h"
1320 FORCE_LINK_ME(basewxmediabackends)
1321
1322 #endif //wxUSE_MEDIACTRL