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