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