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