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)saveVideoSnapshotAt: (NSString *)path withWidth:(NSUInteger)width andHeight:(NSUInteger)height
309 libvlc_exception_t ex;
310 libvlc_exception_init( &ex );
311 libvlc_video_take_snapshot( instance, [path UTF8String], width, height, &ex );
312 catch_exception( &ex );
315 - (void)setDeinterlaceFilter: (NSString *)name enabled: (BOOL)enabled
317 libvlc_exception_t ex;
318 libvlc_exception_init( &ex );
319 libvlc_video_set_deinterlace( instance, (int)enabled , [name UTF8String], &ex );
320 catch_exception( &ex );
323 - (void)setRate:(float)value
325 libvlc_media_player_set_rate( instance, value, NULL );
330 libvlc_exception_t ex;
331 libvlc_exception_init( &ex );
332 float result = libvlc_media_player_get_rate( instance, &ex );
333 catch_exception( &ex );
339 libvlc_exception_t ex;
340 libvlc_exception_init( &ex );
341 NSSize result = NSMakeSize(libvlc_video_get_height((libvlc_media_player_t *)instance, &ex),
342 libvlc_video_get_width((libvlc_media_player_t *)instance, &ex));
343 catch_exception( &ex );
349 libvlc_exception_t ex;
350 libvlc_exception_init( &ex );
351 BOOL result = libvlc_media_player_has_vout((libvlc_media_player_t *)instance, &ex);
352 if (libvlc_exception_raised( &ex ))
354 libvlc_exception_clear( &ex );
361 - (float)framesPerSecond
363 libvlc_exception_t ex;
364 libvlc_exception_init( &ex );
365 float result = libvlc_media_player_get_fps( (libvlc_media_player_t *)instance, &ex );
366 catch_exception( &ex );
370 - (void)setTime:(VLCTime *)value
372 libvlc_exception_t ex;
373 libvlc_exception_init( &ex );
374 // Time is managed in seconds, while duration is managed in microseconds
375 // TODO: Redo VLCTime to provide value numberAsMilliseconds, numberAsMicroseconds, numberAsSeconds, numberAsMinutes, numberAsHours
376 libvlc_media_player_set_time( (libvlc_media_player_t *)instance,
377 (value ? [[value numberValue] longLongValue] / 1000 : 0),
379 catch_exception( &ex );
387 - (void)setChapter:(int)value;
389 libvlc_media_player_set_chapter( instance, value, NULL );
394 libvlc_exception_t ex;
395 libvlc_exception_init( &ex );
396 int result = libvlc_media_player_get_chapter( instance, &ex );
397 catch_exception( &ex );
401 - (int)countOfChapters
403 libvlc_exception_t ex;
404 libvlc_exception_init( &ex );
405 int result = libvlc_media_player_get_chapter_count( instance, &ex );
406 catch_exception( &ex );
410 - (void)setAudioTrack:(int)value
412 libvlc_audio_set_track( instance, value, NULL );
417 libvlc_exception_t ex;
418 libvlc_exception_init( &ex );
419 int result = libvlc_audio_get_track( instance, &ex );
420 catch_exception( &ex );
424 - (int)countOfAudioTracks
426 libvlc_exception_t ex;
427 libvlc_exception_init( &ex );
428 int result = libvlc_audio_get_track_count( instance, &ex );
429 catch_exception( &ex );
433 - (void)setAudioChannel:(int)value
435 libvlc_audio_set_channel( instance, value, NULL );
440 libvlc_exception_t ex;
441 libvlc_exception_init( &ex );
442 int result = libvlc_audio_get_channel( instance, &ex );
443 catch_exception( &ex );
447 - (void)setMedia:(VLCMedia *)value
451 if (media && [media compare:value] == NSOrderedSame)
455 media = [value retain];
457 libvlc_exception_t ex;
458 libvlc_exception_init( &ex );
459 libvlc_media_player_set_media( instance, [media libVLCMediaDescriptor], &ex );
460 catch_exception( &ex );
471 libvlc_exception_t ex;
472 libvlc_exception_init( &ex );
473 libvlc_media_player_play( (libvlc_media_player_t *)instance, &ex );
474 catch_exception( &ex );
480 if( [NSThread isMainThread] )
482 /* Hack because we create a dead lock here, when the vout is stopped
483 * and tries to recontact us on the main thread */
484 /* FIXME: to do this properly we need to do some locking. We may want
485 * to move that to libvlc */
486 [self performSelectorInBackground:@selector(pause) withObject:nil];
491 libvlc_exception_t ex;
492 libvlc_exception_init( &ex );
493 libvlc_media_player_pause( (libvlc_media_player_t *)instance, &ex );
494 catch_exception( &ex );
499 if( 0 && [NSThread isMainThread] )
501 /* Hack because we create a dead lock here, when the vout is stopped
502 * and tries to recontact us on the main thread */
503 /* FIXME: to do this properly we need to do some locking. We may want
504 * to move that to libvlc */
505 [self performSelectorInBackground:@selector(stop) withObject:nil];
509 libvlc_exception_t ex;
510 libvlc_exception_init( &ex );
511 libvlc_media_player_stop((libvlc_media_player_t *)instance, &ex);
512 catch_exception( &ex );
517 [self fastForwardAtRate: 2.0];
520 - (void)fastForwardAtRate:(float)rate
527 [self rewindAtRate: 2.0];
530 - (void)rewindAtRate:(float)rate
532 [self setRate: -rate];
535 - (void)jumpBackward:(NSInteger)interval
537 if( [self isSeekable] )
539 interval = interval * 1000000;
540 [self setTime: [VLCTime timeWithInt: ([[self time] intValue] - interval)]];
544 - (void)jumpForward:(NSInteger)interval
546 if( [self isSeekable] )
548 interval = interval * 1000000;
549 [self setTime: [VLCTime timeWithInt: ([[self time] intValue] + interval)]];
553 - (void)extraShortJumpBackward
555 [self jumpBackward:3];
558 - (void)extraShortJumpForward
560 [self jumpForward:3];
563 - (void)shortJumpBackward
565 [self jumpBackward:10];
568 - (void)shortJumpForward
570 [self jumpForward:10];
573 - (void)mediumJumpBackward
575 [self jumpBackward:60];
578 - (void)mediumJumpForward
580 [self jumpForward:60];
583 - (void)longJumpBackward
585 [self jumpBackward:300];
588 - (void)longJumpForward
590 [self jumpForward:300];
593 + (NSSet *)keyPathsForValuesAffectingIsPlaying
595 return [NSSet setWithObjects:@"state", nil];
600 VLCMediaPlayerState state = [self state];
601 return ((state == VLCMediaPlayerStateOpening) || (state == VLCMediaPlayerStateBuffering) ||
602 (state == VLCMediaPlayerStatePlaying));
607 libvlc_exception_t ex;
608 libvlc_exception_init( &ex );
609 BOOL ret = libvlc_media_player_will_play( (libvlc_media_player_t *)instance, &ex );
610 if (libvlc_exception_raised(&ex))
612 libvlc_exception_clear(&ex);
619 static const VLCMediaPlayerState libvlc_to_local_state[] =
621 [libvlc_Stopped] = VLCMediaPlayerStateStopped,
622 [libvlc_Opening] = VLCMediaPlayerStateOpening,
623 [libvlc_Buffering] = VLCMediaPlayerStateBuffering,
624 [libvlc_Playing] = VLCMediaPlayerStatePlaying,
625 [libvlc_Paused] = VLCMediaPlayerStatePaused,
626 [libvlc_Ended] = VLCMediaPlayerStateEnded,
627 [libvlc_Error] = VLCMediaPlayerStateError
630 - (VLCMediaPlayerState)state
640 - (void)setPosition:(float)newPosition
642 libvlc_exception_t ex;
643 libvlc_exception_init( &ex );
644 libvlc_media_player_set_position( instance, newPosition, &ex );
645 catch_exception( &ex );
650 libvlc_exception_t ex;
651 libvlc_exception_init( &ex );
652 BOOL ret = libvlc_media_player_is_seekable( instance, &ex );
653 catch_exception( &ex );
659 libvlc_exception_t ex;
660 libvlc_exception_init( &ex );
661 BOOL ret = libvlc_media_player_can_pause( instance, &ex );
662 catch_exception( &ex );
666 - (void *)libVLCMediaPlayer
672 @implementation VLCMediaPlayer (Private)
673 - (id)initWithDrawable:(id)aDrawable
675 if (self = [super init])
679 cachedTime = [[VLCTime nullTime] retain];
681 cachedState = VLCMediaPlayerStateStopped;
683 // Create a media instance, it doesn't matter what library we start off with
684 // it will change depending on the media descriptor provided to the media
686 libvlc_exception_t ex;
687 libvlc_exception_init( &ex );
688 instance = (void *)libvlc_media_player_new([VLCLibrary sharedInstance], &ex);
689 catch_exception( &ex );
691 [self registerObservers];
693 [self setDrawable:aDrawable];
698 - (void)registerObservers
700 libvlc_exception_t ex;
701 libvlc_exception_init( &ex );
703 // Attach event observers into the media instance
704 libvlc_event_manager_t * p_em = libvlc_media_player_event_manager( instance, &ex );
705 libvlc_event_attach( p_em, libvlc_MediaPlayerPlaying, HandleMediaInstanceStateChanged, self, &ex );
706 libvlc_event_attach( p_em, libvlc_MediaPlayerPaused, HandleMediaInstanceStateChanged, self, &ex );
707 libvlc_event_attach( p_em, libvlc_MediaPlayerEncounteredError, HandleMediaInstanceStateChanged, self, &ex );
708 libvlc_event_attach( p_em, libvlc_MediaPlayerEndReached, HandleMediaInstanceStateChanged, self, &ex );
709 /* FIXME: We may want to turn that off when none is interested by that */
710 libvlc_event_attach( p_em, libvlc_MediaPlayerPositionChanged, HandleMediaPositionChanged, self, &ex );
711 libvlc_event_attach( p_em, libvlc_MediaPlayerTimeChanged, HandleMediaTimeChanged, self, &ex );
712 catch_exception( &ex );
715 - (void)unregisterObservers
717 libvlc_event_manager_t * p_em = libvlc_media_player_event_manager( instance, NULL );
718 libvlc_event_detach( p_em, libvlc_MediaPlayerPlaying, HandleMediaInstanceStateChanged, self, NULL );
719 libvlc_event_detach( p_em, libvlc_MediaPlayerPaused, HandleMediaInstanceStateChanged, self, NULL );
720 libvlc_event_detach( p_em, libvlc_MediaPlayerEncounteredError, HandleMediaInstanceStateChanged, self, NULL );
721 libvlc_event_detach( p_em, libvlc_MediaPlayerEndReached, HandleMediaInstanceStateChanged, self, NULL );
722 libvlc_event_detach( p_em, libvlc_MediaPlayerPositionChanged, HandleMediaPositionChanged, self, NULL );
723 libvlc_event_detach( p_em, libvlc_MediaPlayerTimeChanged, HandleMediaTimeChanged, self, NULL );
726 - (void)mediaPlayerTimeChanged:(NSNumber *)newTime
728 [self willChangeValueForKey:@"time"];
729 [cachedTime release];
730 cachedTime = [[VLCTime timeWithNumber:newTime] retain];
732 [self didChangeValueForKey:@"time"];
737 UpdateSystemActivity(UsrActivity);
740 - (void)mediaPlayerPositionChanged:(NSNumber *)newPosition
742 // This seems to be the most relevant place to delay sleeping and screen saver.
745 [self willChangeValueForKey:@"position"];
746 position = [newPosition floatValue];
747 [self didChangeValueForKey:@"position"];
750 - (void)mediaPlayerStateChanged:(NSNumber *)newState
752 [self willChangeValueForKey:@"state"];
753 cachedState = [newState intValue];
754 [self didChangeValueForKey:@"state"];