]> git.saurik.com Git - wxWidgets.git/blob - src/cocoa/mediactrl.mm
better implementation from VZ
[wxWidgets.git] / src / cocoa / mediactrl.mm
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$
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
60 class WXDLLIMPEXP_MEDIA wxQTMediaBackend : public wxMediaBackend
61 {
62 public:
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
114 IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend, wxMediaBackend);
115
116 //Time between timer calls
117 #define MOVIE_DELAY 100
118
119 // --------------------------------------------------------------------------
120 // wxQTTimer - Handle Asyncronous Playing
121 // --------------------------------------------------------------------------
122 class _wxQTTimer : public wxTimer
123 {
124 public:
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
172 protected:
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 //---------------------------------------------------------------------------
183 wxQTMediaBackend::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 //---------------------------------------------------------------------------
196 wxQTMediaBackend::~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 //---------------------------------------------------------------------------
211 bool 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 //---------------------------------------------------------------------------
250 bool 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 //---------------------------------------------------------------------------
265 bool 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 //---------------------------------------------------------------------------
303 void 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 //---------------------------------------------------------------------------
337 bool 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 //---------------------------------------------------------------------------
351 bool 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 //---------------------------------------------------------------------------
366 bool 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 //---------------------------------------------------------------------------
384 double 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 //---------------------------------------------------------------------------
394 bool 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 //---------------------------------------------------------------------------
406 bool 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 //---------------------------------------------------------------------------
426 wxLongLong wxQTMediaBackend::GetPosition()
427 {
428 return ::GetMovieTime(m_movie, NULL);
429 }
430
431 //---------------------------------------------------------------------------
432 // wxQTMediaBackend::GetDuration
433 //
434 // Calls GetMovieDuration
435 //---------------------------------------------------------------------------
436 wxLongLong 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 //---------------------------------------------------------------------------
447 wxMediaState 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 //---------------------------------------------------------------------------
465 void 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 //---------------------------------------------------------------------------
479 wxSize 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 //---------------------------------------------------------------------------
489 void 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"
496 FORCE_LINK_ME(basewxmediabackends);
497
498 #endif //wxUSE_MEDIACTRL