MacOSX/Framework/VLCMediaPlayer.m: Enable bindings uses on the @"time" key.
[vlc.git] / extras / MacOSX / Framework / Sources / VLCMediaPlayer.m
1 /*****************************************************************************
2  * VLCMediaPlayer.m: VLC.framework VLCMediaPlayer implementation
3  *****************************************************************************
4  * Copyright (C) 2007 Pierre d'Herbemont
5  * Copyright (C) 2007 the VideoLAN team
6  * $Id$
7  *
8  * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
9  *          Faustion Osuna <enrique.osuna # gmail.com>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 #import "VLCLibrary.h"
27 #import "VLCMediaPlayer.h"
28 #import "VLCEventManager.h"
29 #import "VLCLibVLCBridging.h"
30 #include <vlc/vlc.h>
31
32 /* Notification Messages */
33 NSString *VLCMediaPlayerTimeChanged   = @"VLCMediaPlayerTimeChanged";
34 NSString *VLCMediaPlayerStateChanged  = @"VLCMediaPlayerStateChanged";
35
36 /* libvlc event callback */
37 static void HandleMediaInstanceVolumeChanged(const libvlc_event_t *event, void *self)
38 {
39     [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
40                                                    withDelegateMethod:@selector(mediaPlayerVolumeChanged:)
41                                                  withNotificationName:VLCMediaPlayerVolumeChanged];
42 }
43
44 static void HandleMediaTimeChanged(const libvlc_event_t * event, void * self)
45 {
46     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
47     [[VLCEventManager sharedManager] callOnMainThreadObject:self 
48                                                  withMethod:@selector(mediaPlayerTimeChanged:) 
49                                        withArgumentAsObject:[NSNumber numberWithLongLong:event->u.media_instance_position_changed.new_position]];
50
51     [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
52                                                    withDelegateMethod:@selector(mediaPlayerTimeChanged:)
53                                                  withNotificationName:VLCMediaPlayerTimeChanged];
54     [pool release];
55 }
56
57 static void HandleMediaInstanceStateChanged(const libvlc_event_t *event, void *self)
58 {
59     [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
60                                                    withDelegateMethod:@selector(mediaPlayerStateChanged:)
61                                                  withNotificationName:VLCMediaPlayerStateChanged];
62 }
63
64 NSString *VLCMediaPlayerStateToString(VLCMediaPlayerState state)
65 {
66     static NSString *stateToStrings[] = {
67         [VLCMediaPlayerStateStopped]      = @"VLCMediaPlayerStateStopped",
68         [VLCMediaPlayerStateOpening]      = @"VLCMediaPlayerStateOpening",
69         [VLCMediaPlayerStateBuffering]    = @"VLCMediaPlayerStateBuffering",
70         [VLCMediaPlayerStateEnded]        = @"VLCMediaPlayerStateEnded",
71         [VLCMediaPlayerStateError]        = @"VLCMediaPlayerStateError",
72         [VLCMediaPlayerStatePlaying]      = @"VLCMediaPlayerStatePlaying",
73         [VLCMediaPlayerStatePaused]       = @"VLCMediaPlayerStatePaused"
74     };
75     return stateToStrings[state];
76 }
77
78 // TODO: Documentation
79 @interface VLCMediaPlayer (Private)
80 - (void)registerObservers;
81 - (void)unregisterObservers;
82 - (void)mediaPlayerTimeChanged:(NSNumber *)newTime;
83 @end
84
85 @implementation VLCMediaPlayer
86 - (id)init
87 {
88     return [self initWithVideoView:nil];
89 }
90
91 - (id)initWithVideoView:(VLCVideoView *)aVideoView
92 {
93     if (self = [super init])
94     {
95         delegate = nil;
96         media = nil;
97         cachedTime = [[VLCTime nullTime] retain];
98         // Create a media instance, it doesn't matter what library we start off with
99         // it will change depending on the media descriptor provided to the media
100         // instance
101         libvlc_exception_t ex;
102         libvlc_exception_init( &ex );
103         instance = (void *)libvlc_media_instance_new([VLCLibrary sharedInstance], &ex);
104         quit_on_exception( &ex );
105         
106         [self registerObservers];
107         
108         [self setVideoView:aVideoView];
109     }
110     return self;
111 }
112
113 - (void)dealloc
114 {
115     // Always get rid of the delegate first so we can stop sending messages to it
116     // TODO: Should we tell the delegate that we're shutting down?
117     delegate = nil;
118
119     // Next get rid of the event managers so we can stop trapping events
120     [self unregisterObservers];
121     libvlc_media_instance_release((libvlc_media_instance_t *)instance);
122     
123     // Get rid of everything else
124     [media release];
125     [cachedTime release];
126
127     [super dealloc];
128 }
129
130 - (void)setDelegate:(id)value
131 {
132     delegate = value;
133 }
134
135 - (id)delegate
136 {
137     return delegate;
138 }
139
140 - (void)setVideoView:(VLCVideoView *)value
141 {
142     videoView = value;
143     
144     // Make sure that this instance has been associated with the drawing canvas.
145     libvlc_exception_t ex;
146     libvlc_exception_init( &ex );
147     libvlc_media_instance_set_drawable ((libvlc_media_instance_t *)instance, 
148                                         (libvlc_drawable_t)videoView, 
149                                         &ex);
150     quit_on_exception( &ex );
151 }
152
153 - (VLCVideoView *)videoView
154 {
155     return videoView;
156 }
157
158 - (void)setFullscreen:(BOOL)value
159 {
160     libvlc_set_fullscreen(instance, value, NULL);
161 }
162
163 - (BOOL)fullscreen
164 {
165     libvlc_exception_t ex;
166     libvlc_exception_init( &ex );
167     int result = libvlc_get_fullscreen( instance, &ex );
168     quit_on_exception( &ex );
169     return result;
170 }
171
172 - (void)setVideoAspectRatio:(char *)value
173 {
174     libvlc_video_set_aspect_ratio( instance, value, NULL );
175 }
176
177 - (char *)videoAspectRatio
178 {
179     libvlc_exception_t ex;
180     libvlc_exception_init( &ex );
181     char *result = libvlc_video_get_aspect_ratio( instance, &ex );
182     quit_on_exception( &ex );
183     return result;
184 }
185
186 - (void)setVideoSubTitles:(int)value
187 {
188     libvlc_video_set_spu( instance, value, NULL );
189 }
190
191 - (int)videoSubTitles
192 {
193     libvlc_exception_t ex;
194     libvlc_exception_init( &ex );
195     int result = libvlc_video_get_spu( instance, &ex );
196     quit_on_exception( &ex );
197     return result;
198 }
199
200 - (void)setVideoCropGeometry:(char *)value
201 {
202     libvlc_video_set_crop_geometry( instance, value, NULL );
203 }
204
205 - (char *)videoCropGeometry
206 {
207     libvlc_exception_t ex;
208     libvlc_exception_init( &ex );
209     char *result = libvlc_video_get_crop_geometry( instance, &ex );
210     quit_on_exception( &ex );
211     return result;
212 }
213
214 - (void)setVideoTeleText:(int)value
215 {
216     libvlc_video_set_teletext( instance, value, NULL );
217 }
218
219 - (int)videoTeleText
220 {
221     libvlc_exception_t ex;
222     libvlc_exception_init( &ex );
223     int result = libvlc_video_get_teletext( instance, &ex );
224     quit_on_exception( &ex );
225     return result;
226 }
227
228 - (void)setRate:(int)value
229 {
230     libvlc_media_instance_set_rate( instance, value, NULL );
231 }
232
233 - (int)rate
234 {
235     libvlc_exception_t ex;
236     libvlc_exception_init( &ex );
237     float result = libvlc_media_instance_get_rate( instance, &ex );
238     quit_on_exception( &ex );
239     return result;
240 }
241
242 - (NSSize)videoSize
243 {
244     libvlc_exception_t ex;
245     libvlc_exception_init( &ex );
246     NSSize result = NSMakeSize(libvlc_video_get_height((libvlc_media_instance_t *)instance, &ex),
247                                libvlc_video_get_width((libvlc_media_instance_t *)instance, &ex));
248     quit_on_exception( &ex );
249     return result;    
250 }
251
252 - (BOOL)hasVideoOut
253 {
254     libvlc_exception_t ex;
255     libvlc_exception_init( &ex );
256     BOOL result = libvlc_media_instance_has_vout((libvlc_media_instance_t *)instance, &ex);
257     if (libvlc_exception_raised( &ex ))
258     {
259         libvlc_exception_clear( &ex );
260         return NO;
261     }
262     else
263         return result;
264 }
265
266 - (float)framesPerSecond
267 {
268     libvlc_exception_t ex;
269     libvlc_exception_init( &ex );
270     float result = libvlc_media_instance_get_fps( (libvlc_media_instance_t *)instance, &ex );
271     quit_on_exception( &ex );
272     return result;
273 }
274
275 - (void)setTime:(VLCTime *)value
276 {
277     libvlc_exception_t ex;
278     libvlc_exception_init( &ex );
279     // Time is managed in seconds, while duration is managed in microseconds
280     // TODO: Redo VLCTime to provide value numberAsMilliseconds, numberAsMicroseconds, numberAsSeconds, numberAsMinutes, numberAsHours
281     libvlc_media_instance_set_time( (libvlc_media_instance_t *)instance, 
282                                     (value ? [[value numberValue] longLongValue] / 1000 : 0),
283                                     &ex );
284     quit_on_exception( &ex );
285 }
286
287 - (VLCTime *)time
288 {
289     return cachedTime;
290 }
291
292 - (void)setChapter:(int)value;
293 {
294     libvlc_media_instance_set_chapter( instance, value, NULL );
295 }
296
297 - (int)chapter
298 {
299     libvlc_exception_t ex;
300     libvlc_exception_init( &ex );
301     int result = libvlc_media_instance_get_chapter( instance, &ex );
302     quit_on_exception( &ex );
303     return result;
304 }
305
306 - (int)countOfChapters
307 {
308     libvlc_exception_t ex;
309     libvlc_exception_init( &ex );
310     int result = libvlc_media_instance_get_chapter_count( instance, &ex );
311     quit_on_exception( &ex );
312     return result;
313 }
314
315 - (void)setAudioTrack:(int)value
316 {
317     libvlc_audio_set_track( instance, value, NULL );
318 }
319
320 - (int)audioTrack
321 {
322     libvlc_exception_t ex;
323     libvlc_exception_init( &ex );
324     int result = libvlc_audio_get_track( instance, &ex );
325     quit_on_exception( &ex );
326     return result;
327 }
328
329 - (int)countOfAudioTracks
330 {
331     libvlc_exception_t ex;
332     libvlc_exception_init( &ex );
333     int result = libvlc_audio_get_track_count( instance, &ex );
334     quit_on_exception( &ex );
335     return result;
336 }
337
338 - (void)setAudioChannel:(int)value
339 {
340     libvlc_audio_set_channel( instance, value, NULL );
341 }
342
343 - (int)audioChannel
344 {
345     libvlc_exception_t ex;
346     libvlc_exception_init( &ex );
347     int result = libvlc_audio_get_channel( instance, &ex );
348     quit_on_exception( &ex );
349     return result;
350 }
351
352 - (void)setMedia:(VLCMedia *)value
353 {
354     // We only know how to play media files...not media resources with subitems
355     if (media != value && [media subitems] == nil)
356     {
357         if (media && [media compare:value] == NSOrderedSame)
358             return;
359         
360         BOOL wasPlaying;
361         if (wasPlaying = [self isPlaying])
362         {
363             [self pause];
364 //            // TODO: Should we wait until it stops playing?
365 //            while ([self isPlaying])
366 //                usleep(1000);
367         }
368         
369         [self willChangeValueForKey:@"media"];
370         [media release];
371         media = [value retain];
372         [self didChangeValueForKey:@"media"];
373
374         libvlc_exception_t ex;
375         libvlc_exception_init( &ex );
376         libvlc_media_instance_set_media_descriptor( instance, [media libVLCMediaDescriptor], &ex );
377         quit_on_exception( &ex );
378         
379         if (media) {
380             if (wasPlaying)
381                 [self play];
382         }
383     }
384 }
385
386 - (VLCMedia *)media
387 {
388     return media;
389 }
390
391 - (BOOL)play
392 {
393     // Return if there is no media available or if the stream is already playing something
394     if (!media || [self isPlaying])
395         return [self isPlaying];
396     
397     libvlc_exception_t ex;
398     libvlc_exception_init( &ex );
399
400     libvlc_media_instance_play( (libvlc_media_instance_t *)instance, &ex );
401     quit_on_exception( &ex );
402
403     return YES;
404 }
405
406 - (void)pause
407 {
408     // Return if there is no media available or if the stream is not paused or 
409     // playing something else
410     if (!media || (![self isPlaying] && [self state] != VLCMediaPlayerStatePaused))
411         return;
412
413     // Should never get here.
414     if (!instance)
415         return;
416
417     // Pause the stream
418     libvlc_exception_t ex;
419     libvlc_exception_init( &ex );
420     libvlc_media_instance_pause( (libvlc_media_instance_t *)instance, &ex );
421     quit_on_exception( &ex );
422     
423     // TODO: Should we record the time in case the media instance is destroyed
424     // then rebuilt?
425 }
426
427 - (void)stop
428 {
429     // Return if there is no media available or if the system is not in play status 
430     // or pause status.
431     if (!media || (![self isPlaying] && [self state] != VLCMediaPlayerStatePaused))
432         return;
433     
434     // The following is not implemented in the core, should I fix it or just
435     // compensate?
436     //    libvlc_exception_t ex;
437     //    libvlc_exception_init( &ex );
438     //    libvlc_media_instance_stop((libvlc_media_instance_t *)instance, &ex);
439     //    quit_on_exception( &ex );
440     
441     // Pause and reposition to the begining of the stream.
442     [self pause];
443     [self setTime:0];
444     // TODO: Should we pause this or destroy the media instance so that it appears as being "stopped"?
445 }
446
447 //- (void)fastForward;
448 //- (void)fastForwardAtRate:(int)rate;
449 //- (void)rewind;
450 //- (void)rewindAtRate:(int)rate;
451
452 - (BOOL)isPlaying
453 {
454     VLCMediaPlayerState state = [self state];
455     return ((state == VLCMediaPlayerStateOpening) || (state == VLCMediaPlayerStateBuffering) ||
456             (state == VLCMediaPlayerStatePlaying));
457 }
458
459 - (BOOL)willPlay
460 {
461     libvlc_exception_t ex;
462     libvlc_exception_init( &ex );
463     BOOL ret = libvlc_media_instance_will_play( (libvlc_media_instance_t *)instance, &ex );
464     if (libvlc_exception_raised(&ex))
465     {
466         libvlc_exception_clear(&ex);
467         return NO;
468     }
469     else
470         return ret;
471 }
472
473 static const VLCMediaPlayerState libvlc_to_local_state[] =
474 {
475     [libvlc_Stopped]    = VLCMediaPlayerStateStopped,
476     [libvlc_Opening]    = VLCMediaPlayerStateOpening,
477     [libvlc_Buffering]  = VLCMediaPlayerStateBuffering,
478     [libvlc_Playing]    = VLCMediaPlayerStatePlaying,
479     [libvlc_Paused]     = VLCMediaPlayerStatePaused,
480     [libvlc_Ended]      = VLCMediaPlayerStateEnded,
481     [libvlc_Error]      = VLCMediaPlayerStateError
482 };
483
484 - (VLCMediaPlayerState)state
485 {
486     // If there is no instance, assume that we're in a stopped state
487     if (!instance)
488         return VLCMediaPlayerStateStopped;
489     
490     libvlc_exception_t ex;
491     libvlc_exception_init( &ex );
492     libvlc_state_t libvlc_state = libvlc_media_instance_get_state( (libvlc_media_instance_t *)instance, &ex );
493     if (libvlc_exception_raised( &ex ))
494     {
495         libvlc_exception_clear( &ex );
496         return VLCMediaPlayerStateError;
497     }
498     else
499         return libvlc_to_local_state[libvlc_state];
500 }
501 @end
502
503 @implementation VLCMediaPlayer (Private)
504 - (void)registerObservers
505 {
506     libvlc_exception_t ex;
507     libvlc_exception_init( &ex );
508
509     // Attach event observers into the media instance
510     libvlc_event_manager_t *p_em = libvlc_media_instance_event_manager( instance, &ex );
511     libvlc_event_attach( p_em, libvlc_MediaInstancePlayed,          HandleMediaInstanceStateChanged, self, &ex );
512     libvlc_event_attach( p_em, libvlc_MediaInstancePaused,          HandleMediaInstanceStateChanged, self, &ex );
513     libvlc_event_attach( p_em, libvlc_MediaInstanceReachedEnd,      HandleMediaInstanceStateChanged, self, &ex );
514     /* FIXME: We may want to turn that off when none is interested by that */
515     libvlc_event_attach( p_em, libvlc_MediaInstancePositionChanged, HandleMediaTimeChanged,            self, &ex );
516     quit_on_exception( &ex );
517 }
518
519 - (void)unregisterObservers
520 {
521     libvlc_event_manager_t *p_em = libvlc_media_instance_event_manager( instance, NULL );
522     libvlc_event_detach( p_em, libvlc_MediaInstancePlayed,          HandleMediaInstanceStateChanged, self, NULL );
523     libvlc_event_detach( p_em, libvlc_MediaInstancePaused,          HandleMediaInstanceStateChanged, self, NULL );
524     libvlc_event_detach( p_em, libvlc_MediaInstanceReachedEnd,      HandleMediaInstanceStateChanged, self, NULL );
525     libvlc_event_detach( p_em, libvlc_MediaInstancePositionChanged, HandleMediaTimeChanged,            self, NULL );
526 }
527 - (void)mediaPlayerTimeChanged:(NSNumber *)newTime
528 {
529     [self willChangeValueForKey:@"time"];
530     [cachedTime release];
531     cachedTime = [[VLCTime timeWithNumber:newTime] retain];
532     [self didChangeValueForKey:@"time"];
533 }
534 @end