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