1///////////////////////////////////////////////////////////////////////////// 2// Name: src/cocoa/mediactrl.mm 3// Purpose: Built-in Media Backends for Cocoa 4// Author: Ryan Norton <wxprojects@comcast.net> 5// Modified by: 6// Created: 02/03/05 7// Copyright: (c) 2004-2005 Ryan Norton, (c) 2005 David Elliot 8// Licence: wxWindows licence 9///////////////////////////////////////////////////////////////////////////// 10 11//=========================================================================== 12// DECLARATIONS 13//=========================================================================== 14 15//--------------------------------------------------------------------------- 16// Pre-compiled header stuff 17//--------------------------------------------------------------------------- 18 19// For compilers that support precompilation, includes "wx.h". 20#include "wx/wxprec.h" 21 22#ifdef __BORLANDC__ 23#pragma hdrstop 24#endif 25 26//--------------------------------------------------------------------------- 27// Compilation guard 28//--------------------------------------------------------------------------- 29#if wxUSE_MEDIACTRL 30 31#include "wx/mediactrl.h" 32 33#ifndef WX_PRECOMP 34 #include "wx/timer.h" 35#endif 36 37//=========================================================================== 38// BACKEND DECLARATIONS 39//=========================================================================== 40 41//--------------------------------------------------------------------------- 42// 43// wxQTMediaBackend 44// 45//--------------------------------------------------------------------------- 46 47//--------------------------------------------------------------------------- 48// QT Includes 49//--------------------------------------------------------------------------- 50#include <QuickTime/QuickTime.h> 51 52#include "wx/cocoa/autorelease.h" 53#include "wx/cocoa/string.h" 54 55#import <AppKit/NSMovie.h> 56#import <AppKit/NSMovieView.h> 57 58 59class WXDLLIMPEXP_MEDIA wxQTMediaBackend : public wxMediaBackend 60{ 61public: 62 63 wxQTMediaBackend(); 64 ~wxQTMediaBackend(); 65 66 virtual bool CreateControl(wxControl* ctrl, wxWindow* parent, 67 wxWindowID id, 68 const wxPoint& pos, 69 const wxSize& size, 70 long style, 71 const wxValidator& validator, 72 const wxString& name); 73 74 virtual bool Play(); 75 virtual bool Pause(); 76 virtual bool Stop(); 77 78 virtual bool Load(const wxString& fileName); 79 virtual bool Load(const wxURI& location); 80 81 virtual wxMediaState GetState(); 82 83 virtual bool SetPosition(wxLongLong where); 84 virtual wxLongLong GetPosition(); 85 virtual wxLongLong GetDuration(); 86 87 virtual void Move(int x, int y, int w, int h); 88 wxSize GetVideoSize() const; 89 90 virtual double GetPlaybackRate(); 91 virtual bool SetPlaybackRate(double dRate); 92 93 void Cleanup(); 94 void FinishLoad(); 95 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 102 103 DECLARE_DYNAMIC_CLASS(wxQTMediaBackend); 104}; 105 106 107//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 108// 109// wxQTMediaBackend 110// 111//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 112 113IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend, wxMediaBackend); 114 115//Time between timer calls 116#define MOVIE_DELAY 100 117 118// -------------------------------------------------------------------------- 119// wxQTTimer - Handle Asyncronous Playing 120// -------------------------------------------------------------------------- 121class _wxQTTimer : public wxTimer 122{ 123public: 124 _wxQTTimer(Movie movie, wxQTMediaBackend* parent) : 125 m_movie(movie), m_bPaused(false), m_parent(parent) 126 { 127 } 128 129 ~_wxQTTimer() 130 { 131 } 132 133 bool GetPaused() {return m_bPaused;} 134 void SetPaused(bool bPaused) {m_bPaused = bPaused;} 135 136 //----------------------------------------------------------------------- 137 // _wxQTTimer::Notify 138 // 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 142 // the movie. 143 //----------------------------------------------------------------------- 144 void Notify() 145 { 146 if (!m_bPaused) 147 { 148 if(!IsMovieDone(m_movie)) 149 MoviesTask(m_movie, MOVIE_DELAY); 150 else 151 { 152 wxMediaEvent theEvent(wxEVT_MEDIA_STOP, 153 m_parent->m_ctrl->GetId()); 154 m_parent->m_ctrl->ProcessEvent(theEvent); 155 156 if(theEvent.IsAllowed()) 157 { 158 Stop(); 159 m_parent->Stop(); 160 wxASSERT(::GetMoviesError() == noErr); 161 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); 166 } 167 } 168 } 169 } 170 171protected: 172 Movie m_movie; //Our movie instance 173 bool m_bPaused; //Whether we are paused or not 174 wxQTMediaBackend* m_parent; //Backend pointer 175}; 176 177//--------------------------------------------------------------------------- 178// wxQTMediaBackend Constructor 179// 180// Sets m_timer to NULL signifying we havn't loaded anything yet 181//--------------------------------------------------------------------------- 182wxQTMediaBackend::wxQTMediaBackend() : m_timer(NULL) 183{ 184} 185 186//--------------------------------------------------------------------------- 187// wxQTMediaBackend Destructor 188// 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//--------------------------------------------------------------------------- 195wxQTMediaBackend::~wxQTMediaBackend() 196{ 197 if(m_timer) 198 Cleanup(); 199 200 //Note that ExitMovies() is not necessary... 201 ExitMovies(); 202} 203 204//--------------------------------------------------------------------------- 205// wxQTMediaBackend::CreateControl 206// 207// 1) Intializes QuickTime 208// 2) Creates the control window 209//--------------------------------------------------------------------------- 210bool wxQTMediaBackend::CreateControl(wxControl* inctrl, wxWindow* parent, 211 wxWindowID wid, 212 const wxPoint& pos, 213 const wxSize& size, 214 long style, 215 const wxValidator& validator, 216 const wxString& name) 217{ 218 EnterMovies(); 219 220 wxMediaCtrl* ctrl = (wxMediaCtrl*) inctrl; 221 222 //Create the control base 223 wxASSERT(ctrl->CreateBase(parent,wid,pos,size,style, validator, name)); 224 225 //Create the NSMovieView 226 ctrl->SetNSView(NULL); 227 NSMovieView* theView = [[NSMovieView alloc] initWithFrame: ctrl->MakeDefaultNSRect(size)]; 228 ctrl->SetNSView(theView); 229 [theView release]; 230 231 if (parent) 232 { 233 parent->AddChild(ctrl); 234 parent->CocoaAddChild(ctrl); 235 ctrl->SetInitialFrameRect(pos,size); 236 } 237 238 [theView showController:false adjustingSize:true]; 239 m_movieview = theView; 240 m_ctrl = ctrl; 241 return true; 242} 243 244//--------------------------------------------------------------------------- 245// wxQTMediaBackend::Load (file version) 246// 247// Calls the URI version 248//--------------------------------------------------------------------------- 249bool wxQTMediaBackend::Load(const wxString& fileName) 250{ 251 return Load( 252 wxURI( 253 wxString( wxT("file://") ) + fileName 254 ) 255 ); 256} 257 258//--------------------------------------------------------------------------- 259// wxQTMediaBackend::Load (URL Version) 260// 261// 1) Build an escaped URI from location 262// ... 263//--------------------------------------------------------------------------- 264bool wxQTMediaBackend::Load(const wxURI& location) 265{ 266 if(m_timer) 267 Cleanup(); 268 269 wxString theURI = location.BuildURI(); 270 271 [m_movieview setMovie:[[NSMovie alloc] initWithURL: [NSURL URLWithString: wxNSStringWithWxString(theURI)] 272 byReference: YES ] ]; 273 274 m_movie = (Movie) [[m_movieview movie] QTMovie]; 275 276 //preroll movie for streaming 277 //TODO:Async this using threads? 278 TimeValue timeNow; 279 Fixed playRate; 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); 285 286 FinishLoad(); 287 288 return ::GetMoviesError() == noErr; 289} 290 291//--------------------------------------------------------------------------- 292// wxQTMediaBackend::FinishLoad 293// 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//--------------------------------------------------------------------------- 302void wxQTMediaBackend::FinishLoad() 303{ 304 m_timer = new _wxQTTimer(m_movie, (wxQTMediaBackend*) this); 305 wxASSERT(m_timer); 306 307 //get the real size of the movie 308 Rect outRect; 309 ::GetMovieNaturalBoundsRect (m_movie, &outRect); 310 wxASSERT(::GetMoviesError() == noErr); 311 312 m_bestSize.x = outRect.right - outRect.left; 313 m_bestSize.y = outRect.bottom - outRect.top; 314 315 //we want millisecond precision 316 ::SetMovieTimeScale(m_movie, 1000); 317 wxASSERT(::GetMoviesError() == noErr); 318 319 // 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 323 // 324 m_ctrl->InvalidateBestSize(); 325 m_ctrl->GetParent()->Layout(); 326 m_ctrl->GetParent()->Refresh(); 327 m_ctrl->GetParent()->Update(); 328} 329 330//--------------------------------------------------------------------------- 331// wxQTMediaBackend::Play 332// 333// 1) Start the QT movie 334// 2) Start the movie loading timer 335//--------------------------------------------------------------------------- 336bool wxQTMediaBackend::Play() 337{ 338 ::StartMovie(m_movie); 339 m_timer->SetPaused(false); 340 m_timer->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS); 341 return ::GetMoviesError() == noErr; 342} 343 344//--------------------------------------------------------------------------- 345// wxQTMediaBackend::Pause 346// 347// 1) Stop the movie 348// 2) Stop the movie timer 349//--------------------------------------------------------------------------- 350bool wxQTMediaBackend::Pause() 351{ 352 ::StopMovie(m_movie); 353 m_timer->SetPaused(true); 354 m_timer->Stop(); 355 return ::GetMoviesError() == noErr; 356} 357 358//--------------------------------------------------------------------------- 359// wxQTMediaBackend::Stop 360// 361// 1) Stop the movie 362// 2) Stop the movie timer 363// 3) Seek to the beginning of the movie 364//--------------------------------------------------------------------------- 365bool wxQTMediaBackend::Stop() 366{ 367 m_timer->SetPaused(false); 368 m_timer->Stop(); 369 370 ::StopMovie(m_movie); 371 if(::GetMoviesError() != noErr) 372 return false; 373 374 ::GoToBeginningOfMovie(m_movie); 375 return ::GetMoviesError() == noErr; 376} 377 378//--------------------------------------------------------------------------- 379// wxQTMediaBackend::GetPlaybackRate 380// 381// 1) Get the movie playback rate from ::GetMovieRate 382//--------------------------------------------------------------------------- 383double wxQTMediaBackend::GetPlaybackRate() 384{ 385 return ( ((double)::GetMovieRate(m_movie)) / 0x10000); 386} 387 388//--------------------------------------------------------------------------- 389// wxQTMediaBackend::SetPlaybackRate 390// 391// 1) Convert dRate to Fixed and Set the movie rate through SetMovieRate 392//--------------------------------------------------------------------------- 393bool wxQTMediaBackend::SetPlaybackRate(double dRate) 394{ 395 ::SetMovieRate(m_movie, (Fixed) (dRate * 0x10000)); 396 return ::GetMoviesError() == noErr; 397} 398 399//--------------------------------------------------------------------------- 400// wxQTMediaBackend::SetPosition 401// 402// 1) Create a time record struct (TimeRecord) with appropriate values 403// 2) Pass struct to SetMovieTime 404//--------------------------------------------------------------------------- 405bool wxQTMediaBackend::SetPosition(wxLongLong where) 406{ 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); 413 414 if (::GetMoviesError() != noErr) 415 return false; 416 417 return true; 418} 419 420//--------------------------------------------------------------------------- 421// wxQTMediaBackend::GetPosition 422// 423// Calls GetMovieTime 424//--------------------------------------------------------------------------- 425wxLongLong wxQTMediaBackend::GetPosition() 426{ 427 return ::GetMovieTime(m_movie, NULL); 428} 429 430//--------------------------------------------------------------------------- 431// wxQTMediaBackend::GetDuration 432// 433// Calls GetMovieDuration 434//--------------------------------------------------------------------------- 435wxLongLong wxQTMediaBackend::GetDuration() 436{ 437 return ::GetMovieDuration(m_movie); 438} 439 440//--------------------------------------------------------------------------- 441// wxQTMediaBackend::GetState 442// 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//--------------------------------------------------------------------------- 446wxMediaState wxQTMediaBackend::GetState() 447{ 448 if ( !m_timer || (m_timer->IsRunning() == false && 449 m_timer->GetPaused() == false) ) 450 return wxMEDIASTATE_STOPPED; 451 452 if( m_timer->IsRunning() == true ) 453 return wxMEDIASTATE_PLAYING; 454 else 455 return wxMEDIASTATE_PAUSED; 456} 457 458//--------------------------------------------------------------------------- 459// wxQTMediaBackend::Cleanup 460// 461// Diposes of the movie timer, Control if native, and stops and disposes 462// of the QT movie 463//--------------------------------------------------------------------------- 464void wxQTMediaBackend::Cleanup() 465{ 466 delete m_timer; 467 m_timer = NULL; 468 469 [[m_movieview movie] release]; 470 [m_movieview setMovie:NULL]; 471} 472 473//--------------------------------------------------------------------------- 474// wxQTMediaBackend::GetVideoSize 475// 476// Returns the actual size of the QT movie 477//--------------------------------------------------------------------------- 478wxSize wxQTMediaBackend::GetVideoSize() const 479{ 480 return m_bestSize; 481} 482 483//--------------------------------------------------------------------------- 484// wxQTMediaBackend::Move 485// 486// Nothin... cocoa takes care of this for us 487//--------------------------------------------------------------------------- 488void wxQTMediaBackend::Move(int x, int y, int w, int h) 489{ 490} 491 492 493//in source file that contains stuff you don't directly use 494#include "wx/html/forcelnk.h" 495FORCE_LINK_ME(basewxmediabackends); 496 497#endif //wxUSE_MEDIACTRL 498