MacOSX/Framework/VLCMediaPlayer.m: Formatting fixes.
[vlc.git] / extras / MacOSX / Framework / Sources / VLCMediaPlayer.m
1 /*****************************************************************************
2  * VLCMediaPlayer.m: VLC.framework VLCMediaPlayer implementation
3  *****************************************************************************
4  * Copyright (C) 2007 Pierre d'Herbemont
5  * Copyright (C) 2007 the VideoLAN team
6  * $Id$
7  *
8  * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
9  *          Faustion Osuna <enrique.osuna # gmail.com>
10  *
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.
15  *
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.
20  *
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  *****************************************************************************/
25
26 #import "VLCLibrary.h"
27 #import "VLCMediaPlayer.h"
28 #import "VLCEventManager.h"
29 #import "VLCLibVLCBridging.h"
30 #include <vlc/vlc.h>
31
32 /* Notification Messages */
33 NSString *VLCMediaPlayerTimeChanged   = @"VLCMediaPlayerTimeChanged";
34 NSString *VLCMediaPlayerStateChanged  = @"VLCMediaPlayerStateChanged";
35
36 /* libvlc event callback */
37 static void HandleMediaInstanceVolumeChanged(const libvlc_event_t *event, void *self)
38 {
39     [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
40                                                    withDelegateMethod:@selector(mediaPlayerVolumeChanged:)
41                                                  withNotificationName:VLCMediaPlayerVolumeChanged];
42 }
43
44 static void HandleMediaTimeChanged(const libvlc_event_t * event, void * self)
45 {
46     [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
47                                                    withDelegateMethod:@selector(mediaPlayerTimeChanged:)
48                                                  withNotificationName:VLCMediaPlayerTimeChanged];
49         
50 }
51
52 static void HandleMediaInstanceStateChanged(const libvlc_event_t *event, void *self)
53 {
54     [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self
55                                                    withDelegateMethod:@selector(mediaPlayerStateChanged:)
56                                                  withNotificationName:VLCMediaPlayerStateChanged];
57 }
58
59 NSString *VLCMediaPlayerStateToString(VLCMediaPlayerState state)
60 {
61     static NSString *stateToStrings[] = {
62         [VLCMediaPlayerStateStopped]      = @"VLCMediaPlayerStateStopped",
63         [VLCMediaPlayerStateOpening]      = @"VLCMediaPlayerStateOpening",
64         [VLCMediaPlayerStateBuffering]    = @"VLCMediaPlayerStateBuffering",
65         [VLCMediaPlayerStateEnded]        = @"VLCMediaPlayerStateEnded",
66         [VLCMediaPlayerStateError]        = @"VLCMediaPlayerStateError",
67         [VLCMediaPlayerStatePlaying]      = @"VLCMediaPlayerStatePlaying",
68         [VLCMediaPlayerStatePaused]       = @"VLCMediaPlayerStatePaused"
69     };
70     return stateToStrings[state];
71 }
72
73 // TODO: Documentation
74 @interface VLCMediaPlayer (Private)
75 - (void)registerObservers;
76 - (void)unregisterObservers;
77 @end
78
79 @implementation VLCMediaPlayer
80 - (id)init
81 {
82     self = [self initWithVideoView:nil];
83     return self;
84 }
85
86 - (id)initWithVideoView:(VLCVideoView *)aVideoView
87 {
88     if (self = [super init])
89     {
90         delegate = nil;
91         media = nil;
92         
93         // Create a media instance, it doesn't matter what library we start off with
94         // it will change depending on the media descriptor provided to the media
95         // instance
96         libvlc_exception_t ex;
97         libvlc_exception_init( &ex );
98         instance = (void *)libvlc_media_instance_new([VLCLibrary sharedInstance], &ex);
99         quit_on_exception( &ex );
100         
101         [self registerObservers];
102         
103         [self setVideoView:aVideoView];
104     }
105     return self;
106 }
107
108 - (void)dealloc
109 {
110     // Always get rid of the delegate first so we can stop sending messages to it
111     // TODO: Should we tell the delegate that we're shutting down?
112     delegate = nil;
113
114     // Next get rid of the event managers so we can stop trapping events
115     [self unregisterObservers];
116     libvlc_media_instance_release((libvlc_media_instance_t *)instance);
117     
118     // Get rid of everything else
119     instance = nil;
120     videoView = nil;
121     [media release];
122     
123     [super dealloc];
124 }
125
126 - (void)setDelegate:(id)value
127 {
128     delegate = value;
129 }
130
131 - (id)delegate
132 {
133     return delegate;
134 }
135
136 - (void)setVideoView:(VLCVideoView *)value
137 {
138     videoView = value;
139     
140     // Make sure that this instance has been associated with the drawing canvas.
141     libvlc_exception_t ex;
142     libvlc_exception_init( &ex );
143     libvlc_media_instance_set_drawable ((libvlc_media_instance_t *)instance, 
144                                         (libvlc_drawable_t)videoView, 
145                                         &ex);
146     quit_on_exception( &ex );
147 }
148
149 - (VLCVideoView *)videoView
150 {
151     return videoView;
152 }
153
154 - (void)setFullscreen:(BOOL)value
155 {
156     libvlc_set_fullscreen(instance, value, NULL);
157 }
158
159 - (BOOL)fullscreen
160 {
161     libvlc_exception_t ex;
162     libvlc_exception_init( &ex );
163     int result = libvlc_get_fullscreen( instance, &ex );
164     quit_on_exception( &ex );
165     return result;
166 }
167
168 - (void)setVideoAspectRatio:(char *)value
169 {
170     libvlc_video_set_aspect_ratio( instance, value, NULL );
171 }
172
173 - (char *)videoAspectRatio
174 {
175     libvlc_exception_t ex;
176     libvlc_exception_init( &ex );
177     char *result = libvlc_video_get_aspect_ratio( instance, &ex );
178     quit_on_exception( &ex );
179     return result;
180 }
181
182 - (void)setVideoSubTitles:(int)value
183 {
184     libvlc_video_set_spu( instance, value, NULL );
185 }
186
187 - (int)videoSubTitles
188 {
189     libvlc_exception_t ex;
190     libvlc_exception_init( &ex );
191     int result = libvlc_video_get_spu( instance, &ex );
192     quit_on_exception( &ex );
193     return result;
194 }
195
196 - (void)setVideoCropGeometry:(char *)value
197 {
198     libvlc_video_set_crop_geometry( instance, value, NULL );
199 }
200
201 - (char *)videoCropGeometry
202 {
203     libvlc_exception_t ex;
204     libvlc_exception_init( &ex );
205     char *result = libvlc_video_get_crop_geometry( instance, &ex );
206     quit_on_exception( &ex );
207     return result;
208 }
209
210 - (void)setVideoTeleText:(int)value
211 {
212     libvlc_video_set_teletext( instance, value, NULL );
213 }
214
215 - (int)videoTeleText
216 {
217     libvlc_exception_t ex;
218     libvlc_exception_init( &ex );
219     int result = libvlc_video_get_teletext( instance, &ex );
220     quit_on_exception( &ex );
221     return result;
222 }
223
224 - (void)setRate:(int)value
225 {
226     libvlc_media_instance_set_rate( instance, value, NULL );
227 }
228
229 - (int)rate
230 {
231     libvlc_exception_t ex;
232     libvlc_exception_init( &ex );
233     float result = libvlc_media_instance_get_rate( instance, &ex );
234     quit_on_exception( &ex );
235     return result;
236 }
237
238 - (NSSize)videoSize
239 {
240     libvlc_exception_t ex;
241     libvlc_exception_init( &ex );
242     NSSize result = NSMakeSize(libvlc_video_get_height((libvlc_media_instance_t *)instance, &ex),
243                                libvlc_video_get_width((libvlc_media_instance_t *)instance, &ex));
244     quit_on_exception( &ex );
245     return result;    
246 }
247
248 - (BOOL)hasVideoOut
249 {
250     libvlc_exception_t ex;
251     libvlc_exception_init( &ex );
252     BOOL result = libvlc_media_instance_has_vout((libvlc_media_instance_t *)instance, &ex);
253     if (libvlc_exception_raised( &ex ))
254     {
255         libvlc_exception_clear( &ex );
256         return NO;
257     }
258     else
259         return result;
260 }
261
262 - (float)framesPerSecond
263 {
264     libvlc_exception_t ex;
265     libvlc_exception_init( &ex );
266     float result = libvlc_media_instance_get_fps( (libvlc_media_instance_t *)instance, &ex );
267     quit_on_exception( &ex );
268     return result;
269 }
270
271 - (void)setTime:(VLCTime *)value
272 {
273     libvlc_exception_t ex;
274     libvlc_exception_init( &ex );
275     // Time is managed in seconds, while duration is managed in microseconds
276     // TODO: Redo VLCTime to provide value numberAsMilliseconds, numberAsMicroseconds, numberAsSeconds, numberAsMinutes, numberAsHours
277     libvlc_media_instance_set_time( (libvlc_media_instance_t *)instance, 
278                                     (value ? [[value numberValue] longLongValue] / 1000 : 0),
279                                     &ex );
280     quit_on_exception( &ex );
281 }
282
283 - (VLCTime *)time
284 {
285     libvlc_exception_t ex;
286     libvlc_exception_init( &ex );
287     
288     // Results are returned in seconds...duration is returned in milliseconds
289     long long time = libvlc_media_instance_get_time( (libvlc_media_instance_t *)instance, &ex ) * 1000;
290     if (libvlc_exception_raised( &ex ))
291     {
292         libvlc_exception_clear( &ex );
293         return [VLCTime nullTime];        // Error in obtaining the time, return a null time defintition (--:--:--)
294     }
295     else
296         return [VLCTime timeWithNumber:[NSNumber numberWithLongLong:time]];
297 }
298
299 - (void)setChapter:(int)value;
300 {
301     libvlc_media_instance_set_chapter( instance, value, NULL );
302 }
303
304 - (int)chapter
305 {
306     libvlc_exception_t ex;
307     libvlc_exception_init( &ex );
308     int result = libvlc_media_instance_get_chapter( instance, &ex );
309     quit_on_exception( &ex );
310     return result;
311 }
312
313 - (int)countOfChapters
314 {
315     libvlc_exception_t ex;
316     libvlc_exception_init( &ex );
317     int result = libvlc_media_instance_get_chapter_count( instance, &ex );
318     quit_on_exception( &ex );
319     return result;
320 }
321
322 - (void)setAudioTrack:(int)value
323 {
324     libvlc_audio_set_track( instance, value, NULL );
325 }
326
327 - (int)audioTrack
328 {
329     libvlc_exception_t ex;
330     libvlc_exception_init( &ex );
331     int result = libvlc_audio_get_track( instance, &ex );
332     quit_on_exception( &ex );
333     return result;
334 }
335
336 - (int)countOfAudioTracks
337 {
338     libvlc_exception_t ex;
339     libvlc_exception_init( &ex );
340     int result = libvlc_audio_get_track_count( instance, &ex );
341     quit_on_exception( &ex );
342     return result;
343 }
344
345 - (void)setAudioChannel:(int)value
346 {
347     libvlc_audio_set_channel( instance, value, NULL );
348 }
349
350 - (int)audioChannel
351 {
352     libvlc_exception_t ex;
353     libvlc_exception_init( &ex );
354     int result = libvlc_audio_get_channel( instance, &ex );
355     quit_on_exception( &ex );
356     return result;
357 }
358
359 - (void)setMedia:(VLCMedia *)value
360 {
361     // We only know how to play media files...not media resources with subitems
362     if (media != value && [media subitems] == nil)
363     {
364         if (media && [media compare:value] == NSOrderedSame)
365             return;
366         
367         BOOL wasPlaying;
368         if (wasPlaying = [self isPlaying])
369         {
370             [self pause];
371 //            // TODO: Should we wait until it stops playing?
372 //            while ([self isPlaying])
373 //                usleep(1000);
374         }
375         
376         [self willChangeValueForKey:@"media"];
377         [media release];
378         media = [value retain];
379         [self didChangeValueForKey:@"media"];
380
381         libvlc_exception_t ex;
382         libvlc_exception_init( &ex );
383         libvlc_media_instance_set_media_descriptor( instance, [media libVLCMediaDescriptor], &ex );
384         quit_on_exception( &ex );
385         
386         if (media) {
387             if (wasPlaying)
388                 [self play];
389         }
390     }
391 }
392
393 - (VLCMedia *)media
394 {
395     return media;
396 }
397
398 - (BOOL)play
399 {
400     // Return if there is no media available or if the stream is already playing something
401     if (!media || [self isPlaying])
402         return [self isPlaying];
403     
404     libvlc_exception_t ex;
405     libvlc_exception_init( &ex );
406
407     libvlc_media_instance_play( (libvlc_media_instance_t *)instance, &ex );
408     quit_on_exception( &ex );
409
410     return YES;
411 }
412
413 - (void)pause
414 {
415     // Return if there is no media available or if the stream is not paused or 
416     // playing something else
417     if (!media || (![self isPlaying] && [self state] != VLCMediaPlayerStatePaused))
418         return;
419
420     // Should never get here.
421     if (!instance)
422         return;
423
424     // Pause the stream
425     libvlc_exception_t ex;
426     libvlc_exception_init( &ex );
427     libvlc_media_instance_pause( (libvlc_media_instance_t *)instance, &ex );
428     quit_on_exception( &ex );
429     
430     // TODO: Should we record the time in case the media instance is destroyed
431     // then rebuilt?
432 }
433
434 - (void)stop
435 {
436     // Return if there is no media available or if the system is not in play status 
437     // or pause status.
438     if (!media || (![self isPlaying] && [self state] != VLCMediaPlayerStatePaused))
439         return;
440     
441     // The following is not implemented in the core, should I fix it or just
442     // compensate?
443     //    libvlc_exception_t ex;
444     //    libvlc_exception_init( &ex );
445     //    libvlc_media_instance_stop((libvlc_media_instance_t *)instance, &ex);
446     //    quit_on_exception( &ex );
447     
448     // Pause and reposition to the begining of the stream.
449     [self pause];
450     [self setTime:0];
451     // TODO: Should we pause this or destroy the media instance so that it appears as being "stopped"?
452 }
453
454 //- (void)fastForward;
455 //- (void)fastForwardAtRate:(int)rate;
456 //- (void)rewind;
457 //- (void)rewindAtRate:(int)rate;
458
459 - (BOOL)isPlaying
460 {
461     VLCMediaPlayerState state = [self state];
462     return ((state == VLCMediaPlayerStateOpening) || (state == VLCMediaPlayerStateBuffering) ||
463             (state == VLCMediaPlayerStatePlaying));
464 }
465
466 - (BOOL)willPlay
467 {
468     libvlc_exception_t ex;
469     libvlc_exception_init( &ex );
470     BOOL ret = libvlc_media_instance_will_play( (libvlc_media_instance_t *)instance, &ex );
471     if (libvlc_exception_raised(&ex))
472     {
473         libvlc_exception_clear(&ex);
474         return NO;
475     }
476     else
477         return ret;
478 }
479
480 static const VLCMediaPlayerState libvlc_to_local_state[] =
481 {
482     [libvlc_Stopped]    = VLCMediaPlayerStateStopped,
483     [libvlc_Opening]    = VLCMediaPlayerStateOpening,
484     [libvlc_Buffering]  = VLCMediaPlayerStateBuffering,
485     [libvlc_Playing]    = VLCMediaPlayerStatePlaying,
486     [libvlc_Paused]     = VLCMediaPlayerStatePaused,
487     [libvlc_Ended]      = VLCMediaPlayerStateEnded,
488     [libvlc_Error]      = VLCMediaPlayerStateError
489 };
490
491 - (VLCMediaPlayerState)state
492 {
493     // If there is no instance, assume that we're in a stopped state
494     if (!instance)
495         return VLCMediaPlayerStateStopped;
496     
497     libvlc_exception_t ex;
498     libvlc_exception_init( &ex );
499     libvlc_state_t libvlc_state = libvlc_media_instance_get_state( (libvlc_media_instance_t *)instance, &ex );
500     if (libvlc_exception_raised( &ex ))
501     {
502         libvlc_exception_clear( &ex );
503         return VLCMediaPlayerStateError;
504     }
505     else
506         return libvlc_to_local_state[libvlc_state];
507 }
508 @end
509
510 @implementation VLCMediaPlayer (Private)
511 - (void)registerObservers
512 {
513     libvlc_exception_t ex;
514     libvlc_exception_init( &ex );
515
516     // Attach event observers into the media instance
517     libvlc_event_manager_t *p_em = libvlc_media_instance_event_manager( instance, &ex );
518     libvlc_event_attach( p_em, libvlc_MediaInstancePlayed,          HandleMediaInstanceStateChanged, self, &ex );
519     libvlc_event_attach( p_em, libvlc_MediaInstancePaused,          HandleMediaInstanceStateChanged, self, &ex );
520     libvlc_event_attach( p_em, libvlc_MediaInstanceReachedEnd,      HandleMediaInstanceStateChanged, self, &ex );
521     libvlc_event_attach( p_em, libvlc_MediaInstancePositionChanged, HandleMediaTimeChanged,            self, &ex );
522     quit_on_exception( &ex );
523 }
524
525 - (void)unregisterObservers
526 {
527     libvlc_event_manager_t *p_em = libvlc_media_instance_event_manager( instance, NULL );
528     libvlc_event_detach( p_em, libvlc_MediaInstancePlayed,          HandleMediaInstanceStateChanged, self, NULL );
529     libvlc_event_detach( p_em, libvlc_MediaInstancePaused,          HandleMediaInstanceStateChanged, self, NULL );
530     libvlc_event_detach( p_em, libvlc_MediaInstanceReachedEnd,      HandleMediaInstanceStateChanged, self, NULL );
531     libvlc_event_detach( p_em, libvlc_MediaInstancePositionChanged, HandleMediaTimeChanged,            self, NULL );
532 }
533 @end