1 /////////////////////////////////////////////////////////////////////////////
2 // Name: mac/cocoa/mediactrl.cpp
3 // Purpose: Built-in Media Backends for Cocoa
4 // Author: Ryan Norton <wxprojects@comcast.net>
8 // Copyright: (c) 2004-2005 Ryan Norton, (c) 2005 David Elliot
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 // BACKEND DECLARATIONS
43 //===========================================================================
45 //---------------------------------------------------------------------------
49 //---------------------------------------------------------------------------
51 //---------------------------------------------------------------------------
53 //---------------------------------------------------------------------------
55 #include <QuickTime/QuickTime.h>
57 #include "wx/cocoa/autorelease.h"
58 #include "wx/cocoa/string.h"
60 #import <AppKit/NSMovie.h>
61 #import <AppKit/NSMovieView.h>
64 class WXDLLIMPEXP_MEDIA wxQTMediaBackend : public wxMediaBackend
71 virtual bool CreateControl(wxControl* ctrl, wxWindow* parent,
76 const wxValidator& validator,
77 const wxString& name);
83 virtual bool Load(const wxString& fileName);
84 virtual bool Load(const wxURI& location);
86 virtual wxMediaState GetState();
88 virtual bool SetPosition(wxLongLong where);
89 virtual wxLongLong GetPosition();
90 virtual wxLongLong GetDuration();
92 virtual void Move(int x, int y, int w, int h);
93 wxSize GetVideoSize() const;
95 virtual double GetPlaybackRate();
96 virtual bool SetPlaybackRate(double dRate);
101 wxSize m_bestSize; //Original movie size
102 Movie m_movie; //QT Movie handle/instance
103 NSMovieView* m_movieview; //NSMovieView instance
104 wxControl* m_ctrl; //Parent control
105 bool m_bVideo; //Whether or not we have video
106 class _wxQTTimer* m_timer; //Timer for streaming the movie
108 DECLARE_DYNAMIC_CLASS(wxQTMediaBackend);
112 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
116 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
118 IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend, wxMediaBackend);
120 //Time between timer calls
121 #define MOVIE_DELAY 100
123 // --------------------------------------------------------------------------
124 // wxQTTimer - Handle Asyncronous Playing
125 // --------------------------------------------------------------------------
126 class _wxQTTimer : public wxTimer
129 _wxQTTimer(Movie movie, wxQTMediaBackend* parent) :
130 m_movie(movie), m_bPaused(false), m_parent(parent)
138 bool GetPaused() {return m_bPaused;}
139 void SetPaused(bool bPaused) {m_bPaused = bPaused;}
141 //-----------------------------------------------------------------------
142 // _wxQTTimer::Notify
144 // 1) Checks to see if the movie is done, and if not continues
145 // streaming the movie
146 // 2) Sends the wxEVT_MEDIA_STOP event if we have reached the end of
148 //-----------------------------------------------------------------------
153 if(!IsMovieDone(m_movie))
154 MoviesTask(m_movie, MOVIE_DELAY);
157 wxMediaEvent theEvent(wxEVT_MEDIA_STOP,
158 m_parent->m_ctrl->GetId());
159 m_parent->m_ctrl->ProcessEvent(theEvent);
161 if(theEvent.IsAllowed())
165 wxASSERT(::GetMoviesError() == noErr);
167 //send the event to our child
168 wxMediaEvent theEvent(wxEVT_MEDIA_FINISHED,
169 m_parent->m_ctrl->GetId());
170 m_parent->m_ctrl->ProcessEvent(theEvent);
177 Movie m_movie; //Our movie instance
178 bool m_bPaused; //Whether we are paused or not
179 wxQTMediaBackend* m_parent; //Backend pointer
182 //---------------------------------------------------------------------------
183 // wxQTMediaBackend Constructor
185 // Sets m_timer to NULL signifying we havn't loaded anything yet
186 //---------------------------------------------------------------------------
187 wxQTMediaBackend::wxQTMediaBackend() : m_timer(NULL)
191 //---------------------------------------------------------------------------
192 // wxQTMediaBackend Destructor
194 // 1) Cleans up the QuickTime movie instance
195 // 2) Decrements the QuickTime reference counter - if this reaches
196 // 0, QuickTime shuts down
197 // 3) Decrements the QuickTime Windows Media Layer reference counter -
198 // if this reaches 0, QuickTime shuts down the Windows Media Layer
199 //---------------------------------------------------------------------------
200 wxQTMediaBackend::~wxQTMediaBackend()
205 //Note that ExitMovies() is not neccessary...
209 //---------------------------------------------------------------------------
210 // wxQTMediaBackend::CreateControl
212 // 1) Intializes QuickTime
213 // 2) Creates the control window
214 //---------------------------------------------------------------------------
215 bool wxQTMediaBackend::CreateControl(wxControl* inctrl, wxWindow* parent,
220 const wxValidator& validator,
221 const wxString& name)
225 wxMediaCtrl* ctrl = (wxMediaCtrl*) inctrl;
227 //Create the control base
228 wxASSERT(ctrl->CreateBase(parent,wid,pos,size,style, validator, name));
230 //Create the NSMovieView
231 ctrl->SetNSView(NULL);
232 NSMovieView* theView = [[NSMovieView alloc] initWithFrame: ctrl->MakeDefaultNSRect(size)];
233 ctrl->SetNSView(theView);
238 parent->AddChild(ctrl);
239 parent->CocoaAddChild(ctrl);
240 ctrl->SetInitialFrameRect(pos,size);
243 [theView showController:false adjustingSize:true];
244 m_movieview = theView;
249 //---------------------------------------------------------------------------
250 // wxQTMediaBackend::Load (file version)
252 // Calls the URI version
253 //---------------------------------------------------------------------------
254 bool wxQTMediaBackend::Load(const wxString& fileName)
258 wxString( wxT("file://") ) + fileName
263 //---------------------------------------------------------------------------
264 // wxQTMediaBackend::Load (URL Version)
266 // 1) Build an escaped URI from location
268 //---------------------------------------------------------------------------
269 bool wxQTMediaBackend::Load(const wxURI& location)
274 wxString theURI = location.BuildURI();
276 [m_movieview setMovie:[[NSMovie alloc] initWithURL: [NSURL URLWithString: wxNSStringWithWxString(theURI)]
277 byReference: YES ] ];
279 m_movie = (Movie) [[m_movieview movie] QTMovie];
281 //preroll movie for streaming
282 //TODO:Async this using threads?
285 timeNow = GetMovieTime(m_movie, NULL);
286 playRate = GetMoviePreferredRate(m_movie);
287 PrePrerollMovie(m_movie, timeNow, playRate, NULL, NULL);
288 PrerollMovie(m_movie, timeNow, playRate);
289 SetMovieRate(m_movie, playRate);
293 return ::GetMoviesError() == noErr;
296 //---------------------------------------------------------------------------
297 // wxQTMediaBackend::FinishLoad
299 // 1) Create the movie timer
300 // 2) Get real size of movie for GetBestSize/sizers
301 // 3) See if there is video in the movie, and if so then either
302 // SetMovieGWorld if < 10.2 or use Native CreateMovieControl
303 // 4) Set the movie time scale to something usable so that seeking
304 // etc. will work correctly
305 // 5) Refresh parent window
306 //---------------------------------------------------------------------------
307 void wxQTMediaBackend::FinishLoad()
309 m_timer = new _wxQTTimer(m_movie, (wxQTMediaBackend*) this);
312 //get the real size of the movie
314 ::GetMovieNaturalBoundsRect (m_movie, &outRect);
315 wxASSERT(::GetMoviesError() == noErr);
317 m_bestSize.x = outRect.right - outRect.left;
318 m_bestSize.y = outRect.bottom - outRect.top;
320 //we want millisecond precision
321 ::SetMovieTimeScale(m_movie, 1000);
322 wxASSERT(::GetMoviesError() == noErr);
325 //Here, if the parent of the control has a sizer - we
326 //tell it to recalculate the size of this control since
327 //the user opened a seperate media file
329 m_ctrl->InvalidateBestSize();
330 m_ctrl->GetParent()->Layout();
331 m_ctrl->GetParent()->Refresh();
332 m_ctrl->GetParent()->Update();
335 //---------------------------------------------------------------------------
336 // wxQTMediaBackend::Play
338 // 1) Start the QT movie
339 // 2) Start the movie loading timer
340 //---------------------------------------------------------------------------
341 bool wxQTMediaBackend::Play()
343 ::StartMovie(m_movie);
344 m_timer->SetPaused(false);
345 m_timer->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS);
346 return ::GetMoviesError() == noErr;
349 //---------------------------------------------------------------------------
350 // wxQTMediaBackend::Pause
353 // 2) Stop the movie timer
354 //---------------------------------------------------------------------------
355 bool wxQTMediaBackend::Pause()
357 ::StopMovie(m_movie);
358 m_timer->SetPaused(true);
360 return ::GetMoviesError() == noErr;
363 //---------------------------------------------------------------------------
364 // wxQTMediaBackend::Stop
367 // 2) Stop the movie timer
368 // 3) Seek to the beginning of the movie
369 //---------------------------------------------------------------------------
370 bool wxQTMediaBackend::Stop()
372 m_timer->SetPaused(false);
375 ::StopMovie(m_movie);
376 if(::GetMoviesError() != noErr)
379 ::GoToBeginningOfMovie(m_movie);
380 return ::GetMoviesError() == noErr;
383 //---------------------------------------------------------------------------
384 // wxQTMediaBackend::GetPlaybackRate
386 // 1) Get the movie playback rate from ::GetMovieRate
387 //---------------------------------------------------------------------------
388 double wxQTMediaBackend::GetPlaybackRate()
390 return ( ((double)::GetMovieRate(m_movie)) / 0x10000);
393 //---------------------------------------------------------------------------
394 // wxQTMediaBackend::SetPlaybackRate
396 // 1) Convert dRate to Fixed and Set the movie rate through SetMovieRate
397 //---------------------------------------------------------------------------
398 bool wxQTMediaBackend::SetPlaybackRate(double dRate)
400 ::SetMovieRate(m_movie, (Fixed) (dRate * 0x10000));
401 return ::GetMoviesError() == noErr;
404 //---------------------------------------------------------------------------
405 // wxQTMediaBackend::SetPosition
407 // 1) Create a time record struct (TimeRecord) with appropriate values
408 // 2) Pass struct to SetMovieTime
409 //---------------------------------------------------------------------------
410 bool wxQTMediaBackend::SetPosition(wxLongLong where)
412 TimeRecord theTimeRecord;
413 memset(&theTimeRecord, 0, sizeof(TimeRecord));
414 theTimeRecord.value.lo = where.GetValue();
415 theTimeRecord.scale = ::GetMovieTimeScale(m_movie);
416 theTimeRecord.base = ::GetMovieTimeBase(m_movie);
417 ::SetMovieTime(m_movie, &theTimeRecord);
419 if (::GetMoviesError() != noErr)
425 //---------------------------------------------------------------------------
426 // wxQTMediaBackend::GetPosition
428 // Calls GetMovieTime
429 //---------------------------------------------------------------------------
430 wxLongLong wxQTMediaBackend::GetPosition()
432 return ::GetMovieTime(m_movie, NULL);
435 //---------------------------------------------------------------------------
436 // wxQTMediaBackend::GetDuration
438 // Calls GetMovieDuration
439 //---------------------------------------------------------------------------
440 wxLongLong wxQTMediaBackend::GetDuration()
442 return ::GetMovieDuration(m_movie);
445 //---------------------------------------------------------------------------
446 // wxQTMediaBackend::GetState
448 // Determines the current state - the timer keeps track of whether or not
449 // we are paused or stopped (if the timer is running we are playing)
450 //---------------------------------------------------------------------------
451 wxMediaState wxQTMediaBackend::GetState()
453 if ( !m_timer || (m_timer->IsRunning() == false &&
454 m_timer->GetPaused() == false) )
455 return wxMEDIASTATE_STOPPED;
457 if( m_timer->IsRunning() == true )
458 return wxMEDIASTATE_PLAYING;
460 return wxMEDIASTATE_PAUSED;
463 //---------------------------------------------------------------------------
464 // wxQTMediaBackend::Cleanup
466 // Diposes of the movie timer, Control if native, and stops and disposes
468 //---------------------------------------------------------------------------
469 void wxQTMediaBackend::Cleanup()
474 [[m_movieview movie] release];
475 [m_movieview setMovie:NULL];
478 //---------------------------------------------------------------------------
479 // wxQTMediaBackend::GetVideoSize
481 // Returns the actual size of the QT movie
482 //---------------------------------------------------------------------------
483 wxSize wxQTMediaBackend::GetVideoSize() const
488 //---------------------------------------------------------------------------
489 // wxQTMediaBackend::Move
491 // Nothin... cocoa takes care of this for us
492 //---------------------------------------------------------------------------
493 void wxQTMediaBackend::Move(int x, int y, int w, int h)
498 //in source file that contains stuff you don't directly use
499 #include <wx/html/forcelnk.h>
500 FORCE_LINK_ME(basewxmediabackends);
502 #endif //wxUSE_MEDIACTRL