]> git.saurik.com Git - wxWidgets.git/blame - src/mac/carbon/mediactrl.cpp
just added a comment
[wxWidgets.git] / src / mac / carbon / mediactrl.cpp
CommitLineData
1a680109 1/////////////////////////////////////////////////////////////////////////////
ff4aedc5
RN
2// Name: mac/carbon/mediactrl.cpp
3// Purpose: Built-in Media Backends for Mac
1a680109 4// Author: Ryan Norton <wxprojects@comcast.net>
ff4aedc5 5// Modified by:
1a680109
RN
6// Created: 11/07/04
7// RCS-ID: $Id$
bf354396 8// Copyright: (c) 2004-2005 Ryan Norton
1a680109
RN
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
bf354396
VZ
12//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13// There are several known bugs with CreateMovieControl
14// on systems > 10.2 - see main.c of QTCarbonShell sample for details
15//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16
ff4aedc5
RN
17//===========================================================================
18// DECLARATIONS
19//===========================================================================
20
21//---------------------------------------------------------------------------
22// Pre-compiled header stuff
23//---------------------------------------------------------------------------
24
1a680109
RN
25// For compilers that support precompilation, includes "wx.h".
26#include "wx/wxprec.h"
27
28#ifdef __BORLANDC__
29#pragma hdrstop
30#endif
31
ff4aedc5
RN
32//---------------------------------------------------------------------------
33// Includes
34//---------------------------------------------------------------------------
35#include "wx/mediactrl.h"
1a680109 36
ff4aedc5
RN
37//---------------------------------------------------------------------------
38// Compilation guard
39//---------------------------------------------------------------------------
1a680109
RN
40#if wxUSE_MEDIACTRL
41
3b5023b9
RN
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
bf354396
VZ
47# if defined( __WXMAC_OSX__ ) && \
48 ( MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_2 )
d0ee33f5
WS
49# define wxUSE_CREATEMOVIECONTROL 1
50# else
51# define wxUSE_CREATEMOVIECONTROL 0
52# endif
3b5023b9
RN
53#endif
54
bf354396
VZ
55//---------------------------------------------------------------------------
56// Height and Width of movie controller in the movie control
57//---------------------------------------------------------------------------
58#define wxMCWIDTH 320
59#define wxMCHEIGHT 16
60
ff4aedc5
RN
61//===========================================================================
62// BACKEND DECLARATIONS
63//===========================================================================
212945de 64
ff4aedc5
RN
65//---------------------------------------------------------------------------
66//
67// wxQTMediaBackend
68//
69//---------------------------------------------------------------------------
1a680109 70
ff4aedc5
RN
71//---------------------------------------------------------------------------
72// QT Includes
73//---------------------------------------------------------------------------
1a680109 74//uma is for wxMacFSSpec
1a680109 75#include "wx/mac/uma.h"
ff4aedc5 76#include "wx/timer.h"
768c6e8b 77#ifndef __DARWIN__
1a680109
RN
78#include <Movies.h>
79#include <Gestalt.h>
ff4aedc5 80#include <QuickTimeComponents.h> //Standard QT stuff
768c6e8b
SC
81#else
82#include <QuickTime/QuickTimeComponents.h>
83#endif
1a680109 84
bf354396 85class WXDLLIMPEXP_MEDIA wxQTMediaBackend : public wxMediaBackendCommonBase
ff4aedc5
RN
86{
87public:
ff4aedc5
RN
88 wxQTMediaBackend();
89 ~wxQTMediaBackend();
90
d0ee33f5 91 virtual bool CreateControl(wxControl* ctrl, wxWindow* parent,
ff4aedc5 92 wxWindowID id,
d0ee33f5 93 const wxPoint& pos,
ff4aedc5 94 const wxSize& size,
d0ee33f5 95 long style,
ff4aedc5
RN
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
c5191fbd
VZ
118 virtual double GetVolume();
119 virtual bool SetVolume(double);
120
ff4aedc5
RN
121 void Cleanup();
122 void FinishLoad();
123
bf354396
VZ
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
20b69855 146#ifdef __WXMAC_OSX__
bf354396 147 struct MovieType** m_movie; // QT Movie handle/instance
20b69855 148#else
bf354396 149 Movie m_movie; // Movie instance
20b69855 150#endif
bf354396
VZ
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
ff4aedc5 157
bf354396
VZ
158 friend class wxQTMediaEvtHandler;
159#endif
d0ee33f5 160 DECLARE_DYNAMIC_CLASS(wxQTMediaBackend)
ff4aedc5
RN
161};
162
bf354396
VZ
163#if !wxUSE_CREATEMOVIECONTROL
164// helper to hijack background erasing for the QT window
165class WXDLLIMPEXP_MEDIA wxQTMediaEvtHandler : public wxEvtHandler
166{
167public:
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
179private:
180 wxQTMediaBackend *m_qtb;
181
182 DECLARE_NO_COPY_CLASS(wxQTMediaEvtHandler)
183};
184
185// Window event handler
186static pascal OSStatus wxQTMediaWindowEventHandler(
187 EventHandlerCallRef inHandlerCallRef,
188 EventRef inEvent, void *inUserData);
189DEFINE_ONE_SHOT_HANDLER_GETTER( wxQTMediaWindowEventHandler );
190
191#endif
192
193//===========================================================================
194// IMPLEMENTATION
195//===========================================================================
196
ff4aedc5
RN
197
198//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
199//
200// wxQTMediaBackend
d0ee33f5 201//
ff4aedc5
RN
202//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
203
412e0d47 204IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend, wxMediaBackend)
1a680109 205
bf354396
VZ
206//Time between timer calls - this is the Apple recommondation to the TCL
207//team I believe
208#define MOVIE_DELAY 20
1a680109 209
bf354396
VZ
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//---------------------------------------------------------------------------
219class wxQTMediaLoadTimer : public wxTimer
1a680109
RN
220{
221public:
bf354396
VZ
222 wxQTMediaLoadTimer(Movie movie, wxQTMediaBackend* parent) :
223 m_movie(movie), m_parent(parent) {}
1a680109 224
bf354396
VZ
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)
1a680109 236 {
bf354396
VZ
237 m_parent->FinishLoad();
238 delete this;
239 }
1a680109
RN
240 }
241
bf354396
VZ
242protected:
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// --------------------------------------------------------------------------
255class wxQTMediaPlayTimer : public wxTimer
256{
257public:
258 wxQTMediaPlayTimer(Movie movie, wxQTMediaBackend* parent) :
259 m_movie(movie), m_parent(parent) {}
1a680109
RN
260
261 void Notify()
1a680109 262 {
bf354396
VZ
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
ff4aedc5 279
bf354396
VZ
280 //
281 // Handle the stop event - if the movie has reached
282 // the end, notify our handler
283 //
284 if(::IsMovieDone(m_movie))
ff4aedc5 285 {
bf354396
VZ
286 if ( m_parent->SendStopEvent() )
287 {
ff4aedc5
RN
288 m_parent->Stop();
289 wxASSERT(::GetMoviesError() == noErr);
290
bf354396 291 m_parent->QueueFinishEvent();
1a680109
RN
292 }
293 }
294 }
295
296protected:
ff4aedc5 297 Movie m_movie; //Our movie instance
ff4aedc5 298 wxQTMediaBackend* m_parent; //Backend pointer
1a680109
RN
299};
300
bf354396 301
ff4aedc5 302//---------------------------------------------------------------------------
e7b97da3 303// wxQTMediaBackend Constructor
ff4aedc5
RN
304//
305// Sets m_timer to NULL signifying we havn't loaded anything yet
306//---------------------------------------------------------------------------
bf354396
VZ
307wxQTMediaBackend::wxQTMediaBackend()
308 : m_movie(NULL), m_bPlaying(false), m_timer(NULL)
309 , m_mc(NULL), m_interfaceflags(wxMEDIACTRLPLAYERCONTROLS_NONE)
1a680109 310{
1a680109
RN
311}
312
ff4aedc5
RN
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//---------------------------------------------------------------------------
322wxQTMediaBackend::~wxQTMediaBackend()
1a680109 323{
bf354396 324 if(m_movie)
ff4aedc5 325 Cleanup();
1a680109 326
bf354396
VZ
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
3103e8a9 338 //Note that ExitMovies() is not necessary...
ff4aedc5 339 ExitMovies();
1a680109
RN
340}
341
ff4aedc5
RN
342//---------------------------------------------------------------------------
343// wxQTMediaBackend::CreateControl
344//
345// 1) Intializes QuickTime
346// 2) Creates the control window
347//---------------------------------------------------------------------------
d0ee33f5 348bool wxQTMediaBackend::CreateControl(wxControl* ctrl, wxWindow* parent,
ff4aedc5 349 wxWindowID id,
d0ee33f5 350 const wxPoint& pos,
ff4aedc5 351 const wxSize& size,
d0ee33f5 352 long style,
ff4aedc5
RN
353 const wxValidator& validator,
354 const wxString& name)
1a680109 355{
3b5023b9 356 //Don't bother in Native control mode
bf354396
VZ
357#if !wxUSE_CREATEMOVIECONTROL
358 if (!IsQuickTime4Installed())
1a680109 359 return false;
3b5023b9 360#endif
1a680109 361
ff4aedc5
RN
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 //
bf354396 372 if ( !ctrl->wxControl::Create(parent, id, pos, size,
3dee36ae 373 wxWindow::MacRemoveBordersFromStyle(style),
3dee36ae 374 validator, name)
3b5023b9 375 )
1a680109
RN
376 return false;
377
ecd20d4a 378#if wxUSE_VALIDATORS
3dee36ae 379 ctrl->SetValidator(validator);
ecd20d4a
RN
380#endif
381
bf354396 382 m_ctrl = (wxMediaCtrl*)ctrl;
1a680109
RN
383 return true;
384}
385
bf354396
VZ
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
393Boolean 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
ff4aedc5
RN
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
3b5023b9
RN
409// 4) Close the movie resource
410// 5) Finish loading
ff4aedc5
RN
411//---------------------------------------------------------------------------
412bool wxQTMediaBackend::Load(const wxString& fileName)
1a680109 413{
bf354396 414 if(m_movie)
1a680109
RN
415 Cleanup();
416
1a680109
RN
417 OSErr err = noErr;
418 short movieResFile;
419 FSSpec sfFile;
d0ee33f5
WS
420
421 //FIXME:wxMacFilename2FSSpec crashes on empty string -
099c3b27
RN
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;
d0ee33f5 426
ff4aedc5 427 wxMacFilename2FSSpec( fileName , &sfFile );
d0ee33f5 428
1a680109
RN
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
bf354396
VZ
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);
1a680109 448
bf354396
VZ
449 // Create movie controller/control
450#if wxUSE_CREATEMOVIECONTROL
451 DoCreateMovieControl();
452#else
453 DoNewMovieController();
454#endif
1a680109 455 FinishLoad();
bf354396
VZ
456 return true;
457 }
458 else
459 {
460 return false;
461 }
462}
1a680109 463
bf354396
VZ
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
472void 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);
1a680109 487}
bf354396 488#endif
1a680109 489
ff4aedc5 490//---------------------------------------------------------------------------
3b5023b9 491// wxQTMediaBackend::Load (URL Version)
ff4aedc5 492//
3b5023b9
RN
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
ff4aedc5
RN
500//---------------------------------------------------------------------------
501bool wxQTMediaBackend::Load(const wxURI& location)
1a680109 502{
bf354396 503 if(m_movie)
1a680109
RN
504 Cleanup();
505
1a680109
RN
506 wxString theURI = location.BuildURI();
507
508 OSErr err = noErr;
509
bf354396 510 Handle theHandle = ::NewHandleClear(theURI.length() + 1);
1a680109
RN
511 wxASSERT(theHandle);
512
bf354396 513 ::BlockMove(theURI.mb_str(), *theHandle, theURI.length() + 1);
1a680109
RN
514
515 //create the movie from the handle that refers to the URI
bf354396
VZ
516 err = ::NewMovieFromDataRef(&m_movie, newMovieActive |
517 newMovieAsyncOK
518 /*|newMovieIdleImportOK*/,
d0ee33f5 519 NULL, theHandle,
1a680109
RN
520 URLDataHandlerSubType);
521
bf354396 522 ::DisposeHandle(theHandle);
1a680109 523
bf354396
VZ
524 if (err == noErr)
525 {
526#if wxUSE_CREATEMOVIECONTROL
527 // Movie control resets prerolling, so we must create first
528 DoCreateMovieControl();
1a680109 529
bf354396
VZ
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;
1a680109 538 Fixed playRate;
1a680109 539
bf354396
VZ
540 timeNow = ::GetMovieTime(m_movie, NULL);
541 wxASSERT(::GetMoviesError() == noErr);
1a680109 542
bf354396
VZ
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;
1a680109
RN
563}
564
ff4aedc5 565//---------------------------------------------------------------------------
bf354396
VZ
566// wxQTMediaBackend::DoCreateMovieControl
567//
568// Calls CreateMovieControl and performs setup related to it
ff4aedc5 569//
bf354396
VZ
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
ff4aedc5 575//---------------------------------------------------------------------------
d0ee33f5 576#if wxUSE_CREATEMOVIECONTROL
bf354396
VZ
577void wxQTMediaBackend::DoCreateMovieControl()
578{
3dee36ae
WS
579 //
580 //Native CreateMovieControl QT control (Thanks to Kevin Olliver's
581 //wxQTMovie for some of this).
582 //
3dee36ae
WS
583 Rect bounds = wxMacGetBoundsForControl(m_ctrl,
584 m_ctrl->GetPosition(),
585 m_ctrl->GetSize());
586
587 //Dispose of old control for new one
bf354396
VZ
588 if (m_ctrl->m_peer && m_ctrl->m_peer->Ok() )
589 m_ctrl->m_peer->Dispose();
3dee36ae
WS
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
d0ee33f5 599 ::CreateMovieControl(
3b5023b9 600 (WindowRef)
bf354396 601 m_ctrl->MacGetTopLevelWindowRef(), //parent
d0ee33f5
WS
602 &bounds, //control bounds
603 m_movie, //movie handle
604 kMovieControlOptionHideController
605 | kMovieControlOptionLocateTopLeft
606 | kMovieControlOptionSetKeysEnabled
bf354396 607 // | kMovieControlOptionManuallyIdled
d0ee33f5 608 , //flags
bf354396 609 m_ctrl->m_peer->GetControlRefAddr() );
d0ee33f5 610
bf354396
VZ
611 ::EmbedControl(m_ctrl->m_peer->GetControlRef(),
612 (ControlRef)m_ctrl->GetParent()->GetHandle());
613
3dee36ae 614 //
bf354396 615 // Setup MovieController for the new movie
3dee36ae 616 //
bf354396
VZ
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}
3b5023b9 629#endif
bf354396
VZ
630
631//---------------------------------------------------------------------------
632// wxQTMediaBackend::DoNewMovieController
633//
634// Attaches movie to moviecontroller or creates moviecontroller
635// if not created yet
636//---------------------------------------------------------------------------
637#if !wxUSE_CREATEMOVIECONTROL
638void 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);
1a680109 706 }
bf354396
VZ
707}
708#endif
709
710//---------------------------------------------------------------------------
711// wxQTMediaBackend::FinishLoad
712//
713// Performs operations after a movie ready to play/loaded.
714//---------------------------------------------------------------------------
715void 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);
1a680109 723
1a680109
RN
724 //we want millisecond precision
725 ::SetMovieTimeScale(m_movie, 1000);
ff4aedc5
RN
726 wxASSERT(::GetMoviesError() == noErr);
727
bf354396
VZ
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//---------------------------------------------------------------------------
742void 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;
1a680109
RN
752}
753
ff4aedc5 754//---------------------------------------------------------------------------
e7b97da3 755// wxQTMediaBackend::Play
ff4aedc5 756//
bf354396 757// Start the QT movie
ff4aedc5
RN
758//---------------------------------------------------------------------------
759bool wxQTMediaBackend::Play()
1a680109 760{
bf354396
VZ
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;
1a680109
RN
772 return ::GetMoviesError() == noErr;
773}
774
ff4aedc5 775//---------------------------------------------------------------------------
e7b97da3 776// wxQTMediaBackend::Pause
ff4aedc5 777//
bf354396 778// Stop the movie
ff4aedc5
RN
779//---------------------------------------------------------------------------
780bool wxQTMediaBackend::Pause()
1a680109 781{
bf354396
VZ
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;
1a680109 788 return ::GetMoviesError() == noErr;
bf354396
VZ
789 }
790 return true; //already paused
1a680109
RN
791}
792
ff4aedc5 793//---------------------------------------------------------------------------
e7b97da3 794// wxQTMediaBackend::Stop
ff4aedc5 795//
3b5023b9 796// 1) Stop the movie
bf354396 797// 2) Seek to the beginning of the movie
ff4aedc5
RN
798//---------------------------------------------------------------------------
799bool wxQTMediaBackend::Stop()
1a680109 800{
bf354396 801 if(!wxQTMediaBackend::Pause())
1a680109 802 return false;
d0ee33f5 803
1a680109
RN
804 ::GoToBeginningOfMovie(m_movie);
805 return ::GetMoviesError() == noErr;
806}
807
ff4aedc5 808//---------------------------------------------------------------------------
e7b97da3 809// wxQTMediaBackend::GetPlaybackRate
ff4aedc5 810//
3b5023b9 811// 1) Get the movie playback rate from ::GetMovieRate
ff4aedc5
RN
812//---------------------------------------------------------------------------
813double wxQTMediaBackend::GetPlaybackRate()
1a680109 814{
1a680109
RN
815 return ( ((double)::GetMovieRate(m_movie)) / 0x10000);
816}
817
ff4aedc5 818//---------------------------------------------------------------------------
e7b97da3 819// wxQTMediaBackend::SetPlaybackRate
ff4aedc5 820//
3b5023b9 821// 1) Convert dRate to Fixed and Set the movie rate through SetMovieRate
ff4aedc5
RN
822//---------------------------------------------------------------------------
823bool wxQTMediaBackend::SetPlaybackRate(double dRate)
1a680109 824{
1a680109
RN
825 ::SetMovieRate(m_movie, (Fixed) (dRate * 0x10000));
826 return ::GetMoviesError() == noErr;
827}
828
ff4aedc5 829//---------------------------------------------------------------------------
e7b97da3 830// wxQTMediaBackend::SetPosition
ff4aedc5 831//
3b5023b9
RN
832// 1) Create a time record struct (TimeRecord) with appropriate values
833// 2) Pass struct to SetMovieTime
ff4aedc5
RN
834//---------------------------------------------------------------------------
835bool wxQTMediaBackend::SetPosition(wxLongLong where)
1a680109 836{
1a680109
RN
837 TimeRecord theTimeRecord;
838 memset(&theTimeRecord, 0, sizeof(TimeRecord));
ff4aedc5 839 theTimeRecord.value.lo = where.GetValue();
1a680109
RN
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
ff4aedc5 850//---------------------------------------------------------------------------
e7b97da3 851// wxQTMediaBackend::GetPosition
ff4aedc5 852//
3b5023b9 853// Calls GetMovieTime
ff4aedc5
RN
854//---------------------------------------------------------------------------
855wxLongLong wxQTMediaBackend::GetPosition()
1a680109 856{
1a680109
RN
857 return ::GetMovieTime(m_movie, NULL);
858}
859
ff4aedc5 860//---------------------------------------------------------------------------
c5191fbd
VZ
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//---------------------------------------------------------------------------
875double wxQTMediaBackend::GetVolume()
876{
bf354396 877 short sVolume = ::GetMovieVolume(m_movie);
c5191fbd
VZ
878
879 if(sVolume & (128 << 8)) //negative - no sound
880 return 0.0;
881
bf354396 882 return sVolume/256.0;
c5191fbd
VZ
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//---------------------------------------------------------------------------
900bool wxQTMediaBackend::SetVolume(double dVolume)
901{
bf354396 902 ::SetMovieVolume(m_movie, (short) (dVolume * 256));
c5191fbd
VZ
903 return true;
904}
905
906 //---------------------------------------------------------------------------
e7b97da3 907// wxQTMediaBackend::GetDuration
ff4aedc5 908//
3b5023b9 909// Calls GetMovieDuration
ff4aedc5
RN
910//---------------------------------------------------------------------------
911wxLongLong wxQTMediaBackend::GetDuration()
1a680109 912{
1a680109
RN
913 return ::GetMovieDuration(m_movie);
914}
915
ff4aedc5 916//---------------------------------------------------------------------------
e7b97da3 917// wxQTMediaBackend::GetState
ff4aedc5 918//
3b5023b9
RN
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)
ff4aedc5
RN
921//---------------------------------------------------------------------------
922wxMediaState wxQTMediaBackend::GetState()
1a680109 923{
bf354396
VZ
924 // Could use
925 // GetMovieActive/IsMovieDone/SetMovieActive
926 // combo if implemented that way
927 if (m_bPlaying == true)
1a680109 928 return wxMEDIASTATE_PLAYING;
bf354396
VZ
929 else if ( !m_movie || wxQTMediaBackend::GetPosition() == 0)
930 return wxMEDIASTATE_STOPPED;
1a680109
RN
931 else
932 return wxMEDIASTATE_PAUSED;
933}
934
ff4aedc5 935//---------------------------------------------------------------------------
e7b97da3 936// wxQTMediaBackend::Cleanup
ff4aedc5 937//
3b5023b9
RN
938// Diposes of the movie timer, Control if native, and stops and disposes
939// of the QT movie
ff4aedc5
RN
940//---------------------------------------------------------------------------
941void wxQTMediaBackend::Cleanup()
1a680109 942{
bf354396
VZ
943 m_bPlaying = false;
944 if(m_timer)
945 {
1a680109
RN
946 delete m_timer;
947 m_timer = NULL;
bf354396 948 }
d0ee33f5 949
bf354396
VZ
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 //
d0ee33f5 961#if wxUSE_CREATEMOVIECONTROL
bf354396
VZ
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);
3b5023b9 969#endif
1a680109 970
bf354396
VZ
971 ::DisposeMovie(m_movie);
972}
973
974//---------------------------------------------------------------------------
975// wxQTMediaBackend::MCFilterProc (static)
976//
977// Callback for when the movie controller recieves a message
978//---------------------------------------------------------------------------
979Boolean 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;
1a680109
RN
1000}
1001
ff4aedc5 1002//---------------------------------------------------------------------------
e7b97da3 1003// wxQTMediaBackend::GetVideoSize
ff4aedc5 1004//
3b5023b9 1005// Returns the actual size of the QT movie
ff4aedc5
RN
1006//---------------------------------------------------------------------------
1007wxSize wxQTMediaBackend::GetVideoSize() const
1a680109
RN
1008{
1009 return m_bestSize;
1010}
1011
ff4aedc5
RN
1012//---------------------------------------------------------------------------
1013// wxQTMediaBackend::Move
1014//
d0ee33f5 1015// We need to do this even when using native qt control because
4b60077b 1016// CreateMovieControl is broken in this regard...
ff4aedc5
RN
1017//---------------------------------------------------------------------------
1018void wxQTMediaBackend::Move(int x, int y, int w, int h)
1a680109 1019{
d0ee33f5 1020#if !wxUSE_CREATEMOVIECONTROL
ff4aedc5 1021 if(m_timer)
1a680109 1022 {
e7b97da3 1023 m_ctrl->GetParent()->MacWindowToRootWindow(&x, &y);
1a680109 1024 Rect theRect = {y, x, y+h, x+w};
ff4aedc5 1025
bf354396 1026 ::MCSetControllerBoundsRect(m_mc, &theRect);
1a680109
RN
1027 wxASSERT(::GetMoviesError() == noErr);
1028 }
4b60077b
RN
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 }
3b5023b9 1038#endif
1a680109
RN
1039}
1040
bf354396
VZ
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//---------------------------------------------------------------------------
1047void 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//---------------------------------------------------------------------------
1086bool 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
1125void 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
1145OSStatus 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
ff4aedc5
RN
1160
1161//in source file that contains stuff you don't directly use
f572d649 1162#include "wx/html/forcelnk.h"
412e0d47 1163FORCE_LINK_ME(basewxmediabackends)
ff4aedc5
RN
1164
1165#endif //wxUSE_MEDIACTRL