1 /*****************************************************************************
2 * VLCMediaPlayer.m: VLCKit.framework VLCMediaPlayer implementation
3 *****************************************************************************
4 * Copyright (C) 2007 Pierre d'Herbemont
5 * Copyright (C) 2007 the VideoLAN team
8 * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
9 * Faustion Osuna <enrique.osuna # gmail.com>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 #import "VLCLibrary.h"
27 #import "VLCMediaPlayer.h"
28 #import "VLCEventManager.h"
29 #import "VLCLibVLCBridging.h"
30 #import "VLCVideoView.h"
35 /* prevent system sleep */
36 #import <CoreServices/CoreServices.h>
37 /* FIXME: Ugly hack! */
39 #import <CoreServices/../Frameworks/OSServices.framework/Headers/Power.h>
44 /* Notification Messages */
45 NSString * VLCMediaPlayerTimeChanged = @"VLCMediaPlayerTimeChanged";
46 NSString * VLCMediaPlayerStateChanged = @"VLCMediaPlayerStateChanged";
48 NSString * VLCMediaPlayerStateToString(VLCMediaPlayerState state)
50 static NSString * stateToStrings[] = {
51 [VLCMediaPlayerStateStopped] = @"VLCMediaPlayerStateStopped",
52 [VLCMediaPlayerStateOpening] = @"VLCMediaPlayerStateOpening",
53 [VLCMediaPlayerStateBuffering] = @"VLCMediaPlayerStateBuffering",
54 [VLCMediaPlayerStateEnded] = @"VLCMediaPlayerStateEnded",
55 [VLCMediaPlayerStateError] = @"VLCMediaPlayerStateError",
56 [VLCMediaPlayerStatePlaying] = @"VLCMediaPlayerStatePlaying",
57 [VLCMediaPlayerStatePaused] = @"VLCMediaPlayerStatePaused"
59 return stateToStrings[state];
62 /* libvlc event callback */
63 static void HandleMediaInstanceVolumeChanged(const libvlc_event_t * event, void * self)
65 [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
66 withDelegateMethod:@selector(mediaPlayerVolumeChanged:)
67 withNotificationName:VLCMediaPlayerVolumeChanged];
70 static void HandleMediaTimeChanged(const libvlc_event_t * event, void * self)
72 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
73 [[VLCEventManager sharedManager] callOnMainThreadObject:self
74 withMethod:@selector(mediaPlayerTimeChanged:)
75 withArgumentAsObject:[NSNumber numberWithLongLong:event->u.media_player_time_changed.new_time]];
77 [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
78 withDelegateMethod:@selector(mediaPlayerTimeChanged:)
79 withNotificationName:VLCMediaPlayerTimeChanged];
83 static void HandleMediaPositionChanged(const libvlc_event_t * event, void * self)
85 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
87 [[VLCEventManager sharedManager] callOnMainThreadObject:self
88 withMethod:@selector(mediaPlayerPositionChanged:)
89 withArgumentAsObject:[NSNumber numberWithFloat:event->u.media_player_position_changed.new_position]];
93 static void HandleMediaInstanceStateChanged(const libvlc_event_t * event, void * self)
95 VLCMediaPlayerState newState;
97 if( event->type == libvlc_MediaPlayerPlaying )
98 newState = VLCMediaPlayerStatePlaying;
99 else if( event->type == libvlc_MediaPlayerPaused )
100 newState = VLCMediaPlayerStatePaused;
101 else if( event->type == libvlc_MediaPlayerEndReached )
102 newState = VLCMediaPlayerStateStopped;
103 else if( event->type == libvlc_MediaPlayerEncounteredError )
104 newState = VLCMediaPlayerStateError;
107 NSLog(@"%s: Unknown event", __FUNCTION__);
111 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
113 [[VLCEventManager sharedManager] callOnMainThreadObject:self
114 withMethod:@selector(mediaPlayerStateChanged:)
115 withArgumentAsObject:[NSNumber numberWithInt:newState]];
117 [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
118 withDelegateMethod:@selector(mediaPlayerStateChanged:)
119 withNotificationName:VLCMediaPlayerStateChanged];
126 // TODO: Documentation
127 @interface VLCMediaPlayer (Private)
128 - (id)initWithDrawable:(id)aDrawable;
130 - (void)registerObservers;
131 - (void)unregisterObservers;
132 - (void)mediaPlayerTimeChanged:(NSNumber *)newTime;
133 - (void)mediaPlayerPositionChanged:(NSNumber *)newTime;
134 - (void)mediaPlayerStateChanged:(NSNumber *)newState;
137 @implementation VLCMediaPlayer
140 + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
142 static NSDictionary * dict = nil;
143 NSSet * superKeyPaths;
146 dict = [[NSDictionary dictionaryWithObjectsAndKeys:
147 [NSSet setWithObject:@"state"], @"playing",
148 [NSSet setWithObjects:@"state", @"media", nil], @"seekable",
149 [NSSet setWithObjects:@"state", @"media", nil], @"canPause",
150 [NSSet setWithObjects:@"state", @"media", nil], @"description",
153 if( (superKeyPaths = [super keyPathsForValuesAffectingValueForKey: key]) )
155 NSMutableSet * ret = [NSMutableSet setWithSet:[dict objectForKey: key]];
156 [ret unionSet:superKeyPaths];
159 return [dict objectForKey: key];
165 return [self initWithDrawable:nil];
168 - (id)initWithVideoView:(VLCVideoView *)aVideoView
170 return [self initWithDrawable: aVideoView];
173 - (id)initWithVideoLayer:(VLCVideoLayer *)aVideoLayer
175 return [self initWithDrawable: aVideoLayer];
182 if([self retainCount] <= 1)
184 /* We must make sure we won't receive new event after an upcoming dealloc
185 * We also may receive a -retain in some event callback that may occcur
186 * Before libvlc_event_detach. So this can't happen in dealloc */
187 [self unregisterObservers];
195 // Always get rid of the delegate first so we can stop sending messages to it
196 // TODO: Should we tell the delegate that we're shutting down?
199 libvlc_media_player_release((libvlc_media_player_t *)instance);
201 // Get rid of everything else
203 [cachedTime release];
208 - (void)setDelegate:(id)value
218 - (void)setVideoView:(VLCVideoView *)aVideoView
220 [self setDrawable: aVideoView];
223 - (void)setVideoLayer:(VLCVideoLayer *)aVideoLayer
225 [self setDrawable: aVideoLayer];
228 - (void)setDrawable:(id)aDrawable
230 // Make sure that this instance has been associated with the drawing canvas.
231 libvlc_exception_t ex;
232 libvlc_exception_init( &ex );
233 libvlc_media_player_set_nsobject(instance, aDrawable, &ex);
234 catch_exception( &ex );
239 libvlc_exception_t ex;
240 libvlc_exception_init( &ex );
241 id ret = libvlc_media_player_get_nsobject(instance);
242 catch_exception( &ex );
248 return [[VLCLibrary sharedLibrary] audio];
251 - (void)setVideoAspectRatio:(char *)value
253 libvlc_video_set_aspect_ratio( instance, value, NULL );
256 - (char *)videoAspectRatio
258 libvlc_exception_t ex;
259 libvlc_exception_init( &ex );
260 char * result = libvlc_video_get_aspect_ratio( instance, &ex );
261 catch_exception( &ex );
265 - (void)setVideoSubTitles:(int)value
267 libvlc_video_set_spu( instance, value, NULL );
270 - (int)videoSubTitles
272 libvlc_exception_t ex;
273 libvlc_exception_init( &ex );
274 int result = libvlc_video_get_spu( instance, &ex );
275 catch_exception( &ex );
279 - (void)setVideoCropGeometry:(char *)value
281 libvlc_video_set_crop_geometry( instance, value, NULL );
284 - (char *)videoCropGeometry
286 libvlc_exception_t ex;
287 libvlc_exception_init( &ex );
288 char * result = libvlc_video_get_crop_geometry( instance, &ex );
289 catch_exception( &ex );
293 - (void)setVideoTeleText:(int)value
295 libvlc_video_set_teletext( instance, value, NULL );
300 libvlc_exception_t ex;
301 libvlc_exception_init( &ex );
302 int result = libvlc_video_get_teletext( instance, &ex );
303 catch_exception( &ex );
307 - (void)setRate:(float)value
309 libvlc_media_player_set_rate( instance, value, NULL );
314 libvlc_exception_t ex;
315 libvlc_exception_init( &ex );
316 float result = libvlc_media_player_get_rate( instance, &ex );
317 catch_exception( &ex );
323 libvlc_exception_t ex;
324 libvlc_exception_init( &ex );
325 NSSize result = NSMakeSize(libvlc_video_get_height((libvlc_media_player_t *)instance, &ex),
326 libvlc_video_get_width((libvlc_media_player_t *)instance, &ex));
327 catch_exception( &ex );
333 libvlc_exception_t ex;
334 libvlc_exception_init( &ex );
335 BOOL result = libvlc_media_player_has_vout((libvlc_media_player_t *)instance, &ex);
336 if (libvlc_exception_raised( &ex ))
338 libvlc_exception_clear( &ex );
345 - (float)framesPerSecond
347 libvlc_exception_t ex;
348 libvlc_exception_init( &ex );
349 float result = libvlc_media_player_get_fps( (libvlc_media_player_t *)instance, &ex );
350 catch_exception( &ex );
354 - (void)setTime:(VLCTime *)value
356 libvlc_exception_t ex;
357 libvlc_exception_init( &ex );
358 // Time is managed in seconds, while duration is managed in microseconds
359 // TODO: Redo VLCTime to provide value numberAsMilliseconds, numberAsMicroseconds, numberAsSeconds, numberAsMinutes, numberAsHours
360 libvlc_media_player_set_time( (libvlc_media_player_t *)instance,
361 (value ? [[value numberValue] longLongValue] / 1000 : 0),
363 catch_exception( &ex );
371 - (void)setChapter:(int)value;
373 libvlc_media_player_set_chapter( instance, value, NULL );
378 libvlc_exception_t ex;
379 libvlc_exception_init( &ex );
380 int result = libvlc_media_player_get_chapter( instance, &ex );
381 catch_exception( &ex );
385 - (int)countOfChapters
387 libvlc_exception_t ex;
388 libvlc_exception_init( &ex );
389 int result = libvlc_media_player_get_chapter_count( instance, &ex );
390 catch_exception( &ex );
394 - (void)setAudioTrack:(int)value
396 libvlc_audio_set_track( instance, value, NULL );
401 libvlc_exception_t ex;
402 libvlc_exception_init( &ex );
403 int result = libvlc_audio_get_track( instance, &ex );
404 catch_exception( &ex );
408 - (int)countOfAudioTracks
410 libvlc_exception_t ex;
411 libvlc_exception_init( &ex );
412 int result = libvlc_audio_get_track_count( instance, &ex );
413 catch_exception( &ex );
417 - (void)setAudioChannel:(int)value
419 libvlc_audio_set_channel( instance, value, NULL );
424 libvlc_exception_t ex;
425 libvlc_exception_init( &ex );
426 int result = libvlc_audio_get_channel( instance, &ex );
427 catch_exception( &ex );
431 - (void)setMedia:(VLCMedia *)value
435 if (media && [media compare:value] == NSOrderedSame)
439 media = [value retain];
441 libvlc_exception_t ex;
442 libvlc_exception_init( &ex );
443 libvlc_media_player_set_media( instance, [media libVLCMediaDescriptor], &ex );
444 catch_exception( &ex );
455 libvlc_exception_t ex;
456 libvlc_exception_init( &ex );
457 libvlc_media_player_play( (libvlc_media_player_t *)instance, &ex );
458 catch_exception( &ex );
464 if( [NSThread isMainThread] )
466 /* Hack because we create a dead lock here, when the vout is stopped
467 * and tries to recontact us on the main thread */
468 /* FIXME: to do this properly we need to do some locking. We may want
469 * to move that to libvlc */
470 [self performSelectorInBackground:@selector(pause) withObject:nil];
475 libvlc_exception_t ex;
476 libvlc_exception_init( &ex );
477 libvlc_media_player_pause( (libvlc_media_player_t *)instance, &ex );
478 catch_exception( &ex );
483 if( 0 && [NSThread isMainThread] )
485 /* Hack because we create a dead lock here, when the vout is stopped
486 * and tries to recontact us on the main thread */
487 /* FIXME: to do this properly we need to do some locking. We may want
488 * to move that to libvlc */
489 [self performSelectorInBackground:@selector(stop) withObject:nil];
493 libvlc_exception_t ex;
494 libvlc_exception_init( &ex );
495 libvlc_media_player_stop((libvlc_media_player_t *)instance, &ex);
496 catch_exception( &ex );
501 [self fastForwardAtRate: 2.0];
504 - (void)fastForwardAtRate:(float)rate
511 [self rewindAtRate: 2.0];
514 - (void)rewindAtRate:(float)rate
516 [self setRate: -rate];
519 + (NSSet *)keyPathsForValuesAffectingIsPlaying
521 return [NSSet setWithObjects:@"state", nil];
526 VLCMediaPlayerState state = [self state];
527 return ((state == VLCMediaPlayerStateOpening) || (state == VLCMediaPlayerStateBuffering) ||
528 (state == VLCMediaPlayerStatePlaying));
533 libvlc_exception_t ex;
534 libvlc_exception_init( &ex );
535 BOOL ret = libvlc_media_player_will_play( (libvlc_media_player_t *)instance, &ex );
536 if (libvlc_exception_raised(&ex))
538 libvlc_exception_clear(&ex);
545 static const VLCMediaPlayerState libvlc_to_local_state[] =
547 [libvlc_Stopped] = VLCMediaPlayerStateStopped,
548 [libvlc_Opening] = VLCMediaPlayerStateOpening,
549 [libvlc_Buffering] = VLCMediaPlayerStateBuffering,
550 [libvlc_Playing] = VLCMediaPlayerStatePlaying,
551 [libvlc_Paused] = VLCMediaPlayerStatePaused,
552 [libvlc_Ended] = VLCMediaPlayerStateEnded,
553 [libvlc_Error] = VLCMediaPlayerStateError
556 - (VLCMediaPlayerState)state
566 - (void)setPosition:(float)newPosition
568 libvlc_exception_t ex;
569 libvlc_exception_init( &ex );
570 libvlc_media_player_set_position( instance, newPosition, &ex );
571 catch_exception( &ex );
576 libvlc_exception_t ex;
577 libvlc_exception_init( &ex );
578 BOOL ret = libvlc_media_player_is_seekable( instance, &ex );
579 catch_exception( &ex );
585 libvlc_exception_t ex;
586 libvlc_exception_init( &ex );
587 BOOL ret = libvlc_media_player_can_pause( instance, &ex );
588 catch_exception( &ex );
592 - (void *)libVLCMediaPlayer
598 @implementation VLCMediaPlayer (Private)
599 - (id)initWithDrawable:(id)aDrawable
601 if (self = [super init])
605 cachedTime = [[VLCTime nullTime] retain];
607 cachedState = VLCMediaPlayerStateStopped;
609 // Create a media instance, it doesn't matter what library we start off with
610 // it will change depending on the media descriptor provided to the media
612 libvlc_exception_t ex;
613 libvlc_exception_init( &ex );
614 instance = (void *)libvlc_media_player_new([VLCLibrary sharedInstance], &ex);
615 catch_exception( &ex );
617 [self registerObservers];
619 [self setDrawable:aDrawable];
624 - (void)registerObservers
626 libvlc_exception_t ex;
627 libvlc_exception_init( &ex );
629 // Attach event observers into the media instance
630 libvlc_event_manager_t * p_em = libvlc_media_player_event_manager( instance, &ex );
631 libvlc_event_attach( p_em, libvlc_MediaPlayerPlaying, HandleMediaInstanceStateChanged, self, &ex );
632 libvlc_event_attach( p_em, libvlc_MediaPlayerPaused, HandleMediaInstanceStateChanged, self, &ex );
633 libvlc_event_attach( p_em, libvlc_MediaPlayerEncounteredError, HandleMediaInstanceStateChanged, self, &ex );
634 libvlc_event_attach( p_em, libvlc_MediaPlayerEndReached, HandleMediaInstanceStateChanged, self, &ex );
635 /* FIXME: We may want to turn that off when none is interested by that */
636 libvlc_event_attach( p_em, libvlc_MediaPlayerPositionChanged, HandleMediaPositionChanged, self, &ex );
637 libvlc_event_attach( p_em, libvlc_MediaPlayerTimeChanged, HandleMediaTimeChanged, self, &ex );
638 catch_exception( &ex );
641 - (void)unregisterObservers
643 libvlc_event_manager_t * p_em = libvlc_media_player_event_manager( instance, NULL );
644 libvlc_event_detach( p_em, libvlc_MediaPlayerPlaying, HandleMediaInstanceStateChanged, self, NULL );
645 libvlc_event_detach( p_em, libvlc_MediaPlayerPaused, HandleMediaInstanceStateChanged, self, NULL );
646 libvlc_event_detach( p_em, libvlc_MediaPlayerEncounteredError, HandleMediaInstanceStateChanged, self, NULL );
647 libvlc_event_detach( p_em, libvlc_MediaPlayerEndReached, HandleMediaInstanceStateChanged, self, NULL );
648 libvlc_event_detach( p_em, libvlc_MediaPlayerPositionChanged, HandleMediaPositionChanged, self, NULL );
649 libvlc_event_detach( p_em, libvlc_MediaPlayerTimeChanged, HandleMediaTimeChanged, self, NULL );
652 - (void)mediaPlayerTimeChanged:(NSNumber *)newTime
654 [self willChangeValueForKey:@"time"];
655 [cachedTime release];
656 cachedTime = [[VLCTime timeWithNumber:newTime] retain];
658 [self didChangeValueForKey:@"time"];
663 UpdateSystemActivity(UsrActivity);
666 - (void)mediaPlayerPositionChanged:(NSNumber *)newPosition
668 // This seems to be the most relevant place to delay sleeping and screen saver.
671 [self willChangeValueForKey:@"position"];
672 position = [newPosition floatValue];
673 [self didChangeValueForKey:@"position"];
676 - (void)mediaPlayerStateChanged:(NSNumber *)newState
678 [self willChangeValueForKey:@"state"];
679 cachedState = [newState intValue];
680 [self didChangeValueForKey:@"state"];