1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/cocoa/mediactrl.mm
3 // Purpose: Built-in Media Backends for Cocoa
4 // Author: Ryan Norton <wxprojects@comcast.net>
7 // Copyright: (c) 2004-2005 Ryan Norton, (c) 2005 David Elliot
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 //===========================================================================
13 //===========================================================================
15 //---------------------------------------------------------------------------
16 // Pre-compiled header stuff
17 //---------------------------------------------------------------------------
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
26 //---------------------------------------------------------------------------
28 //---------------------------------------------------------------------------
31 #include "wx/mediactrl.h"
37 //===========================================================================
38 // BACKEND DECLARATIONS
39 //===========================================================================
41 //---------------------------------------------------------------------------
45 //---------------------------------------------------------------------------
47 //---------------------------------------------------------------------------
49 //---------------------------------------------------------------------------
50 #include <QuickTime/QuickTime.h>
52 #include "wx/cocoa/autorelease.h"
53 #include "wx/cocoa/string.h"
55 #import <AppKit/NSMovie.h>
56 #import <AppKit/NSMovieView.h>
59 class WXDLLIMPEXP_MEDIA wxQTMediaBackend : public wxMediaBackend
66 virtual bool CreateControl(wxControl* ctrl, wxWindow* parent,
71 const wxValidator& validator,
72 const wxString& name);
78 virtual bool Load(const wxString& fileName);
79 virtual bool Load(const wxURI& location);
81 virtual wxMediaState GetState();
83 virtual bool SetPosition(wxLongLong where);
84 virtual wxLongLong GetPosition();
85 virtual wxLongLong GetDuration();
87 virtual void Move(int x, int y, int w, int h);
88 wxSize GetVideoSize() const;
90 virtual double GetPlaybackRate();
91 virtual bool SetPlaybackRate(double dRate);
96 wxSize m_bestSize; //Original movie size
97 Movie m_movie; //QT Movie handle/instance
98 NSMovieView* m_movieview; //NSMovieView instance
99 wxControl* m_ctrl; //Parent control
100 bool m_bVideo; //Whether or not we have video
101 class _wxQTTimer* m_timer; //Timer for streaming the movie
103 DECLARE_DYNAMIC_CLASS(wxQTMediaBackend);
107 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
111 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
113 IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend, wxMediaBackend);
115 //Time between timer calls
116 #define MOVIE_DELAY 100
118 // --------------------------------------------------------------------------
119 // wxQTTimer - Handle Asyncronous Playing
120 // --------------------------------------------------------------------------
121 class _wxQTTimer : public wxTimer
124 _wxQTTimer(Movie movie, wxQTMediaBackend* parent) :
125 m_movie(movie), m_bPaused(false), m_parent(parent)
133 bool GetPaused() {return m_bPaused;}
134 void SetPaused(bool bPaused) {m_bPaused = bPaused;}
136 //-----------------------------------------------------------------------
137 // _wxQTTimer::Notify
139 // 1) Checks to see if the movie is done, and if not continues
140 // streaming the movie
141 // 2) Sends the wxEVT_MEDIA_STOP event if we have reached the end of
143 //-----------------------------------------------------------------------
148 if(!IsMovieDone(m_movie))
149 MoviesTask(m_movie, MOVIE_DELAY);
152 wxMediaEvent theEvent(wxEVT_MEDIA_STOP,
153 m_parent->m_ctrl->GetId());
154 m_parent->m_ctrl->ProcessEvent(theEvent);
156 if(theEvent.IsAllowed())
160 wxASSERT(::GetMoviesError() == noErr);
162 //send the event to our child
163 wxMediaEvent theEvent(wxEVT_MEDIA_FINISHED,
164 m_parent->m_ctrl->GetId());
165 m_parent->m_ctrl->ProcessEvent(theEvent);
172 Movie m_movie; //Our movie instance
173 bool m_bPaused; //Whether we are paused or not
174 wxQTMediaBackend* m_parent; //Backend pointer
177 //---------------------------------------------------------------------------
178 // wxQTMediaBackend Constructor
180 // Sets m_timer to NULL signifying we havn't loaded anything yet
181 //---------------------------------------------------------------------------
182 wxQTMediaBackend::wxQTMediaBackend() : m_timer(NULL)
186 //---------------------------------------------------------------------------
187 // wxQTMediaBackend Destructor
189 // 1) Cleans up the QuickTime movie instance
190 // 2) Decrements the QuickTime reference counter - if this reaches
191 // 0, QuickTime shuts down
192 // 3) Decrements the QuickTime Windows Media Layer reference counter -
193 // if this reaches 0, QuickTime shuts down the Windows Media Layer
194 //---------------------------------------------------------------------------
195 wxQTMediaBackend::~wxQTMediaBackend()
200 //Note that ExitMovies() is not necessary...
204 //---------------------------------------------------------------------------
205 // wxQTMediaBackend::CreateControl
207 // 1) Intializes QuickTime
208 // 2) Creates the control window
209 //---------------------------------------------------------------------------
210 bool wxQTMediaBackend::CreateControl(wxControl* inctrl, wxWindow* parent,
215 const wxValidator& validator,
216 const wxString& name)
220 wxMediaCtrl* ctrl = (wxMediaCtrl*) inctrl;
222 //Create the control base
223 wxASSERT(ctrl->CreateBase(parent,wid,pos,size,style, validator, name));
225 //Create the NSMovieView
226 ctrl->SetNSView(NULL);
227 NSMovieView* theView = [[NSMovieView alloc] initWithFrame: ctrl->MakeDefaultNSRect(size)];
228 ctrl->SetNSView(theView);
233 parent->AddChild(ctrl);
234 parent->CocoaAddChild(ctrl);
235 ctrl->SetInitialFrameRect(pos,size);
238 [theView showController:false adjustingSize:true];
239 m_movieview = theView;
244 //---------------------------------------------------------------------------
245 // wxQTMediaBackend::Load (file version)
247 // Calls the URI version
248 //---------------------------------------------------------------------------
249 bool wxQTMediaBackend::Load(const wxString& fileName)
253 wxString( wxT("file://") ) + fileName
258 //---------------------------------------------------------------------------
259 // wxQTMediaBackend::Load (URL Version)
261 // 1) Build an escaped URI from location
263 //---------------------------------------------------------------------------
264 bool wxQTMediaBackend::Load(const wxURI& location)
269 wxString theURI = location.BuildURI();
271 [m_movieview setMovie:[[NSMovie alloc] initWithURL: [NSURL URLWithString: wxNSStringWithWxString(theURI)]
272 byReference: YES ] ];
274 m_movie = (Movie) [[m_movieview movie] QTMovie];
276 //preroll movie for streaming
277 //TODO:Async this using threads?
280 timeNow = GetMovieTime(m_movie, NULL);
281 playRate = GetMoviePreferredRate(m_movie);
282 PrePrerollMovie(m_movie, timeNow, playRate, NULL, NULL);
283 PrerollMovie(m_movie, timeNow, playRate);
284 SetMovieRate(m_movie, playRate);
288 return ::GetMoviesError() == noErr;
291 //---------------------------------------------------------------------------
292 // wxQTMediaBackend::FinishLoad
294 // 1) Create the movie timer
295 // 2) Get real size of movie for GetBestSize/sizers
296 // 3) See if there is video in the movie, and if so then either
297 // SetMovieGWorld if < 10.2 or use Native CreateMovieControl
298 // 4) Set the movie time scale to something usable so that seeking
299 // etc. will work correctly
300 // 5) Refresh parent window
301 //---------------------------------------------------------------------------
302 void wxQTMediaBackend::FinishLoad()
304 m_timer = new _wxQTTimer(m_movie, (wxQTMediaBackend*) this);
307 //get the real size of the movie
309 ::GetMovieNaturalBoundsRect (m_movie, &outRect);
310 wxASSERT(::GetMoviesError() == noErr);
312 m_bestSize.x = outRect.right - outRect.left;
313 m_bestSize.y = outRect.bottom - outRect.top;
315 //we want millisecond precision
316 ::SetMovieTimeScale(m_movie, 1000);
317 wxASSERT(::GetMoviesError() == noErr);
320 //Here, if the parent of the control has a sizer - we
321 //tell it to recalculate the size of this control since
322 //the user opened a separate media file
324 m_ctrl->InvalidateBestSize();
325 m_ctrl->GetParent()->Layout();
326 m_ctrl->GetParent()->Refresh();
327 m_ctrl->GetParent()->Update();
330 //---------------------------------------------------------------------------
331 // wxQTMediaBackend::Play
333 // 1) Start the QT movie
334 // 2) Start the movie loading timer
335 //---------------------------------------------------------------------------
336 bool wxQTMediaBackend::Play()
338 ::StartMovie(m_movie);
339 m_timer->SetPaused(false);
340 m_timer->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS);
341 return ::GetMoviesError() == noErr;
344 //---------------------------------------------------------------------------
345 // wxQTMediaBackend::Pause
348 // 2) Stop the movie timer
349 //---------------------------------------------------------------------------
350 bool wxQTMediaBackend::Pause()
352 ::StopMovie(m_movie);
353 m_timer->SetPaused(true);
355 return ::GetMoviesError() == noErr;
358 //---------------------------------------------------------------------------
359 // wxQTMediaBackend::Stop
362 // 2) Stop the movie timer
363 // 3) Seek to the beginning of the movie
364 //---------------------------------------------------------------------------
365 bool wxQTMediaBackend::Stop()
367 m_timer->SetPaused(false);
370 ::StopMovie(m_movie);
371 if(::GetMoviesError() != noErr)
374 ::GoToBeginningOfMovie(m_movie);
375 return ::GetMoviesError() == noErr;
378 //---------------------------------------------------------------------------
379 // wxQTMediaBackend::GetPlaybackRate
381 // 1) Get the movie playback rate from ::GetMovieRate
382 //---------------------------------------------------------------------------
383 double wxQTMediaBackend::GetPlaybackRate()
385 return ( ((double)::GetMovieRate(m_movie)) / 0x10000);
388 //---------------------------------------------------------------------------
389 // wxQTMediaBackend::SetPlaybackRate
391 // 1) Convert dRate to Fixed and Set the movie rate through SetMovieRate
392 //---------------------------------------------------------------------------
393 bool wxQTMediaBackend::SetPlaybackRate(double dRate)
395 ::SetMovieRate(m_movie, (Fixed) (dRate * 0x10000));
396 return ::GetMoviesError() == noErr;
399 //---------------------------------------------------------------------------
400 // wxQTMediaBackend::SetPosition
402 // 1) Create a time record struct (TimeRecord) with appropriate values
403 // 2) Pass struct to SetMovieTime
404 //---------------------------------------------------------------------------
405 bool wxQTMediaBackend::SetPosition(wxLongLong where)
407 TimeRecord theTimeRecord;
408 memset(&theTimeRecord, 0, sizeof(TimeRecord));
409 theTimeRecord.value.lo = where.GetValue();
410 theTimeRecord.scale = ::GetMovieTimeScale(m_movie);
411 theTimeRecord.base = ::GetMovieTimeBase(m_movie);
412 ::SetMovieTime(m_movie, &theTimeRecord);
414 if (::GetMoviesError() != noErr)
420 //---------------------------------------------------------------------------
421 // wxQTMediaBackend::GetPosition
423 // Calls GetMovieTime
424 //---------------------------------------------------------------------------
425 wxLongLong wxQTMediaBackend::GetPosition()
427 return ::GetMovieTime(m_movie, NULL);
430 //---------------------------------------------------------------------------
431 // wxQTMediaBackend::GetDuration
433 // Calls GetMovieDuration
434 //---------------------------------------------------------------------------
435 wxLongLong wxQTMediaBackend::GetDuration()
437 return ::GetMovieDuration(m_movie);
440 //---------------------------------------------------------------------------
441 // wxQTMediaBackend::GetState
443 // Determines the current state - the timer keeps track of whether or not
444 // we are paused or stopped (if the timer is running we are playing)
445 //---------------------------------------------------------------------------
446 wxMediaState wxQTMediaBackend::GetState()
448 if ( !m_timer || (m_timer->IsRunning() == false &&
449 m_timer->GetPaused() == false) )
450 return wxMEDIASTATE_STOPPED;
452 if( m_timer->IsRunning() == true )
453 return wxMEDIASTATE_PLAYING;
455 return wxMEDIASTATE_PAUSED;
458 //---------------------------------------------------------------------------
459 // wxQTMediaBackend::Cleanup
461 // Diposes of the movie timer, Control if native, and stops and disposes
463 //---------------------------------------------------------------------------
464 void wxQTMediaBackend::Cleanup()
469 [[m_movieview movie] release];
470 [m_movieview setMovie:NULL];
473 //---------------------------------------------------------------------------
474 // wxQTMediaBackend::GetVideoSize
476 // Returns the actual size of the QT movie
477 //---------------------------------------------------------------------------
478 wxSize wxQTMediaBackend::GetVideoSize() const
483 //---------------------------------------------------------------------------
484 // wxQTMediaBackend::Move
486 // Nothin... cocoa takes care of this for us
487 //---------------------------------------------------------------------------
488 void wxQTMediaBackend::Move(int x, int y, int w, int h)
493 //in source file that contains stuff you don't directly use
494 #include "wx/html/forcelnk.h"
495 FORCE_LINK_ME(basewxmediabackends);
497 #endif //wxUSE_MEDIACTRL