]> git.sesse.net Git - vlc/blob - projects/macosx/framework/Sources/VLCMediaPlayer.m
macosx/framework: Simplify the API for audio tracks, chapters, titles, and subtitles.
[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 #pragma mark -
262 #pragma mark Subtitles
263
264 - (void)setCurrentVideoSubTitleIndex:(NSUInteger)index
265 {
266     libvlc_exception_t ex;
267     libvlc_exception_init( &ex );
268     libvlc_video_set_spu( instance, (int)index, &ex );
269     catch_exception( &ex );
270 }
271
272 - (NSUInteger)currentVideoSubTitleIndex
273 {
274     libvlc_exception_t ex;
275     libvlc_exception_init( &ex );
276     int count = libvlc_video_get_spu_count( instance, &ex );
277     catch_exception( &ex );
278     if (count <= 0)
279         return NSNotFound;
280     NSUInteger result = libvlc_video_get_spu( instance, &ex );
281     catch_exception( &ex );
282     return result;
283 }
284
285 - (BOOL)openVideoSubTitlesFromFile:(NSString *)path
286 {
287     libvlc_exception_t ex;
288     libvlc_exception_init( &ex );
289     BOOL result = libvlc_video_set_subtitle_file( instance, [path UTF8String], &ex );
290     catch_exception( &ex );
291     return result;
292 }
293
294 - (NSArray *)videoSubTitles
295 {
296     libvlc_exception_t ex;
297     libvlc_exception_init( &ex );
298     int count = libvlc_video_get_spu_count( instance, &ex );
299     catch_exception( &ex );
300
301     libvlc_track_description_t *tracks = libvlc_video_get_spu_description( instance, &ex );
302     NSMutableArray *tempArray = [NSMutableArray array];
303     NSInteger i;
304     for (i = 0; i < count; i++)
305     {
306         [tempArray addObject:[NSString stringWithUTF8String: tracks->psz_name]];
307         tracks = tracks->p_next;
308     }
309     return [NSArray arrayWithArray: tempArray];
310 }
311
312
313 #pragma mark -
314 #pragma mark Video Crop geometry
315
316 - (void)setVideoCropGeometry:(char *)value
317 {
318     libvlc_exception_t ex;
319     libvlc_exception_init( &ex );
320     libvlc_video_set_crop_geometry( instance, value, &ex );
321     catch_exception( &ex );
322 }
323
324 - (char *)videoCropGeometry
325 {
326     libvlc_exception_t ex;
327     libvlc_exception_init( &ex );
328     char * result = libvlc_video_get_crop_geometry( instance, &ex );
329     catch_exception( &ex );
330     return result;
331 }
332
333 - (void)setVideoAspectRatio:(char *)value
334 {
335     libvlc_exception_t ex;
336     libvlc_exception_init( &ex );
337     libvlc_video_set_aspect_ratio( instance, value, &ex );
338     catch_exception( &ex );
339 }
340
341 - (char *)videoAspectRatio
342 {
343     libvlc_exception_t ex;
344     libvlc_exception_init( &ex );
345     char * result = libvlc_video_get_aspect_ratio( instance, &ex );
346     catch_exception( &ex );
347     return result;
348 }
349
350 - (void)setVideoTeleText:(int)value
351 {
352     libvlc_exception_t ex;
353     libvlc_exception_init( &ex );
354     libvlc_video_set_teletext( instance, value, &ex );
355     catch_exception( &ex );
356 }
357
358 - (int)videoTeleText
359 {
360     libvlc_exception_t ex;
361     libvlc_exception_init( &ex );
362     int result = libvlc_video_get_teletext( instance, &ex );
363     catch_exception( &ex );
364     return result;
365 }
366
367 - (void)saveVideoSnapshotAt: (NSString *)path withWidth:(NSUInteger)width andHeight:(NSUInteger)height
368 {
369     libvlc_exception_t ex;
370     libvlc_exception_init( &ex );
371     libvlc_video_take_snapshot( instance, [path UTF8String], width, height, &ex );
372     catch_exception( &ex );
373 }
374
375 - (void)setDeinterlaceFilter: (NSString *)name enabled: (BOOL)enabled
376 {
377     libvlc_exception_t ex;
378     libvlc_exception_init( &ex );
379     libvlc_video_set_deinterlace( instance, (int)enabled , [name UTF8String], &ex );
380     catch_exception( &ex );
381 }
382
383 - (void)setRate:(float)value
384 {
385     libvlc_exception_t ex;
386     libvlc_exception_init( &ex );
387     libvlc_media_player_set_rate( instance, value, &ex );
388     catch_exception( &ex );
389 }
390
391 - (float)rate
392 {
393     libvlc_exception_t ex;
394     libvlc_exception_init( &ex );
395     float result = libvlc_media_player_get_rate( instance, &ex );
396     catch_exception( &ex );
397     return result;
398 }
399
400 - (NSSize)videoSize
401 {
402     libvlc_exception_t ex;
403     libvlc_exception_init( &ex );
404     NSSize result = NSMakeSize(libvlc_video_get_height((libvlc_media_player_t *)instance, &ex),
405                                libvlc_video_get_width((libvlc_media_player_t *)instance, &ex));
406     catch_exception( &ex );
407     return result;
408 }
409
410 - (BOOL)hasVideoOut
411 {
412     libvlc_exception_t ex;
413     libvlc_exception_init( &ex );
414     BOOL result = libvlc_media_player_has_vout((libvlc_media_player_t *)instance, &ex);
415     if (libvlc_exception_raised( &ex ))
416     {
417         libvlc_exception_clear( &ex );
418         return NO;
419     }
420     else
421         return result;
422 }
423
424 - (float)framesPerSecond
425 {
426     libvlc_exception_t ex;
427     libvlc_exception_init( &ex );
428     float result = libvlc_media_player_get_fps( (libvlc_media_player_t *)instance, &ex );
429     catch_exception( &ex );
430     return result;
431 }
432
433 - (void)setTime:(VLCTime *)value
434 {
435     libvlc_exception_t ex;
436     libvlc_exception_init( &ex );
437     // Time is managed in seconds, while duration is managed in microseconds
438     // TODO: Redo VLCTime to provide value numberAsMilliseconds, numberAsMicroseconds, numberAsSeconds, numberAsMinutes, numberAsHours
439     libvlc_media_player_set_time( (libvlc_media_player_t *)instance, 
440                                     (value ? [[value numberValue] longLongValue] / 1000 : 0),
441                                     &ex );
442     catch_exception( &ex );
443 }
444
445 - (VLCTime *)time
446 {
447     return cachedTime;
448 }
449
450 - (VLCTime *)remainingTime
451 {
452     double currentTime = [[cachedTime numberValue] doubleValue];
453     double remaining = currentTime / position * (1 - position);
454     return [VLCTime timeWithNumber:[NSNumber numberWithDouble:-remaining]];
455 }
456
457 - (int)fps
458 {
459     libvlc_exception_t ex;
460     libvlc_exception_init( &ex );
461     int result = libvlc_media_player_get_fps( instance, &ex );
462     catch_exception( &ex );
463     return result;
464 }
465
466 #pragma mark -
467 #pragma mark Chapters
468 - (void)setCurrentChapterIndex:(NSUInteger)value;
469 {
470     libvlc_exception_t ex;
471     libvlc_exception_init( &ex );
472     libvlc_media_player_set_chapter( instance, value, &ex );
473     catch_exception( &ex );
474 }
475
476 - (NSUInteger)currentChapterIndex
477 {
478     libvlc_exception_t ex;
479     libvlc_exception_init( &ex );
480     int count = libvlc_media_player_get_chapter_count( instance, &ex );
481     catch_exception( &ex );
482     if (count <= 0)
483         return NSNotFound;
484     NSUInteger result = libvlc_media_player_get_chapter( instance, &ex );
485     catch_exception( &ex );
486     return result;
487 }
488
489 - (void)nextChapter
490 {
491     libvlc_exception_t ex;
492     libvlc_exception_init( &ex );
493     libvlc_media_player_next_chapter( instance, &ex );
494     catch_exception( &ex );
495 }
496
497 - (void)previousChapter
498 {
499     libvlc_exception_t ex;
500     libvlc_exception_init( &ex );
501     libvlc_media_player_previous_chapter( instance, &ex );
502     catch_exception( &ex );
503 }
504
505 - (NSArray *)chaptersForTitleIndex:(NSUInteger)title
506 {
507     libvlc_exception_t ex;
508     libvlc_exception_init( &ex );
509     int count = libvlc_media_player_get_chapter_count(instance, &ex);
510     if (count <= 0)
511         return [NSArray array];
512
513     libvlc_track_description_t *tracks = libvlc_video_get_chapter_description( instance, title, &ex );
514     NSMutableArray *tempArray = [NSMutableArray array];
515     NSInteger i;
516     for (i = 0; i < count ; i++)
517     {
518         [tempArray addObject:[NSString stringWithUTF8String: tracks->psz_name]];
519         tracks = tracks->p_next;
520     }
521     return [NSArray arrayWithArray: tempArray];
522 }
523
524 #pragma mark -
525 #pragma mark Titles
526
527 - (void)setCurrentTitleIndex:(NSUInteger)value
528 {
529     libvlc_exception_t ex;
530     libvlc_exception_init( &ex );    
531     libvlc_media_player_set_title( instance, value, &ex );
532     catch_exception( &ex );
533 }
534
535 - (NSUInteger)currentTitleIndex
536 {
537     libvlc_exception_t ex;
538     libvlc_exception_init( &ex );
539
540     int count = libvlc_media_player_get_title_count( instance, &ex );
541     catch_exception( &ex );
542     if (count <= 0)
543         return NSNotFound;
544     
545     NSUInteger result = libvlc_media_player_get_title( instance, &ex );
546     catch_exception( &ex );
547     return result;
548 }
549
550 - (int)countOfTitles
551 {
552     libvlc_exception_t ex;
553     libvlc_exception_init( &ex );
554     int result = libvlc_media_player_get_title_count( instance, &ex );
555     catch_exception( &ex );
556     return result;
557 }
558
559 - (NSArray *)titles
560 {
561     libvlc_exception_t ex;
562     libvlc_exception_init( &ex );
563     libvlc_track_description_t *tracks = libvlc_video_get_title_description( instance, &ex );
564     NSMutableArray *tempArray = [NSMutableArray array];
565     NSInteger i;
566     for (i = 0; i < [self countOfTitles] ; i++)
567     {
568         [tempArray addObject:[NSString stringWithUTF8String: tracks->psz_name]];
569         tracks = tracks->p_next;
570     }
571     return [NSArray arrayWithArray: tempArray];
572 }
573
574 #pragma mark -
575 #pragma mark Audio tracks
576 - (void)setCurrentAudioTrackIndex:(NSUInteger)value
577 {
578     libvlc_exception_t ex;
579     libvlc_exception_init( &ex );
580     libvlc_audio_set_track( instance, (int)value, &ex );
581     catch_exception( &ex );
582 }
583
584 - (NSUInteger)currentAudioTrackIndex
585 {
586     libvlc_exception_t ex;
587     libvlc_exception_init( &ex );
588     int count = libvlc_audio_get_track_count( instance, &ex );
589     catch_exception( &ex );
590     if (count <= 0)
591         return NSNotFound;
592     
593     NSUInteger result = libvlc_audio_get_track( instance, &ex );
594     catch_exception( &ex );
595     return result;
596 }
597
598 - (NSArray *)audioTracks
599 {
600     libvlc_exception_t ex;
601     libvlc_exception_init( &ex );
602     int count = libvlc_audio_get_track_count( instance, &ex );
603     catch_exception( &ex );
604     if (count <= 0)
605         return [NSArray array];
606
607     libvlc_track_description_t *tracks = libvlc_audio_get_track_description( instance, &ex );
608     NSMutableArray *tempArray = [NSMutableArray array];
609     NSInteger i;
610     for (i = 0; i < count ; i++)
611     {
612         [tempArray addObject:[NSString stringWithUTF8String: tracks->psz_name]];
613         tracks = tracks->p_next;
614     }
615
616     return [NSArray arrayWithArray: tempArray];
617 }
618
619 - (void)setAudioChannel:(int)value
620 {
621     libvlc_exception_t ex;
622     libvlc_exception_init( &ex );
623     libvlc_audio_set_channel( instance, value, &ex );
624     catch_exception( &ex );
625 }
626
627 - (int)audioChannel
628 {
629     libvlc_exception_t ex;
630     libvlc_exception_init( &ex );
631     int result = libvlc_audio_get_channel( instance, &ex );
632     catch_exception( &ex );
633     return result;
634 }
635
636 - (void)setMedia:(VLCMedia *)value
637 {
638     if (media != value)
639     {
640         if (media && [media compare:value] == NSOrderedSame)
641             return;
642         
643         [media release];
644         media = [value retain];
645
646         libvlc_exception_t ex;
647         libvlc_exception_init( &ex );
648         libvlc_media_player_set_media( instance, [media libVLCMediaDescriptor], &ex );
649         catch_exception( &ex );
650     }
651 }
652
653 - (VLCMedia *)media
654 {
655     return media;
656 }
657
658 - (BOOL)play
659 {    
660     libvlc_exception_t ex;
661     libvlc_exception_init( &ex );
662     libvlc_media_player_play( (libvlc_media_player_t *)instance, &ex );
663     catch_exception( &ex );
664     return YES;
665 }
666
667 - (void)pause
668 {
669     if( [NSThread isMainThread] )
670     {
671         /* Hack because we create a dead lock here, when the vout is stopped
672          * and tries to recontact us on the main thread */
673         /* FIXME: to do this properly we need to do some locking. We may want 
674          * to move that to libvlc */
675         [self performSelectorInBackground:@selector(pause) withObject:nil];
676         return;
677     }
678
679     // Pause the stream
680     libvlc_exception_t ex;
681     libvlc_exception_init( &ex );
682     libvlc_media_player_pause( (libvlc_media_player_t *)instance, &ex );
683
684     // fail gracefully
685     // in most cases, it's just EOF so let's stop
686     if (libvlc_exception_raised(&ex))
687         [self stop];
688
689     libvlc_exception_clear(&ex);
690 }
691
692 - (void)stop
693 {
694     libvlc_exception_t ex;
695     libvlc_exception_init( &ex );
696     libvlc_media_player_stop((libvlc_media_player_t *)instance, &ex);
697     catch_exception( &ex );
698 }
699
700 - (void)fastForward
701 {
702     [self fastForwardAtRate: 2.0];
703 }
704
705 - (void)fastForwardAtRate:(float)rate
706 {
707     [self setRate:rate];
708 }
709
710 - (void)rewind
711 {
712     [self rewindAtRate: 2.0];
713 }
714
715 - (void)rewindAtRate:(float)rate
716 {
717     [self setRate: -rate];
718 }
719
720 - (void)jumpBackward:(NSInteger)interval
721 {
722     if( [self isSeekable] )
723     {
724         interval = interval * 1000000;
725         [self setTime: [VLCTime timeWithInt: ([[self time] intValue] - interval)]];
726     }
727 }
728
729 - (void)jumpForward:(NSInteger)interval
730 {
731     if( [self isSeekable] )
732     {
733         interval = interval * 1000000;
734         [self setTime: [VLCTime timeWithInt: ([[self time] intValue] + interval)]];
735     }
736 }
737
738 - (void)extraShortJumpBackward
739 {
740     [self jumpBackward:3];
741 }
742
743 - (void)extraShortJumpForward
744 {
745     [self jumpForward:3];
746 }
747
748 - (void)shortJumpBackward
749 {
750     [self jumpBackward:10];
751 }
752
753 - (void)shortJumpForward
754 {
755     [self jumpForward:10];
756 }
757
758 - (void)mediumJumpBackward
759 {
760     [self jumpBackward:60];
761 }
762
763 - (void)mediumJumpForward
764 {
765     [self jumpForward:60];
766 }
767
768 - (void)longJumpBackward
769 {
770     [self jumpBackward:300];
771 }
772
773 - (void)longJumpForward
774 {
775     [self jumpForward:300];
776 }
777
778 + (NSSet *)keyPathsForValuesAffectingIsPlaying
779 {
780     return [NSSet setWithObjects:@"state", nil];
781 }
782
783 - (BOOL)isPlaying
784 {
785     VLCMediaPlayerState state = [self state];
786     return ((state == VLCMediaPlayerStateOpening) || (state == VLCMediaPlayerStateBuffering) ||
787             (state == VLCMediaPlayerStatePlaying));
788 }
789
790 - (BOOL)willPlay
791 {
792     libvlc_exception_t ex;
793     libvlc_exception_init( &ex );
794     BOOL ret = libvlc_media_player_will_play( (libvlc_media_player_t *)instance, &ex );
795     if (libvlc_exception_raised(&ex))
796     {
797         libvlc_exception_clear(&ex);
798         return NO;
799     }
800     else
801         return ret;
802 }
803
804 static const VLCMediaPlayerState libvlc_to_local_state[] =
805 {
806     [libvlc_Stopped]    = VLCMediaPlayerStateStopped,
807     [libvlc_Opening]    = VLCMediaPlayerStateOpening,
808     [libvlc_Buffering]  = VLCMediaPlayerStateBuffering,
809     [libvlc_Playing]    = VLCMediaPlayerStatePlaying,
810     [libvlc_Paused]     = VLCMediaPlayerStatePaused,
811     [libvlc_Ended]      = VLCMediaPlayerStateEnded,
812     [libvlc_Error]      = VLCMediaPlayerStateError
813 };
814
815 - (VLCMediaPlayerState)state
816 {
817     return cachedState;
818 }
819
820 - (float)position
821 {
822     return position;
823 }
824
825 - (void)setPosition:(float)newPosition
826 {
827     libvlc_exception_t ex;
828     libvlc_exception_init( &ex );
829     libvlc_media_player_set_position( instance, newPosition, &ex );
830     catch_exception( &ex );
831 }
832
833 - (BOOL)isSeekable
834 {
835     libvlc_exception_t ex;
836     libvlc_exception_init( &ex );
837     BOOL ret = libvlc_media_player_is_seekable( instance, &ex );
838     catch_exception( &ex );
839     return ret;
840 }
841
842 - (BOOL)canPause
843 {
844     libvlc_exception_t ex;
845     libvlc_exception_init( &ex );
846     BOOL ret = libvlc_media_player_can_pause( instance, &ex );
847     catch_exception( &ex );
848     return ret;
849 }
850
851 - (void *)libVLCMediaPlayer
852 {
853     return instance;
854 }
855 @end
856
857 @implementation VLCMediaPlayer (Private)
858 - (id)initWithDrawable:(id)aDrawable
859 {
860     if (self = [super init])
861     {
862         delegate = nil;
863         media = nil;
864         cachedTime = [[VLCTime nullTime] retain];
865         position = 0.0f;
866         cachedState = VLCMediaPlayerStateStopped;
867
868         // Create a media instance, it doesn't matter what library we start off with
869         // it will change depending on the media descriptor provided to the media
870         // instance
871         libvlc_exception_t ex;
872         libvlc_exception_init( &ex );
873         instance = (void *)libvlc_media_player_new([VLCLibrary sharedInstance], &ex);
874         catch_exception( &ex );
875         
876         [self registerObservers];
877         
878         [self setDrawable:aDrawable];
879     }
880     return self;
881 }
882
883 - (void)registerObservers
884 {
885     libvlc_exception_t ex;
886     libvlc_exception_init( &ex );
887
888     // Attach event observers into the media instance
889     libvlc_event_manager_t * p_em = libvlc_media_player_event_manager( instance, &ex );
890     libvlc_event_attach( p_em, libvlc_MediaPlayerPlaying,          HandleMediaInstanceStateChanged, self, &ex );
891     libvlc_event_attach( p_em, libvlc_MediaPlayerPaused,           HandleMediaInstanceStateChanged, self, &ex );
892     libvlc_event_attach( p_em, libvlc_MediaPlayerEncounteredError, HandleMediaInstanceStateChanged, self, &ex );
893     libvlc_event_attach( p_em, libvlc_MediaPlayerEndReached,       HandleMediaInstanceStateChanged, self, &ex );
894     /* FIXME: We may want to turn that off when none is interested by that */
895     libvlc_event_attach( p_em, libvlc_MediaPlayerPositionChanged, HandleMediaPositionChanged,      self, &ex );
896     libvlc_event_attach( p_em, libvlc_MediaPlayerTimeChanged,     HandleMediaTimeChanged,          self, &ex );
897     catch_exception( &ex );
898 }
899
900 - (void)unregisterObservers
901 {
902     libvlc_event_manager_t * p_em = libvlc_media_player_event_manager( instance, NULL );
903     libvlc_event_detach( p_em, libvlc_MediaPlayerPlaying,          HandleMediaInstanceStateChanged, self, NULL );
904     libvlc_event_detach( p_em, libvlc_MediaPlayerPaused,           HandleMediaInstanceStateChanged, self, NULL );
905     libvlc_event_detach( p_em, libvlc_MediaPlayerEncounteredError, HandleMediaInstanceStateChanged, self, NULL );
906     libvlc_event_detach( p_em, libvlc_MediaPlayerEndReached,       HandleMediaInstanceStateChanged, self, NULL );
907     libvlc_event_detach( p_em, libvlc_MediaPlayerPositionChanged,  HandleMediaPositionChanged,      self, NULL );
908     libvlc_event_detach( p_em, libvlc_MediaPlayerTimeChanged,      HandleMediaTimeChanged,          self, NULL );
909 }
910
911 - (void)mediaPlayerTimeChanged:(NSNumber *)newTime
912 {
913     [self willChangeValueForKey:@"time"];
914     [self willChangeValueForKey:@"remainingTime"];
915     [cachedTime release];
916     cachedTime = [[VLCTime timeWithNumber:newTime] retain];
917
918     [self didChangeValueForKey:@"remainingTime"];
919     [self didChangeValueForKey:@"time"];
920 }
921
922 - (void)delaySleep
923 {
924     UpdateSystemActivity(UsrActivity);
925 }
926
927 - (void)mediaPlayerPositionChanged:(NSNumber *)newPosition
928 {
929     // This seems to be the most relevant place to delay sleeping and screen saver.
930     [self delaySleep];
931
932     [self willChangeValueForKey:@"position"];
933     position = [newPosition floatValue];
934     [self didChangeValueForKey:@"position"];
935 }
936
937 - (void)mediaPlayerStateChanged:(NSNumber *)newState
938 {
939     [self willChangeValueForKey:@"state"];
940     cachedState = [newState intValue];
941     [self didChangeValueForKey:@"state"];
942 }
943
944 @end