]> git.sesse.net Git - vlc/blob - extras/MacOSX/Framework/Sources/VLCMediaPlayer.m
dce52100f8c6d6635aa3ec863674ffff4660b204
[vlc] / 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)setAudioTrack:(int)value
300 {
301     libvlc_audio_set_track(instance, value, NULL);
302 }
303
304 - (int)audioTrack
305 {
306     libvlc_exception_t ex;
307     libvlc_exception_init(&ex);
308     int result = libvlc_audio_get_track(instance, &ex);
309     quit_on_exception(&ex);
310     return result;
311 }
312
313 - (void)setAudioChannel:(int)value
314 {
315     libvlc_audio_set_channel(instance, value, NULL);
316 }
317
318 - (int)audioChannel
319 {
320     libvlc_exception_t ex;
321     libvlc_exception_init(&ex);
322     int result = libvlc_audio_get_channel(instance, &ex);
323     quit_on_exception(&ex);
324     return result;
325 }
326
327 - (void)setMedia:(VLCMedia *)value
328 {
329     // We only know how to play media files...not media resources with subitems
330     if (media != value && [media subitems] == nil)
331     {
332         if (media && [media compare:value] == NSOrderedSame)
333             return;
334         
335         BOOL wasPlaying;
336         if (wasPlaying = [self isPlaying])
337         {
338             [self pause];
339 //            // TODO: Should we wait until it stops playing?
340 //            while ([self isPlaying])
341 //                usleep(1000);
342         }
343         
344         [self willChangeValueForKey:@"media"];
345         [media release];
346         media = [value retain];
347         [self didChangeValueForKey:@"media"];
348
349         libvlc_exception_t ex;
350         libvlc_exception_init(&ex);
351         libvlc_media_instance_set_media_descriptor(instance, [media libVLCMediaDescriptor], &ex);
352         quit_on_exception(&ex);
353         
354         if (media) {
355             if (wasPlaying)
356                 [self play];
357         }
358     }
359 }
360
361 - (VLCMedia *)media
362 {
363     return media;
364 }
365
366 - (BOOL)play
367 {
368     // Return if there is no media available or if the stream is already playing something
369     if (!media || [self isPlaying])
370         return [self isPlaying];
371     
372     libvlc_exception_t ex;
373     libvlc_exception_init(&ex);
374
375     libvlc_media_instance_play((libvlc_media_instance_t *)instance, &ex);
376     quit_on_exception(&ex);
377
378     return YES;
379 }
380
381 - (void)pause
382 {
383     // Return if there is no media available or if the stream is not paused or 
384     // playing something else
385     if (!media || (![self isPlaying] && [self state] != VLCMediaPlayerStatePaused))
386         return;
387
388     // Should never get here.
389     if (!instance)
390         return;
391
392     // Pause the stream
393     libvlc_exception_t ex;
394     libvlc_exception_init(&ex);
395     libvlc_media_instance_pause((libvlc_media_instance_t *)instance, &ex);
396     quit_on_exception(&ex);
397     
398     // TODO: Should we record the time in case the media instance is destroyed
399     // then rebuilt?
400 }
401
402 - (void)stop
403 {
404     // Return if there is no media available or if the system is not in play status 
405     // or pause status.
406     if (!media || (![self isPlaying] && [self state] != VLCMediaPlayerStatePaused))
407         return;
408     
409     // The following is not implemented in the core, should I fix it or just
410     // compensate?
411     //    libvlc_exception_t ex;
412     //    libvlc_exception_init(&ex);
413     //    libvlc_media_instance_stop((libvlc_media_instance_t *)instance, &ex);
414     //    quit_on_exception(&ex);
415     
416     // Pause and reposition to the begining of the stream.
417     [self pause];
418     [self setTime:0];
419     // TODO: Should we pause this or destroy the media instance so that it appears as being "stopped"?
420 }
421
422 //- (void)fastForward;
423 //- (void)fastForwardAtRate:(int)rate;
424 //- (void)rewind;
425 //- (void)rewindAtRate:(int)rate;
426
427 - (BOOL)isPlaying
428 {
429     VLCMediaPlayerState state = [self state];
430     return ((state == VLCMediaPlayerStateOpening) || (state == VLCMediaPlayerStateBuffering) ||
431             (state == VLCMediaPlayerStatePlaying));
432 }
433
434 - (BOOL)willPlay
435 {
436     libvlc_exception_t ex;
437     libvlc_exception_init(&ex);
438     BOOL ret = libvlc_media_instance_will_play((libvlc_media_instance_t *)instance, &ex);
439     if (libvlc_exception_raised(&ex))
440     {
441         libvlc_exception_clear(&ex);
442         return NO;
443     }
444     else
445         return ret;
446 }
447
448 static VLCMediaPlayerState libvlc_to_local_state [] =
449 {
450     [libvlc_Stopped]    = VLCMediaPlayerStateStopped,
451     [libvlc_Opening]    = VLCMediaPlayerStateOpening,
452     [libvlc_Buffering]    = VLCMediaPlayerStateBuffering,
453     [libvlc_Playing]    = VLCMediaPlayerStatePlaying,
454     [libvlc_Paused]        = VLCMediaPlayerStatePaused,
455     [libvlc_Ended]        = VLCMediaPlayerStateEnded,
456     [libvlc_Error]        = VLCMediaPlayerStateError
457 };
458
459 - (VLCMediaPlayerState)state
460 {
461     // If there is no instance, assume that we're in a stopped state
462     if (!instance)
463         return VLCMediaPlayerStateStopped;
464     
465     libvlc_exception_t ex;
466     libvlc_exception_init(&ex);
467     libvlc_state_t libvlc_state = libvlc_media_instance_get_state((libvlc_media_instance_t *)instance, &ex);
468     if (libvlc_exception_raised(&ex))
469     {
470         libvlc_exception_clear(&ex);
471         return VLCMediaPlayerStateError;
472     }
473     else
474         return libvlc_to_local_state[libvlc_state];
475 }
476 @end
477
478 @implementation VLCMediaPlayer (Private)
479 - (void)registerObservers
480 {
481     libvlc_exception_t ex;
482     libvlc_exception_init(&ex);
483
484     // Attach event observers into the media instance
485     libvlc_event_manager_t *p_em = libvlc_media_instance_event_manager(instance, &ex);
486     libvlc_event_attach(p_em, libvlc_MediaInstancePlayed,          HandleMediaInstanceStateChanged, self, &ex);
487     libvlc_event_attach(p_em, libvlc_MediaInstancePaused,          HandleMediaInstanceStateChanged, self, &ex);
488     libvlc_event_attach(p_em, libvlc_MediaInstanceReachedEnd,      HandleMediaInstanceStateChanged, self, &ex);
489     libvlc_event_attach(p_em, libvlc_MediaInstancePositionChanged, HandleMediaTimeChanged,            self, &ex);
490     quit_on_exception(&ex);
491 }
492
493 - (void)unregisterObservers
494 {
495     libvlc_event_manager_t *p_em = libvlc_media_instance_event_manager(instance, NULL);
496     libvlc_event_detach(p_em, libvlc_MediaInstancePlayed,          HandleMediaInstanceStateChanged, self, NULL);
497     libvlc_event_detach(p_em, libvlc_MediaInstancePaused,          HandleMediaInstanceStateChanged, self, NULL);
498     libvlc_event_detach(p_em, libvlc_MediaInstanceReachedEnd,      HandleMediaInstanceStateChanged, self, NULL);
499     libvlc_event_detach(p_em, libvlc_MediaInstancePositionChanged, HandleMediaTimeChanged,            self, NULL);
500 }
501 @end