]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/mac/carbon/mediactrl.cpp
fixing sign conversion warning
[wxWidgets.git] / src / mac / carbon / mediactrl.cpp
... / ...
CommitLineData
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-2006 Ryan Norton
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13// OK, a casual overseer of this file may wonder why we don't use
14// either CreateMovieControl or HIMovieView...
15//
16// CreateMovieControl
17// 1) Need to dispose and create each time a new movie is loaded
18// 2) Not that many real advantages
19// 3) Progressively buggier in higher OSX versions
20// (see main.c of QTCarbonShell sample for details)
21// HIMovieView
22// 1) Crashes on destruction in ALL cases on quite a few systems!
23// (With the only real "alternative" is to simply not
24// dispose of it and let it leak...)
25// 2) Massive refreshing bugs with its movie controller between
26// movies
27//
28// At one point we had a complete implementation for CreateMovieControl
29// and on my (RN) local copy I had one for HIMovieView - but they
30// were simply deemed to be too buggy/unuseful. HIMovieView could
31// have been useful as well because it uses OpenGL contexts instead
32// of GWorlds. Perhaps someday when someone comes out with some
33// ingenious workarounds :).
34//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35
36// For compilers that support precompilation, includes "wx.h".
37#include "wx/wxprec.h"
38
39#include "wx/mediactrl.h"
40
41// uma is for wxMacFSSpec
42#include "wx/mac/uma.h"
43#include "wx/timer.h"
44
45// standard QT stuff
46#ifndef __DARWIN__
47#include <Movies.h>
48#include <Gestalt.h>
49#include <QuickTimeComponents.h>
50#else
51#include <QuickTime/QuickTimeComponents.h>
52#endif
53
54#if wxUSE_MEDIACTRL
55
56//---------------------------------------------------------------------------
57// Height and Width of movie controller in the movie control (apple samples)
58//---------------------------------------------------------------------------
59#define wxMCWIDTH 320
60#define wxMCHEIGHT 16
61
62//===========================================================================
63// BACKEND DECLARATIONS
64//===========================================================================
65
66//---------------------------------------------------------------------------
67// wxQTMediaBackend
68//---------------------------------------------------------------------------
69
70class WXDLLIMPEXP_MEDIA wxQTMediaBackend : public wxMediaBackendCommonBase
71{
72public:
73 wxQTMediaBackend();
74 ~wxQTMediaBackend();
75
76 virtual bool CreateControl(wxControl* ctrl, wxWindow* parent,
77 wxWindowID id,
78 const wxPoint& pos,
79 const wxSize& size,
80 long style,
81 const wxValidator& validator,
82 const wxString& name);
83
84 virtual bool Load(const wxString& fileName);
85 virtual bool Load(const wxURI& location);
86
87 virtual bool Play();
88 virtual bool Pause();
89 virtual bool Stop();
90
91 virtual wxMediaState GetState();
92
93 virtual bool SetPosition(wxLongLong where);
94 virtual wxLongLong GetPosition();
95 virtual wxLongLong GetDuration();
96
97 virtual void Move(int x, int y, int w, int h);
98 wxSize GetVideoSize() const;
99
100 virtual double GetPlaybackRate();
101 virtual bool SetPlaybackRate(double dRate);
102
103 virtual double GetVolume();
104 virtual bool SetVolume(double);
105
106 void Cleanup();
107 void FinishLoad();
108
109 virtual bool ShowPlayerControls(wxMediaCtrlPlayerControls flags);
110
111 virtual wxLongLong GetDownloadProgress();
112 virtual wxLongLong GetDownloadTotal();
113
114 virtual void MacVisibilityChanged();
115
116 //
117 // ------ Implementation from now on --------
118 //
119 bool DoPause();
120 bool DoStop();
121
122 void DoLoadBestSize();
123 void DoSetControllerVisible(wxMediaCtrlPlayerControls flags);
124
125 wxLongLong GetDataSizeFromStart(TimeValue end);
126
127 Boolean IsQuickTime4Installed();
128 void DoNewMovieController();
129
130 static pascal void PPRMProc(
131 Movie theMovie, OSErr theErr, void* theRefCon);
132
133 //TODO: Last param actually long - does this work on 64bit machines?
134 static pascal Boolean MCFilterProc(MovieController theController,
135 short action, void *params, long refCon);
136
137 static pascal OSStatus WindowEventHandler(
138 EventHandlerCallRef inHandlerCallRef,
139 EventRef inEvent, void *inUserData );
140
141 wxSize m_bestSize; // Original movie size
142 Movie m_movie; // Movie instance
143 bool m_bPlaying; // Whether media is playing or not
144 class wxTimer* m_timer; // Timer for streaming the movie
145 MovieController m_mc; // MovieController instance
146 wxMediaCtrlPlayerControls m_interfaceflags; // Saved interface flags
147
148 // Event handlers and UPPs/Callbacks
149 EventHandlerRef m_windowEventHandler;
150 EventHandlerUPP m_windowUPP;
151
152 MoviePrePrerollCompleteUPP m_preprerollupp;
153 MCActionFilterWithRefConUPP m_mcactionupp;
154
155 GWorldPtr m_movieWorld; //Offscreen movie GWorld
156
157 friend class wxQTMediaEvtHandler;
158
159 DECLARE_DYNAMIC_CLASS(wxQTMediaBackend)
160};
161
162// helper to hijack background erasing for the QT window
163class WXDLLIMPEXP_MEDIA wxQTMediaEvtHandler : public wxEvtHandler
164{
165public:
166 wxQTMediaEvtHandler(wxQTMediaBackend *qtb)
167 {
168 m_qtb = qtb;
169
170 qtb->m_ctrl->Connect(
171 qtb->m_ctrl->GetId(), wxEVT_ERASE_BACKGROUND,
172 wxEraseEventHandler(wxQTMediaEvtHandler::OnEraseBackground),
173 NULL, this);
174 }
175
176 void OnEraseBackground(wxEraseEvent& event);
177
178private:
179 wxQTMediaBackend *m_qtb;
180
181 DECLARE_NO_COPY_CLASS(wxQTMediaEvtHandler)
182};
183
184//===========================================================================
185// IMPLEMENTATION
186//===========================================================================
187
188
189//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
190//
191// wxQTMediaBackend
192//
193//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
194
195IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend, wxMediaBackend)
196
197//Time between timer calls - this is the Apple recommondation to the TCL
198//team I believe
199#define MOVIE_DELAY 20
200
201//---------------------------------------------------------------------------
202// wxQTMediaLoadTimer
203//
204// QT, esp. QT for Windows is very picky about how you go about
205// async loading. If you were to go through a Windows message loop
206// or a MoviesTask or both and then check the movie load state
207// it would still return 1000 (loading)... even (pre)prerolling doesn't
208// help. However, making a load timer like this works
209//---------------------------------------------------------------------------
210class wxQTMediaLoadTimer : public wxTimer
211{
212public:
213 wxQTMediaLoadTimer(wxQTMediaBackend* parent) :
214 m_parent(parent) {}
215
216 void Notify()
217 {
218 ::MCIdle(m_parent->m_mc);
219
220 // kMovieLoadStatePlayable is not enough on MAC:
221 // it plays, but IsMovieDone might return true (!)
222 // sure we need to wait until kMovieLoadStatePlaythroughOK
223 if (::GetMovieLoadState(m_parent->m_movie) >= 20000)
224 {
225 m_parent->FinishLoad();
226 delete this;
227 }
228 }
229
230protected:
231 wxQTMediaBackend *m_parent; // Backend pointer
232};
233
234// --------------------------------------------------------------------------
235// wxQTMediaPlayTimer - Handle Asyncronous Playing
236//
237// 1) Checks to see if the movie is done, and if not continues
238// streaming the movie
239// 2) Sends the wxEVT_MEDIA_STOP event if we have reached the end of
240// the movie.
241// --------------------------------------------------------------------------
242class wxQTMediaPlayTimer : public wxTimer
243{
244public:
245 wxQTMediaPlayTimer(wxQTMediaBackend* parent) :
246 m_parent(parent) {}
247
248 void Notify()
249 {
250 //
251 // OK, a little explaining - basically originally
252 // we only called MoviesTask if the movie was actually
253 // playing (not paused or stopped)... this was before
254 // we realized MoviesTask actually handles repainting
255 // of the current frame - so if you were to resize
256 // or something it would previously not redraw that
257 // portion of the movie.
258 //
259 // So now we call MoviesTask always so that it repaints
260 // correctly.
261 //
262 ::MCIdle(m_parent->m_mc);
263
264 //
265 // Handle the stop event - if the movie has reached
266 // the end, notify our handler
267 //
268 if (::IsMovieDone(m_parent->m_movie))
269 {
270 if ( m_parent->SendStopEvent() )
271 {
272 m_parent->Stop();
273 wxASSERT(::GetMoviesError() == noErr);
274
275 m_parent->QueueFinishEvent();
276 }
277 }
278 }
279
280protected:
281 wxQTMediaBackend* m_parent; // Backend pointer
282};
283
284
285//---------------------------------------------------------------------------
286// wxQTMediaBackend Constructor
287//
288// Sets m_timer to NULL signifying we havn't loaded anything yet
289//---------------------------------------------------------------------------
290wxQTMediaBackend::wxQTMediaBackend()
291 : m_movie(NULL), m_bPlaying(false), m_timer(NULL)
292 , m_mc(NULL), m_interfaceflags(wxMEDIACTRLPLAYERCONTROLS_NONE)
293 , m_preprerollupp(NULL), m_movieWorld(NULL)
294{
295}
296
297//---------------------------------------------------------------------------
298// wxQTMediaBackend Destructor
299//
300// 1) Cleans up the QuickTime movie instance
301// 2) Decrements the QuickTime reference counter - if this reaches
302// 0, QuickTime shuts down
303// 3) Decrements the QuickTime Windows Media Layer reference counter -
304// if this reaches 0, QuickTime shuts down the Windows Media Layer
305//---------------------------------------------------------------------------
306wxQTMediaBackend::~wxQTMediaBackend()
307{
308 if (m_movie)
309 Cleanup();
310
311 // Cleanup for moviecontroller
312 if (m_mc)
313 {
314 // destroy wxQTMediaEvtHandler we pushed on it
315 m_ctrl->PopEventHandler(true);
316 RemoveEventHandler(m_windowEventHandler);
317 DisposeEventHandlerUPP(m_windowUPP);
318
319 // Dispose of the movie controller
320 ::DisposeMovieController(m_mc);
321 m_mc = NULL;
322 DisposeMCActionFilterWithRefConUPP(m_mcactionupp);
323
324 // Dispose of offscreen GWorld
325 ::DisposeGWorld(m_movieWorld);
326 }
327
328 // Note that ExitMovies() is not necessary...
329 ExitMovies();
330}
331
332//---------------------------------------------------------------------------
333// wxQTMediaBackend::CreateControl
334//
335// 1) Intializes QuickTime
336// 2) Creates the control window
337//---------------------------------------------------------------------------
338bool wxQTMediaBackend::CreateControl(
339 wxControl* ctrl,
340 wxWindow* parent,
341 wxWindowID id,
342 const wxPoint& pos,
343 const wxSize& size,
344 long style,
345 const wxValidator& validator,
346 const wxString& name)
347{
348 if (!IsQuickTime4Installed())
349 return false;
350
351 EnterMovies();
352
353 wxMediaCtrl* mediactrl = (wxMediaCtrl*)ctrl;
354
355 //
356 // Create window
357 // By default wxWindow(s) is created with a border -
358 // so we need to get rid of those
359 //
360 // Since we don't have a child window like most other
361 // backends, we don't need wxCLIP_CHILDREN
362 //
363 if ( !mediactrl->wxControl::Create(
364 parent, id, pos, size,
365 wxWindow::MacRemoveBordersFromStyle(style),
366 validator, name))
367 {
368 return false;
369 }
370
371#if wxUSE_VALIDATORS
372 mediactrl->SetValidator(validator);
373#endif
374
375 m_ctrl = mediactrl;
376 return true;
377}
378
379//---------------------------------------------------------------------------
380// wxQTMediaBackend::IsQuickTime4Installed
381//
382// Determines whether version 4 of QT is installed
383// (Pretty much for Classic only)
384//---------------------------------------------------------------------------
385Boolean wxQTMediaBackend::IsQuickTime4Installed()
386{
387 OSErr error;
388 long result;
389
390 error = Gestalt(gestaltQuickTime, &result);
391 return (error == noErr) && (((result >> 16) & 0xffff) >= 0x0400);
392}
393
394//---------------------------------------------------------------------------
395// wxQTMediaBackend::Load (file version)
396//
397// 1) Get an FSSpec from the Windows path name
398// 2) Open the movie
399// 3) Obtain the movie instance from the movie resource
400// 4) Close the movie resource
401// 5) Finish loading
402//---------------------------------------------------------------------------
403bool wxQTMediaBackend::Load(const wxString& fileName)
404{
405 if (m_movie)
406 Cleanup();
407
408 ::ClearMoviesStickyError(); // clear previous errors so
409 // GetMoviesStickyError is useful
410
411 OSErr err = noErr;
412 short movieResFile;
413 FSSpec sfFile;
414
415 wxMacFilename2FSSpec( fileName, &sfFile );
416 if (OpenMovieFile( &sfFile, &movieResFile, fsRdPerm ) != noErr)
417 return false;
418
419 short movieResID = 0;
420 Str255 movieName;
421
422 err = NewMovieFromFile(
423 &m_movie,
424 movieResFile,
425 &movieResID,
426 movieName,
427 newMovieActive,
428 NULL); // wasChanged
429
430 //
431 // check GetMoviesStickyError() because it may not find the
432 // proper codec and play black video and other strange effects,
433 // not to mention mess up the dynamic backend loading scheme
434 // of wxMediaCtrl - so it just does what the QuickTime player does
435 //
436 if (err == noErr && ::GetMoviesStickyError() == noErr)
437 {
438 ::CloseMovieFile(movieResFile);
439
440 // Create movie controller/control
441 DoNewMovieController();
442
443 FinishLoad();
444 return true;
445 }
446
447 return false;
448}
449
450//---------------------------------------------------------------------------
451// wxQTMediaBackend::Load (URL Version)
452//
453// 1) Build an escaped URI from location
454// 2) Create a handle to store the URI string
455// 3) Put the URI string inside the handle
456// 4) Make a QuickTime URL data ref from the handle with the URI in it
457// 5) Clean up the URI string handle
458// 6) Do some prerolling
459// 7) Finish Loading
460//---------------------------------------------------------------------------
461bool wxQTMediaBackend::Load(const wxURI& location)
462{
463 if (m_movie)
464 Cleanup();
465
466 ::ClearMoviesStickyError(); // clear previous errors so
467 // GetMoviesStickyError is useful
468
469 wxString theURI = location.BuildURI();
470 OSErr err;
471
472 size_t len;
473 const char* theURIString;
474
475#if wxUSE_UNICODE
476 wxCharBuffer buf = wxConvLocal.cWC2MB(theURI, theURI.length(), &len);
477 theURIString = buf;
478#else
479 theURIString = theURI;
480 len = theURI.length();
481#endif
482
483 Handle theHandle = ::NewHandleClear(len + 1);
484 wxASSERT(theHandle);
485
486 ::BlockMoveData(theURIString, *theHandle, len + 1);
487
488 // create the movie from the handle that refers to the URI
489 err = ::NewMovieFromDataRef(
490 &m_movie,
491 newMovieActive | newMovieAsyncOK /* | newMovieIdleImportOK*/,
492 NULL, theHandle,
493 URLDataHandlerSubType);
494
495 ::DisposeHandle(theHandle);
496
497 if (err == noErr && ::GetMoviesStickyError() == noErr)
498 {
499 // Movie controller resets prerolling, so we must create first
500 DoNewMovieController();
501
502 long timeNow;
503 Fixed playRate;
504
505 timeNow = ::GetMovieTime(m_movie, NULL);
506 wxASSERT(::GetMoviesError() == noErr);
507
508 playRate = ::GetMoviePreferredRate(m_movie);
509 wxASSERT(::GetMoviesError() == noErr);
510
511 //
512 // Note that the callback here is optional,
513 // but without it PrePrerollMovie can be buggy
514 // (see Apple ml). Also, some may wonder
515 // why we need this at all - this is because
516 // Apple docs say QuickTime streamed movies
517 // require it if you don't use a Movie Controller,
518 // which we don't by default.
519 //
520 m_preprerollupp =
521 NewMoviePrePrerollCompleteUPP( wxQTMediaBackend::PPRMProc );
522 ::PrePrerollMovie( m_movie, timeNow, playRate,
523 m_preprerollupp, (void*)this);
524
525 return true;
526 }
527
528 return false;
529}
530
531//---------------------------------------------------------------------------
532// wxQTMediaBackend::DoNewMovieController
533//
534// Attaches movie to moviecontroller or creates moviecontroller
535// if not created yet
536//---------------------------------------------------------------------------
537void wxQTMediaBackend::DoNewMovieController()
538{
539 if (!m_mc)
540 {
541 // Get top level window ref for some mac functions
542 WindowRef wrTLW = (WindowRef) m_ctrl->MacGetTopLevelWindowRef();
543
544 // MovieController not set up yet, so we need to create a new one.
545 // You have to pass a valid movie to NewMovieController, evidently
546 ::SetMovieGWorld(m_movie,
547 (CGrafPtr) GetWindowPort(wrTLW),
548 NULL);
549 wxASSERT(::GetMoviesError() == noErr);
550
551 Rect bounds = wxMacGetBoundsForControl(
552 m_ctrl,
553 m_ctrl->GetPosition(),
554 m_ctrl->GetSize());
555
556 m_mc = ::NewMovieController(
557 m_movie, &bounds,
558 mcTopLeftMovie | mcNotVisible /* | mcWithFrame */ );
559 wxASSERT(::GetMoviesError() == noErr);
560
561 ::MCDoAction(m_mc, 32, (void*)true); // mcActionSetKeysEnabled
562 wxASSERT(::GetMoviesError() == noErr);
563
564 // Setup a callback so we can tell when the user presses
565 // play on the player controls
566 m_mcactionupp =
567 NewMCActionFilterWithRefConUPP( wxQTMediaBackend::MCFilterProc );
568 ::MCSetActionFilterWithRefCon( m_mc, m_mcactionupp, (long)this );
569 wxASSERT(::GetMoviesError() == noErr);
570
571 // Part of a suggestion from Greg Hazel to repaint movie when idle
572 m_ctrl->PushEventHandler(new wxQTMediaEvtHandler(this));
573
574 // Create offscreen GWorld for where to "show" when window is hidden
575 Rect worldRect;
576 worldRect.left = worldRect.top = 0;
577 worldRect.right = worldRect.bottom = 1;
578 ::NewGWorld(&m_movieWorld, 0, &worldRect, NULL, NULL, 0);
579
580 // Catch window messages:
581 // if we do not do this and if the user clicks the play
582 // button on the controller, for instance, nothing will happen...
583 EventTypeSpec theWindowEventTypes[] =
584 {
585 { kEventClassMouse, kEventMouseDown },
586 { kEventClassMouse, kEventMouseUp },
587 { kEventClassMouse, kEventMouseDragged },
588 { kEventClassKeyboard, kEventRawKeyDown },
589 { kEventClassKeyboard, kEventRawKeyRepeat },
590 { kEventClassKeyboard, kEventRawKeyUp },
591 { kEventClassWindow, kEventWindowUpdate },
592 { kEventClassWindow, kEventWindowActivated },
593 { kEventClassWindow, kEventWindowDeactivated }
594 };
595 m_windowUPP =
596 NewEventHandlerUPP( wxQTMediaBackend::WindowEventHandler );
597 InstallWindowEventHandler(
598 wrTLW,
599 m_windowUPP,
600 GetEventTypeCount( theWindowEventTypes ), theWindowEventTypes,
601 this,
602 &m_windowEventHandler );
603 }
604 else
605 {
606 // MovieController already created:
607 // Just change the movie in it and we're good to go
608 Point thePoint;
609 thePoint.h = thePoint.v = 0;
610 ::MCSetMovie(m_mc, m_movie,
611 (WindowRef)m_ctrl->MacGetTopLevelWindowRef(),
612 thePoint);
613 wxASSERT(::GetMoviesError() == noErr);
614 }
615}
616
617//---------------------------------------------------------------------------
618// wxQTMediaBackend::FinishLoad
619//
620// Performs operations after a movie ready to play/loaded.
621//---------------------------------------------------------------------------
622void wxQTMediaBackend::FinishLoad()
623{
624 // Dispose of the PrePrerollMovieUPP if we used it
625 DisposeMoviePrePrerollCompleteUPP(m_preprerollupp);
626
627 // get the real size of the movie
628 DoLoadBestSize();
629
630 // show the player controls if the user wants to
631 if (m_interfaceflags)
632 DoSetControllerVisible(m_interfaceflags);
633
634 // we want millisecond precision
635 ::SetMovieTimeScale(m_movie, 1000);
636 wxASSERT(::GetMoviesError() == noErr);
637
638 // start movie progress timer
639 m_timer = new wxQTMediaPlayTimer(this);
640 wxASSERT(m_timer);
641 m_timer->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS);
642
643 // send loaded event and refresh size
644 NotifyMovieLoaded();
645}
646
647//---------------------------------------------------------------------------
648// wxQTMediaBackend::DoLoadBestSize
649//
650// Sets the best size of the control from the real size of the movie
651//---------------------------------------------------------------------------
652void wxQTMediaBackend::DoLoadBestSize()
653{
654 // get the real size of the movie
655 Rect outRect;
656 ::GetMovieNaturalBoundsRect(m_movie, &outRect);
657 wxASSERT(::GetMoviesError() == noErr);
658
659 // determine best size
660 m_bestSize.x = outRect.right - outRect.left;
661 m_bestSize.y = outRect.bottom - outRect.top;
662}
663
664//---------------------------------------------------------------------------
665// wxQTMediaBackend::Play
666//
667// Start the QT movie
668// (Apple recommends mcActionPrerollAndPlay but that's QT 4.1+)
669//---------------------------------------------------------------------------
670bool wxQTMediaBackend::Play()
671{
672 Fixed fixRate = (Fixed) (wxQTMediaBackend::GetPlaybackRate() * 0x10000);
673 if (!fixRate)
674 fixRate = ::GetMoviePreferredRate(m_movie);
675
676 wxASSERT(fixRate != 0);
677
678 if (!m_bPlaying)
679 ::MCDoAction( m_mc, 8 /* mcActionPlay */, (void*) fixRate);
680
681 bool result = (::GetMoviesError() == noErr);
682 if (result)
683 {
684 m_bPlaying = true;
685 QueuePlayEvent();
686 }
687
688 return result;
689}
690
691//---------------------------------------------------------------------------
692// wxQTMediaBackend::Pause
693//
694// Stop the movie
695//---------------------------------------------------------------------------
696bool wxQTMediaBackend::DoPause()
697{
698 // Stop the movie A.K.A. ::StopMovie(m_movie);
699 if (m_bPlaying)
700 {
701 ::MCDoAction( m_mc, 8 /*mcActionPlay*/, (void *) 0);
702 m_bPlaying = false;
703 return ::GetMoviesError() == noErr;
704 }
705
706 // already paused
707 return true;
708}
709
710bool wxQTMediaBackend::Pause()
711{
712 bool bSuccess = DoPause();
713 if (bSuccess)
714 this->QueuePauseEvent();
715
716 return bSuccess;
717}
718
719//---------------------------------------------------------------------------
720// wxQTMediaBackend::Stop
721//
722// 1) Stop the movie
723// 2) Seek to the beginning of the movie
724//---------------------------------------------------------------------------
725bool wxQTMediaBackend::DoStop()
726{
727 if (!wxQTMediaBackend::DoPause())
728 return false;
729
730 ::GoToBeginningOfMovie(m_movie);
731 return ::GetMoviesError() == noErr;
732}
733
734bool wxQTMediaBackend::Stop()
735{
736 bool bSuccess = DoStop();
737 if (bSuccess)
738 QueueStopEvent();
739
740 return bSuccess;
741}
742
743//---------------------------------------------------------------------------
744// wxQTMediaBackend::GetPlaybackRate
745//
746// 1) Get the movie playback rate from ::GetMovieRate
747//---------------------------------------------------------------------------
748double wxQTMediaBackend::GetPlaybackRate()
749{
750 return ( ((double)::GetMovieRate(m_movie)) / 0x10000);
751}
752
753//---------------------------------------------------------------------------
754// wxQTMediaBackend::SetPlaybackRate
755//
756// 1) Convert dRate to Fixed and Set the movie rate through SetMovieRate
757//---------------------------------------------------------------------------
758bool wxQTMediaBackend::SetPlaybackRate(double dRate)
759{
760 ::SetMovieRate(m_movie, (Fixed) (dRate * 0x10000));
761 return ::GetMoviesError() == noErr;
762}
763
764//---------------------------------------------------------------------------
765// wxQTMediaBackend::SetPosition
766//
767// 1) Create a time record struct (TimeRecord) with appropriate values
768// 2) Pass struct to SetMovieTime
769//---------------------------------------------------------------------------
770bool wxQTMediaBackend::SetPosition(wxLongLong where)
771{
772 TimeRecord theTimeRecord;
773 memset(&theTimeRecord, 0, sizeof(TimeRecord));
774 theTimeRecord.value.lo = where.GetValue();
775 theTimeRecord.scale = ::GetMovieTimeScale(m_movie);
776 theTimeRecord.base = ::GetMovieTimeBase(m_movie);
777 ::SetMovieTime(m_movie, &theTimeRecord);
778
779 if (::GetMoviesError() != noErr)
780 return false;
781
782 return true;
783}
784
785//---------------------------------------------------------------------------
786// wxQTMediaBackend::GetPosition
787//
788// Calls GetMovieTime
789//---------------------------------------------------------------------------
790wxLongLong wxQTMediaBackend::GetPosition()
791{
792 return ::GetMovieTime(m_movie, NULL);
793}
794
795//---------------------------------------------------------------------------
796// wxQTMediaBackend::GetVolume
797//
798// Gets the volume through GetMovieVolume - which returns a 16 bit short -
799//
800// +--------+--------+
801// + (1) + (2) +
802// +--------+--------+
803//
804// (1) first 8 bits are value before decimal
805// (2) second 8 bits are value after decimal
806//
807// Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to
808// 1 (full gain and sound)
809//---------------------------------------------------------------------------
810double wxQTMediaBackend::GetVolume()
811{
812 short sVolume = ::GetMovieVolume(m_movie);
813
814 if (sVolume & (128 << 8)) //negative - no sound
815 return 0.0;
816
817 return sVolume / 256.0;
818}
819
820//---------------------------------------------------------------------------
821// wxQTMediaBackend::SetVolume
822//
823// Sets the volume through SetMovieVolume - which takes a 16 bit short -
824//
825// +--------+--------+
826// + (1) + (2) +
827// +--------+--------+
828//
829// (1) first 8 bits are value before decimal
830// (2) second 8 bits are value after decimal
831//
832// Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to
833// 1 (full gain and sound)
834//---------------------------------------------------------------------------
835bool wxQTMediaBackend::SetVolume(double dVolume)
836{
837 ::SetMovieVolume(m_movie, (short) (dVolume * 256));
838 return true;
839}
840
841//---------------------------------------------------------------------------
842// wxQTMediaBackend::GetDuration
843//
844// Calls GetMovieDuration
845//---------------------------------------------------------------------------
846wxLongLong wxQTMediaBackend::GetDuration()
847{
848 return ::GetMovieDuration(m_movie);
849}
850
851//---------------------------------------------------------------------------
852// wxQTMediaBackend::GetState
853//
854// Determines the current state - the timer keeps track of whether or not
855// we are paused or stopped (if the timer is running we are playing)
856//---------------------------------------------------------------------------
857wxMediaState wxQTMediaBackend::GetState()
858{
859 // Could use
860 // GetMovieActive/IsMovieDone/SetMovieActive
861 // combo if implemented that way
862 if (m_bPlaying)
863 return wxMEDIASTATE_PLAYING;
864 else if (!m_movie || wxQTMediaBackend::GetPosition() == 0)
865 return wxMEDIASTATE_STOPPED;
866 else
867 return wxMEDIASTATE_PAUSED;
868}
869
870//---------------------------------------------------------------------------
871// wxQTMediaBackend::Cleanup
872//
873// Diposes of the movie timer, Control if native, and stops and disposes
874// of the QT movie
875//---------------------------------------------------------------------------
876void wxQTMediaBackend::Cleanup()
877{
878 m_bPlaying = false;
879 if (m_timer)
880 {
881 delete m_timer;
882 m_timer = NULL;
883 }
884
885 // Stop the movie:
886 // Apple samples with CreateMovieControl typically
887 // install a event handler and do this on the dispose
888 // event, but we do it here for simplicity
889 // (It might keep playing for several seconds after
890 // control destruction if not)
891 wxQTMediaBackend::Pause();
892
893 // Dispose of control or remove movie from MovieController
894 Point thePoint;
895 thePoint.h = thePoint.v = 0;
896 ::MCSetVisible(m_mc, false);
897 ::MCSetMovie(m_mc, NULL, NULL, thePoint);
898
899 ::DisposeMovie(m_movie);
900 m_movie = NULL;
901}
902
903//---------------------------------------------------------------------------
904// wxQTMediaBackend::GetVideoSize
905//
906// Returns the actual size of the QT movie
907//---------------------------------------------------------------------------
908wxSize wxQTMediaBackend::GetVideoSize() const
909{
910 return m_bestSize;
911}
912
913//---------------------------------------------------------------------------
914// wxQTMediaBackend::Move
915//
916// Move the movie controller or movie control
917// (we need to actually move the movie control manually...)
918// Top 10 things to do with quicktime in March 93's issue
919// of DEVELOP - very useful
920// http:// www.mactech.com/articles/develop/issue_13/031-033_QuickTime_column.html
921// OLD NOTE: Calling MCSetControllerBoundsRect without detaching
922// supposively resulted in a crash back then. Current code even
923// with CFM classic runs fine. If there is ever a problem,
924// take out the if 0 lines below
925//---------------------------------------------------------------------------
926void wxQTMediaBackend::Move(int x, int y, int w, int h)
927{
928 if (m_timer)
929 {
930 m_ctrl->GetParent()->MacWindowToRootWindow(&x, &y);
931 Rect theRect = {y, x, y + h, x + w};
932
933#if 0 // see note above
934 ::MCSetControllerAttached(m_mc, false);
935 wxASSERT(::GetMoviesError() == noErr);
936#endif
937
938 ::MCSetControllerBoundsRect(m_mc, &theRect);
939 wxASSERT(::GetMoviesError() == noErr);
940
941#if 0 // see note above
942 if (m_interfaceflags)
943 {
944 ::MCSetVisible(m_mc, true);
945 wxASSERT(::GetMoviesError() == noErr);
946 }
947#endif
948 }
949}
950
951//---------------------------------------------------------------------------
952// wxQTMediaBackend::DoSetControllerVisible
953//
954// Utility function that takes care of showing the moviecontroller
955// and showing/hiding the particular controls on it
956//---------------------------------------------------------------------------
957void wxQTMediaBackend::DoSetControllerVisible(
958 wxMediaCtrlPlayerControls flags)
959{
960 ::MCSetVisible(m_mc, true);
961
962 // Take care of subcontrols
963 if (::GetMoviesError() == noErr)
964 {
965 long mcFlags = 0;
966 ::MCDoAction(m_mc, 39/*mcActionGetFlags*/, (void*)&mcFlags);
967
968 if (::GetMoviesError() == noErr)
969 {
970 mcFlags |= ( //(1<<0)/*mcFlagSuppressMovieFrame*/ |
971 (1 << 3)/*mcFlagsUseWindowPalette*/
972 | ((flags & wxMEDIACTRLPLAYERCONTROLS_STEP)
973 ? 0 : (1 << 1)/*mcFlagSuppressStepButtons*/)
974 | ((flags & wxMEDIACTRLPLAYERCONTROLS_VOLUME)
975 ? 0 : (1 << 2)/*mcFlagSuppressSpeakerButton*/)
976 //if we take care of repainting ourselves
977 // | (1 << 4) /*mcFlagDontInvalidate*/
978 );
979
980 ::MCDoAction(m_mc, 38/*mcActionSetFlags*/, (void*)mcFlags);
981 }
982 }
983
984 // Adjust height and width of best size for movie controller
985 // if the user wants it shown
986 m_bestSize.x = m_bestSize.x > wxMCWIDTH ? m_bestSize.x : wxMCWIDTH;
987 m_bestSize.y += wxMCHEIGHT;
988}
989
990//---------------------------------------------------------------------------
991// wxQTMediaBackend::ShowPlayerControls
992//
993// Shows/Hides subcontrols on the media control
994//---------------------------------------------------------------------------
995bool wxQTMediaBackend::ShowPlayerControls(wxMediaCtrlPlayerControls flags)
996{
997 if (!m_mc)
998 return false; // no movie controller...
999
1000 bool bSizeChanged = false;
1001
1002 // if the controller is visible and we want to hide it do so
1003 if (m_interfaceflags && !flags)
1004 {
1005 bSizeChanged = true;
1006 DoLoadBestSize();
1007 ::MCSetVisible(m_mc, false);
1008 }
1009 else if (!m_interfaceflags && flags) // show controller if hidden
1010 {
1011 bSizeChanged = true;
1012 DoSetControllerVisible(flags);
1013 }
1014
1015 // readjust parent sizers
1016 if (bSizeChanged)
1017 {
1018 NotifyMovieSizeChanged();
1019
1020 // remember state in case of loading new media
1021 m_interfaceflags = flags;
1022 }
1023
1024 return ::GetMoviesError() == noErr;
1025}
1026
1027//---------------------------------------------------------------------------
1028// wxQTMediaBackend::GetDataSizeFromStart
1029//
1030// Calls either GetMovieDataSize or GetMovieDataSize64 with a value
1031// of 0 for the starting value
1032//---------------------------------------------------------------------------
1033wxLongLong wxQTMediaBackend::GetDataSizeFromStart(TimeValue end)
1034{
1035#if 0 // old pre-qt4 way
1036 return ::GetMovieDataSize(m_movie, 0, end)
1037#else // qt4 way
1038 wide llDataSize;
1039 ::GetMovieDataSize64(m_movie, 0, end, &llDataSize);
1040 return wxLongLong(llDataSize.hi, llDataSize.lo);
1041#endif
1042}
1043
1044//---------------------------------------------------------------------------
1045// wxQTMediaBackend::GetDownloadProgress
1046//---------------------------------------------------------------------------
1047wxLongLong wxQTMediaBackend::GetDownloadProgress()
1048{
1049#if 0 // hackish and slow
1050 Handle hMovie = NewHandle(0);
1051 PutMovieIntoHandle(m_movie, hMovie);
1052 long lSize = GetHandleSize(hMovie);
1053 DisposeHandle(hMovie);
1054
1055 return lSize;
1056#else
1057 TimeValue tv;
1058 if (::GetMaxLoadedTimeInMovie(m_movie, &tv) != noErr)
1059 {
1060 wxLogDebug(wxT("GetMaxLoadedTimeInMovie failed"));
1061 return 0;
1062 }
1063
1064 return wxQTMediaBackend::GetDataSizeFromStart(tv);
1065#endif
1066}
1067
1068//---------------------------------------------------------------------------
1069// wxQTMediaBackend::GetDownloadTotal
1070//---------------------------------------------------------------------------
1071wxLongLong wxQTMediaBackend::GetDownloadTotal()
1072{
1073 return wxQTMediaBackend::GetDataSizeFromStart(
1074 ::GetMovieDuration(m_movie)
1075 );
1076}
1077
1078//---------------------------------------------------------------------------
1079// wxQTMediaBackend::MacVisibilityChanged
1080//
1081// The main problem here is that Windows quicktime, for example,
1082// renders more directly to a HWND. Mac quicktime does not do this
1083// and instead renders to the port of the WindowRef/WindowPtr on top
1084// of everything else/all other windows.
1085//
1086// So, for example, if you were to have a CreateTabsControl/wxNotebook
1087// and change pages, even if you called HIViewSetVisible/SetControlVisibility
1088// directly the movie will still continue playing on top of everything else
1089// if you went to a different tab.
1090//
1091// Note that another issue, and why we call MCSetControllerPort instead
1092// of SetMovieGWorld directly, is that in addition to rendering on
1093// top of everything else the last created controller steals mouse and
1094// other input from everything else in the window, including other
1095// controllers. Setting the port of it releases this behaviour.
1096//---------------------------------------------------------------------------
1097void wxQTMediaBackend::MacVisibilityChanged()
1098{
1099 if(!m_mc || !m_ctrl->m_bLoaded)
1100 return; //not initialized yet
1101
1102 if(m_ctrl->MacIsReallyShown())
1103 {
1104 //The window is being shown again, so set the GWorld of the
1105 //controller back to the port of the parent WindowRef
1106 WindowRef wrTLW =
1107 (WindowRef) m_ctrl->MacGetTopLevelWindowRef();
1108
1109 ::MCSetControllerPort(m_mc, (CGrafPtr) GetWindowPort(wrTLW));
1110 wxASSERT(::GetMoviesError() == noErr);
1111 }
1112 else
1113 {
1114 //We are being hidden - set the GWorld of the controller
1115 //to the offscreen GWorld
1116 ::MCSetControllerPort(m_mc, m_movieWorld);
1117 wxASSERT(::GetMoviesError() == noErr);
1118 }
1119}
1120
1121//---------------------------------------------------------------------------
1122// wxQTMediaBackend::OnEraseBackground
1123//
1124// Suggestion from Greg Hazel to repaint the movie when idle
1125// (on pause also)
1126//---------------------------------------------------------------------------
1127void wxQTMediaEvtHandler::OnEraseBackground(wxEraseEvent& evt)
1128{
1129 // Work around Nasty OSX drawing bug:
1130 // http://lists.apple.com/archives/QuickTime-API/2002/Feb/msg00311.html
1131 WindowRef wrTLW = (WindowRef) m_qtb->m_ctrl->MacGetTopLevelWindowRef();
1132
1133 RgnHandle region = ::MCGetControllerBoundsRgn(m_qtb->m_mc);
1134 ::MCInvalidate(m_qtb->m_mc, wrTLW, region);
1135 ::MCIdle(m_qtb->m_mc);
1136}
1137
1138//---------------------------------------------------------------------------
1139// wxQTMediaBackend::PPRMProc (static)
1140//
1141// Called when done PrePrerolling the movie.
1142// Note that in 99% of the cases this does nothing...
1143// Anyway we set up the loading timer here to tell us when the movie is done
1144//---------------------------------------------------------------------------
1145pascal void wxQTMediaBackend::PPRMProc(
1146 Movie theMovie,
1147 OSErr WXUNUSED_UNLESS_DEBUG(theErr),
1148 void* theRefCon)
1149{
1150 wxASSERT( theMovie );
1151 wxASSERT( theRefCon );
1152 wxASSERT( theErr == noErr );
1153
1154 wxQTMediaBackend* pBE = (wxQTMediaBackend*) theRefCon;
1155
1156 long lTime = ::GetMovieTime(theMovie,NULL);
1157 Fixed rate = ::GetMoviePreferredRate(theMovie);
1158 ::PrerollMovie(theMovie,lTime,rate);
1159 pBE->m_timer = new wxQTMediaLoadTimer(pBE);
1160 pBE->m_timer->Start(MOVIE_DELAY);
1161}
1162
1163//---------------------------------------------------------------------------
1164// wxQTMediaBackend::MCFilterProc (static)
1165//
1166// Callback for when the movie controller recieves a message
1167//---------------------------------------------------------------------------
1168pascal Boolean wxQTMediaBackend::MCFilterProc(
1169 MovieController WXUNUSED(theController),
1170 short action,
1171 void * WXUNUSED(params),
1172 long refCon)
1173{
1174 wxQTMediaBackend* pThis = (wxQTMediaBackend*)refCon;
1175
1176 switch (action)
1177 {
1178 case 1:
1179 // don't process idle events
1180 break;
1181
1182 case 8:
1183 // play button triggered - MC will set movie to opposite state
1184 // of current - playing ? paused : playing
1185 pThis->m_bPlaying = !(pThis->m_bPlaying);
1186 break;
1187
1188 default:
1189 break;
1190 }
1191
1192 return 0;
1193}
1194
1195//---------------------------------------------------------------------------
1196// wxQTMediaBackend::WindowEventHandler [static]
1197//
1198// Event callback for the top level window of our control that passes
1199// messages to our moviecontroller so it can receive mouse clicks etc.
1200//---------------------------------------------------------------------------
1201pascal OSStatus wxQTMediaBackend::WindowEventHandler(
1202 EventHandlerCallRef inHandlerCallRef,
1203 EventRef inEvent,
1204 void *inUserData)
1205{
1206 wxQTMediaBackend* be = (wxQTMediaBackend*) inUserData;
1207
1208 // Only process keyboard messages on this window if it actually
1209 // has focus, otherwise it will steal keystrokes from other windows!
1210 // As well as when it is not loaded properly as it
1211 // will crash in MCIsPlayerEvent
1212 if((GetEventClass(inEvent) == kEventClassKeyboard &&
1213 wxWindow::FindFocus() != be->m_ctrl)
1214 || !be->m_ctrl->m_bLoaded)
1215 return eventNotHandledErr;
1216
1217 // Pass the event onto the movie controller
1218 EventRecord theEvent;
1219 ConvertEventRefToEventRecord( inEvent, &theEvent );
1220 OSStatus err;
1221
1222 // TODO: Apple says MCIsPlayerEvent is depreciated and
1223 // MCClick, MCKey, MCIdle etc. should be used
1224 // (RN: Of course that's what they say about
1225 // CreateMovieControl and HIMovieView as well, LOL!)
1226 err = ::MCIsPlayerEvent( be->m_mc, &theEvent );
1227
1228 // Pass on to other event handlers if not handled- i.e. wx
1229 if (err != noErr)
1230 return noErr;
1231 else
1232 return eventNotHandledErr;
1233}
1234
1235// in source file that contains stuff you don't directly use
1236#include "wx/html/forcelnk.h"
1237FORCE_LINK_ME(basewxmediabackends)
1238
1239#endif // wxUSE_MEDIACTRL