1 /*****************************************************************************
2 * VLCMediaPlayer.m: VLC.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"
33 /* Notification Messages */
34 NSString * VLCMediaPlayerTimeChanged = @"VLCMediaPlayerTimeChanged";
35 NSString * VLCMediaPlayerStateChanged = @"VLCMediaPlayerStateChanged";
37 NSString * VLCMediaPlayerStateToString(VLCMediaPlayerState state)
39 static NSString * stateToStrings[] = {
40 [VLCMediaPlayerStateStopped] = @"VLCMediaPlayerStateStopped",
41 [VLCMediaPlayerStateOpening] = @"VLCMediaPlayerStateOpening",
42 [VLCMediaPlayerStateBuffering] = @"VLCMediaPlayerStateBuffering",
43 [VLCMediaPlayerStateEnded] = @"VLCMediaPlayerStateEnded",
44 [VLCMediaPlayerStateError] = @"VLCMediaPlayerStateError",
45 [VLCMediaPlayerStatePlaying] = @"VLCMediaPlayerStatePlaying",
46 [VLCMediaPlayerStatePaused] = @"VLCMediaPlayerStatePaused"
48 return stateToStrings[state];
51 /* libvlc event callback */
52 static void HandleMediaInstanceVolumeChanged(const libvlc_event_t * event, void * self)
54 [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
55 withDelegateMethod:@selector(mediaPlayerVolumeChanged:)
56 withNotificationName:VLCMediaPlayerVolumeChanged];
59 static void HandleMediaTimeChanged(const libvlc_event_t * event, void * self)
61 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
62 [[VLCEventManager sharedManager] callOnMainThreadObject:self
63 withMethod:@selector(mediaPlayerTimeChanged:)
64 withArgumentAsObject:[NSNumber numberWithLongLong:event->u.media_instance_time_changed.new_time]];
66 [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
67 withDelegateMethod:@selector(mediaPlayerTimeChanged:)
68 withNotificationName:VLCMediaPlayerTimeChanged];
72 static void HandleMediaPositionChanged(const libvlc_event_t * event, void * self)
74 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
76 [[VLCEventManager sharedManager] callOnMainThreadObject:self
77 withMethod:@selector(mediaPlayerPositionChanged:)
78 withArgumentAsObject:[NSNumber numberWithFloat:event->u.media_instance_position_changed.new_position]];
82 static void HandleMediaInstanceStateChanged(const libvlc_event_t * event, void * self)
84 VLCMediaPlayerState newState;
86 if( event->type == libvlc_MediaInstancePlayed )
87 newState = VLCMediaPlayerStatePlaying;
88 else if( event->type == libvlc_MediaInstancePaused )
89 newState = VLCMediaPlayerStatePaused;
90 else if( event->type == libvlc_MediaInstanceReachedEnd )
91 newState = VLCMediaPlayerStateStopped;
94 NSLog(@"%s: Unknown event", __FUNCTION__);
98 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
100 [[VLCEventManager sharedManager] callOnMainThreadObject:self
101 withMethod:@selector(mediaPlayerStateChanged:)
102 withArgumentAsObject:[NSNumber numberWithInt:newState]];
104 [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
105 withDelegateMethod:@selector(mediaPlayerStateChanged:)
106 withNotificationName:VLCMediaPlayerStateChanged];
113 // TODO: Documentation
114 @interface VLCMediaPlayer (Private)
115 - (id)initWithDrawable:(id)aDrawable;
116 - (void)setDrawable:(id)aDrawable;
118 - (void)registerObservers;
119 - (void)unregisterObservers;
120 - (void)mediaPlayerTimeChanged:(NSNumber *)newTime;
121 - (void)mediaPlayerPositionChanged:(NSNumber *)newTime;
122 - (void)mediaPlayerStateChanged:(NSNumber *)newState;
125 @implementation VLCMediaPlayer
128 + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
130 static NSDictionary * dict = nil;
133 dict = [[NSDictionary dictionaryWithObjectsAndKeys:
134 [NSSet setWithObject:@"state"], @"playing",
135 [NSSet setWithObjects:@"state", @"media", nil], @"seekable",
138 return [dict objectForKey: key];
144 return [self initWithDrawable:nil];
147 - (id)initWithVideoView:(VLCVideoView *)aVideoView
149 return [self initWithDrawable: aVideoView];
152 - (id)initWithVideoLayer:(VLCVideoLayer *)aVideoLayer
154 return [self initWithDrawable: aVideoLayer];
161 if([self retainCount] <= 1)
163 /* We must make sure we won't receive new event after an upcoming dealloc
164 * We also may receive a -retain in some event callback that may occcur
165 * Before libvlc_event_detach. So this can't happen in dealloc */
166 [self unregisterObservers];
174 // Always get rid of the delegate first so we can stop sending messages to it
175 // TODO: Should we tell the delegate that we're shutting down?
178 libvlc_media_instance_release((libvlc_media_instance_t *)instance);
180 // Get rid of everything else
182 [cachedTime release];
187 - (void)setDelegate:(id)value
197 - (void)setVideoView:(VLCVideoView *)aVideoView
199 [self setDrawable: aVideoView];
202 - (void)setVideoLayer:(VLCVideoLayer *)aVideoLayer
204 [self setDrawable: aVideoLayer];
207 - (void)setVideoAspectRatio:(char *)value
209 libvlc_video_set_aspect_ratio( instance, value, NULL );
212 - (char *)videoAspectRatio
214 libvlc_exception_t ex;
215 libvlc_exception_init( &ex );
216 char * result = libvlc_video_get_aspect_ratio( instance, &ex );
217 catch_exception( &ex );
221 - (void)setVideoSubTitles:(int)value
223 libvlc_video_set_spu( instance, value, NULL );
226 - (int)videoSubTitles
228 libvlc_exception_t ex;
229 libvlc_exception_init( &ex );
230 int result = libvlc_video_get_spu( instance, &ex );
231 catch_exception( &ex );
235 - (void)setVideoCropGeometry:(char *)value
237 libvlc_video_set_crop_geometry( instance, value, NULL );
240 - (char *)videoCropGeometry
242 libvlc_exception_t ex;
243 libvlc_exception_init( &ex );
244 char * result = libvlc_video_get_crop_geometry( instance, &ex );
245 catch_exception( &ex );
249 - (void)setVideoTeleText:(int)value
251 libvlc_video_set_teletext( instance, value, NULL );
256 libvlc_exception_t ex;
257 libvlc_exception_init( &ex );
258 int result = libvlc_video_get_teletext( instance, &ex );
259 catch_exception( &ex );
263 - (void)setRate:(int)value
265 libvlc_media_instance_set_rate( instance, value, NULL );
270 libvlc_exception_t ex;
271 libvlc_exception_init( &ex );
272 float result = libvlc_media_instance_get_rate( instance, &ex );
273 catch_exception( &ex );
279 libvlc_exception_t ex;
280 libvlc_exception_init( &ex );
281 NSSize result = NSMakeSize(libvlc_video_get_height((libvlc_media_instance_t *)instance, &ex),
282 libvlc_video_get_width((libvlc_media_instance_t *)instance, &ex));
283 catch_exception( &ex );
289 libvlc_exception_t ex;
290 libvlc_exception_init( &ex );
291 BOOL result = libvlc_media_instance_has_vout((libvlc_media_instance_t *)instance, &ex);
292 if (libvlc_exception_raised( &ex ))
294 libvlc_exception_clear( &ex );
301 - (float)framesPerSecond
303 libvlc_exception_t ex;
304 libvlc_exception_init( &ex );
305 float result = libvlc_media_instance_get_fps( (libvlc_media_instance_t *)instance, &ex );
306 catch_exception( &ex );
310 - (void)setTime:(VLCTime *)value
312 libvlc_exception_t ex;
313 libvlc_exception_init( &ex );
314 // Time is managed in seconds, while duration is managed in microseconds
315 // TODO: Redo VLCTime to provide value numberAsMilliseconds, numberAsMicroseconds, numberAsSeconds, numberAsMinutes, numberAsHours
316 libvlc_media_instance_set_time( (libvlc_media_instance_t *)instance,
317 (value ? [[value numberValue] longLongValue] / 1000 : 0),
319 catch_exception( &ex );
327 - (void)setChapter:(int)value;
329 libvlc_media_instance_set_chapter( instance, value, NULL );
334 libvlc_exception_t ex;
335 libvlc_exception_init( &ex );
336 int result = libvlc_media_instance_get_chapter( instance, &ex );
337 catch_exception( &ex );
341 - (int)countOfChapters
343 libvlc_exception_t ex;
344 libvlc_exception_init( &ex );
345 int result = libvlc_media_instance_get_chapter_count( instance, &ex );
346 catch_exception( &ex );
350 - (void)setAudioTrack:(int)value
352 libvlc_audio_set_track( instance, value, NULL );
357 libvlc_exception_t ex;
358 libvlc_exception_init( &ex );
359 int result = libvlc_audio_get_track( instance, &ex );
360 catch_exception( &ex );
364 - (int)countOfAudioTracks
366 libvlc_exception_t ex;
367 libvlc_exception_init( &ex );
368 int result = libvlc_audio_get_track_count( instance, &ex );
369 catch_exception( &ex );
373 - (void)setAudioChannel:(int)value
375 libvlc_audio_set_channel( instance, value, NULL );
380 libvlc_exception_t ex;
381 libvlc_exception_init( &ex );
382 int result = libvlc_audio_get_channel( instance, &ex );
383 catch_exception( &ex );
387 - (void)setMedia:(VLCMedia *)value
389 // We only know how to play media files...not media resources with subitems
390 if (media != value && [media subitems] == nil)
392 if (media && [media compare:value] == NSOrderedSame)
396 if (wasPlaying = [self isPlaying])
399 // // TODO: Should we wait until it stops playing?
400 // while ([self isPlaying])
405 media = [value retain];
407 libvlc_exception_t ex;
408 libvlc_exception_init( &ex );
409 libvlc_media_instance_set_media_descriptor( instance, [media libVLCMediaDescriptor], &ex );
410 catch_exception( &ex );
421 libvlc_exception_t ex;
422 libvlc_exception_init( &ex );
423 libvlc_media_instance_play( (libvlc_media_instance_t *)instance, &ex );
424 catch_exception( &ex );
430 // Return if there is no media available or if the stream is not paused or
431 // playing something else
432 if (!media || (![self isPlaying] && [self state] != VLCMediaPlayerStatePaused))
435 // Should never get here.
440 libvlc_exception_t ex;
441 libvlc_exception_init( &ex );
442 libvlc_media_instance_pause( (libvlc_media_instance_t *)instance, &ex );
443 catch_exception( &ex );
445 // TODO: Should we record the time in case the media instance is destroyed
451 // Return if there is no media available or if the system is not in play status
453 if (!media || (![self isPlaying] && [self state] != VLCMediaPlayerStatePaused))
456 // The following is not implemented in the core, should I fix it or just
458 // libvlc_exception_t ex;
459 // libvlc_exception_init( &ex );
460 // libvlc_media_instance_stop((libvlc_media_instance_t *)instance, &ex);
461 // catch_exception( &ex );
463 // Pause and reposition to the begining of the stream.
466 // TODO: Should we pause this or destroy the media instance so that it appears as being "stopped"?
469 //- (void)fastForward;
470 //- (void)fastForwardAtRate:(int)rate;
472 //- (void)rewindAtRate:(int)rate;
476 VLCMediaPlayerState state = [self state];
477 return ((state == VLCMediaPlayerStateOpening) || (state == VLCMediaPlayerStateBuffering) ||
478 (state == VLCMediaPlayerStatePlaying));
483 libvlc_exception_t ex;
484 libvlc_exception_init( &ex );
485 BOOL ret = libvlc_media_instance_will_play( (libvlc_media_instance_t *)instance, &ex );
486 if (libvlc_exception_raised(&ex))
488 libvlc_exception_clear(&ex);
495 static const VLCMediaPlayerState libvlc_to_local_state[] =
497 [libvlc_Stopped] = VLCMediaPlayerStateStopped,
498 [libvlc_Opening] = VLCMediaPlayerStateOpening,
499 [libvlc_Buffering] = VLCMediaPlayerStateBuffering,
500 [libvlc_Playing] = VLCMediaPlayerStatePlaying,
501 [libvlc_Paused] = VLCMediaPlayerStatePaused,
502 [libvlc_Ended] = VLCMediaPlayerStateEnded,
503 [libvlc_Error] = VLCMediaPlayerStateError
506 - (VLCMediaPlayerState)state
516 - (void)setPosition:(float)newPosition
518 libvlc_exception_t ex;
519 libvlc_exception_init( &ex );
520 libvlc_media_instance_set_position( instance, newPosition, &ex );
521 catch_exception( &ex );
526 libvlc_exception_t ex;
527 libvlc_exception_init( &ex );
528 BOOL ret = libvlc_media_instance_is_seekable( instance, &ex );
529 catch_exception( &ex );
535 @implementation VLCMediaPlayer (Private)
536 - (id)initWithDrawable:(id)aDrawable
538 if (self = [super init])
542 cachedTime = [[VLCTime nullTime] retain];
544 cachedState = VLCMediaPlayerStateStopped;
546 // Create a media instance, it doesn't matter what library we start off with
547 // it will change depending on the media descriptor provided to the media
549 libvlc_exception_t ex;
550 libvlc_exception_init( &ex );
551 instance = (void *)libvlc_media_instance_new([VLCLibrary sharedInstance], &ex);
552 catch_exception( &ex );
554 [self registerObservers];
556 [self setDrawable:aDrawable];
561 - (void)setDrawable:(id)aDrawable
563 // Make sure that this instance has been associated with the drawing canvas.
564 libvlc_exception_t ex;
565 libvlc_exception_init( &ex );
566 libvlc_media_instance_set_drawable ((libvlc_media_instance_t *)instance,
567 (libvlc_drawable_t)aDrawable,
569 catch_exception( &ex );
572 - (void)registerObservers
574 libvlc_exception_t ex;
575 libvlc_exception_init( &ex );
577 // Attach event observers into the media instance
578 libvlc_event_manager_t * p_em = libvlc_media_instance_event_manager( instance, &ex );
579 libvlc_event_attach( p_em, libvlc_MediaInstancePlayed, HandleMediaInstanceStateChanged, self, &ex );
580 libvlc_event_attach( p_em, libvlc_MediaInstancePaused, HandleMediaInstanceStateChanged, self, &ex );
581 libvlc_event_attach( p_em, libvlc_MediaInstanceReachedEnd, HandleMediaInstanceStateChanged, self, &ex );
582 /* FIXME: We may want to turn that off when none is interested by that */
583 libvlc_event_attach( p_em, libvlc_MediaInstancePositionChanged, HandleMediaPositionChanged, self, &ex );
584 libvlc_event_attach( p_em, libvlc_MediaInstanceTimeChanged, HandleMediaTimeChanged, self, &ex );
585 catch_exception( &ex );
588 - (void)unregisterObservers
590 libvlc_event_manager_t * p_em = libvlc_media_instance_event_manager( instance, NULL );
591 libvlc_event_detach( p_em, libvlc_MediaInstancePlayed, HandleMediaInstanceStateChanged, self, NULL );
592 libvlc_event_detach( p_em, libvlc_MediaInstancePaused, HandleMediaInstanceStateChanged, self, NULL );
593 libvlc_event_detach( p_em, libvlc_MediaInstanceReachedEnd, HandleMediaInstanceStateChanged, self, NULL );
594 //libvlc_event_detach( p_em, libvlc_MediaInstancePositionChanged, HandleMediaTimeChanged, self, NULL );
595 libvlc_event_detach( p_em, libvlc_MediaInstanceTimeChanged, HandleMediaTimeChanged, self, NULL );
598 - (void)mediaPlayerTimeChanged:(NSNumber *)newTime
600 [self willChangeValueForKey:@"time"];
601 [cachedTime release];
602 cachedTime = [[VLCTime timeWithNumber:newTime] retain];
603 [self didChangeValueForKey:@"time"];
606 - (void)mediaPlayerPositionChanged:(NSNumber *)newPosition
608 if( [newPosition floatValue] - position < 0.005 && position - [newPosition floatValue] < 0.005 )
609 return; /* Forget that, this is too much precision for our uses */
610 [self willChangeValueForKey:@"position"];
611 position = ((float)((int)([newPosition floatValue]*1000)))/1000.;
612 [self didChangeValueForKey:@"position"];
615 - (void)mediaPlayerStateChanged:(NSNumber *)newState
617 [self willChangeValueForKey:@"state"];
618 cachedState = [newState intValue];
619 [self didChangeValueForKey:@"state"];