1 /*****************************************************************************
2 * VLCMedia.m: VLC.framework VLCMedia implementation
3 *****************************************************************************
4 * Copyright (C) 2007 Pierre d'Herbemont
5 * Copyright (C) 2007 the VideoLAN team
8 * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
26 #import "VLCMediaList.h"
27 #import "VLCEventManager.h"
28 #import "VLCLibrary.h"
29 #import "VLCLibVLCBridging.h"
30 #include <vlc/libvlc.h>
32 /* Meta Dictionary Keys */
33 NSString * VLCMetaInformationTitle = @"title";
34 NSString * VLCMetaInformationArtist = @"artist";
35 NSString * VLCMetaInformationGenre = @"genre";
36 NSString * VLCMetaInformationCopyright = @"copyright";
37 NSString * VLCMetaInformationAlbum = @"album";
38 NSString * VLCMetaInformationTrackNumber = @"trackNumber";
39 NSString * VLCMetaInformationDescription = @"description";
40 NSString * VLCMetaInformationRating = @"rating";
41 NSString * VLCMetaInformationDate = @"date";
42 NSString * VLCMetaInformationSetting = @"setting";
43 NSString * VLCMetaInformationURL = @"url";
44 NSString * VLCMetaInformationLanguage = @"language";
45 NSString * VLCMetaInformationNowPlaying = @"nowPlaying";
46 NSString * VLCMetaInformationPublisher = @"publisher";
47 NSString * VLCMetaInformationEncodedBy = @"encodedBy";
48 NSString * VLCMetaInformationArtworkURL = @"artworkURL";
49 NSString * VLCMetaInformationArtwork = @"artwork";
50 NSString * VLCMetaInformationTrackID = @"trackID";
52 /* Notification Messages */
53 NSString * VLCMediaMetaChanged = @"VLCMediaMetaChanged";
55 /******************************************************************************
56 * @property (readwrite)
58 @interface VLCMedia ()
59 @property (readwrite) VLCMediaState state;
62 /******************************************************************************
65 // TODO: Documentation
66 @interface VLCMedia (Private)
68 + (libvlc_meta_t)stringToMetaType:(NSString *)string;
69 + (NSString *)metaTypeToString:(libvlc_meta_t)type;
72 - (void)initInternalMediaDescriptor;
75 - (void)fetchMetaInformationFromLibVLCWithType:(NSString*)metaType;
76 - (void)fetchMetaInformationForArtWorkWithURL:(NSString *)anURL;
77 - (void)setArtwork:(NSImage *)art;
79 /* Callback Methods */
80 - (void)metaChanged:(NSString *)metaType;
82 - (void)setStateAsNumber:(NSNumber *)newStateAsNumber;
85 static VLCMediaState libvlc_state_to_media_state[] =
87 [libvlc_NothingSpecial] = VLCMediaStateNothingSpecial,
88 [libvlc_Stopped] = VLCMediaStateNothingSpecial,
89 [libvlc_Opening] = VLCMediaStateNothingSpecial,
90 [libvlc_Buffering] = VLCMediaStateBuffering,
91 [libvlc_Ended] = VLCMediaStateNothingSpecial,
92 [libvlc_Error] = VLCMediaStateError,
93 [libvlc_Playing] = VLCMediaStatePlaying,
94 [libvlc_Paused] = VLCMediaStatePlaying,
97 static inline VLCMediaState LibVLCStateToMediaState( libvlc_state_t state )
99 return libvlc_state_to_media_state[state];
102 /******************************************************************************
103 * LibVLC Event Callback
105 static void HandleMediaMetaChanged(const libvlc_event_t * event, void * self)
107 if( event->u.media_descriptor_meta_changed.meta_type == libvlc_meta_Publisher ||
108 event->u.media_descriptor_meta_changed.meta_type == libvlc_meta_NowPlaying )
110 /* Skip those meta. We don't really care about them for now.
111 * And they occure a lot */
114 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
115 [[VLCEventManager sharedManager] callOnMainThreadObject:self
116 withMethod:@selector(metaChanged:)
117 withArgumentAsObject:[VLCMedia metaTypeToString:event->u.media_descriptor_meta_changed.meta_type]];
121 //static void HandleMediaDurationChanged(const libvlc_event_t * event, void * self)
123 // NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
125 // [[VLCEventManager sharedManager] callOnMainThreadObject:self
126 // withMethod:@selector(setLength:)
127 // withArgumentAsObject:[VLCTime timeWithNumber:
128 // [NSNumber numberWithLongLong:event->u.media_descriptor_duration_changed.new_duration]]];
132 static void HandleMediaStateChanged(const libvlc_event_t * event, void * self)
134 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
135 [[VLCEventManager sharedManager] callOnMainThreadObject:self
136 withMethod:@selector(setStateAsNumber:)
137 withArgumentAsObject:[NSNumber numberWithInt:
138 LibVLCStateToMediaState(event->u.media_descriptor_state_changed.new_state)]];
142 static void HandleMediaSubItemAdded(const libvlc_event_t * event, void * self)
144 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
145 [[VLCEventManager sharedManager] callOnMainThreadObject:self
146 withMethod:@selector(subItemAdded)
147 withArgumentAsObject:nil];
151 /******************************************************************************
154 @implementation VLCMedia
155 + (id)mediaWithURL:(NSURL *)anURL;
157 return [[[VLCMedia alloc] initWithURL:anURL] autorelease];
160 + (id)mediaWithPath:(NSString *)aPath;
162 return [[[VLCMedia alloc] initWithPath:aPath] autorelease];
165 + (id)mediaAsNodeWithName:(NSString *)aName;
167 return [[[VLCMedia alloc] initAsNodeWithName:aName] autorelease];
170 - (id)initWithURL:(NSURL *)anURL
172 return [self initWithPath:[anURL path]];
175 - (id)initWithPath:(NSString *)aPath
177 if (self = [super init])
179 libvlc_exception_t ex;
180 libvlc_exception_init(&ex);
182 p_md = libvlc_media_descriptor_new([VLCLibrary sharedInstance],
185 catch_exception(&ex);
188 metaDictionary = [[NSMutableDictionary alloc] initWithCapacity:3];
190 // This value is set whenever the demuxer figures out what the length is.
191 // TODO: Easy way to tell the length of the movie without having to instiate the demuxer. Maybe cached info?
194 [self initInternalMediaDescriptor];
199 - (id)initAsNodeWithName:(NSString *)aName
201 if (self = [super init])
203 libvlc_exception_t ex;
204 libvlc_exception_init(&ex);
206 p_md = libvlc_media_descriptor_new_as_node([VLCLibrary sharedInstance],
209 catch_exception(&ex);
212 metaDictionary = [[NSMutableDictionary alloc] initWithCapacity:3];
214 // This value is set whenever the demuxer figures out what the length is.
215 // TODO: Easy way to tell the length of the movie without having to instiate the demuxer. Maybe cached info?
218 [self initInternalMediaDescriptor];
227 if([self retainCount] <= 1)
229 /* We must make sure we won't receive new event after an upcoming dealloc
230 * We also may receive a -retain in some event callback that may occcur
231 * Before libvlc_event_detach. So this can't happen in dealloc */
232 libvlc_event_manager_t * p_em = libvlc_media_descriptor_event_manager(p_md, NULL);
233 libvlc_event_detach(p_em, libvlc_MediaDescriptorMetaChanged, HandleMediaMetaChanged, self, NULL);
234 // libvlc_event_detach(p_em, libvlc_MediaDescriptorDurationChanged, HandleMediaDurationChanged, self, NULL);
235 libvlc_event_detach(p_em, libvlc_MediaDescriptorStateChanged, HandleMediaStateChanged, self, NULL);
236 libvlc_event_detach(p_em, libvlc_MediaDescriptorSubItemAdded, HandleMediaSubItemAdded, self, NULL);
244 // Testing to see if the pointer exists is not required, if the pointer is null
245 // then the release message is not sent to it.
247 [self setLength:nil];
251 [metaDictionary release];
253 libvlc_media_descriptor_release( p_md );
258 - (NSString *)description
260 NSString * result = [metaDictionary objectForKey:VLCMetaInformationTitle];
261 return [NSString stringWithFormat:@"<%@ %p> %@", [self className], self, (result ? result : [url absoluteString])];
264 - (NSComparisonResult)compare:(VLCMedia *)media
266 libvlc_media_descriptor_t * anOtherMd = [media libVLCMediaDescriptor];
267 /* We can release, we'll just use ptr */
268 libvlc_media_descriptor_release(anOtherMd);
269 if (self == media || p_md == anOtherMd)
270 return NSOrderedSame;
272 return NSOrderedDescending;
273 return NSOrderedAscending;
276 @synthesize delegate;
282 // Try figuring out what the length is
283 long long duration = libvlc_media_descriptor_get_duration( p_md, NULL );
286 [self setLength:[VLCTime timeWithNumber:[NSNumber numberWithLongLong:duration]]];
287 return [[length retain] autorelease];
290 return [VLCTime nullTime];
293 - (VLCTime *)lengthWaitUntilDate:(NSDate *)aDate
295 static const long long thread_sleep = 10000;
299 while (!length && ![self isPreparsed] && [aDate timeIntervalSinceNow] > 0)
301 usleep( thread_sleep );
304 // So we're done waiting, but sometimes we trap the fact that the parsing
305 // was done before the length gets assigned, so lets go ahead and assign
308 return [self length];
311 return [[length retain] autorelease];
316 return libvlc_media_descriptor_is_preparsed( p_md, NULL );
320 @synthesize subitems;
321 @synthesize metaDictionary;
326 /******************************************************************************
327 * Implementation VLCMedia (LibVLCBridging)
329 @implementation VLCMedia (LibVLCBridging)
331 + (id)mediaWithLibVLCMediaDescriptor:(void *)md
333 return [[[VLCMedia alloc] initWithLibVLCMediaDescriptor:md] autorelease];
336 - (id)initWithLibVLCMediaDescriptor:(void *)md
338 if (self = [super init])
340 libvlc_exception_t ex;
341 libvlc_exception_init( &ex );
343 libvlc_media_descriptor_retain( md );
346 metaDictionary = [[NSMutableDictionary alloc] initWithCapacity:3];
347 [self initInternalMediaDescriptor];
352 - (void *)libVLCMediaDescriptor
357 + (id)mediaWithMedia:(VLCMedia *)media andLibVLCOptions:(NSDictionary *)options
359 libvlc_media_descriptor_t * p_md;
360 p_md = libvlc_media_descriptor_duplicate( [media libVLCMediaDescriptor] );
361 for( NSString * key in [options allKeys] )
363 NSLog(@"Adding %@", [NSString stringWithFormat:@"--%@ %@", key, [options objectForKey:key]]);
364 libvlc_media_descriptor_add_option(p_md, [[NSString stringWithFormat:@"%@=#%@", key, [options objectForKey:key]] UTF8String], NULL);
366 return [VLCMedia mediaWithLibVLCMediaDescriptor:p_md];
371 /******************************************************************************
372 * Implementation VLCMedia (Private)
374 @implementation VLCMedia (Private)
376 + (libvlc_meta_t)stringToMetaType:(NSString *)string
378 static NSDictionary * stringToMetaDictionary = nil;
379 // TODO: Thread safe-ize
380 if( !stringToMetaDictionary )
382 #define VLCStringToMeta( name ) [NSNumber numberWithInt: libvlc_meta_##name], VLCMetaInformation##name
383 stringToMetaDictionary =
384 [[NSDictionary dictionaryWithObjectsAndKeys:
385 VLCStringToMeta(Title),
386 VLCStringToMeta(Artist),
387 VLCStringToMeta(Genre),
388 VLCStringToMeta(Copyright),
389 VLCStringToMeta(Album),
390 VLCStringToMeta(TrackNumber),
391 VLCStringToMeta(Description),
392 VLCStringToMeta(Rating),
393 VLCStringToMeta(Date),
394 VLCStringToMeta(Setting),
395 VLCStringToMeta(URL),
396 VLCStringToMeta(Language),
397 VLCStringToMeta(NowPlaying),
398 VLCStringToMeta(Publisher),
399 VLCStringToMeta(ArtworkURL),
400 VLCStringToMeta(TrackID),
402 #undef VLCStringToMeta
404 NSNumber * number = [stringToMetaDictionary objectForKey:string];
405 return number ? [number intValue] : -1;
408 + (NSString *)metaTypeToString:(libvlc_meta_t)type
410 #define VLCMetaToString( name, type ) if (libvlc_meta_##name == type) return VLCMetaInformation##name;
411 VLCMetaToString(Title, type);
412 VLCMetaToString(Artist, type);
413 VLCMetaToString(Genre, type);
414 VLCMetaToString(Copyright, type);
415 VLCMetaToString(Album, type);
416 VLCMetaToString(TrackNumber, type);
417 VLCMetaToString(Description, type);
418 VLCMetaToString(Rating, type);
419 VLCMetaToString(Date, type);
420 VLCMetaToString(Setting, type);
421 VLCMetaToString(URL, type);
422 VLCMetaToString(Language, type);
423 VLCMetaToString(NowPlaying, type);
424 VLCMetaToString(Publisher, type);
425 VLCMetaToString(ArtworkURL, type);
426 VLCMetaToString(TrackID, type);
427 #undef VLCMetaToString
431 - (void)initInternalMediaDescriptor
433 libvlc_exception_t ex;
434 libvlc_exception_init( &ex );
436 char * p_url = libvlc_media_descriptor_get_mrl( p_md, &ex );
437 catch_exception( &ex );
439 url = [[NSURL URLWithString:[NSString stringWithUTF8String:p_url]] retain];
440 if( !url ) /* Attempt to interpret as a file path then */
441 url = [[NSURL fileURLWithPath:[NSString stringWithUTF8String:p_url]] retain];
444 libvlc_media_descriptor_set_user_data( p_md, (void*)self, &ex );
445 catch_exception( &ex );
447 libvlc_event_manager_t * p_em = libvlc_media_descriptor_event_manager( p_md, &ex );
448 libvlc_event_attach(p_em, libvlc_MediaDescriptorMetaChanged, HandleMediaMetaChanged, self, &ex);
449 // libvlc_event_attach(p_em, libvlc_MediaDescriptorDurationChanged, HandleMediaDurationChanged, self, &ex);
450 libvlc_event_attach(p_em, libvlc_MediaDescriptorStateChanged, HandleMediaStateChanged, self, &ex);
451 libvlc_event_attach(p_em, libvlc_MediaDescriptorSubItemAdded, HandleMediaSubItemAdded, self, &ex);
452 catch_exception( &ex );
454 libvlc_media_list_t * p_mlist = libvlc_media_descriptor_subitems( p_md, NULL );
460 subitems = [[VLCMediaList mediaListWithLibVLCMediaList:p_mlist] retain];
461 libvlc_media_list_release( p_mlist );
464 state = LibVLCStateToMediaState(libvlc_media_descriptor_get_state( p_md, NULL ));
465 /* Force VLCMetaInformationTitle, that will trigger preparsing
466 * And all the other meta will be added through the libvlc event system */
467 [self fetchMetaInformationFromLibVLCWithType: VLCMetaInformationTitle];
470 - (void)fetchMetaInformationFromLibVLCWithType:(NSString *)metaType
472 char * psz_value = libvlc_media_descriptor_get_meta( p_md, [VLCMedia stringToMetaType:metaType], NULL);
473 NSString * newValue = psz_value ? [NSString stringWithUTF8String: psz_value] : nil;
474 NSString * oldValue = [metaDictionary valueForKey:metaType];
477 if ( !(newValue && oldValue && [oldValue compare:newValue] == NSOrderedSame) )
479 if ([metaType isEqualToString:VLCMetaInformationArtworkURL])
481 [NSThread detachNewThreadSelector:@selector(fetchMetaInformationForArtWorkWithURL:)
483 withObject:newValue];
487 [metaDictionary setValue:newValue forKeyPath:metaType];
491 - (void)fetchMetaInformationForArtWorkWithURL:(NSString *)anURL
493 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
495 // Go ahead and load up the art work
496 NSURL * artUrl = [NSURL URLWithString:[anURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
497 NSImage * art = [[[NSImage alloc] initWithContentsOfURL:artUrl] autorelease];
499 // If anything was found, lets save it to the meta data dictionary
502 [self performSelectorOnMainThread:@selector(setArtwork:) withObject:art waitUntilDone:NO];
508 - (void)setArtwork:(NSImage *)art
510 [metaDictionary setObject:art forKey:@"artwork"];
513 - (void)metaChanged:(NSString *)metaType
515 [self fetchMetaInformationFromLibVLCWithType:metaType];
521 return; /* Nothing to do */
523 libvlc_media_list_t * p_mlist = libvlc_media_descriptor_subitems( p_md, NULL );
525 NSAssert( p_mlist, @"The mlist shouldn't be nil, we are receiving a subItemAdded");
527 [self willChangeValueForKey:@"subitems"];
528 subitems = [[VLCMediaList mediaListWithLibVLCMediaList:p_mlist] retain];
529 [self didChangeValueForKey:@"subitems"];
530 libvlc_media_list_release( p_mlist );
533 - (void)setStateAsNumber:(NSNumber *)newStateAsNumber
535 [self setState: [newStateAsNumber intValue]];
539 /******************************************************************************
540 * Implementation VLCMedia (VLCMediaPlayerBridging)
543 @implementation VLCMedia (VLCMediaPlayerBridging)
545 - (void)setLength:(VLCTime *)value
547 if (length && value && [length compare:value] == NSOrderedSame)
551 length = value ? [value retain] : nil;