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