VLCMediaPlayer.m 22.3 KB
Newer Older
1
/*****************************************************************************
2
 * VLCMediaPlayer.m: VLCKit.framework VLCMediaPlayer implementation
3
 *****************************************************************************
4 5 6
 * Copyright (C) 2007-2009 Pierre d'Herbemont
 * Copyright (C) 2007-2009 the VideoLAN team
 * Partial Copyright (C) 2009 Felix Paul Kühne
7 8 9 10
 * $Id$
 *
 * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
 *          Faustion Osuna <enrique.osuna # gmail.com>
11
 *          Felix Paul Kühne <fkuehne # videolan.org>
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#import "VLCLibrary.h"
#import "VLCMediaPlayer.h"
#import "VLCEventManager.h"
#import "VLCLibVLCBridging.h"
32
#import "VLCVideoView.h"
33 34 35 36
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

37 38
/* prevent system sleep */
#import <CoreServices/CoreServices.h>
39 40 41 42
/* FIXME: Ugly hack! */
#ifdef __x86_64__
#import <CoreServices/../Frameworks/OSServices.framework/Headers/Power.h>
#endif
43

44 45 46
#include <vlc/vlc.h>

/* Notification Messages */
47 48
NSString * VLCMediaPlayerTimeChanged    = @"VLCMediaPlayerTimeChanged";
NSString * VLCMediaPlayerStateChanged   = @"VLCMediaPlayerStateChanged";
49

50
NSString * VLCMediaPlayerStateToString(VLCMediaPlayerState state)
51
{
52
    static NSString * stateToStrings[] = {
53 54 55 56 57 58 59 60 61 62 63
        [VLCMediaPlayerStateStopped]      = @"VLCMediaPlayerStateStopped",
        [VLCMediaPlayerStateOpening]      = @"VLCMediaPlayerStateOpening",
        [VLCMediaPlayerStateBuffering]    = @"VLCMediaPlayerStateBuffering",
        [VLCMediaPlayerStateEnded]        = @"VLCMediaPlayerStateEnded",
        [VLCMediaPlayerStateError]        = @"VLCMediaPlayerStateError",
        [VLCMediaPlayerStatePlaying]      = @"VLCMediaPlayerStatePlaying",
        [VLCMediaPlayerStatePaused]       = @"VLCMediaPlayerStatePaused"
    };
    return stateToStrings[state];
}

64
/* libvlc event callback */
65
static void HandleMediaInstanceVolumeChanged(const libvlc_event_t * event, void * self)
66 67
{
    [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
68 69
                                                   withDelegateMethod:@selector(mediaPlayerVolumeChanged:)
                                                 withNotificationName:VLCMediaPlayerVolumeChanged];
70 71 72 73
}

static void HandleMediaTimeChanged(const libvlc_event_t * event, void * self)
{
74
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
75 76
    [[VLCEventManager sharedManager] callOnMainThreadObject:self
                                                 withMethod:@selector(mediaPlayerTimeChanged:)
77
                                       withArgumentAsObject:[NSNumber numberWithLongLong:event->u.media_player_time_changed.new_time]];
78

79 80 81
    [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
                                                   withDelegateMethod:@selector(mediaPlayerTimeChanged:)
                                                 withNotificationName:VLCMediaPlayerTimeChanged];
82
    [pool release];
83 84
}

85 86 87 88
static void HandleMediaPositionChanged(const libvlc_event_t * event, void * self)
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

89 90
    [[VLCEventManager sharedManager] callOnMainThreadObject:self
                                                 withMethod:@selector(mediaPlayerPositionChanged:)
91
                                       withArgumentAsObject:[NSNumber numberWithFloat:event->u.media_player_position_changed.new_position]];
92 93 94
    [pool release];
}

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 108 109 110 111
    else
    {
        NSLog(@"%s: Unknown event", __FUNCTION__);
        return;
    }
112

113
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
114 115 116

    [[VLCEventManager sharedManager] callOnMainThreadObject:self
                                                 withMethod:@selector(mediaPlayerStateChanged:)
117
                                       withArgumentAsObject:[NSNumber numberWithInt:newState]];
118

119
    [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
120 121
                                                   withDelegateMethod:@selector(mediaPlayerStateChanged:)
                                                 withNotificationName:VLCMediaPlayerStateChanged];
122

123
    [pool release];
124

125
}
126

127 128 129
static void HandleMediaPlayerMediaChanged(const libvlc_event_t * event, void * self)
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
130 131 132

    [[VLCEventManager sharedManager] callOnMainThreadObject:self
                                                 withMethod:@selector(mediaPlayerMediaChanged:)
133
                                       withArgumentAsObject:[VLCMedia mediaWithLibVLCMediaDescriptor:event->u.media_player_media_changed.new_media]];
134

135
    [pool release];
136

137 138
}

139

140
// TODO: Documentation
141
@interface VLCMediaPlayer (Private)
142 143
- (id)initWithDrawable:(id)aDrawable;

144 145
- (void)registerObservers;
- (void)unregisterObservers;
146
- (void)mediaPlayerTimeChanged:(NSNumber *)newTime;
147 148
- (void)mediaPlayerPositionChanged:(NSNumber *)newTime;
- (void)mediaPlayerStateChanged:(NSNumber *)newState;
149
- (void)mediaPlayerMediaChanged:(VLCMedia *)media;
150 151 152
@end

@implementation VLCMediaPlayer
153

154
/* Bindings */
155 156 157
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
{
    static NSDictionary * dict = nil;
158
    NSSet * superKeyPaths;
159 160 161 162 163
    if( !dict )
    {
        dict = [[NSDictionary dictionaryWithObjectsAndKeys:
            [NSSet setWithObject:@"state"], @"playing",
            [NSSet setWithObjects:@"state", @"media", nil], @"seekable",
164
            [NSSet setWithObjects:@"state", @"media", nil], @"canPause",
165
            [NSSet setWithObjects:@"state", @"media", nil], @"description",
166 167
            nil] retain];
    }
168 169 170 171 172 173
    if( (superKeyPaths = [super keyPathsForValuesAffectingValueForKey: key]) )
    {
        NSMutableSet * ret = [NSMutableSet setWithSet:[dict objectForKey: key]];
        [ret unionSet:superKeyPaths];
        return ret;
    }
174 175
    return [dict objectForKey: key];
}
176 177

/* Contructor */
178 179
- (id)init
{
180
    return [self initWithDrawable:nil];
181 182 183 184
}

- (id)initWithVideoView:(VLCVideoView *)aVideoView
{
185 186
    return [self initWithDrawable: aVideoView];
}
187

188 189 190
- (id)initWithVideoLayer:(VLCVideoLayer *)aVideoLayer
{
    return [self initWithDrawable: aVideoLayer];
191 192
}

193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
- (void)release
{
    @synchronized(self)
    {
        if([self retainCount] <= 1)
        {
            /* We must make sure we won't receive new event after an upcoming dealloc
             * We also may receive a -retain in some event callback that may occcur
             * Before libvlc_event_detach. So this can't happen in dealloc */
            [self unregisterObservers];
        }
        [super release];
    }
}

208 209
- (void)dealloc
{
210
    NSAssert(libvlc_media_player_get_state(instance) == libvlc_Stopped, @"You released the media player before ensuring that it is stopped");
211

212 213 214
    // Always get rid of the delegate first so we can stop sending messages to it
    // TODO: Should we tell the delegate that we're shutting down?
    delegate = nil;
215

216 217 218
    // Clear our drawable as we are going to release it, we don't
    // want the core to use it from this point. This won't happen as
    // the media player must be stopped.
219
    libvlc_media_player_set_nsobject(instance, nil);
220 221

    libvlc_media_player_release(instance);
222

223 224
    // Get rid of everything else
    [media release];
225
    [cachedTime release];
226
    [cachedRemainingTime release];
227
    [drawable release];
228
    [audio release];
229

230
    [super dealloc];
231 232 233 234
}

- (void)setDelegate:(id)value
{
235
    delegate = value;
236 237 238 239
}

- (id)delegate
{
240
    return delegate;
241 242
}

243
- (void)setVideoView:(VLCVideoView *)aVideoView
244
{
245
    [self setDrawable: aVideoView];
246 247
}

248
- (void)setVideoLayer:(VLCVideoLayer *)aVideoLayer
249
{
250
    [self setDrawable: aVideoLayer];
251 252
}

253 254 255
- (void)setDrawable:(id)aDrawable
{
    // Make sure that this instance has been associated with the drawing canvas.
256
    libvlc_media_player_set_nsobject(instance, aDrawable);
257 258 259 260
}

- (id)drawable
{
261
    return libvlc_media_player_get_nsobject(instance);
262 263
}

264 265
- (VLCAudio *)audio
{
266 267 268
    if (!audio)
        audio = [[VLCAudio alloc] initWithMediaPlayer:self];
    return audio;
269 270
}

271 272
#pragma mark -
#pragma mark Subtitles
273

274
- (void)setCurrentVideoSubTitleIndex:(NSUInteger)index
275
{
276
    libvlc_video_set_spu(instance, (int)index);
277 278
}

279
- (NSUInteger)currentVideoSubTitleIndex
280
{
281 282
    NSInteger count = libvlc_video_get_spu_count(instance);

283 284
    if (count <= 0)
        return NSNotFound;
285

286
    return libvlc_video_get_spu(instance);
287 288
}

289 290
- (BOOL)openVideoSubTitlesFromFile:(NSString *)path
{
291
    return libvlc_video_set_subtitle_file(instance, [path UTF8String]);
292 293
}

294 295
- (NSArray *)videoSubTitles
{
296
    libvlc_track_description_t *currentTrack = libvlc_video_get_spu_description(instance);
297

298
    NSMutableArray *tempArray = [NSMutableArray array];
299 300
    while (currentTrack) {
        [tempArray addObject:[NSString stringWithUTF8String:currentTrack->psz_name]];
301
        currentTrack = currentTrack->p_next;
302
    }
303
    libvlc_track_description_release(currentTrack);
304 305 306
    return [NSArray arrayWithArray: tempArray];
}

307 308 309 310

#pragma mark -
#pragma mark Video Crop geometry

311 312
- (void)setVideoCropGeometry:(char *)value
{
313
    libvlc_video_set_crop_geometry(instance, value);
314 315 316 317
}

- (char *)videoCropGeometry
{
318
    char * result = libvlc_video_get_crop_geometry(instance);
319
    return result;
320 321
}

322 323
- (void)setVideoAspectRatio:(char *)value
{
324
    libvlc_video_set_aspect_ratio( instance, value );
325 326 327 328
}

- (char *)videoAspectRatio
{
329
    char * result = libvlc_video_get_aspect_ratio( instance );
330 331 332
    return result;
}

333
- (void)saveVideoSnapshotAt:(NSString *)path withWidth:(NSUInteger)width andHeight:(NSUInteger)height
334
{
335 336 337
    int failure = libvlc_video_take_snapshot(instance, 0, [path UTF8String], width, height);
    if (failure)
        [[NSException exceptionWithName:@"Can't take a video snapshot" reason:@"No video output" userInfo:nil] raise];
338 339
}

340
- (void)setDeinterlaceFilter:(NSString *)name
341
{
342
    libvlc_video_set_deinterlace(instance, [name UTF8String]);
343 344
}

345
- (void)setRate:(float)value
346
{
347
    libvlc_media_player_set_rate(instance, value);
348 349
}

350
- (float)rate
351
{
352
    return libvlc_media_player_get_rate(instance);
353 354 355 356
}

- (NSSize)videoSize
{
357 358 359 360 361
    unsigned height = 0, width = 0;
    int failure = libvlc_video_get_size(instance, 0, &width, &height);
    if (failure)
        [[NSException exceptionWithName:@"Can't get video size" reason:@"No video output" userInfo:nil] raise];
    return NSMakeSize(width, height);
362 363 364 365
}

- (BOOL)hasVideoOut
{
366
    return libvlc_media_player_has_vout(instance);
367 368 369 370
}

- (float)framesPerSecond
{
371
    return libvlc_media_player_get_fps(instance);
372 373 374 375
}

- (void)setTime:(VLCTime *)value
{
376 377
    // Time is managed in seconds, while duration is managed in microseconds
    // TODO: Redo VLCTime to provide value numberAsMilliseconds, numberAsMicroseconds, numberAsSeconds, numberAsMinutes, numberAsHours
378
    libvlc_media_player_set_time(instance, value ? [[value numberValue] longLongValue] : 0);
379 380 381 382
}

- (VLCTime *)time
{
383
    return cachedTime;
384 385
}

386 387
- (VLCTime *)remainingTime
{
388
    return cachedRemainingTime;
389 390
}

391
- (NSUInteger)fps
392
{
393
    return libvlc_media_player_get_fps(instance);
394 395
}

396 397 398
#pragma mark -
#pragma mark Chapters
- (void)setCurrentChapterIndex:(NSUInteger)value;
399
{
400
    libvlc_media_player_set_chapter(instance, value);
401 402
}

403
- (NSUInteger)currentChapterIndex
404
{
405
    NSInteger count = libvlc_media_player_get_chapter_count(instance);
406 407
    if (count <= 0)
        return NSNotFound;
408
    NSUInteger result = libvlc_media_player_get_chapter(instance);
409 410 411
    return result;
}

412 413
- (void)nextChapter
{
414
    libvlc_media_player_next_chapter(instance);
415 416 417 418
}

- (void)previousChapter
{
419
    libvlc_media_player_previous_chapter(instance);
420 421
}

422
- (NSArray *)chaptersForTitleIndex:(NSUInteger)title
423
{
424
    NSInteger count = libvlc_media_player_get_chapter_count(instance);
425 426 427
    if (count <= 0)
        return [NSArray array];

428
    libvlc_track_description_t *tracks = libvlc_video_get_chapter_description(instance, title);
429 430
    NSMutableArray *tempArray = [NSMutableArray array];
    NSInteger i;
431
    for (i = 0; i < count ; i++)
432
    {
433
        [tempArray addObject:[NSString stringWithUTF8String:tracks->psz_name]];
434
        tracks = tracks->p_next;
435
    }
436
    libvlc_track_description_release(tracks);
437
    return [NSArray arrayWithArray:tempArray];
438 439
}

440 441 442 443
#pragma mark -
#pragma mark Titles

- (void)setCurrentTitleIndex:(NSUInteger)value
444
{
445
    libvlc_media_player_set_title(instance, value);
446 447
}

448
- (NSUInteger)currentTitleIndex
449
{
450
    NSInteger count = libvlc_media_player_get_title_count(instance);
451 452
    if (count <= 0)
        return NSNotFound;
453

454
    return libvlc_media_player_get_title(instance);
455 456
}

457
- (NSUInteger)countOfTitles
458
{
459
    NSUInteger result = libvlc_media_player_get_title_count(instance);
460 461 462
    return result;
}

463 464
- (NSArray *)titles
{
465
    libvlc_track_description_t *tracks = libvlc_video_get_title_description(instance);
466 467 468 469 470
    NSMutableArray *tempArray = [NSMutableArray array];
    NSInteger i;
    for (i = 0; i < [self countOfTitles] ; i++)
    {
        [tempArray addObject:[NSString stringWithUTF8String: tracks->psz_name]];
471
        tracks = tracks->p_next;
472
    }
473
    libvlc_track_description_release(tracks);
474 475 476
    return [NSArray arrayWithArray: tempArray];
}

477 478 479
#pragma mark -
#pragma mark Audio tracks
- (void)setCurrentAudioTrackIndex:(NSUInteger)value
480
{
481
    libvlc_audio_set_track( instance, (int)value);
482 483
}

484
- (NSUInteger)currentAudioTrackIndex
485
{
486
    NSInteger count = libvlc_audio_get_track_count(instance);
487 488
    if (count <= 0)
        return NSNotFound;
489

490
    NSUInteger result = libvlc_audio_get_track(instance);
491 492 493
    return result;
}

494 495
- (NSArray *)audioTracks
{
496
    NSInteger count = libvlc_audio_get_track_count(instance);
497 498 499
    if (count <= 0)
        return [NSArray array];

500
    libvlc_track_description_t *tracks = libvlc_audio_get_track_description(instance);
501
    NSMutableArray *tempArray = [NSMutableArray array];
502
    NSUInteger i;
503
    for (i = 0; i < count ; i++)
504 505
    {
        [tempArray addObject:[NSString stringWithUTF8String: tracks->psz_name]];
506
        tracks = tracks->p_next;
507
    }
508
    libvlc_track_description_release(tracks);
509 510 511 512

    return [NSArray arrayWithArray: tempArray];
}

513
- (void)setAudioChannel:(NSInteger)value
514
{
515
    libvlc_audio_set_channel(instance, value);
516 517
}

518
- (NSInteger)audioChannel
519
{
520
    return libvlc_audio_get_channel(instance);
521 522 523 524
}

- (void)setMedia:(VLCMedia *)value
{
525
    if (media != value)
526 527 528
    {
        if (media && [media compare:value] == NSOrderedSame)
            return;
529

530 531 532
        [media release];
        media = [value retain];

533
        libvlc_media_player_set_media(instance, [media libVLCMediaDescriptor]);
534
    }
535 536 537 538
}

- (VLCMedia *)media
{
539
    return media;
540 541 542
}

- (BOOL)play
543
{
544
    libvlc_media_player_play(instance);
545
    return YES;
546 547 548 549
}

- (void)pause
{
550 551 552 553
    if( [NSThread isMainThread] )
    {
        /* Hack because we create a dead lock here, when the vout is stopped
         * and tries to recontact us on the main thread */
554
        /* FIXME: to do this properly we need to do some locking. We may want
555 556 557 558 559
         * to move that to libvlc */
        [self performSelectorInBackground:@selector(pause) withObject:nil];
        return;
    }

560
    // Pause the stream
561
    libvlc_media_player_pause(instance);
562 563 564 565
}

- (void)stop
{
566
    libvlc_media_player_stop(instance);
567 568
}

569 570
- (void)gotoNextFrame
{
571
    libvlc_media_player_next_frame(instance);
572 573 574

}

575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593
- (void)fastForward
{
    [self fastForwardAtRate: 2.0];
}

- (void)fastForwardAtRate:(float)rate
{
    [self setRate:rate];
}

- (void)rewind
{
    [self rewindAtRate: 2.0];
}

- (void)rewindAtRate:(float)rate
{
    [self setRate: -rate];
}
594

595 596 597 598
- (void)jumpBackward:(NSInteger)interval
{
    if( [self isSeekable] )
    {
599
        interval = interval * 1000;
600 601 602 603 604 605 606 607
        [self setTime: [VLCTime timeWithInt: ([[self time] intValue] - interval)]];
    }
}

- (void)jumpForward:(NSInteger)interval
{
    if( [self isSeekable] )
    {
608
        interval = interval * 1000;
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652
        [self setTime: [VLCTime timeWithInt: ([[self time] intValue] + interval)]];
    }
}

- (void)extraShortJumpBackward
{
    [self jumpBackward:3];
}

- (void)extraShortJumpForward
{
    [self jumpForward:3];
}

- (void)shortJumpBackward
{
    [self jumpBackward:10];
}

- (void)shortJumpForward
{
    [self jumpForward:10];
}

- (void)mediumJumpBackward
{
    [self jumpBackward:60];
}

- (void)mediumJumpForward
{
    [self jumpForward:60];
}

- (void)longJumpBackward
{
    [self jumpBackward:300];
}

- (void)longJumpForward
{
    [self jumpForward:300];
}

653 654 655 656 657
+ (NSSet *)keyPathsForValuesAffectingIsPlaying
{
    return [NSSet setWithObjects:@"state", nil];
}

658 659
- (BOOL)isPlaying
{
660
    VLCMediaPlayerState state = [self state];
661
    return ((state == VLCMediaPlayerStateOpening) || (state == VLCMediaPlayerStateBuffering) ||
662
            (state == VLCMediaPlayerStatePlaying));
663 664 665 666
}

- (BOOL)willPlay
{
667
    return libvlc_media_player_will_play(instance);
668 669
}

670
static const VLCMediaPlayerState libvlc_to_local_state[] =
671
{
672 673
    [libvlc_Stopped]    = VLCMediaPlayerStateStopped,
    [libvlc_Opening]    = VLCMediaPlayerStateOpening,
674
    [libvlc_Buffering]  = VLCMediaPlayerStateBuffering,
675
    [libvlc_Playing]    = VLCMediaPlayerStatePlaying,
676 677 678
    [libvlc_Paused]     = VLCMediaPlayerStatePaused,
    [libvlc_Ended]      = VLCMediaPlayerStateEnded,
    [libvlc_Error]      = VLCMediaPlayerStateError
679 680 681 682
};

- (VLCMediaPlayerState)state
{
683 684
    return cachedState;
}
685

686 687 688 689
- (float)position
{
    return position;
}
690

691 692
- (void)setPosition:(float)newPosition
{
693
    libvlc_media_player_set_position(instance, newPosition);
694
}
695 696 697

- (BOOL)isSeekable
{
698
    return libvlc_media_player_is_seekable(instance);
699 700
}

701 702
- (BOOL)canPause
{
703
    return libvlc_media_player_can_pause(instance);
704
}
705

706 707 708 709
- (void *)libVLCMediaPlayer
{
    return instance;
}
710 711
@end

712
@implementation VLCMediaPlayer (Private)
713 714 715 716 717 718 719
- (id)initWithDrawable:(id)aDrawable
{
    if (self = [super init])
    {
        delegate = nil;
        media = nil;
        cachedTime = [[VLCTime nullTime] retain];
720
        cachedRemainingTime = [[VLCTime nullTime] retain];
721 722 723 724 725 726
        position = 0.0f;
        cachedState = VLCMediaPlayerStateStopped;

        // Create a media instance, it doesn't matter what library we start off with
        // it will change depending on the media descriptor provided to the media
        // instance
727
        instance = libvlc_media_player_new([VLCLibrary sharedInstance]);
728

729
        [self registerObservers];
730

731 732 733 734 735
        [self setDrawable:aDrawable];
    }
    return self;
}

736 737
- (void)registerObservers
{
738
    // Attach event observers into the media instance
739
    libvlc_event_manager_t * p_em = libvlc_media_player_event_manager(instance);
740 741 742 743
    libvlc_event_attach(p_em, libvlc_MediaPlayerPlaying,          HandleMediaInstanceStateChanged, self);
    libvlc_event_attach(p_em, libvlc_MediaPlayerPaused,           HandleMediaInstanceStateChanged, self);
    libvlc_event_attach(p_em, libvlc_MediaPlayerEncounteredError, HandleMediaInstanceStateChanged, self);
    libvlc_event_attach(p_em, libvlc_MediaPlayerEndReached,       HandleMediaInstanceStateChanged, self);
744
    /* FIXME: We may want to turn that off when none is interested by that */
745 746 747
    libvlc_event_attach(p_em, libvlc_MediaPlayerPositionChanged, HandleMediaPositionChanged,      self);
    libvlc_event_attach(p_em, libvlc_MediaPlayerTimeChanged,     HandleMediaTimeChanged,          self);
    libvlc_event_attach(p_em, libvlc_MediaPlayerMediaChanged,    HandleMediaPlayerMediaChanged,  self);
748 749 750 751
}

- (void)unregisterObservers
{
752 753 754 755 756 757 758 759
    libvlc_event_manager_t * p_em = libvlc_media_player_event_manager(instance);
    libvlc_event_detach(p_em, libvlc_MediaPlayerPlaying,          HandleMediaInstanceStateChanged, self);
    libvlc_event_detach(p_em, libvlc_MediaPlayerPaused,           HandleMediaInstanceStateChanged, self);
    libvlc_event_detach(p_em, libvlc_MediaPlayerEncounteredError, HandleMediaInstanceStateChanged, self);
    libvlc_event_detach(p_em, libvlc_MediaPlayerEndReached,       HandleMediaInstanceStateChanged, self);
    libvlc_event_detach(p_em, libvlc_MediaPlayerPositionChanged,  HandleMediaPositionChanged,      self);
    libvlc_event_detach(p_em, libvlc_MediaPlayerTimeChanged,      HandleMediaTimeChanged,          self);
    libvlc_event_detach(p_em, libvlc_MediaPlayerMediaChanged,     HandleMediaPlayerMediaChanged,   self);
760
}
761

762 763 764
- (void)mediaPlayerTimeChanged:(NSNumber *)newTime
{
    [self willChangeValueForKey:@"time"];
765
    [self willChangeValueForKey:@"remainingTime"];
766 767
    [cachedTime release];
    cachedTime = [[VLCTime timeWithNumber:newTime] retain];
768 769 770 771
    [cachedRemainingTime release];
    double currentTime = [[cachedTime numberValue] doubleValue];
    double remaining = currentTime / position * (1 - position);
    cachedRemainingTime = [[VLCTime timeWithNumber:[NSNumber numberWithDouble:-remaining]] retain];
772
    [self didChangeValueForKey:@"remainingTime"];
773 774
    [self didChangeValueForKey:@"time"];
}
775

776 777 778 779 780
- (void)delaySleep
{
    UpdateSystemActivity(UsrActivity);
}

781 782
- (void)mediaPlayerPositionChanged:(NSNumber *)newPosition
{
783 784 785
    // This seems to be the most relevant place to delay sleeping and screen saver.
    [self delaySleep];

786
    [self willChangeValueForKey:@"position"];
787
    position = [newPosition floatValue];
788 789 790 791 792 793 794 795 796
    [self didChangeValueForKey:@"position"];
}

- (void)mediaPlayerStateChanged:(NSNumber *)newState
{
    [self willChangeValueForKey:@"state"];
    cachedState = [newState intValue];
    [self didChangeValueForKey:@"state"];
}
797

798 799 800 801 802 803 804 805 806 807 808
- (void)mediaPlayerMediaChanged:(VLCMedia *)newMedia
{
    [self willChangeValueForKey:@"media"];
    if (media != newMedia)
    {
        [media release];
        media = [newMedia retain];
    }
    [self didChangeValueForKey:@"media"];
}

809
@end