No changes, synchronised source names that appear commented at the top of files with...
[wxWidgets.git] / src / cocoa / mediactrl.mm
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 // 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