]>
Commit | Line | Data |
---|---|---|
4fc81cbc | 1 | ///////////////////////////////////////////////////////////////////////////// |
80fdcdb9 | 2 | // Name: src/cocoa/mediactrl.mm |
4fc81cbc RN |
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 | ||
4fc81cbc RN |
20 | // For compilers that support precompilation, includes "wx.h". |
21 | #include "wx/wxprec.h" | |
22 | ||
23 | #ifdef __BORLANDC__ | |
24 | #pragma hdrstop | |
25 | #endif | |
26 | ||
4fc81cbc RN |
27 | //--------------------------------------------------------------------------- |
28 | // Compilation guard | |
29 | //--------------------------------------------------------------------------- | |
30 | #if wxUSE_MEDIACTRL | |
31 | ||
c0badb70 WS |
32 | #include "wx/mediactrl.h" |
33 | ||
34 | #ifndef WX_PRECOMP | |
35 | #include "wx/timer.h" | |
36 | #endif | |
37 | ||
4fc81cbc RN |
38 | //=========================================================================== |
39 | // BACKEND DECLARATIONS | |
40 | //=========================================================================== | |
41 | ||
42 | //--------------------------------------------------------------------------- | |
43 | // | |
44 | // wxQTMediaBackend | |
45 | // | |
46 | //--------------------------------------------------------------------------- | |
47 | ||
48 | //--------------------------------------------------------------------------- | |
49 | // QT Includes | |
50 | //--------------------------------------------------------------------------- | |
4fc81cbc RN |
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 | ||
7ec69821 | 67 | virtual bool CreateControl(wxControl* ctrl, wxWindow* parent, |
4fc81cbc | 68 | wxWindowID id, |
7ec69821 | 69 | const wxPoint& pos, |
4fc81cbc | 70 | const wxSize& size, |
7ec69821 | 71 | long style, |
4fc81cbc RN |
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 | |
7ec69821 | 98 | Movie m_movie; //QT Movie handle/instance |
4fc81cbc RN |
99 | NSMovieView* m_movieview; //NSMovieView instance |
100 | wxControl* m_ctrl; //Parent control | |
101 | bool m_bVideo; //Whether or not we have video | |
0b6d4714 | 102 | class _wxQTTimer* m_timer; //Timer for streaming the movie |
4fc81cbc RN |
103 | |
104 | DECLARE_DYNAMIC_CLASS(wxQTMediaBackend); | |
105 | }; | |
106 | ||
107 | ||
108 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | |
109 | // | |
110 | // wxQTMediaBackend | |
7ec69821 | 111 | // |
4fc81cbc RN |
112 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
113 | ||
114 | IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend, wxMediaBackend); | |
115 | ||
0b6d4714 RN |
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)) | |
7ec69821 | 150 | MoviesTask(m_movie, MOVIE_DELAY); |
0b6d4714 RN |
151 | else |
152 | { | |
7ec69821 | 153 | wxMediaEvent theEvent(wxEVT_MEDIA_STOP, |
0b6d4714 RN |
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 | |
7ec69821 | 164 | wxMediaEvent theEvent(wxEVT_MEDIA_FINISHED, |
0b6d4714 RN |
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 | ||
4fc81cbc RN |
178 | //--------------------------------------------------------------------------- |
179 | // wxQTMediaBackend Constructor | |
180 | // | |
181 | // Sets m_timer to NULL signifying we havn't loaded anything yet | |
182 | //--------------------------------------------------------------------------- | |
0b6d4714 | 183 | wxQTMediaBackend::wxQTMediaBackend() : m_timer(NULL) |
4fc81cbc RN |
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 | { | |
0b6d4714 RN |
198 | if(m_timer) |
199 | Cleanup(); | |
4fc81cbc | 200 | |
3103e8a9 | 201 | //Note that ExitMovies() is not necessary... |
0b6d4714 | 202 | ExitMovies(); |
4fc81cbc RN |
203 | } |
204 | ||
205 | //--------------------------------------------------------------------------- | |
206 | // wxQTMediaBackend::CreateControl | |
207 | // | |
208 | // 1) Intializes QuickTime | |
209 | // 2) Creates the control window | |
210 | //--------------------------------------------------------------------------- | |
7ec69821 | 211 | bool wxQTMediaBackend::CreateControl(wxControl* inctrl, wxWindow* parent, |
4fc81cbc | 212 | wxWindowID wid, |
7ec69821 | 213 | const wxPoint& pos, |
4fc81cbc | 214 | const wxSize& size, |
7ec69821 | 215 | long style, |
4fc81cbc RN |
216 | const wxValidator& validator, |
217 | const wxString& name) | |
218 | { | |
219 | EnterMovies(); | |
7ec69821 | 220 | |
4fc81cbc RN |
221 | wxMediaCtrl* ctrl = (wxMediaCtrl*) inctrl; |
222 | ||
223 | //Create the control base | |
ecd20d4a | 224 | wxASSERT(ctrl->CreateBase(parent,wid,pos,size,style, validator, name)); |
7ec69821 | 225 | |
4fc81cbc RN |
226 | //Create the NSMovieView |
227 | ctrl->SetNSView(NULL); | |
ecd20d4a | 228 | NSMovieView* theView = [[NSMovieView alloc] initWithFrame: ctrl->MakeDefaultNSRect(size)]; |
4fc81cbc RN |
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 | } | |
0b6d4714 | 238 | |
ecd20d4a | 239 | [theView showController:false adjustingSize:true]; |
4fc81cbc RN |
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( | |
7ec69821 | 254 | wxString( wxT("file://") ) + fileName |
4fc81cbc RN |
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 | { | |
0b6d4714 RN |
267 | if(m_timer) |
268 | Cleanup(); | |
4fc81cbc RN |
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 | ||
0b6d4714 RN |
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 | ||
4fc81cbc RN |
287 | FinishLoad(); |
288 | ||
289 | return ::GetMoviesError() == noErr; | |
290 | } | |
291 | ||
292 | //--------------------------------------------------------------------------- | |
293 | // wxQTMediaBackend::FinishLoad | |
0b6d4714 RN |
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 | |
4fc81cbc RN |
302 | //--------------------------------------------------------------------------- |
303 | void wxQTMediaBackend::FinishLoad() | |
304 | { | |
0b6d4714 RN |
305 | m_timer = new _wxQTTimer(m_movie, (wxQTMediaBackend*) this); |
306 | wxASSERT(m_timer); | |
307 | ||
4fc81cbc RN |
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 | |
3103e8a9 | 323 | //the user opened a separate media file |
4fc81cbc RN |
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 | { | |
0b6d4714 RN |
339 | ::StartMovie(m_movie); |
340 | m_timer->SetPaused(false); | |
341 | m_timer->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS); | |
4fc81cbc RN |
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 | { | |
0b6d4714 RN |
353 | ::StopMovie(m_movie); |
354 | m_timer->SetPaused(true); | |
355 | m_timer->Stop(); | |
4fc81cbc RN |
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 | { | |
0b6d4714 RN |
368 | m_timer->SetPaused(false); |
369 | m_timer->Stop(); | |
370 | ||
371 | ::StopMovie(m_movie); | |
372 | if(::GetMoviesError() != noErr) | |
373 | return false; | |
7ec69821 | 374 | |
0b6d4714 | 375 | ::GoToBeginningOfMovie(m_movie); |
4fc81cbc RN |
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 | { | |
7ec69821 | 449 | if ( !m_timer || (m_timer->IsRunning() == false && |
0b6d4714 | 450 | m_timer->GetPaused() == false) ) |
ecd20d4a | 451 | return wxMEDIASTATE_STOPPED; |
0b6d4714 RN |
452 | |
453 | if( m_timer->IsRunning() == true ) | |
454 | return wxMEDIASTATE_PLAYING; | |
4fc81cbc RN |
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 | { | |
0b6d4714 RN |
467 | delete m_timer; |
468 | m_timer = NULL; | |
469 | ||
470 | [[m_movieview movie] release]; | |
471 | [m_movieview setMovie:NULL]; | |
4fc81cbc RN |
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 | |
7ec69821 | 495 | #include "wx/html/forcelnk.h" |
4fc81cbc RN |
496 | FORCE_LINK_ME(basewxmediabackends); |
497 | ||
498 | #endif //wxUSE_MEDIACTRL |