VLCMediaPlayer.m 18.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*****************************************************************************
 * VLCMediaPlayer.m: VLC.framework VLCMediaPlayer implementation
 *****************************************************************************
 * Copyright (C) 2007 Pierre d'Herbemont
 * Copyright (C) 2007 the VideoLAN team
 * $Id$
 *
 * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
 *          Faustion Osuna <enrique.osuna # gmail.com>
 *
 * 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"
30
#import "VLCVideoView.h"
31
32
33
#include <vlc/vlc.h>

/* Notification Messages */
34
35
NSString * VLCMediaPlayerTimeChanged    = @"VLCMediaPlayerTimeChanged";
NSString * VLCMediaPlayerStateChanged   = @"VLCMediaPlayerStateChanged";
36

37
NSString * VLCMediaPlayerStateToString(VLCMediaPlayerState state)
38
{
39
    static NSString * stateToStrings[] = {
40
41
42
43
44
45
46
47
48
49
50
        [VLCMediaPlayerStateStopped]      = @"VLCMediaPlayerStateStopped",
        [VLCMediaPlayerStateOpening]      = @"VLCMediaPlayerStateOpening",
        [VLCMediaPlayerStateBuffering]    = @"VLCMediaPlayerStateBuffering",
        [VLCMediaPlayerStateEnded]        = @"VLCMediaPlayerStateEnded",
        [VLCMediaPlayerStateError]        = @"VLCMediaPlayerStateError",
        [VLCMediaPlayerStatePlaying]      = @"VLCMediaPlayerStatePlaying",
        [VLCMediaPlayerStatePaused]       = @"VLCMediaPlayerStatePaused"
    };
    return stateToStrings[state];
}

51
/* libvlc event callback */
52
static void HandleMediaInstanceVolumeChanged(const libvlc_event_t * event, void * self)
53
54
{
    [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
55
56
                                                   withDelegateMethod:@selector(mediaPlayerVolumeChanged:)
                                                 withNotificationName:VLCMediaPlayerVolumeChanged];
57
58
59
60
}

static void HandleMediaTimeChanged(const libvlc_event_t * event, void * self)
{
61
62
63
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    [[VLCEventManager sharedManager] callOnMainThreadObject:self 
                                                 withMethod:@selector(mediaPlayerTimeChanged:) 
64
                                       withArgumentAsObject:[NSNumber numberWithLongLong:event->u.media_instance_time_changed.new_time]];
65

66
67
68
    [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
                                                   withDelegateMethod:@selector(mediaPlayerTimeChanged:)
                                                 withNotificationName:VLCMediaPlayerTimeChanged];
69
    [pool release];
70
71
}

72
73
74
75
76
77
78
79
80
81
static void HandleMediaPositionChanged(const libvlc_event_t * event, void * self)
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    [[VLCEventManager sharedManager] callOnMainThreadObject:self 
                                                 withMethod:@selector(mediaPlayerPositionChanged:) 
                                       withArgumentAsObject:[NSNumber numberWithFloat:event->u.media_instance_position_changed.new_position]];
    [pool release];
}

82
static void HandleMediaInstanceStateChanged(const libvlc_event_t * event, void * self)
83
{
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
    VLCMediaPlayerState newState;
    
    if( event->type == libvlc_MediaInstancePlayed )
        newState = VLCMediaPlayerStatePlaying;
    else if( event->type == libvlc_MediaInstancePaused )
        newState = VLCMediaPlayerStatePaused;
    else if( event->type == libvlc_MediaInstanceReachedEnd )
        newState = VLCMediaPlayerStateStopped;
    else
    {
        NSLog(@"%s: Unknown event", __FUNCTION__);
        return;
    }

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

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

104
    [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
105
106
                                                   withDelegateMethod:@selector(mediaPlayerStateChanged:)
                                                 withNotificationName:VLCMediaPlayerStateChanged];
107

108
109
    [pool release];

110
111
}

112

113
// TODO: Documentation
114
@interface VLCMediaPlayer (Private)
115
116
117
- (id)initWithDrawable:(id)aDrawable;
- (void)setDrawable:(id)aDrawable;

118
119
- (void)registerObservers;
- (void)unregisterObservers;
120
- (void)mediaPlayerTimeChanged:(NSNumber *)newTime;
121
122
- (void)mediaPlayerPositionChanged:(NSNumber *)newTime;
- (void)mediaPlayerStateChanged:(NSNumber *)newState;
123
124
125
@end

@implementation VLCMediaPlayer
126

127
128
129
130
131
/* Bindings */
+ (id)keysPathsForValuesAffectingPlaying { return [NSSet setWithObject:@"state"]; }
+ (id)keysPathsForValuesAffectingSeekable { return [NSSet setWithObjects:@"state", @"media"]; }

/* Contructor */
132
133
- (id)init
{
134
    return [self initWithDrawable:nil];
135
136
137
138
}

- (id)initWithVideoView:(VLCVideoView *)aVideoView
{
139
140
    return [self initWithDrawable: aVideoView];
}
141

142
143
144
- (id)initWithVideoLayer:(VLCVideoLayer *)aVideoLayer
{
    return [self initWithDrawable: aVideoLayer];
145
146
}

147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
- (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];
    }
}

162
163
- (void)dealloc
{
164
165
166
    // 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;
167

168
169
170
171
    libvlc_media_instance_release((libvlc_media_instance_t *)instance);
    
    // Get rid of everything else
    [media release];
172
173
    [cachedTime release];

174
    [super dealloc];
175
176
177
178
}

- (void)setDelegate:(id)value
{
179
    delegate = value;
180
181
182
183
}

- (id)delegate
{
184
    return delegate;
185
186
}

187
188
189
- (void)setVideoView:(VLCVideoView *)aVideoView
{    
    [self setDrawable: aVideoView];
190
191
}

192
- (void)setVideoLayer:(VLCVideoLayer *)aVideoLayer
193
{
194
    [self setDrawable: aVideoLayer];
195
196
197
198
}

- (void)setFullscreen:(BOOL)value
{
199
    libvlc_set_fullscreen(instance, value, NULL);
200
201
202
203
}

- (BOOL)fullscreen
{
204
    libvlc_exception_t ex;
205
206
    libvlc_exception_init( &ex );
    int result = libvlc_get_fullscreen( instance, &ex );
207
    catch_exception( &ex );
208
    return result;
209
210
211
212
}

- (void)setVideoAspectRatio:(char *)value
{
213
    libvlc_video_set_aspect_ratio( instance, value, NULL );
214
215
216
217
}

- (char *)videoAspectRatio
{
218
    libvlc_exception_t ex;
219
    libvlc_exception_init( &ex );
220
    char * result = libvlc_video_get_aspect_ratio( instance, &ex );
221
    catch_exception( &ex );
222
    return result;
223
224
225
226
}

- (void)setVideoSubTitles:(int)value
{
227
    libvlc_video_set_spu( instance, value, NULL );
228
229
230
231
}

- (int)videoSubTitles
{
232
    libvlc_exception_t ex;
233
234
    libvlc_exception_init( &ex );
    int result = libvlc_video_get_spu( instance, &ex );
235
    catch_exception( &ex );
236
    return result;
237
238
239
240
}

- (void)setVideoCropGeometry:(char *)value
{
241
    libvlc_video_set_crop_geometry( instance, value, NULL );
242
243
244
245
}

- (char *)videoCropGeometry
{
246
    libvlc_exception_t ex;
247
    libvlc_exception_init( &ex );
248
    char * result = libvlc_video_get_crop_geometry( instance, &ex );
249
    catch_exception( &ex );
250
    return result;
251
252
253
254
}

- (void)setVideoTeleText:(int)value
{
255
    libvlc_video_set_teletext( instance, value, NULL );
256
257
258
259
}

- (int)videoTeleText
{
260
    libvlc_exception_t ex;
261
262
    libvlc_exception_init( &ex );
    int result = libvlc_video_get_teletext( instance, &ex );
263
    catch_exception( &ex );
264
    return result;
265
266
267
268
}

- (void)setRate:(int)value
{
269
    libvlc_media_instance_set_rate( instance, value, NULL );
270
271
272
273
}

- (int)rate
{
274
    libvlc_exception_t ex;
275
276
    libvlc_exception_init( &ex );
    float result = libvlc_media_instance_get_rate( instance, &ex );
277
    catch_exception( &ex );
278
    return result;
279
280
281
282
}

- (NSSize)videoSize
{
283
    libvlc_exception_t ex;
284
    libvlc_exception_init( &ex );
285
286
    NSSize result = NSMakeSize(libvlc_video_get_height((libvlc_media_instance_t *)instance, &ex),
                               libvlc_video_get_width((libvlc_media_instance_t *)instance, &ex));
287
    catch_exception( &ex );
288
    return result;    
289
290
291
292
}

- (BOOL)hasVideoOut
{
293
    libvlc_exception_t ex;
294
    libvlc_exception_init( &ex );
295
    BOOL result = libvlc_media_instance_has_vout((libvlc_media_instance_t *)instance, &ex);
296
    if (libvlc_exception_raised( &ex ))
297
    {
298
        libvlc_exception_clear( &ex );
299
300
301
302
        return NO;
    }
    else
        return result;
303
304
305
306
}

- (float)framesPerSecond
{
307
    libvlc_exception_t ex;
308
309
    libvlc_exception_init( &ex );
    float result = libvlc_media_instance_get_fps( (libvlc_media_instance_t *)instance, &ex );
310
    catch_exception( &ex );
311
    return result;
312
313
314
315
}

- (void)setTime:(VLCTime *)value
{
316
    libvlc_exception_t ex;
317
    libvlc_exception_init( &ex );
318
319
    // Time is managed in seconds, while duration is managed in microseconds
    // TODO: Redo VLCTime to provide value numberAsMilliseconds, numberAsMicroseconds, numberAsSeconds, numberAsMinutes, numberAsHours
320
321
322
    libvlc_media_instance_set_time( (libvlc_media_instance_t *)instance, 
                                    (value ? [[value numberValue] longLongValue] / 1000 : 0),
                                    &ex );
323
    catch_exception( &ex );
324
325
326
327
}

- (VLCTime *)time
{
328
    return cachedTime;
329
330
}

331
332
333
334
335
336
337
338
339
340
- (void)setChapter:(int)value;
{
    libvlc_media_instance_set_chapter( instance, value, NULL );
}

- (int)chapter
{
    libvlc_exception_t ex;
    libvlc_exception_init( &ex );
    int result = libvlc_media_instance_get_chapter( instance, &ex );
341
    catch_exception( &ex );
342
343
344
345
346
347
348
349
    return result;
}

- (int)countOfChapters
{
    libvlc_exception_t ex;
    libvlc_exception_init( &ex );
    int result = libvlc_media_instance_get_chapter_count( instance, &ex );
350
    catch_exception( &ex );
351
352
353
    return result;
}

354
355
- (void)setAudioTrack:(int)value
{
356
    libvlc_audio_set_track( instance, value, NULL );
357
358
359
360
}

- (int)audioTrack
{
361
    libvlc_exception_t ex;
362
363
    libvlc_exception_init( &ex );
    int result = libvlc_audio_get_track( instance, &ex );
364
    catch_exception( &ex );
365
    return result;
366
367
}

368
369
370
371
372
- (int)countOfAudioTracks
{
    libvlc_exception_t ex;
    libvlc_exception_init( &ex );
    int result = libvlc_audio_get_track_count( instance, &ex );
373
    catch_exception( &ex );
374
375
376
    return result;
}

377
378
- (void)setAudioChannel:(int)value
{
379
    libvlc_audio_set_channel( instance, value, NULL );
380
381
382
383
}

- (int)audioChannel
{
384
    libvlc_exception_t ex;
385
386
    libvlc_exception_init( &ex );
    int result = libvlc_audio_get_channel( instance, &ex );
387
    catch_exception( &ex );
388
    return result;
389
390
391
392
}

- (void)setMedia:(VLCMedia *)value
{
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
    // We only know how to play media files...not media resources with subitems
    if (media != value && [media subitems] == nil)
    {
        if (media && [media compare:value] == NSOrderedSame)
            return;
        
        BOOL wasPlaying;
        if (wasPlaying = [self isPlaying])
        {
            [self pause];
//            // TODO: Should we wait until it stops playing?
//            while ([self isPlaying])
//                usleep(1000);
        }
        
        [media release];
        media = [value retain];

        libvlc_exception_t ex;
412
413
        libvlc_exception_init( &ex );
        libvlc_media_instance_set_media_descriptor( instance, [media libVLCMediaDescriptor], &ex );
414
        catch_exception( &ex );
415
    }
416
417
418
419
}

- (VLCMedia *)media
{
420
    return media;
421
422
423
}

- (BOOL)play
424
{    
425
    libvlc_exception_t ex;
426
427
    libvlc_exception_init( &ex );
    libvlc_media_instance_play( (libvlc_media_instance_t *)instance, &ex );
428
    catch_exception( &ex );
429
    return YES;
430
431
432
433
}

- (void)pause
{
434
435
436
437
    // Return if there is no media available or if the stream is not paused or 
    // playing something else
    if (!media || (![self isPlaying] && [self state] != VLCMediaPlayerStatePaused))
        return;
438

439
440
441
    // Should never get here.
    if (!instance)
        return;
442

443
444
    // Pause the stream
    libvlc_exception_t ex;
445
446
    libvlc_exception_init( &ex );
    libvlc_media_instance_pause( (libvlc_media_instance_t *)instance, &ex );
447
    catch_exception( &ex );
448
449
450
    
    // TODO: Should we record the time in case the media instance is destroyed
    // then rebuilt?
451
452
453
454
}

- (void)stop
{
455
456
457
458
459
460
461
462
    // Return if there is no media available or if the system is not in play status 
    // or pause status.
    if (!media || (![self isPlaying] && [self state] != VLCMediaPlayerStatePaused))
        return;
    
    // The following is not implemented in the core, should I fix it or just
    // compensate?
    //    libvlc_exception_t ex;
463
    //    libvlc_exception_init( &ex );
464
    //    libvlc_media_instance_stop((libvlc_media_instance_t *)instance, &ex);
465
    //    catch_exception( &ex );
466
467
468
469
470
    
    // Pause and reposition to the begining of the stream.
    [self pause];
    [self setTime:0];
    // TODO: Should we pause this or destroy the media instance so that it appears as being "stopped"?
471
472
473
474
475
476
477
478
479
}

//- (void)fastForward;
//- (void)fastForwardAtRate:(int)rate;
//- (void)rewind;
//- (void)rewindAtRate:(int)rate;

- (BOOL)isPlaying
{
480
    VLCMediaPlayerState state = [self state];
481
    return ((state == VLCMediaPlayerStateOpening) || (state == VLCMediaPlayerStateBuffering) ||
482
            (state == VLCMediaPlayerStatePlaying));
483
484
485
486
}

- (BOOL)willPlay
{
487
    libvlc_exception_t ex;
488
489
    libvlc_exception_init( &ex );
    BOOL ret = libvlc_media_instance_will_play( (libvlc_media_instance_t *)instance, &ex );
490
491
492
493
494
495
496
    if (libvlc_exception_raised(&ex))
    {
        libvlc_exception_clear(&ex);
        return NO;
    }
    else
        return ret;
497
498
}

499
static const VLCMediaPlayerState libvlc_to_local_state[] =
500
{
501
502
    [libvlc_Stopped]    = VLCMediaPlayerStateStopped,
    [libvlc_Opening]    = VLCMediaPlayerStateOpening,
503
    [libvlc_Buffering]  = VLCMediaPlayerStateBuffering,
504
    [libvlc_Playing]    = VLCMediaPlayerStatePlaying,
505
506
507
    [libvlc_Paused]     = VLCMediaPlayerStatePaused,
    [libvlc_Ended]      = VLCMediaPlayerStateEnded,
    [libvlc_Error]      = VLCMediaPlayerStateError
508
509
510
511
};

- (VLCMediaPlayerState)state
{
512
513
    return cachedState;
}
514

515
516
517
518
- (float)position
{
    return position;
}
519

520
521
- (void)setPosition:(float)newPosition
{
522
    libvlc_exception_t ex;
523
    libvlc_exception_init( &ex );
524
    libvlc_media_instance_set_position( instance, newPosition, &ex );
525
    catch_exception( &ex );
526
}
527
528
529
530
531
532

- (BOOL)isSeekable
{
    libvlc_exception_t ex;
    libvlc_exception_init( &ex );
    BOOL ret = libvlc_media_instance_is_seekable( instance, &ex );
533
    catch_exception( &ex );
534
535
536
    return ret;
}

537
538
@end

539
@implementation VLCMediaPlayer (Private)
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
- (id)initWithDrawable:(id)aDrawable
{
    if (self = [super init])
    {
        delegate = nil;
        media = nil;
        cachedTime = [[VLCTime nullTime] retain];
        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
        libvlc_exception_t ex;
        libvlc_exception_init( &ex );
        instance = (void *)libvlc_media_instance_new([VLCLibrary sharedInstance], &ex);
        catch_exception( &ex );
        
        [self registerObservers];
        
        [self setDrawable:aDrawable];
    }
    return self;
}

- (void)setDrawable:(id)aDrawable
{
    // Make sure that this instance has been associated with the drawing canvas.
    libvlc_exception_t ex;
    libvlc_exception_init( &ex );
    libvlc_media_instance_set_drawable ((libvlc_media_instance_t *)instance, 
                                        (libvlc_drawable_t)aDrawable, 
                                        &ex);
    catch_exception( &ex );
}

576
577
- (void)registerObservers
{
578
    libvlc_exception_t ex;
579
    libvlc_exception_init( &ex );
580

581
    // Attach event observers into the media instance
582
    libvlc_event_manager_t * p_em = libvlc_media_instance_event_manager( instance, &ex );
583
584
585
    libvlc_event_attach( p_em, libvlc_MediaInstancePlayed,          HandleMediaInstanceStateChanged, self, &ex );
    libvlc_event_attach( p_em, libvlc_MediaInstancePaused,          HandleMediaInstanceStateChanged, self, &ex );
    libvlc_event_attach( p_em, libvlc_MediaInstanceReachedEnd,      HandleMediaInstanceStateChanged, self, &ex );
586
    /* FIXME: We may want to turn that off when none is interested by that */
587
588
    libvlc_event_attach( p_em, libvlc_MediaInstancePositionChanged, HandleMediaPositionChanged,      self, &ex );
    libvlc_event_attach( p_em, libvlc_MediaInstanceTimeChanged,     HandleMediaTimeChanged,          self, &ex );
589
    catch_exception( &ex );
590
591
592
593
}

- (void)unregisterObservers
{
594
    libvlc_event_manager_t * p_em = libvlc_media_instance_event_manager( instance, NULL );
595
596
597
598
    libvlc_event_detach( p_em, libvlc_MediaInstancePlayed,          HandleMediaInstanceStateChanged, self, NULL );
    libvlc_event_detach( p_em, libvlc_MediaInstancePaused,          HandleMediaInstanceStateChanged, self, NULL );
    libvlc_event_detach( p_em, libvlc_MediaInstanceReachedEnd,      HandleMediaInstanceStateChanged, self, NULL );
    libvlc_event_detach( p_em, libvlc_MediaInstancePositionChanged, HandleMediaTimeChanged,            self, NULL );
599
    libvlc_event_detach( p_em, libvlc_MediaInstanceTimeChanged,     HandleMediaTimeChanged,            self, NULL );
600
}
601

602
603
604
605
606
607
608
- (void)mediaPlayerTimeChanged:(NSNumber *)newTime
{
    [self willChangeValueForKey:@"time"];
    [cachedTime release];
    cachedTime = [[VLCTime timeWithNumber:newTime] retain];
    [self didChangeValueForKey:@"time"];
}
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624

- (void)mediaPlayerPositionChanged:(NSNumber *)newPosition
{
    if( [newPosition floatValue] - position < 0.005 && position - [newPosition floatValue] < 0.005 )
        return; /* Forget that, this is too much precision for our uses */
    [self willChangeValueForKey:@"position"];
    position = ((float)((int)([newPosition floatValue]*1000)))/1000.;
    [self didChangeValueForKey:@"position"];
}

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