1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/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 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
27 //---------------------------------------------------------------------------
29 //---------------------------------------------------------------------------
32 #include "wx/mediactrl.h"
38 //===========================================================================
39 // BACKEND DECLARATIONS
40 //===========================================================================
42 //---------------------------------------------------------------------------
46 //---------------------------------------------------------------------------
48 //---------------------------------------------------------------------------
50 //---------------------------------------------------------------------------
51 #include <QuickTime/QuickTime.h>
53 #include "wx/cocoa/autorelease.h"
54 #include "wx/cocoa/string.h"
56 #import <AppKit/NSMovie.h>
57 #import <AppKit/NSMovieView.h>
60 class WXDLLIMPEXP_MEDIA wxQTMediaBackend : public wxMediaBackend
67 virtual bool CreateControl(wxControl* ctrl, wxWindow* parent,
72 const wxValidator& validator,
73 const wxString& name);
79 virtual bool Load(const wxString& fileName);
80 virtual bool Load(const wxURI& location);
82 virtual wxMediaState GetState();
84 virtual bool SetPosition(wxLongLong where);
85 virtual wxLongLong GetPosition();
86 virtual wxLongLong GetDuration();
88 virtual void Move(int x, int y, int w, int h);
89 wxSize GetVideoSize() const;
91 virtual double GetPlaybackRate();
92 virtual bool SetPlaybackRate(double dRate);
97 wxSize m_bestSize; //Original movie size
98 Movie m_movie; //QT Movie handle/instance
99 NSMovieView* m_movieview; //NSMovieView instance
100 wxControl* m_ctrl; //Parent control
101 bool m_bVideo; //Whether or not we have video
102 class _wxQTTimer* m_timer; //Timer for streaming the movie
104 DECLARE_DYNAMIC_CLASS(wxQTMediaBackend);
108 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
112 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
114 IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend, wxMediaBackend);
116 //Time between timer calls
117 #define MOVIE_DELAY 100
119 // --------------------------------------------------------------------------
120 // wxQTTimer - Handle Asyncronous Playing
121 // --------------------------------------------------------------------------
122 class _wxQTTimer : public wxTimer
125 _wxQTTimer(Movie movie, wxQTMediaBackend* parent) :
126 m_movie(movie), m_bPaused(false), m_parent(parent)
134 bool GetPaused() {return m_bPaused;}
135 void SetPaused(bool bPaused) {m_bPaused = bPaused;}
137 //-----------------------------------------------------------------------
138 // _wxQTTimer::Notify
140 // 1) Checks to see if the movie is done, and if not continues
141 // streaming the movie
142 // 2) Sends the wxEVT_MEDIA_STOP event if we have reached the end of
144 //-----------------------------------------------------------------------
149 if(!IsMovieDone(m_movie))
150 MoviesTask(m_movie, MOVIE_DELAY);
153 wxMediaEvent theEvent(wxEVT_MEDIA_STOP,
154 m_parent->m_ctrl->GetId());
155 m_parent->m_ctrl->ProcessEvent(theEvent);
157 if(theEvent.IsAllowed())
161 wxASSERT(::GetMoviesError() == noErr);
163 //send the event to our child
164 wxMediaEvent theEvent(wxEVT_MEDIA_FINISHED,
165 m_parent->m_ctrl->GetId());
166 m_parent->m_ctrl->ProcessEvent(theEvent);
173 Movie m_movie; //Our movie instance
174 bool m_bPaused; //Whether we are paused or not
175 wxQTMediaBackend* m_parent; //Backend pointer
178 //---------------------------------------------------------------------------
179 // wxQTMediaBackend Constructor
181 // Sets m_timer to NULL signifying we havn't loaded anything yet
182 //---------------------------------------------------------------------------
183 wxQTMediaBackend::wxQTMediaBackend() : m_timer(NULL)
187 //---------------------------------------------------------------------------
188 // wxQTMediaBackend Destructor
190 // 1) Cleans up the QuickTime movie instance
191 // 2) Decrements the QuickTime reference counter - if this reaches
192 // 0, QuickTime shuts down
193 // 3) Decrements the QuickTime Windows Media Layer reference counter -
194 // if this reaches 0, QuickTime shuts down the Windows Media Layer
195 //---------------------------------------------------------------------------
196 wxQTMediaBackend::~wxQTMediaBackend()
201 //Note that ExitMovies() is not necessary...
205 //---------------------------------------------------------------------------
206 // wxQTMediaBackend::CreateControl
208 // 1) Intializes QuickTime
209 // 2) Creates the control window
210 //---------------------------------------------------------------------------
211 bool wxQTMediaBackend::CreateControl(wxControl* inctrl, wxWindow* parent,
216 const wxValidator& validator,
217 const wxString& name)
221 wxMediaCtrl* ctrl = (wxMediaCtrl*) inctrl;
223 //Create the control base
224 wxASSERT(ctrl->CreateBase(parent,wid,pos,size,style, validator, name));
226 //Create the NSMovieView
227 ctrl->SetNSView(NULL);
228 NSMovieView* theView = [[NSMovieView alloc] initWithFrame: ctrl->MakeDefaultNSRect(size)];
229 ctrl->SetNSView(theView);
234 parent->AddChild(ctrl);
235 parent->CocoaAddChild(ctrl);
236 ctrl->SetInitialFrameRect(pos,size);
239 [theView showController:false adjustingSize:true];
240 m_movieview = theView;
245 //---------------------------------------------------------------------------
246 // wxQTMediaBackend::Load (file version)
248 // Calls the URI version
249 //---------------------------------------------------------------------------
250 bool wxQTMediaBackend::Load(const wxString& fileName)
254 wxString( wxT("file://") ) + fileName
259 //---------------------------------------------------------------------------
260 // wxQTMediaBackend::Load (URL Version)
262 // 1) Build an escaped URI from location
264 //---------------------------------------------------------------------------
265 bool wxQTMediaBackend::Load(const wxURI& location)
270 wxString theURI = location.BuildURI();
272 [m_movieview setMovie:[[NSMovie alloc] initWithURL: [NSURL URLWithString: wxNSStringWithWxString(theURI)]
273 byReference: YES ] ];
275 m_movie = (Movie) [[m_movieview movie] QTMovie];
277 //preroll movie for streaming
278 //TODO:Async this using threads?
281 timeNow = GetMovieTime(m_movie, NULL);
282 playRate = GetMoviePreferredRate(m_movie);
283 PrePrerollMovie(m_movie, timeNow, playRate, NULL, NULL);
284 PrerollMovie(m_movie, timeNow, playRate);
285 SetMovieRate(m_movie, playRate);
289 return ::GetMoviesError() == noErr;
292 //---------------------------------------------------------------------------
293 // wxQTMediaBackend::FinishLoad
295 // 1) Create the movie timer
296 // 2) Get real size of movie for GetBestSize/sizers
297 // 3) See if there is video in the movie, and if so then either
298 // SetMovieGWorld if < 10.2 or use Native CreateMovieControl
299 // 4) Set the movie time scale to something usable so that seeking
300 // etc. will work correctly
301 // 5) Refresh parent window
302 //---------------------------------------------------------------------------
303 void wxQTMediaBackend::FinishLoad()
305 m_timer = new _wxQTTimer(m_movie, (wxQTMediaBackend*) this);
308 //get the real size of the movie
310 ::GetMovieNaturalBoundsRect (m_movie, &outRect);
311 wxASSERT(::GetMoviesError() == noErr);
313 m_bestSize.x = outRect.right - outRect.left;
314 m_bestSize.y = outRect.bottom - outRect.top;
316 //we want millisecond precision
317 ::SetMovieTimeScale(m_movie, 1000);
318 wxASSERT(::GetMoviesError() == noErr);
321 //Here, if the parent of the control has a sizer - we
322 //tell it to recalculate the size of this control since
323 //the user opened a separate media file
325 m_ctrl->InvalidateBestSize();
326 m_ctrl->GetParent()->Layout();
327 m_ctrl->GetParent()->Refresh();
328 m_ctrl->GetParent()->Update();
331 //---------------------------------------------------------------------------
332 // wxQTMediaBackend::Play
334 // 1) Start the QT movie
335 // 2) Start the movie loading timer
336 //---------------------------------------------------------------------------
337 bool wxQTMediaBackend::Play()
339 ::StartMovie(m_movie);
340 m_timer->SetPaused(false);
341 m_timer->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS);
342 return ::GetMoviesError() == noErr;
345 //---------------------------------------------------------------------------
346 // wxQTMediaBackend::Pause
349 // 2) Stop the movie timer
350 //---------------------------------------------------------------------------
351 bool wxQTMediaBackend::Pause()
353 ::StopMovie(m_movie);
354 m_timer->SetPaused(true);
356 return ::GetMoviesError() == noErr;
359 //---------------------------------------------------------------------------
360 // wxQTMediaBackend::Stop
363 // 2) Stop the movie timer
364 // 3) Seek to the beginning of the movie
365 //---------------------------------------------------------------------------
366 bool wxQTMediaBackend::Stop()
368 m_timer->SetPaused(false);
371 ::StopMovie(m_movie);
372 if(::GetMoviesError() != noErr)
375 ::GoToBeginningOfMovie(m_movie);
376 return ::GetMoviesError() == noErr;
379 //---------------------------------------------------------------------------
380 // wxQTMediaBackend::GetPlaybackRate
382 // 1) Get the movie playback rate from ::GetMovieRate
383 //---------------------------------------------------------------------------
384 double wxQTMediaBackend::GetPlaybackRate()
386 return ( ((double)::GetMovieRate(m_movie)) / 0x10000);
389 //---------------------------------------------------------------------------
390 // wxQTMediaBackend::SetPlaybackRate
392 // 1) Convert dRate to Fixed and Set the movie rate through SetMovieRate
393 //---------------------------------------------------------------------------
394 bool wxQTMediaBackend::SetPlaybackRate(double dRate)
396 ::SetMovieRate(m_movie, (Fixed) (dRate * 0x10000));
397 return ::GetMoviesError() == noErr;
400 //---------------------------------------------------------------------------
401 // wxQTMediaBackend::SetPosition
403 // 1) Create a time record struct (TimeRecord) with appropriate values
404 // 2) Pass struct to SetMovieTime
405 //---------------------------------------------------------------------------
406 bool wxQTMediaBackend::SetPosition(wxLongLong where)
408 TimeRecord theTimeRecord;
409 memset(&theTimeRecord, 0, sizeof(TimeRecord));
410 theTimeRecord.value.lo = where.GetValue();
411 theTimeRecord.scale = ::GetMovieTimeScale(m_movie);
412 theTimeRecord.base = ::GetMovieTimeBase(m_movie);
413 ::SetMovieTime(m_movie, &theTimeRecord);
415 if (::GetMoviesError() != noErr)
421 //---------------------------------------------------------------------------
422 // wxQTMediaBackend::GetPosition
424 // Calls GetMovieTime
425 //---------------------------------------------------------------------------
426 wxLongLong wxQTMediaBackend::GetPosition()
428 return ::GetMovieTime(m_movie, NULL);
431 //---------------------------------------------------------------------------
432 // wxQTMediaBackend::GetDuration
434 // Calls GetMovieDuration
435 //---------------------------------------------------------------------------
436 wxLongLong wxQTMediaBackend::GetDuration()
438 return ::GetMovieDuration(m_movie);
441 //---------------------------------------------------------------------------
442 // wxQTMediaBackend::GetState
444 // Determines the current state - the timer keeps track of whether or not
445 // we are paused or stopped (if the timer is running we are playing)
446 //---------------------------------------------------------------------------
447 wxMediaState wxQTMediaBackend::GetState()
449 if ( !m_timer || (m_timer->IsRunning() == false &&
450 m_timer->GetPaused() == false) )
451 return wxMEDIASTATE_STOPPED;
453 if( m_timer->IsRunning() == true )
454 return wxMEDIASTATE_PLAYING;
456 return wxMEDIASTATE_PAUSED;
459 //---------------------------------------------------------------------------
460 // wxQTMediaBackend::Cleanup
462 // Diposes of the movie timer, Control if native, and stops and disposes
464 //---------------------------------------------------------------------------
465 void wxQTMediaBackend::Cleanup()
470 [[m_movieview movie] release];
471 [m_movieview setMovie:NULL];
474 //---------------------------------------------------------------------------
475 // wxQTMediaBackend::GetVideoSize
477 // Returns the actual size of the QT movie
478 //---------------------------------------------------------------------------
479 wxSize wxQTMediaBackend::GetVideoSize() const
484 //---------------------------------------------------------------------------
485 // wxQTMediaBackend::Move
487 // Nothin... cocoa takes care of this for us
488 //---------------------------------------------------------------------------
489 void wxQTMediaBackend::Move(int x, int y, int w, int h)
494 //in source file that contains stuff you don't directly use
495 #include "wx/html/forcelnk.h"
496 FORCE_LINK_ME(basewxmediabackends);
498 #endif //wxUSE_MEDIACTRL