]>
Commit | Line | Data |
---|---|---|
4fc81cbc RN |
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 | //--------------------------------------------------------------------------- | |
0b6d4714 | 54 | #include "wx/timer.h" |
4fc81cbc RN |
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 | |
0b6d4714 | 106 | class _wxQTTimer* m_timer; //Timer for streaming the movie |
4fc81cbc RN |
107 | |
108 | DECLARE_DYNAMIC_CLASS(wxQTMediaBackend); | |
109 | }; | |
110 | ||
111 | ||
112 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | |
113 | // | |
114 | // wxQTMediaBackend | |
115 | // | |
116 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | |
117 | ||
118 | IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend, wxMediaBackend); | |
119 | ||
0b6d4714 RN |
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 | ||
4fc81cbc RN |
182 | //--------------------------------------------------------------------------- |
183 | // wxQTMediaBackend Constructor | |
184 | // | |
185 | // Sets m_timer to NULL signifying we havn't loaded anything yet | |
186 | //--------------------------------------------------------------------------- | |
0b6d4714 | 187 | wxQTMediaBackend::wxQTMediaBackend() : m_timer(NULL) |
4fc81cbc RN |
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 | { | |
0b6d4714 RN |
202 | if(m_timer) |
203 | Cleanup(); | |
4fc81cbc | 204 | |
3103e8a9 | 205 | //Note that ExitMovies() is not necessary... |
0b6d4714 | 206 | ExitMovies(); |
4fc81cbc RN |
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 | |
ecd20d4a | 228 | wxASSERT(ctrl->CreateBase(parent,wid,pos,size,style, validator, name)); |
4fc81cbc RN |
229 | |
230 | //Create the NSMovieView | |
231 | ctrl->SetNSView(NULL); | |
ecd20d4a | 232 | NSMovieView* theView = [[NSMovieView alloc] initWithFrame: ctrl->MakeDefaultNSRect(size)]; |
4fc81cbc RN |
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 | } | |
0b6d4714 | 242 | |
ecd20d4a | 243 | [theView showController:false adjustingSize:true]; |
4fc81cbc RN |
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 | { | |
0b6d4714 RN |
271 | if(m_timer) |
272 | Cleanup(); | |
4fc81cbc RN |
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 | ||
0b6d4714 RN |
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 | ||
4fc81cbc RN |
291 | FinishLoad(); |
292 | ||
293 | return ::GetMoviesError() == noErr; | |
294 | } | |
295 | ||
296 | //--------------------------------------------------------------------------- | |
297 | // wxQTMediaBackend::FinishLoad | |
0b6d4714 RN |
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 | |
4fc81cbc RN |
306 | //--------------------------------------------------------------------------- |
307 | void wxQTMediaBackend::FinishLoad() | |
308 | { | |
0b6d4714 RN |
309 | m_timer = new _wxQTTimer(m_movie, (wxQTMediaBackend*) this); |
310 | wxASSERT(m_timer); | |
311 | ||
4fc81cbc RN |
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 | |
3103e8a9 | 327 | //the user opened a separate media file |
4fc81cbc RN |
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 | { | |
0b6d4714 RN |
343 | ::StartMovie(m_movie); |
344 | m_timer->SetPaused(false); | |
345 | m_timer->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS); | |
4fc81cbc RN |
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 | { | |
0b6d4714 RN |
357 | ::StopMovie(m_movie); |
358 | m_timer->SetPaused(true); | |
359 | m_timer->Stop(); | |
4fc81cbc RN |
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 | { | |
0b6d4714 RN |
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); | |
4fc81cbc RN |
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 | { | |
0b6d4714 RN |
453 | if ( !m_timer || (m_timer->IsRunning() == false && |
454 | m_timer->GetPaused() == false) ) | |
ecd20d4a | 455 | return wxMEDIASTATE_STOPPED; |
0b6d4714 RN |
456 | |
457 | if( m_timer->IsRunning() == true ) | |
458 | return wxMEDIASTATE_PLAYING; | |
4fc81cbc RN |
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 | { | |
0b6d4714 RN |
471 | delete m_timer; |
472 | m_timer = NULL; | |
473 | ||
474 | [[m_movieview movie] release]; | |
475 | [m_movieview setMovie:NULL]; | |
4fc81cbc RN |
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 |