]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/mediactrl.cpp
reintroducing direct access, clipping to client area in dc
[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, portions (c) 2004 Kevin Olliver
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 //===========================================================================
13 // DECLARATIONS
14 //===========================================================================
15
16 //---------------------------------------------------------------------------
17 // Pre-compiled header stuff
18 //---------------------------------------------------------------------------
19
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "mediactrl.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 //---------------------------------------------------------------------------
32 // Includes
33 //---------------------------------------------------------------------------
34 #include "wx/mediactrl.h"
35
36 //---------------------------------------------------------------------------
37 // Compilation guard
38 //---------------------------------------------------------------------------
39 #if wxUSE_MEDIACTRL
40
41 //---------------------------------------------------------------------------
42 // Whether or not to use OSX 10.2's CreateMovieControl for native QuickTime
43 // control - i.e. native positioning and event handling etc..
44 //---------------------------------------------------------------------------
45 #ifndef wxUSE_CREATEMOVIECONTROL
46 # if defined( __WXMAC_OSX__ ) && ( MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_2 )
47 # define wxUSE_CREATEMOVIECONTROL 1
48 # else
49 # define wxUSE_CREATEMOVIECONTROL 0
50 # endif
51 #endif
52
53 //===========================================================================
54 // BACKEND DECLARATIONS
55 //===========================================================================
56
57 //---------------------------------------------------------------------------
58 //
59 // wxQTMediaBackend
60 //
61 //---------------------------------------------------------------------------
62
63 //---------------------------------------------------------------------------
64 // QT Includes
65 //---------------------------------------------------------------------------
66 //uma is for wxMacFSSpec
67 #include "wx/mac/uma.h"
68 #include "wx/timer.h"
69 #include <Movies.h>
70 #include <Gestalt.h>
71 #include <QuickTimeComponents.h> //Standard QT stuff
72
73 //Determines whether version 4 of QT is installed (Pretty much for classic only)
74 Boolean _wxIsQuickTime4Installed (void)
75 {
76 short error;
77 long result;
78
79 error = Gestalt (gestaltQuickTime, &result);
80 return (error == noErr) && (((result >> 16) & 0xffff) >= 0x0400);
81 }
82
83 class WXDLLIMPEXP_MEDIA wxQTMediaBackend : public wxMediaBackend
84 {
85 public:
86
87 wxQTMediaBackend();
88 ~wxQTMediaBackend();
89
90 virtual bool CreateControl(wxControl* ctrl, wxWindow* parent,
91 wxWindowID id,
92 const wxPoint& pos,
93 const wxSize& size,
94 long style,
95 const wxValidator& validator,
96 const wxString& name);
97
98 virtual bool Play();
99 virtual bool Pause();
100 virtual bool Stop();
101
102 virtual bool Load(const wxString& fileName);
103 virtual bool Load(const wxURI& location);
104
105 virtual wxMediaState GetState();
106
107 virtual bool SetPosition(wxLongLong where);
108 virtual wxLongLong GetPosition();
109 virtual wxLongLong GetDuration();
110
111 virtual void Move(int x, int y, int w, int h);
112 wxSize GetVideoSize() const;
113
114 virtual double GetPlaybackRate();
115 virtual bool SetPlaybackRate(double dRate);
116
117 void Cleanup();
118 void FinishLoad();
119
120 wxSize m_bestSize; //Original movie size
121 #ifdef __WXMAC_OSX__
122 struct MovieType** m_movie; //QT Movie handle/instance
123 #else
124 Movie m_movie ;
125 #endif
126 wxControl* m_ctrl; //Parent control
127 bool m_bVideo; //Whether or not we have video
128 class _wxQTTimer* m_timer; //Timer for streaming the movie
129
130 DECLARE_DYNAMIC_CLASS(wxQTMediaBackend);
131 };
132
133
134 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
135 //
136 // wxQTMediaBackend
137 //
138 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
139
140 IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend, wxMediaBackend);
141
142 //Time between timer calls
143 #define MOVIE_DELAY 100
144
145 // --------------------------------------------------------------------------
146 // wxQTTimer - Handle Asyncronous Playing
147 // --------------------------------------------------------------------------
148 class _wxQTTimer : public wxTimer
149 {
150 public:
151 _wxQTTimer(Movie movie, wxQTMediaBackend* parent) :
152 m_movie(movie), m_bPaused(false), m_parent(parent)
153 {
154 }
155
156 ~_wxQTTimer()
157 {
158 }
159
160 bool GetPaused() {return m_bPaused;}
161 void SetPaused(bool bPaused) {m_bPaused = bPaused;}
162
163 //-----------------------------------------------------------------------
164 // _wxQTTimer::Notify
165 //
166 // 1) Checks to see if the movie is done, and if not continues
167 // streaming the movie
168 // 2) Sends the wxEVT_MEDIA_STOP event if we have reached the end of
169 // the movie.
170 //-----------------------------------------------------------------------
171 void Notify()
172 {
173 if (!m_bPaused)
174 {
175 if(!IsMovieDone(m_movie))
176 MoviesTask(m_movie, MOVIE_DELAY);
177 else
178 {
179 wxMediaEvent theEvent(wxEVT_MEDIA_STOP,
180 m_parent->m_ctrl->GetId());
181 m_parent->m_ctrl->ProcessEvent(theEvent);
182
183 if(theEvent.IsAllowed())
184 {
185 Stop();
186 m_parent->Stop();
187 wxASSERT(::GetMoviesError() == noErr);
188
189 //send the event to our child
190 wxMediaEvent theEvent(wxEVT_MEDIA_FINISHED,
191 m_parent->m_ctrl->GetId());
192 m_parent->m_ctrl->ProcessEvent(theEvent);
193 }
194 }
195 }
196 }
197
198 protected:
199 Movie m_movie; //Our movie instance
200 bool m_bPaused; //Whether we are paused or not
201 wxQTMediaBackend* m_parent; //Backend pointer
202 };
203
204 //---------------------------------------------------------------------------
205 // wxQTMediaBackend Constructor
206 //
207 // Sets m_timer to NULL signifying we havn't loaded anything yet
208 //---------------------------------------------------------------------------
209 wxQTMediaBackend::wxQTMediaBackend() : m_timer(NULL)
210 {
211 }
212
213 //---------------------------------------------------------------------------
214 // wxQTMediaBackend Destructor
215 //
216 // 1) Cleans up the QuickTime movie instance
217 // 2) Decrements the QuickTime reference counter - if this reaches
218 // 0, QuickTime shuts down
219 // 3) Decrements the QuickTime Windows Media Layer reference counter -
220 // if this reaches 0, QuickTime shuts down the Windows Media Layer
221 //---------------------------------------------------------------------------
222 wxQTMediaBackend::~wxQTMediaBackend()
223 {
224 if(m_timer)
225 Cleanup();
226
227 //Note that ExitMovies() is not neccessary...
228 ExitMovies();
229 }
230
231 //---------------------------------------------------------------------------
232 // wxQTMediaBackend::CreateControl
233 //
234 // 1) Intializes QuickTime
235 // 2) Creates the control window
236 //---------------------------------------------------------------------------
237 bool wxQTMediaBackend::CreateControl(wxControl* ctrl, wxWindow* parent,
238 wxWindowID id,
239 const wxPoint& pos,
240 const wxSize& size,
241 long style,
242 const wxValidator& validator,
243 const wxString& name)
244 {
245 //Don't bother in Native control mode
246 #if defined( __WXMAC__ ) && TARGET_API_MAC_OSX && ( MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_2 )
247 if (!_wxIsQuickTime4Installed())
248 return false;
249 #endif
250
251 EnterMovies();
252
253 //
254 // Create window
255 // By default wxWindow(s) is created with a border -
256 // so we need to get rid of those
257 //
258 // Since we don't have a child window like most other
259 // backends, we don't need wxCLIP_CHILDREN
260 //
261 if ( !
262
263 #if wxUSE_CREATEMOVIECONTROL
264 ctrl->wxWindow::Create(parent, id, pos, size,
265 m_ctrl->MacRemoveBordersFromStyle(style),
266 name)
267 #else
268 ctrl->wxControl::Create(parent, id, pos, size,
269 m_ctrl->MacRemoveBordersFromStyle(style),
270 validator, name)
271 #endif
272 )
273 return false;
274
275 #if wxUSE_VALIDATORS
276 ctrl->SetValidator(validator);
277 #endif
278
279 m_ctrl = ctrl;
280 return true;
281 }
282
283 //---------------------------------------------------------------------------
284 // wxQTMediaBackend::Load (file version)
285 //
286 // 1) Get an FSSpec from the Windows path name
287 // 2) Open the movie
288 // 3) Obtain the movie instance from the movie resource
289 // 4) Close the movie resource
290 // 5) Finish loading
291 //---------------------------------------------------------------------------
292 bool wxQTMediaBackend::Load(const wxString& fileName)
293 {
294 if(m_timer)
295 Cleanup();
296
297 OSErr err = noErr;
298 short movieResFile;
299 FSSpec sfFile;
300
301 wxMacFilename2FSSpec( fileName , &sfFile );
302
303 if (OpenMovieFile (&sfFile, &movieResFile, fsRdPerm) != noErr)
304 return false;
305
306 short movieResID = 0;
307 Str255 movieName;
308
309 err = NewMovieFromFile (
310 &m_movie,
311 movieResFile,
312 &movieResID,
313 movieName,
314 newMovieActive,
315 NULL); //wasChanged
316
317 CloseMovieFile (movieResFile);
318
319 if (err != noErr)
320 return false;
321
322 FinishLoad();
323
324 return ::GetMoviesError() == noErr;
325 }
326
327 //---------------------------------------------------------------------------
328 // wxQTMediaBackend::Load (URL Version)
329 //
330 // 1) Build an escaped URI from location
331 // 2) Create a handle to store the URI string
332 // 3) Put the URI string inside the handle
333 // 4) Make a QuickTime URL data ref from the handle with the URI in it
334 // 5) Clean up the URI string handle
335 // 6) Do some prerolling
336 // 7) Finish Loading
337 //---------------------------------------------------------------------------
338 bool wxQTMediaBackend::Load(const wxURI& location)
339 {
340 if(m_timer)
341 Cleanup();
342
343 wxString theURI = location.BuildURI();
344
345 OSErr err = noErr;
346
347 Handle theHandle = NewHandleClear(theURI.length() + 1);
348 wxASSERT(theHandle);
349
350 BlockMove(theURI.mb_str(), *theHandle, theURI.length() + 1);
351
352 //create the movie from the handle that refers to the URI
353 err = NewMovieFromDataRef(&m_movie, newMovieActive,
354 NULL, theHandle,
355 URLDataHandlerSubType);
356
357 DisposeHandle(theHandle);
358
359 if (err != noErr)
360 return false;
361
362 //preroll movie for streaming
363 //TODO:Async this using threads?
364 TimeValue timeNow;
365 Fixed playRate;
366 timeNow = GetMovieTime(m_movie, NULL);
367 playRate = GetMoviePreferredRate(m_movie);
368 PrePrerollMovie(m_movie, timeNow, playRate, NULL, NULL);
369 PrerollMovie(m_movie, timeNow, playRate);
370 SetMovieRate(m_movie, playRate);
371
372 FinishLoad();
373
374 return ::GetMoviesError() == noErr;
375 }
376
377 //---------------------------------------------------------------------------
378 // wxQTMediaBackend::FinishLoad
379 //
380 // 1) Create the movie timer
381 // 2) Get real size of movie for GetBestSize/sizers
382 // 3) See if there is video in the movie, and if so then either
383 // SetMovieGWorld if < 10.2 or use Native CreateMovieControl
384 // 4) Set the movie time scale to something usable so that seeking
385 // etc. will work correctly
386 // 5) Refresh parent window
387 //---------------------------------------------------------------------------
388 void wxQTMediaBackend::FinishLoad()
389 {
390 m_timer = new _wxQTTimer(m_movie, (wxQTMediaBackend*) this);
391 wxASSERT(m_timer);
392
393 //get the real size of the movie
394 Rect outRect;
395 ::GetMovieNaturalBoundsRect (m_movie, &outRect);
396 wxASSERT(::GetMoviesError() == noErr);
397
398 m_bestSize.x = outRect.right - outRect.left;
399 m_bestSize.y = outRect.bottom - outRect.top;
400
401 //reparent movie/*AudioMediaCharacteristic*/
402 if(GetMovieIndTrackType(m_movie, 1,
403 VisualMediaCharacteristic,
404 movieTrackCharacteristic |
405 movieTrackEnabledOnly) != NULL)
406 {
407 #if wxUSE_CREATEMOVIECONTROL
408 //
409 //Native CreateMovieControl QT control (Thanks to Kevin Olliver's
410 //wxQTMovie for some of this).
411 //
412 #define GetControlPeer(whatever) ctrl->m_peer
413 wxMediaCtrl* ctrl = (wxMediaCtrl*) m_ctrl;
414 Rect bounds = wxMacGetBoundsForControl(m_ctrl,
415 m_ctrl->GetPosition(),
416 m_ctrl->GetSize());
417
418 //Dispose of old control for new one
419 if (GetControlPeer(m_ctrl) && GetControlPeer(m_ctrl)->Ok() )
420 GetControlPeer(m_ctrl)->Dispose();
421
422 //Options-
423 //kMovieControlOptionXXX
424 //HideController - hide the movie controller
425 //LocateTopLeft - movie is pinned to top left rather than centered in the control
426 //EnableEditing - Allows programmatic editing and dragn'drop
427 //HandleEditingHI- Installs event stuff for edit menu - forces EnableEditing also
428 //SetKeysEnabled - Allows keyboard input
429 //ManuallyIdled - app handles movie idling rather than internal timer event loop
430 ::CreateMovieControl(
431 (WindowRef)
432 ctrl->MacGetTopLevelWindowRef(), //parent
433 &bounds, //control bounds
434 m_movie, //movie handle
435 kMovieControlOptionHideController
436 | kMovieControlOptionLocateTopLeft
437 | kMovieControlOptionSetKeysEnabled
438 // | kMovieControlOptionManuallyIdled
439 , //flags
440 ctrl->m_peer->GetControlRefAddr() );
441
442 ::EmbedControl(ctrl->m_peer->GetControlRef(), (ControlRef)ctrl->GetParent()->GetHandle());
443 #else
444 //
445 //"Emulation"
446 //
447 SetMovieGWorld(m_movie,
448 (CGrafPtr)
449 GetWindowPort(
450 (WindowRef)
451 m_ctrl->MacGetTopLevelWindowRef()
452 ),
453 nil);
454 #endif
455 }
456
457 //we want millisecond precision
458 ::SetMovieTimeScale(m_movie, 1000);
459 wxASSERT(::GetMoviesError() == noErr);
460
461 //
462 //Here, if the parent of the control has a sizer - we
463 //tell it to recalculate the size of this control since
464 //the user opened a seperate media file
465 //
466 m_ctrl->InvalidateBestSize();
467 m_ctrl->GetParent()->Layout();
468 m_ctrl->GetParent()->Refresh();
469 m_ctrl->GetParent()->Update();
470 }
471
472 //---------------------------------------------------------------------------
473 // wxQTMediaBackend::Play
474 //
475 // 1) Start the QT movie
476 // 2) Start the movie loading timer
477 //---------------------------------------------------------------------------
478 bool wxQTMediaBackend::Play()
479 {
480 ::StartMovie(m_movie);
481 m_timer->SetPaused(false);
482 m_timer->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS);
483 return ::GetMoviesError() == noErr;
484 }
485
486 //---------------------------------------------------------------------------
487 // wxQTMediaBackend::Pause
488 //
489 // 1) Stop the movie
490 // 2) Stop the movie timer
491 //---------------------------------------------------------------------------
492 bool wxQTMediaBackend::Pause()
493 {
494 ::StopMovie(m_movie);
495 m_timer->SetPaused(true);
496 m_timer->Stop();
497 return ::GetMoviesError() == noErr;
498 }
499
500 //---------------------------------------------------------------------------
501 // wxQTMediaBackend::Stop
502 //
503 // 1) Stop the movie
504 // 2) Stop the movie timer
505 // 3) Seek to the beginning of the movie
506 //---------------------------------------------------------------------------
507 bool wxQTMediaBackend::Stop()
508 {
509 m_timer->SetPaused(false);
510 m_timer->Stop();
511
512 ::StopMovie(m_movie);
513 if(::GetMoviesError() != noErr)
514 return false;
515
516 ::GoToBeginningOfMovie(m_movie);
517 return ::GetMoviesError() == noErr;
518 }
519
520 //---------------------------------------------------------------------------
521 // wxQTMediaBackend::GetPlaybackRate
522 //
523 // 1) Get the movie playback rate from ::GetMovieRate
524 //---------------------------------------------------------------------------
525 double wxQTMediaBackend::GetPlaybackRate()
526 {
527 return ( ((double)::GetMovieRate(m_movie)) / 0x10000);
528 }
529
530 //---------------------------------------------------------------------------
531 // wxQTMediaBackend::SetPlaybackRate
532 //
533 // 1) Convert dRate to Fixed and Set the movie rate through SetMovieRate
534 //---------------------------------------------------------------------------
535 bool wxQTMediaBackend::SetPlaybackRate(double dRate)
536 {
537 ::SetMovieRate(m_movie, (Fixed) (dRate * 0x10000));
538 return ::GetMoviesError() == noErr;
539 }
540
541 //---------------------------------------------------------------------------
542 // wxQTMediaBackend::SetPosition
543 //
544 // 1) Create a time record struct (TimeRecord) with appropriate values
545 // 2) Pass struct to SetMovieTime
546 //---------------------------------------------------------------------------
547 bool wxQTMediaBackend::SetPosition(wxLongLong where)
548 {
549 TimeRecord theTimeRecord;
550 memset(&theTimeRecord, 0, sizeof(TimeRecord));
551 theTimeRecord.value.lo = where.GetValue();
552 theTimeRecord.scale = ::GetMovieTimeScale(m_movie);
553 theTimeRecord.base = ::GetMovieTimeBase(m_movie);
554 ::SetMovieTime(m_movie, &theTimeRecord);
555
556 if (::GetMoviesError() != noErr)
557 return false;
558
559 return true;
560 }
561
562 //---------------------------------------------------------------------------
563 // wxQTMediaBackend::GetPosition
564 //
565 // Calls GetMovieTime
566 //---------------------------------------------------------------------------
567 wxLongLong wxQTMediaBackend::GetPosition()
568 {
569 return ::GetMovieTime(m_movie, NULL);
570 }
571
572 //---------------------------------------------------------------------------
573 // wxQTMediaBackend::GetDuration
574 //
575 // Calls GetMovieDuration
576 //---------------------------------------------------------------------------
577 wxLongLong wxQTMediaBackend::GetDuration()
578 {
579 return ::GetMovieDuration(m_movie);
580 }
581
582 //---------------------------------------------------------------------------
583 // wxQTMediaBackend::GetState
584 //
585 // Determines the current state - the timer keeps track of whether or not
586 // we are paused or stopped (if the timer is running we are playing)
587 //---------------------------------------------------------------------------
588 wxMediaState wxQTMediaBackend::GetState()
589 {
590 if ( !m_timer || (m_timer->IsRunning() == false &&
591 m_timer->GetPaused() == false) )
592 return wxMEDIASTATE_STOPPED;
593
594 if( m_timer->IsRunning() == true )
595 return wxMEDIASTATE_PLAYING;
596 else
597 return wxMEDIASTATE_PAUSED;
598 }
599
600 //---------------------------------------------------------------------------
601 // wxQTMediaBackend::Cleanup
602 //
603 // Diposes of the movie timer, Control if native, and stops and disposes
604 // of the QT movie
605 //---------------------------------------------------------------------------
606 void wxQTMediaBackend::Cleanup()
607 {
608 delete m_timer;
609 m_timer = NULL;
610
611 #if wxUSE_CREATEMOVIECONTROL
612 DisposeControl(((wxMediaCtrl*)m_ctrl)->m_peer->GetControlRef());
613 #endif
614
615 StopMovie(m_movie);
616 DisposeMovie(m_movie);
617 }
618
619 //---------------------------------------------------------------------------
620 // wxQTMediaBackend::GetVideoSize
621 //
622 // Returns the actual size of the QT movie
623 //---------------------------------------------------------------------------
624 wxSize wxQTMediaBackend::GetVideoSize() const
625 {
626 return m_bestSize;
627 }
628
629 //---------------------------------------------------------------------------
630 // wxQTMediaBackend::Move
631 //
632 // We need to do this even when using native qt control because
633 // CreateMovieControl is broken in this regard...
634 //---------------------------------------------------------------------------
635 void wxQTMediaBackend::Move(int x, int y, int w, int h)
636 {
637 #if !wxUSE_CREATEMOVIECONTROL
638 if(m_timer)
639 {
640 if ( m_ctrl )
641 {
642 m_ctrl->GetParent()->MacWindowToRootWindow(&x, &y);
643 }
644
645 Rect theRect = {y, x, y+h, x+w};
646
647 ::SetMovieBox(m_movie, &theRect);
648 wxASSERT(::GetMoviesError() == noErr);
649 }
650 #else
651 if(m_timer && m_ctrl)
652 {
653 m_ctrl->GetParent()->MacWindowToRootWindow(&x, &y);
654
655 ::MoveControl( (ControlRef) m_ctrl->GetHandle(), x, y );
656 m_ctrl->GetParent()->Refresh();
657 m_ctrl->GetParent()->Update();
658 }
659 #endif
660 }
661
662
663 //in source file that contains stuff you don't directly use
664 #include <wx/html/forcelnk.h>
665 FORCE_LINK_ME(basewxmediabackends);
666
667 #endif //wxUSE_MEDIACTRL
668
669
670
671
672