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