]> git.sesse.net Git - vlc/blob - projects/macosx/framework/Sources/VLCMediaPlayer.m
osx/framework: added methods to access end-user-compliant names of subtitles, audio...
[vlc] / projects / macosx / framework / Sources / VLCMediaPlayer.m
1 /*****************************************************************************
2  * VLCMediaPlayer.m: VLCKit.framework VLCMediaPlayer implementation
3  *****************************************************************************
4  * Copyright (C) 2007-2009 Pierre d'Herbemont
5  * Copyright (C) 2007-2009 the VideoLAN team
6  * Partial Copyright (C) 2009 Felix Paul Kühne
7  * $Id$
8  *
9  * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
10  *          Faustion Osuna <enrique.osuna # gmail.com>
11  *          Felix Paul Kühne <fkuehne # videolan.org>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 #import "VLCLibrary.h"
29 #import "VLCMediaPlayer.h"
30 #import "VLCEventManager.h"
31 #import "VLCLibVLCBridging.h"
32 #import "VLCVideoView.h"
33 #ifdef HAVE_CONFIG_H
34 # include "config.h"
35 #endif
36
37 /* prevent system sleep */
38 #import <CoreServices/CoreServices.h>
39 /* FIXME: Ugly hack! */
40 #ifdef __x86_64__
41 #import <CoreServices/../Frameworks/OSServices.framework/Headers/Power.h>
42 #endif
43
44 #include <vlc/vlc.h>
45
46 /* Notification Messages */
47 NSString * VLCMediaPlayerTimeChanged    = @"VLCMediaPlayerTimeChanged";
48 NSString * VLCMediaPlayerStateChanged   = @"VLCMediaPlayerStateChanged";
49
50 NSString * VLCMediaPlayerStateToString(VLCMediaPlayerState state)
51 {
52     static NSString * stateToStrings[] = {
53         [VLCMediaPlayerStateStopped]      = @"VLCMediaPlayerStateStopped",
54         [VLCMediaPlayerStateOpening]      = @"VLCMediaPlayerStateOpening",
55         [VLCMediaPlayerStateBuffering]    = @"VLCMediaPlayerStateBuffering",
56         [VLCMediaPlayerStateEnded]        = @"VLCMediaPlayerStateEnded",
57         [VLCMediaPlayerStateError]        = @"VLCMediaPlayerStateError",
58         [VLCMediaPlayerStatePlaying]      = @"VLCMediaPlayerStatePlaying",
59         [VLCMediaPlayerStatePaused]       = @"VLCMediaPlayerStatePaused"
60     };
61     return stateToStrings[state];
62 }
63
64 /* libvlc event callback */
65 static void HandleMediaInstanceVolumeChanged(const libvlc_event_t * event, void * self)
66 {
67     [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
68                                                    withDelegateMethod:@selector(mediaPlayerVolumeChanged:)
69                                                  withNotificationName:VLCMediaPlayerVolumeChanged];
70 }
71
72 static void HandleMediaTimeChanged(const libvlc_event_t * event, void * self)
73 {
74     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
75     [[VLCEventManager sharedManager] callOnMainThreadObject:self 
76                                                  withMethod:@selector(mediaPlayerTimeChanged:) 
77                                        withArgumentAsObject:[NSNumber numberWithLongLong:event->u.media_player_time_changed.new_time]];
78
79     [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
80                                                    withDelegateMethod:@selector(mediaPlayerTimeChanged:)
81                                                  withNotificationName:VLCMediaPlayerTimeChanged];
82     [pool release];
83 }
84
85 static void HandleMediaPositionChanged(const libvlc_event_t * event, void * self)
86 {
87     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
88
89     [[VLCEventManager sharedManager] callOnMainThreadObject:self 
90                                                  withMethod:@selector(mediaPlayerPositionChanged:) 
91                                        withArgumentAsObject:[NSNumber numberWithFloat:event->u.media_player_position_changed.new_position]];
92     [pool release];
93 }
94
95 static void HandleMediaInstanceStateChanged(const libvlc_event_t * event, void * self)
96 {
97     VLCMediaPlayerState newState;
98
99     if( event->type == libvlc_MediaPlayerPlaying )
100         newState = VLCMediaPlayerStatePlaying;
101     else if( event->type == libvlc_MediaPlayerPaused )
102         newState = VLCMediaPlayerStatePaused;
103     else if( event->type == libvlc_MediaPlayerEndReached )
104         newState = VLCMediaPlayerStateStopped;
105     else if( event->type == libvlc_MediaPlayerEncounteredError )
106         newState = VLCMediaPlayerStateError;
107     else
108     {
109         NSLog(@"%s: Unknown event", __FUNCTION__);
110         return;
111     }
112
113     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
114
115     [[VLCEventManager sharedManager] callOnMainThreadObject:self 
116                                                  withMethod:@selector(mediaPlayerStateChanged:) 
117                                        withArgumentAsObject:[NSNumber numberWithInt:newState]];
118
119     [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
120                                                    withDelegateMethod:@selector(mediaPlayerStateChanged:)
121                                                  withNotificationName:VLCMediaPlayerStateChanged];
122
123     [pool release];
124
125 }
126
127
128 // TODO: Documentation
129 @interface VLCMediaPlayer (Private)
130 - (id)initWithDrawable:(id)aDrawable;
131
132 - (void)registerObservers;
133 - (void)unregisterObservers;
134 - (void)mediaPlayerTimeChanged:(NSNumber *)newTime;
135 - (void)mediaPlayerPositionChanged:(NSNumber *)newTime;
136 - (void)mediaPlayerStateChanged:(NSNumber *)newState;
137 @end
138
139 @implementation VLCMediaPlayer
140
141 /* Bindings */
142 + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
143 {
144     static NSDictionary * dict = nil;
145     NSSet * superKeyPaths;
146     if( !dict )
147     {
148         dict = [[NSDictionary dictionaryWithObjectsAndKeys:
149             [NSSet setWithObject:@"state"], @"playing",
150             [NSSet setWithObjects:@"state", @"media", nil], @"seekable",
151             [NSSet setWithObjects:@"state", @"media", nil], @"canPause",
152             [NSSet setWithObjects:@"state", @"media", nil], @"description",
153             nil] retain];
154     }
155     if( (superKeyPaths = [super keyPathsForValuesAffectingValueForKey: key]) )
156     {
157         NSMutableSet * ret = [NSMutableSet setWithSet:[dict objectForKey: key]];
158         [ret unionSet:superKeyPaths];
159         return ret;
160     }
161     return [dict objectForKey: key];
162 }
163
164 /* Contructor */
165 - (id)init
166 {
167     return [self initWithDrawable:nil];
168 }
169
170 - (id)initWithVideoView:(VLCVideoView *)aVideoView
171 {
172     return [self initWithDrawable: aVideoView];
173 }
174
175 - (id)initWithVideoLayer:(VLCVideoLayer *)aVideoLayer
176 {
177     return [self initWithDrawable: aVideoLayer];
178 }
179
180 - (void)release
181 {
182     @synchronized(self)
183     {
184         if([self retainCount] <= 1)
185         {
186             /* We must make sure we won't receive new event after an upcoming dealloc
187              * We also may receive a -retain in some event callback that may occcur
188              * Before libvlc_event_detach. So this can't happen in dealloc */
189             [self unregisterObservers];
190         }
191         [super release];
192     }
193 }
194
195 - (void)dealloc
196 {
197     NSAssert(libvlc_media_player_get_state(instance, NULL) == libvlc_Stopped, @"You released the media player before ensuring that it is stopped");
198
199     // Always get rid of the delegate first so we can stop sending messages to it
200     // TODO: Should we tell the delegate that we're shutting down?
201     delegate = nil;
202
203     // Clear our drawable as we are going to release it, we don't
204     // want the core to use it from this point. This won't happen as
205     // the media player must be stopped.
206     libvlc_media_player_set_nsobject(instance, nil, NULL);
207
208     libvlc_media_player_release(instance);
209     
210     // Get rid of everything else
211     [media release];
212     [cachedTime release];
213     [drawable release];
214
215     [super dealloc];
216 }
217
218 - (void)setDelegate:(id)value
219 {
220     delegate = value;
221 }
222
223 - (id)delegate
224 {
225     return delegate;
226 }
227
228 - (void)setVideoView:(VLCVideoView *)aVideoView
229 {    
230     [self setDrawable: aVideoView];
231 }
232
233 - (void)setVideoLayer:(VLCVideoLayer *)aVideoLayer
234 {
235     [self setDrawable: aVideoLayer];
236 }
237
238 - (void)setDrawable:(id)aDrawable
239 {
240     // Make sure that this instance has been associated with the drawing canvas.
241     libvlc_exception_t ex;
242     libvlc_exception_init( &ex );
243     libvlc_media_player_set_nsobject(instance, aDrawable, &ex);
244     catch_exception( &ex );
245 }
246
247 - (id)drawable
248 {
249     libvlc_exception_t ex;
250     libvlc_exception_init( &ex );
251     id ret = libvlc_media_player_get_nsobject(instance);
252     catch_exception( &ex );
253     return ret;
254 }
255
256 - (VLCAudio *)audio
257 {
258     return [[VLCLibrary sharedLibrary] audio];
259 }
260
261 - (void)setVideoAspectRatio:(char *)value
262 {
263     libvlc_exception_t ex;
264     libvlc_exception_init( &ex );
265     libvlc_video_set_aspect_ratio( instance, value, &ex );
266     catch_exception( &ex );
267 }
268
269 - (char *)videoAspectRatio
270 {
271     libvlc_exception_t ex;
272     libvlc_exception_init( &ex );
273     char * result = libvlc_video_get_aspect_ratio( instance, &ex );
274     catch_exception( &ex );
275     return result;
276 }
277
278 - (void)setVideoSubTitles:(int)value
279 {
280     libvlc_exception_t ex;
281     libvlc_exception_init( &ex );
282     libvlc_video_set_spu( instance, value, &ex );
283     catch_exception( &ex );
284 }
285
286 - (int)countOfVideoSubTitles
287 {
288     libvlc_exception_t ex;
289     libvlc_exception_init( &ex );
290     int result = libvlc_video_get_spu_count( instance, &ex );
291     catch_exception( &ex );
292     return result;
293 }
294
295 - (int)currentVideoSubTitles
296 {
297     libvlc_exception_t ex;
298     libvlc_exception_init( &ex );
299     int result = libvlc_video_get_spu( instance, &ex );
300     catch_exception( &ex );
301     return result;
302 }
303
304 - (BOOL)openVideoSubTitlesFromFile:(NSString *)path
305 {
306     libvlc_exception_t ex;
307     libvlc_exception_init( &ex );
308     BOOL result = libvlc_video_set_subtitle_file( instance, [path UTF8String], &ex );
309     catch_exception( &ex );
310     return result;
311 }
312
313 - (NSArray *)videoSubTitles
314 {
315     libvlc_exception_t ex;
316     libvlc_exception_init( &ex );
317     libvlc_track_description_t *tracks = libvlc_video_get_spu_description( instance, &ex );
318     NSMutableArray *tempArray = [NSMutableArray array];
319     NSInteger i;
320     for (i = 0; i < [self countOfVideoSubTitles] ; i++)
321     {
322         [tempArray addObject:[NSString stringWithUTF8String: tracks->psz_name]];
323         tracks = tracks->p_next;
324     }
325     return [NSArray arrayWithArray: tempArray];
326 }
327
328 - (void)setVideoCropGeometry:(char *)value
329 {
330     libvlc_exception_t ex;
331     libvlc_exception_init( &ex );
332     libvlc_video_set_crop_geometry( instance, value, &ex );
333     catch_exception( &ex );
334 }
335
336 - (char *)videoCropGeometry
337 {
338     libvlc_exception_t ex;
339     libvlc_exception_init( &ex );
340     char * result = libvlc_video_get_crop_geometry( instance, &ex );
341     catch_exception( &ex );
342     return result;
343 }
344
345 - (void)setVideoTeleText:(int)value
346 {
347     libvlc_exception_t ex;
348     libvlc_exception_init( &ex );
349     libvlc_video_set_teletext( instance, value, &ex );
350     catch_exception( &ex );
351 }
352
353 - (int)videoTeleText
354 {
355     libvlc_exception_t ex;
356     libvlc_exception_init( &ex );
357     int result = libvlc_video_get_teletext( instance, &ex );
358     catch_exception( &ex );
359     return result;
360 }
361
362 - (void)saveVideoSnapshotAt: (NSString *)path withWidth:(NSUInteger)width andHeight:(NSUInteger)height
363 {
364     libvlc_exception_t ex;
365     libvlc_exception_init( &ex );
366     libvlc_video_take_snapshot( instance, [path UTF8String], width, height, &ex );
367     catch_exception( &ex );
368 }
369
370 - (void)setDeinterlaceFilter: (NSString *)name enabled: (BOOL)enabled
371 {
372     libvlc_exception_t ex;
373     libvlc_exception_init( &ex );
374     libvlc_video_set_deinterlace( instance, (int)enabled , [name UTF8String], &ex );
375     catch_exception( &ex );
376 }
377
378 - (void)setRate:(float)value
379 {
380     libvlc_exception_t ex;
381     libvlc_exception_init( &ex );
382     libvlc_media_player_set_rate( instance, value, &ex );
383     catch_exception( &ex );
384 }
385
386 - (float)rate
387 {
388     libvlc_exception_t ex;
389     libvlc_exception_init( &ex );
390     float result = libvlc_media_player_get_rate( instance, &ex );
391     catch_exception( &ex );
392     return result;
393 }
394
395 - (NSSize)videoSize
396 {
397     libvlc_exception_t ex;
398     libvlc_exception_init( &ex );
399     NSSize result = NSMakeSize(libvlc_video_get_height((libvlc_media_player_t *)instance, &ex),
400                                libvlc_video_get_width((libvlc_media_player_t *)instance, &ex));
401     catch_exception( &ex );
402     return result;
403 }
404
405 - (BOOL)hasVideoOut
406 {
407     libvlc_exception_t ex;
408     libvlc_exception_init( &ex );
409     BOOL result = libvlc_media_player_has_vout((libvlc_media_player_t *)instance, &ex);
410     if (libvlc_exception_raised( &ex ))
411     {
412         libvlc_exception_clear( &ex );
413         return NO;
414     }
415     else
416         return result;
417 }
418
419 - (float)framesPerSecond
420 {
421     libvlc_exception_t ex;
422     libvlc_exception_init( &ex );
423     float result = libvlc_media_player_get_fps( (libvlc_media_player_t *)instance, &ex );
424     catch_exception( &ex );
425     return result;
426 }
427
428 - (void)setTime:(VLCTime *)value
429 {
430     libvlc_exception_t ex;
431     libvlc_exception_init( &ex );
432     // Time is managed in seconds, while duration is managed in microseconds
433     // TODO: Redo VLCTime to provide value numberAsMilliseconds, numberAsMicroseconds, numberAsSeconds, numberAsMinutes, numberAsHours
434     libvlc_media_player_set_time( (libvlc_media_player_t *)instance, 
435                                     (value ? [[value numberValue] longLongValue] / 1000 : 0),
436                                     &ex );
437     catch_exception( &ex );
438 }
439
440 - (VLCTime *)time
441 {
442     return cachedTime;
443 }
444
445 - (VLCTime *)remainingTime
446 {
447     double currentTime = [[cachedTime numberValue] doubleValue];
448     double remaining = currentTime / position * (1 - position);
449     return [VLCTime timeWithNumber:[NSNumber numberWithDouble:-remaining]];
450 }
451
452 - (int)fps
453 {
454     libvlc_exception_t ex;
455     libvlc_exception_init( &ex );
456     int result = libvlc_media_player_get_fps( instance, &ex );
457     catch_exception( &ex );
458     return result;
459 }
460
461 - (void)setChapter:(int)value;
462 {
463     libvlc_exception_t ex;
464     libvlc_exception_init( &ex );
465     libvlc_media_player_set_chapter( instance, value, &ex );
466     catch_exception( &ex );
467 }
468
469 - (int)currentChapter
470 {
471     libvlc_exception_t ex;
472     libvlc_exception_init( &ex );
473     int result = libvlc_media_player_get_chapter( instance, &ex );
474     catch_exception( &ex );
475     return result;
476 }
477
478 - (int)countOfChapters
479 {
480     libvlc_exception_t ex;
481     libvlc_exception_init( &ex );
482     int result = libvlc_media_player_get_chapter_count( instance, &ex );
483     catch_exception( &ex );
484     return result;
485 }
486
487 - (void)nextChapter
488 {
489     libvlc_exception_t ex;
490     libvlc_exception_init( &ex );
491     libvlc_media_player_next_chapter( instance, &ex );
492     catch_exception( &ex );
493 }
494
495 - (void)previousChapter
496 {
497     libvlc_exception_t ex;
498     libvlc_exception_init( &ex );
499     libvlc_media_player_previous_chapter( instance, &ex );
500     catch_exception( &ex );
501 }
502
503 - (NSArray *)chaptersForTitle:(int)title
504 {
505     libvlc_exception_t ex;
506     libvlc_exception_init( &ex );
507     libvlc_track_description_t *tracks = libvlc_video_get_chapter_description( instance, title, &ex );
508     NSMutableArray *tempArray = [NSMutableArray array];
509     NSInteger i;
510     for (i = 0; i < [self countOfChapters] ; i++)
511     {
512         [tempArray addObject:[NSString stringWithUTF8String: tracks->psz_name]];
513         tracks = tracks->p_next;
514     }
515     return [NSArray arrayWithArray: tempArray];
516 }
517
518 - (void)setCurrentTitle:(int)value
519 {
520     libvlc_exception_t ex;
521     libvlc_exception_init( &ex );
522     libvlc_media_player_set_title( instance, value, &ex );
523     catch_exception( &ex );
524 }
525
526 - (int)currentTitle
527 {
528     libvlc_exception_t ex;
529     libvlc_exception_init( &ex );
530     int result = libvlc_media_player_get_title( instance, &ex );
531     catch_exception( &ex );
532     return result;
533 }
534
535 - (int)countOfTitles
536 {
537     libvlc_exception_t ex;
538     libvlc_exception_init( &ex );
539     int result = libvlc_media_player_get_title_count( instance, &ex );
540     catch_exception( &ex );
541     return result;
542 }
543
544 - (NSArray *)titles
545 {
546     libvlc_exception_t ex;
547     libvlc_exception_init( &ex );
548     libvlc_track_description_t *tracks = libvlc_video_get_title_description( instance, &ex );
549     NSMutableArray *tempArray = [NSMutableArray array];
550     NSInteger i;
551     for (i = 0; i < [self countOfTitles] ; i++)
552     {
553         [tempArray addObject:[NSString stringWithUTF8String: tracks->psz_name]];
554         tracks = tracks->p_next;
555     }
556     return [NSArray arrayWithArray: tempArray];
557 }
558
559 - (void)setAudioTrack:(int)value
560 {
561     libvlc_exception_t ex;
562     libvlc_exception_init( &ex );
563     libvlc_audio_set_track( instance, value, &ex );
564     catch_exception( &ex );
565 }
566
567 - (int)currentAudioTrack
568 {
569     libvlc_exception_t ex;
570     libvlc_exception_init( &ex );
571     int result = libvlc_audio_get_track( instance, &ex );
572     catch_exception( &ex );
573     return result;
574 }
575
576 - (int)countOfAudioTracks
577 {
578     libvlc_exception_t ex;
579     libvlc_exception_init( &ex );
580     int result = libvlc_audio_get_track_count( instance, &ex );
581     catch_exception( &ex );
582     return result;
583 }
584
585 - (NSArray *)audioTracks
586 {
587     libvlc_exception_t ex;
588     libvlc_exception_init( &ex );
589     libvlc_track_description_t *tracks = libvlc_audio_get_track_description( instance, &ex );
590     NSMutableArray *tempArray = [NSMutableArray array];
591     NSInteger i;
592     for (i = 0; i < [self countOfAudioTracks] ; i++)
593     {
594         [tempArray addObject:[NSString stringWithUTF8String: tracks->psz_name]];
595         tracks = tracks->p_next;
596     }
597
598     return [NSArray arrayWithArray: tempArray];
599 }
600
601 - (void)setAudioChannel:(int)value
602 {
603     libvlc_exception_t ex;
604     libvlc_exception_init( &ex );
605     libvlc_audio_set_channel( instance, value, &ex );
606     catch_exception( &ex );
607 }
608
609 - (int)audioChannel
610 {
611     libvlc_exception_t ex;
612     libvlc_exception_init( &ex );
613     int result = libvlc_audio_get_channel( instance, &ex );
614     catch_exception( &ex );
615     return result;
616 }
617
618 - (void)setMedia:(VLCMedia *)value
619 {
620     if (media != value)
621     {
622         if (media && [media compare:value] == NSOrderedSame)
623             return;
624         
625         [media release];
626         media = [value retain];
627
628         libvlc_exception_t ex;
629         libvlc_exception_init( &ex );
630         libvlc_media_player_set_media( instance, [media libVLCMediaDescriptor], &ex );
631         catch_exception( &ex );
632     }
633 }
634
635 - (VLCMedia *)media
636 {
637     return media;
638 }
639
640 - (BOOL)play
641 {    
642     libvlc_exception_t ex;
643     libvlc_exception_init( &ex );
644     libvlc_media_player_play( (libvlc_media_player_t *)instance, &ex );
645     catch_exception( &ex );
646     return YES;
647 }
648
649 - (void)pause
650 {
651     if( [NSThread isMainThread] )
652     {
653         /* Hack because we create a dead lock here, when the vout is stopped
654          * and tries to recontact us on the main thread */
655         /* FIXME: to do this properly we need to do some locking. We may want 
656          * to move that to libvlc */
657         [self performSelectorInBackground:@selector(pause) withObject:nil];
658         return;
659     }
660
661     // Pause the stream
662     libvlc_exception_t ex;
663     libvlc_exception_init( &ex );
664     libvlc_media_player_pause( (libvlc_media_player_t *)instance, &ex );
665
666     // fail gracefully
667     // in most cases, it's just EOF so let's stop
668     if (libvlc_exception_raised(&ex))
669         [self stop];
670
671     libvlc_exception_clear(&ex);
672 }
673
674 - (void)stop
675 {
676     libvlc_exception_t ex;
677     libvlc_exception_init( &ex );
678     libvlc_media_player_stop((libvlc_media_player_t *)instance, &ex);
679     catch_exception( &ex );
680 }
681
682 - (void)fastForward
683 {
684     [self fastForwardAtRate: 2.0];
685 }
686
687 - (void)fastForwardAtRate:(float)rate
688 {
689     [self setRate:rate];
690 }
691
692 - (void)rewind
693 {
694     [self rewindAtRate: 2.0];
695 }
696
697 - (void)rewindAtRate:(float)rate
698 {
699     [self setRate: -rate];
700 }
701
702 - (void)jumpBackward:(NSInteger)interval
703 {
704     if( [self isSeekable] )
705     {
706         interval = interval * 1000000;
707         [self setTime: [VLCTime timeWithInt: ([[self time] intValue] - interval)]];
708     }
709 }
710
711 - (void)jumpForward:(NSInteger)interval
712 {
713     if( [self isSeekable] )
714     {
715         interval = interval * 1000000;
716         [self setTime: [VLCTime timeWithInt: ([[self time] intValue] + interval)]];
717     }
718 }
719
720 - (void)extraShortJumpBackward
721 {
722     [self jumpBackward:3];
723 }
724
725 - (void)extraShortJumpForward
726 {
727     [self jumpForward:3];
728 }
729
730 - (void)shortJumpBackward
731 {
732     [self jumpBackward:10];
733 }
734
735 - (void)shortJumpForward
736 {
737     [self jumpForward:10];
738 }
739
740 - (void)mediumJumpBackward
741 {
742     [self jumpBackward:60];
743 }
744
745 - (void)mediumJumpForward
746 {
747     [self jumpForward:60];
748 }
749
750 - (void)longJumpBackward
751 {
752     [self jumpBackward:300];
753 }
754
755 - (void)longJumpForward
756 {
757     [self jumpForward:300];
758 }
759
760 + (NSSet *)keyPathsForValuesAffectingIsPlaying
761 {
762     return [NSSet setWithObjects:@"state", nil];
763 }
764
765 - (BOOL)isPlaying
766 {
767     VLCMediaPlayerState state = [self state];
768     return ((state == VLCMediaPlayerStateOpening) || (state == VLCMediaPlayerStateBuffering) ||
769             (state == VLCMediaPlayerStatePlaying));
770 }
771
772 - (BOOL)willPlay
773 {
774     libvlc_exception_t ex;
775     libvlc_exception_init( &ex );
776     BOOL ret = libvlc_media_player_will_play( (libvlc_media_player_t *)instance, &ex );
777     if (libvlc_exception_raised(&ex))
778     {
779         libvlc_exception_clear(&ex);
780         return NO;
781     }
782     else
783         return ret;
784 }
785
786 static const VLCMediaPlayerState libvlc_to_local_state[] =
787 {
788     [libvlc_Stopped]    = VLCMediaPlayerStateStopped,
789     [libvlc_Opening]    = VLCMediaPlayerStateOpening,
790     [libvlc_Buffering]  = VLCMediaPlayerStateBuffering,
791     [libvlc_Playing]    = VLCMediaPlayerStatePlaying,
792     [libvlc_Paused]     = VLCMediaPlayerStatePaused,
793     [libvlc_Ended]      = VLCMediaPlayerStateEnded,
794     [libvlc_Error]      = VLCMediaPlayerStateError
795 };
796
797 - (VLCMediaPlayerState)state
798 {
799     return cachedState;
800 }
801
802 - (float)position
803 {
804     return position;
805 }
806
807 - (void)setPosition:(float)newPosition
808 {
809     libvlc_exception_t ex;
810     libvlc_exception_init( &ex );
811     libvlc_media_player_set_position( instance, newPosition, &ex );
812     catch_exception( &ex );
813 }
814
815 - (BOOL)isSeekable
816 {
817     libvlc_exception_t ex;
818     libvlc_exception_init( &ex );
819     BOOL ret = libvlc_media_player_is_seekable( instance, &ex );
820     catch_exception( &ex );
821     return ret;
822 }
823
824 - (BOOL)canPause
825 {
826     libvlc_exception_t ex;
827     libvlc_exception_init( &ex );
828     BOOL ret = libvlc_media_player_can_pause( instance, &ex );
829     catch_exception( &ex );
830     return ret;
831 }
832
833 - (void *)libVLCMediaPlayer
834 {
835     return instance;
836 }
837 @end
838
839 @implementation VLCMediaPlayer (Private)
840 - (id)initWithDrawable:(id)aDrawable
841 {
842     if (self = [super init])
843     {
844         delegate = nil;
845         media = nil;
846         cachedTime = [[VLCTime nullTime] retain];
847         position = 0.0f;
848         cachedState = VLCMediaPlayerStateStopped;
849
850         // Create a media instance, it doesn't matter what library we start off with
851         // it will change depending on the media descriptor provided to the media
852         // instance
853         libvlc_exception_t ex;
854         libvlc_exception_init( &ex );
855         instance = (void *)libvlc_media_player_new([VLCLibrary sharedInstance], &ex);
856         catch_exception( &ex );
857         
858         [self registerObservers];
859         
860         [self setDrawable:aDrawable];
861     }
862     return self;
863 }
864
865 - (void)registerObservers
866 {
867     libvlc_exception_t ex;
868     libvlc_exception_init( &ex );
869
870     // Attach event observers into the media instance
871     libvlc_event_manager_t * p_em = libvlc_media_player_event_manager( instance, &ex );
872     libvlc_event_attach( p_em, libvlc_MediaPlayerPlaying,          HandleMediaInstanceStateChanged, self, &ex );
873     libvlc_event_attach( p_em, libvlc_MediaPlayerPaused,           HandleMediaInstanceStateChanged, self, &ex );
874     libvlc_event_attach( p_em, libvlc_MediaPlayerEncounteredError, HandleMediaInstanceStateChanged, self, &ex );
875     libvlc_event_attach( p_em, libvlc_MediaPlayerEndReached,       HandleMediaInstanceStateChanged, self, &ex );
876     /* FIXME: We may want to turn that off when none is interested by that */
877     libvlc_event_attach( p_em, libvlc_MediaPlayerPositionChanged, HandleMediaPositionChanged,      self, &ex );
878     libvlc_event_attach( p_em, libvlc_MediaPlayerTimeChanged,     HandleMediaTimeChanged,          self, &ex );
879     catch_exception( &ex );
880 }
881
882 - (void)unregisterObservers
883 {
884     libvlc_event_manager_t * p_em = libvlc_media_player_event_manager( instance, NULL );
885     libvlc_event_detach( p_em, libvlc_MediaPlayerPlaying,          HandleMediaInstanceStateChanged, self, NULL );
886     libvlc_event_detach( p_em, libvlc_MediaPlayerPaused,           HandleMediaInstanceStateChanged, self, NULL );
887     libvlc_event_detach( p_em, libvlc_MediaPlayerEncounteredError, HandleMediaInstanceStateChanged, self, NULL );
888     libvlc_event_detach( p_em, libvlc_MediaPlayerEndReached,       HandleMediaInstanceStateChanged, self, NULL );
889     libvlc_event_detach( p_em, libvlc_MediaPlayerPositionChanged,  HandleMediaPositionChanged,      self, NULL );
890     libvlc_event_detach( p_em, libvlc_MediaPlayerTimeChanged,      HandleMediaTimeChanged,          self, NULL );
891 }
892
893 - (void)mediaPlayerTimeChanged:(NSNumber *)newTime
894 {
895     [self willChangeValueForKey:@"time"];
896     [self willChangeValueForKey:@"remainingTime"];
897     [cachedTime release];
898     cachedTime = [[VLCTime timeWithNumber:newTime] retain];
899
900     [self didChangeValueForKey:@"remainingTime"];
901     [self didChangeValueForKey:@"time"];
902 }
903
904 - (void)delaySleep
905 {
906     UpdateSystemActivity(UsrActivity);
907 }
908
909 - (void)mediaPlayerPositionChanged:(NSNumber *)newPosition
910 {
911     // This seems to be the most relevant place to delay sleeping and screen saver.
912     [self delaySleep];
913
914     [self willChangeValueForKey:@"position"];
915     position = [newPosition floatValue];
916     [self didChangeValueForKey:@"position"];
917 }
918
919 - (void)mediaPlayerStateChanged:(NSNumber *)newState
920 {
921     [self willChangeValueForKey:@"state"];
922     cachedState = [newState intValue];
923     [self didChangeValueForKey:@"state"];
924 }
925
926 @end