1 /////////////////////////////////////////////////////////////////////////////
2 // Name: mac/carbon/mediactrl.cpp
3 // Purpose: Built-in Media Backends for Mac
4 // Author: Ryan Norton <wxprojects@comcast.net>
8 // Copyright: (c) 2004-2005 Ryan Norton, portions (c) 2004 Kevin Olliver
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 //===========================================================================
14 //===========================================================================
16 //---------------------------------------------------------------------------
17 // Pre-compiled header stuff
18 //---------------------------------------------------------------------------
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "mediactrl.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
31 //---------------------------------------------------------------------------
33 //---------------------------------------------------------------------------
34 #include "wx/mediactrl.h"
36 //---------------------------------------------------------------------------
38 //---------------------------------------------------------------------------
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
49 # define wxUSE_CREATEMOVIECONTROL 0
53 //===========================================================================
54 // BACKEND DECLARATIONS
55 //===========================================================================
57 //---------------------------------------------------------------------------
61 //---------------------------------------------------------------------------
63 //---------------------------------------------------------------------------
65 //---------------------------------------------------------------------------
66 //uma is for wxMacFSSpec
67 #include "wx/mac/uma.h"
72 #include <QuickTimeComponents.h> //Standard QT stuff
74 #include <QuickTime/QuickTimeComponents.h>
77 //Determines whether version 4 of QT is installed (Pretty much for classic only)
78 Boolean
_wxIsQuickTime4Installed (void)
83 error
= Gestalt (gestaltQuickTime
, &result
);
84 return (error
== noErr
) && (((result
>> 16) & 0xffff) >= 0x0400);
87 class WXDLLIMPEXP_MEDIA wxQTMediaBackend
: public wxMediaBackend
94 virtual bool CreateControl(wxControl
* ctrl
, wxWindow
* parent
,
99 const wxValidator
& validator
,
100 const wxString
& name
);
103 virtual bool Pause();
106 virtual bool Load(const wxString
& fileName
);
107 virtual bool Load(const wxURI
& location
);
109 virtual wxMediaState
GetState();
111 virtual bool SetPosition(wxLongLong where
);
112 virtual wxLongLong
GetPosition();
113 virtual wxLongLong
GetDuration();
115 virtual void Move(int x
, int y
, int w
, int h
);
116 wxSize
GetVideoSize() const;
118 virtual double GetPlaybackRate();
119 virtual bool SetPlaybackRate(double dRate
);
121 virtual double GetVolume();
122 virtual bool SetVolume(double);
127 wxSize m_bestSize
; //Original movie size
129 struct MovieType
** m_movie
; //QT Movie handle/instance
133 wxControl
* m_ctrl
; //Parent control
134 bool m_bVideo
; //Whether or not we have video
135 class _wxQTTimer
* m_timer
; //Timer for streaming the movie
137 DECLARE_DYNAMIC_CLASS(wxQTMediaBackend
)
141 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
145 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
147 IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend
, wxMediaBackend
);
149 //Time between timer calls
150 #define MOVIE_DELAY 100
152 // --------------------------------------------------------------------------
153 // wxQTTimer - Handle Asyncronous Playing
154 // --------------------------------------------------------------------------
155 class _wxQTTimer
: public wxTimer
158 _wxQTTimer(Movie movie
, wxQTMediaBackend
* parent
) :
159 m_movie(movie
), m_bPaused(false), m_parent(parent
)
167 bool GetPaused() {return m_bPaused
;}
168 void SetPaused(bool bPaused
) {m_bPaused
= bPaused
;}
170 //-----------------------------------------------------------------------
171 // _wxQTTimer::Notify
173 // 1) Checks to see if the movie is done, and if not continues
174 // streaming the movie
175 // 2) Sends the wxEVT_MEDIA_STOP event if we have reached the end of
177 //-----------------------------------------------------------------------
182 if(!IsMovieDone(m_movie
))
183 MoviesTask(m_movie
, MOVIE_DELAY
);
186 wxMediaEvent
theEvent(wxEVT_MEDIA_STOP
,
187 m_parent
->m_ctrl
->GetId());
188 m_parent
->m_ctrl
->ProcessEvent(theEvent
);
190 if(theEvent
.IsAllowed())
194 wxASSERT(::GetMoviesError() == noErr
);
196 //send the event to our child
197 wxMediaEvent
theEvent(wxEVT_MEDIA_FINISHED
,
198 m_parent
->m_ctrl
->GetId());
199 m_parent
->m_ctrl
->ProcessEvent(theEvent
);
206 Movie m_movie
; //Our movie instance
207 bool m_bPaused
; //Whether we are paused or not
208 wxQTMediaBackend
* m_parent
; //Backend pointer
211 //---------------------------------------------------------------------------
212 // wxQTMediaBackend Constructor
214 // Sets m_timer to NULL signifying we havn't loaded anything yet
215 //---------------------------------------------------------------------------
216 wxQTMediaBackend::wxQTMediaBackend() : m_timer(NULL
)
220 //---------------------------------------------------------------------------
221 // wxQTMediaBackend Destructor
223 // 1) Cleans up the QuickTime movie instance
224 // 2) Decrements the QuickTime reference counter - if this reaches
225 // 0, QuickTime shuts down
226 // 3) Decrements the QuickTime Windows Media Layer reference counter -
227 // if this reaches 0, QuickTime shuts down the Windows Media Layer
228 //---------------------------------------------------------------------------
229 wxQTMediaBackend::~wxQTMediaBackend()
234 //Note that ExitMovies() is not necessary...
238 //---------------------------------------------------------------------------
239 // wxQTMediaBackend::CreateControl
241 // 1) Intializes QuickTime
242 // 2) Creates the control window
243 //---------------------------------------------------------------------------
244 bool wxQTMediaBackend::CreateControl(wxControl
* ctrl
, wxWindow
* parent
,
249 const wxValidator
& validator
,
250 const wxString
& name
)
252 //Don't bother in Native control mode
253 #if defined( __WXMAC__ ) && TARGET_API_MAC_OSX && ( MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_2 )
254 if (!_wxIsQuickTime4Installed())
262 // By default wxWindow(s) is created with a border -
263 // so we need to get rid of those
265 // Since we don't have a child window like most other
266 // backends, we don't need wxCLIP_CHILDREN
270 #if wxUSE_CREATEMOVIECONTROL
271 ctrl
->wxWindow::Create(parent
, id
, pos
, size
,
272 wxWindow::MacRemoveBordersFromStyle(style
),
275 ctrl
->wxControl::Create(parent
, id
, pos
, size
,
276 wxWindow::MacRemoveBordersFromStyle(style
),
283 ctrl
->SetValidator(validator
);
290 //---------------------------------------------------------------------------
291 // wxQTMediaBackend::Load (file version)
293 // 1) Get an FSSpec from the Windows path name
295 // 3) Obtain the movie instance from the movie resource
296 // 4) Close the movie resource
298 //---------------------------------------------------------------------------
299 bool wxQTMediaBackend::Load(const wxString
& fileName
)
308 //FIXME:wxMacFilename2FSSpec crashes on empty string -
309 //does it crash on other strings too and should this
310 //"fix" be put in the carbon wxSound?
311 if (fileName
.empty())
314 wxMacFilename2FSSpec( fileName
, &sfFile
);
316 if (OpenMovieFile (&sfFile
, &movieResFile
, fsRdPerm
) != noErr
)
319 short movieResID
= 0;
322 err
= NewMovieFromFile (
330 CloseMovieFile (movieResFile
);
337 return ::GetMoviesError() == noErr
;
340 //---------------------------------------------------------------------------
341 // wxQTMediaBackend::Load (URL Version)
343 // 1) Build an escaped URI from location
344 // 2) Create a handle to store the URI string
345 // 3) Put the URI string inside the handle
346 // 4) Make a QuickTime URL data ref from the handle with the URI in it
347 // 5) Clean up the URI string handle
348 // 6) Do some prerolling
350 //---------------------------------------------------------------------------
351 bool wxQTMediaBackend::Load(const wxURI
& location
)
356 wxString theURI
= location
.BuildURI();
360 Handle theHandle
= NewHandleClear(theURI
.length() + 1);
363 BlockMove(theURI
.mb_str(), *theHandle
, theURI
.length() + 1);
365 //create the movie from the handle that refers to the URI
366 err
= NewMovieFromDataRef(&m_movie
, newMovieActive
,
368 URLDataHandlerSubType
);
370 DisposeHandle(theHandle
);
375 //preroll movie for streaming
376 //TODO:Async this using threads?
379 timeNow
= GetMovieTime(m_movie
, NULL
);
380 playRate
= GetMoviePreferredRate(m_movie
);
381 PrePrerollMovie(m_movie
, timeNow
, playRate
, NULL
, NULL
);
382 PrerollMovie(m_movie
, timeNow
, playRate
);
383 SetMovieRate(m_movie
, playRate
);
387 return ::GetMoviesError() == noErr
;
390 //---------------------------------------------------------------------------
391 // wxQTMediaBackend::FinishLoad
393 // 1) Create the movie timer
394 // 2) Get real size of movie for GetBestSize/sizers
395 // 3) See if there is video in the movie, and if so then either
396 // SetMovieGWorld if < 10.2 or use Native CreateMovieControl
397 // 4) Set the movie time scale to something usable so that seeking
398 // etc. will work correctly
399 // 5) Refresh parent window
400 //---------------------------------------------------------------------------
401 void wxQTMediaBackend::FinishLoad()
403 m_timer
= new _wxQTTimer(m_movie
, (wxQTMediaBackend
*) this);
406 //get the real size of the movie
408 ::GetMovieNaturalBoundsRect (m_movie
, &outRect
);
409 wxASSERT(::GetMoviesError() == noErr
);
411 m_bestSize
.x
= outRect
.right
- outRect
.left
;
412 m_bestSize
.y
= outRect
.bottom
- outRect
.top
;
414 //reparent movie/*AudioMediaCharacteristic*/
415 if(GetMovieIndTrackType(m_movie
, 1,
416 VisualMediaCharacteristic
,
417 movieTrackCharacteristic
|
418 movieTrackEnabledOnly
) != NULL
)
420 #if wxUSE_CREATEMOVIECONTROL
422 //Native CreateMovieControl QT control (Thanks to Kevin Olliver's
423 //wxQTMovie for some of this).
425 #define GetControlPeer(whatever) ctrl->m_peer
426 wxMediaCtrl
* ctrl
= (wxMediaCtrl
*) m_ctrl
;
427 Rect bounds
= wxMacGetBoundsForControl(m_ctrl
,
428 m_ctrl
->GetPosition(),
431 //Dispose of old control for new one
432 if (GetControlPeer(m_ctrl
) && GetControlPeer(m_ctrl
)->Ok() )
433 GetControlPeer(m_ctrl
)->Dispose();
436 //kMovieControlOptionXXX
437 //HideController - hide the movie controller
438 //LocateTopLeft - movie is pinned to top left rather than centered in the control
439 //EnableEditing - Allows programmatic editing and dragn'drop
440 //HandleEditingHI- Installs event stuff for edit menu - forces EnableEditing also
441 //SetKeysEnabled - Allows keyboard input
442 //ManuallyIdled - app handles movie idling rather than internal timer event loop
443 ::CreateMovieControl(
445 ctrl
->MacGetTopLevelWindowRef(), //parent
446 &bounds
, //control bounds
447 m_movie
, //movie handle
448 kMovieControlOptionHideController
449 | kMovieControlOptionLocateTopLeft
450 | kMovieControlOptionSetKeysEnabled
451 // | kMovieControlOptionManuallyIdled
453 ctrl
->m_peer
->GetControlRefAddr() );
455 ::EmbedControl(ctrl
->m_peer
->GetControlRef(), (ControlRef
)ctrl
->GetParent()->GetHandle());
460 SetMovieGWorld(m_movie
,
464 m_ctrl
->MacGetTopLevelWindowRef()
470 //we want millisecond precision
471 ::SetMovieTimeScale(m_movie
, 1000);
472 wxASSERT(::GetMoviesError() == noErr
);
475 //Here, if the parent of the control has a sizer - we
476 //tell it to recalculate the size of this control since
477 //the user opened a separate media file
479 m_ctrl
->InvalidateBestSize();
480 m_ctrl
->GetParent()->Layout();
481 m_ctrl
->GetParent()->Refresh();
482 m_ctrl
->GetParent()->Update();
485 wxMediaEvent
theEvent(wxEVT_MEDIA_LOADED
,
487 m_ctrl
->AddPendingEvent(theEvent
);
490 //---------------------------------------------------------------------------
491 // wxQTMediaBackend::Play
493 // 1) Start the QT movie
494 // 2) Start the movie loading timer
495 //---------------------------------------------------------------------------
496 bool wxQTMediaBackend::Play()
498 ::StartMovie(m_movie
);
499 m_timer
->SetPaused(false);
500 m_timer
->Start(MOVIE_DELAY
, wxTIMER_CONTINUOUS
);
501 return ::GetMoviesError() == noErr
;
504 //---------------------------------------------------------------------------
505 // wxQTMediaBackend::Pause
508 // 2) Stop the movie timer
509 //---------------------------------------------------------------------------
510 bool wxQTMediaBackend::Pause()
512 ::StopMovie(m_movie
);
513 m_timer
->SetPaused(true);
515 return ::GetMoviesError() == noErr
;
518 //---------------------------------------------------------------------------
519 // wxQTMediaBackend::Stop
522 // 2) Stop the movie timer
523 // 3) Seek to the beginning of the movie
524 //---------------------------------------------------------------------------
525 bool wxQTMediaBackend::Stop()
527 m_timer
->SetPaused(false);
530 ::StopMovie(m_movie
);
531 if(::GetMoviesError() != noErr
)
534 ::GoToBeginningOfMovie(m_movie
);
535 return ::GetMoviesError() == noErr
;
538 //---------------------------------------------------------------------------
539 // wxQTMediaBackend::GetPlaybackRate
541 // 1) Get the movie playback rate from ::GetMovieRate
542 //---------------------------------------------------------------------------
543 double wxQTMediaBackend::GetPlaybackRate()
545 return ( ((double)::GetMovieRate(m_movie
)) / 0x10000);
548 //---------------------------------------------------------------------------
549 // wxQTMediaBackend::SetPlaybackRate
551 // 1) Convert dRate to Fixed and Set the movie rate through SetMovieRate
552 //---------------------------------------------------------------------------
553 bool wxQTMediaBackend::SetPlaybackRate(double dRate
)
555 ::SetMovieRate(m_movie
, (Fixed
) (dRate
* 0x10000));
556 return ::GetMoviesError() == noErr
;
559 //---------------------------------------------------------------------------
560 // wxQTMediaBackend::SetPosition
562 // 1) Create a time record struct (TimeRecord) with appropriate values
563 // 2) Pass struct to SetMovieTime
564 //---------------------------------------------------------------------------
565 bool wxQTMediaBackend::SetPosition(wxLongLong where
)
567 TimeRecord theTimeRecord
;
568 memset(&theTimeRecord
, 0, sizeof(TimeRecord
));
569 theTimeRecord
.value
.lo
= where
.GetValue();
570 theTimeRecord
.scale
= ::GetMovieTimeScale(m_movie
);
571 theTimeRecord
.base
= ::GetMovieTimeBase(m_movie
);
572 ::SetMovieTime(m_movie
, &theTimeRecord
);
574 if (::GetMoviesError() != noErr
)
580 //---------------------------------------------------------------------------
581 // wxQTMediaBackend::GetPosition
583 // Calls GetMovieTime
584 //---------------------------------------------------------------------------
585 wxLongLong
wxQTMediaBackend::GetPosition()
587 return ::GetMovieTime(m_movie
, NULL
);
590 //---------------------------------------------------------------------------
591 // wxQTMediaBackend::GetVolume
593 // Gets the volume through GetMovieVolume - which returns a 16 bit short -
595 // +--------+--------+
597 // +--------+--------+
599 // (1) first 8 bits are value before decimal
600 // (2) second 8 bits are value after decimal
602 // Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to
603 // 1 (full gain and sound)
604 //---------------------------------------------------------------------------
605 double wxQTMediaBackend::GetVolume()
607 short sVolume
= GetMovieVolume(m_movie
);
609 if(sVolume
& (128 << 8)) //negative - no sound
612 return (sVolume
& (127 << 8)) ? 1.0 : ((double)(sVolume
& 255)) / 255.0;
615 //---------------------------------------------------------------------------
616 // wxQTMediaBackend::SetVolume
618 // Sets the volume through SetMovieVolume - which takes a 16 bit short -
620 // +--------+--------+
622 // +--------+--------+
624 // (1) first 8 bits are value before decimal
625 // (2) second 8 bits are value after decimal
627 // Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to
628 // 1 (full gain and sound)
629 //---------------------------------------------------------------------------
630 bool wxQTMediaBackend::SetVolume(double dVolume
)
632 short sVolume
= (short) (dVolume
>= .9999 ? 1 << 8 : (dVolume
* 255) );
633 SetMovieVolume(m_movie
, sVolume
);
637 //---------------------------------------------------------------------------
638 // wxQTMediaBackend::GetDuration
640 // Calls GetMovieDuration
641 //---------------------------------------------------------------------------
642 wxLongLong
wxQTMediaBackend::GetDuration()
644 return ::GetMovieDuration(m_movie
);
647 //---------------------------------------------------------------------------
648 // wxQTMediaBackend::GetState
650 // Determines the current state - the timer keeps track of whether or not
651 // we are paused or stopped (if the timer is running we are playing)
652 //---------------------------------------------------------------------------
653 wxMediaState
wxQTMediaBackend::GetState()
655 if ( !m_timer
|| (m_timer
->IsRunning() == false &&
656 m_timer
->GetPaused() == false) )
657 return wxMEDIASTATE_STOPPED
;
659 if( m_timer
->IsRunning() )
660 return wxMEDIASTATE_PLAYING
;
662 return wxMEDIASTATE_PAUSED
;
665 //---------------------------------------------------------------------------
666 // wxQTMediaBackend::Cleanup
668 // Diposes of the movie timer, Control if native, and stops and disposes
670 //---------------------------------------------------------------------------
671 void wxQTMediaBackend::Cleanup()
676 #if wxUSE_CREATEMOVIECONTROL
677 DisposeControl(((wxMediaCtrl
*)m_ctrl
)->m_peer
->GetControlRef());
681 DisposeMovie(m_movie
);
684 //---------------------------------------------------------------------------
685 // wxQTMediaBackend::GetVideoSize
687 // Returns the actual size of the QT movie
688 //---------------------------------------------------------------------------
689 wxSize
wxQTMediaBackend::GetVideoSize() const
694 //---------------------------------------------------------------------------
695 // wxQTMediaBackend::Move
697 // We need to do this even when using native qt control because
698 // CreateMovieControl is broken in this regard...
699 //---------------------------------------------------------------------------
700 void wxQTMediaBackend::Move(int x
, int y
, int w
, int h
)
702 #if !wxUSE_CREATEMOVIECONTROL
707 m_ctrl
->GetParent()->MacWindowToRootWindow(&x
, &y
);
710 Rect theRect
= {y
, x
, y
+h
, x
+w
};
712 ::SetMovieBox(m_movie
, &theRect
);
713 wxASSERT(::GetMoviesError() == noErr
);
716 if(m_timer
&& m_ctrl
)
718 m_ctrl
->GetParent()->MacWindowToRootWindow(&x
, &y
);
720 ::MoveControl( (ControlRef
) m_ctrl
->GetHandle(), x
, y
);
721 m_ctrl
->GetParent()->Refresh();
722 m_ctrl
->GetParent()->Update();
728 //in source file that contains stuff you don't directly use
729 #include "wx/html/forcelnk.h"
730 FORCE_LINK_ME(basewxmediabackends
);
732 #endif //wxUSE_MEDIACTRL