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