]> git.sesse.net Git - vlc/blobdiff - projects/macosx/framework/Sources/VLCMedia.m
VLCKit: Import MobileVLCKit.
[vlc] / projects / macosx / framework / Sources / VLCMedia.m
index 0cb5819d65741f897418661d0c6990e6dcdcda17..5f6fec57881cf3337ec1829c4e689a672eb86cf7 100644 (file)
@@ -1,5 +1,5 @@
 /*****************************************************************************
- * VLCMedia.m: VLC.framework VLCMedia implementation
+ * VLCMedia.m: VLCKit.framework VLCMedia implementation
  *****************************************************************************
  * Copyright (C) 2007 Pierre d'Herbemont
  * Copyright (C) 2007 the VideoLAN team
@@ -73,10 +73,15 @@ NSString * VLCMediaMetaChanged              = @"VLCMediaMetaChanged";
 
 /* Operations */
 - (void)fetchMetaInformationFromLibVLCWithType:(NSString*)metaType;
+#if !TARGET_OS_IPHONE
 - (void)fetchMetaInformationForArtWorkWithURL:(NSString *)anURL;
 - (void)setArtwork:(NSImage *)art;
+#endif
+
+- (void)parseIfNeeded;
 
 /* Callback Methods */
+- (void)parsedChanged:(NSNumber *)isParsedAsNumber;
 - (void)metaChanged:(NSString *)metaType;
 - (void)subItemAdded;
 - (void)setStateAsNumber:(NSNumber *)newStateAsNumber;
@@ -104,8 +109,8 @@ static inline VLCMediaState LibVLCStateToMediaState( libvlc_state_t state )
  */
 static void HandleMediaMetaChanged(const libvlc_event_t * event, void * self)
 {
-    if( event->u.media_descriptor_meta_changed.meta_type == libvlc_meta_Publisher ||
-        event->u.media_descriptor_meta_changed.meta_type == libvlc_meta_NowPlaying )
+    if( event->u.media_meta_changed.meta_type == libvlc_meta_Publisher ||
+        event->u.media_meta_changed.meta_type == libvlc_meta_NowPlaying )
     {
         /* Skip those meta. We don't really care about them for now.
          * And they occure a lot */
@@ -114,30 +119,30 @@ static void HandleMediaMetaChanged(const libvlc_event_t * event, void * self)
     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
     [[VLCEventManager sharedManager] callOnMainThreadObject:self
                                                  withMethod:@selector(metaChanged:)
-                                       withArgumentAsObject:[VLCMedia metaTypeToString:event->u.media_descriptor_meta_changed.meta_type]];
-    [pool release];
+                                       withArgumentAsObject:[VLCMedia metaTypeToString:event->u.media_meta_changed.meta_type]];
+    [pool drain];
 }
 
-//static void HandleMediaDurationChanged(const libvlc_event_t * event, void * self)
-//{
-//    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
-//    
-//    [[VLCEventManager sharedManager] callOnMainThreadObject:self
-//                                                 withMethod:@selector(setLength:)
-//                                       withArgumentAsObject:[VLCTime timeWithNumber:
-//                                           [NSNumber numberWithLongLong:event->u.media_descriptor_duration_changed.new_duration]]];
-//    [pool release];
-//}
+static void HandleMediaDurationChanged(const libvlc_event_t * event, void * self)
+{
+    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+
+    [[VLCEventManager sharedManager] callOnMainThreadObject:self
+                                                 withMethod:@selector(setLength:)
+                                       withArgumentAsObject:[VLCTime timeWithNumber:
+                                           [NSNumber numberWithLongLong:event->u.media_duration_changed.new_duration]]];
+    [pool drain];
+}
 
 static void HandleMediaStateChanged(const libvlc_event_t * event, void * self)
 {
     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
-    
+
     [[VLCEventManager sharedManager] callOnMainThreadObject:self
                                                  withMethod:@selector(setStateAsNumber:)
                                        withArgumentAsObject:[NSNumber numberWithInt:
-                                            LibVLCStateToMediaState(event->u.media_descriptor_state_changed.new_state)]];
-    [pool release];
+                                            LibVLCStateToMediaState(event->u.media_state_changed.new_state)]];
+    [pool drain];
 }
 
 static void HandleMediaSubItemAdded(const libvlc_event_t * event, void * self)
@@ -146,9 +151,19 @@ static void HandleMediaSubItemAdded(const libvlc_event_t * event, void * self)
     [[VLCEventManager sharedManager] callOnMainThreadObject:self
                                                  withMethod:@selector(subItemAdded)
                                        withArgumentAsObject:nil];
+    [pool drain];
+}
+
+static void HandleMediaParsedChanged(const libvlc_event_t * event, void * self)
+{
+    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+    [[VLCEventManager sharedManager] callOnMainThreadObject:self
+                                                 withMethod:@selector(parsedChanged:)
+                                       withArgumentAsObject:[NSNumber numberWithBool:event->u.media_parsed_changed.new_status]];
     [pool release];
 }
 
+
 /******************************************************************************
  * Implementation
  */
@@ -168,26 +183,21 @@ static void HandleMediaSubItemAdded(const libvlc_event_t * event, void * self)
     return [[[VLCMedia alloc] initAsNodeWithName:aName] autorelease];
 }
 
-- (id)initWithURL:(NSURL *)anURL
+- (id)initWithPath:(NSString *)aPath
 {
-    return [self initWithPath:[anURL path]];
+    return [self initWithURL:[NSURL fileURLWithPath:aPath isDirectory:NO]];
 }
 
-- (id)initWithPath:(NSString *)aPath
-{        
+- (id)initWithURL:(NSURL *)anURL
+{
     if (self = [super init])
     {
-        libvlc_exception_t ex;
-        libvlc_exception_init(&ex);
-        
-        p_md = libvlc_media_descriptor_new([VLCLibrary sharedInstance],
-                                           [aPath UTF8String],
-                                           &ex);
-        catch_exception(&ex);
-        
+        p_md = libvlc_media_new_location([VLCLibrary sharedInstance],
+                                           [[anURL absoluteString] UTF8String]);
+
         delegate = nil;
         metaDictionary = [[NSMutableDictionary alloc] initWithCapacity:3];
-        
+
         // This value is set whenever the demuxer figures out what the length is.
         // TODO: Easy way to tell the length of the movie without having to instiate the demuxer.  Maybe cached info?
         length = nil;
@@ -198,60 +208,50 @@ static void HandleMediaSubItemAdded(const libvlc_event_t * event, void * self)
 }
 
 - (id)initAsNodeWithName:(NSString *)aName
-{        
+{
     if (self = [super init])
     {
-        libvlc_exception_t ex;
-        libvlc_exception_init(&ex);
-        
-        p_md = libvlc_media_descriptor_new_as_node([VLCLibrary sharedInstance],
-                                                   [aName UTF8String],
-                                                   &ex);
-        catch_exception(&ex);
+        p_md = libvlc_media_new_as_node([VLCLibrary sharedInstance],
+                                                   [aName UTF8String]);
 
         delegate = nil;
         metaDictionary = [[NSMutableDictionary alloc] initWithCapacity:3];
-        
+
         // This value is set whenever the demuxer figures out what the length is.
         // TODO: Easy way to tell the length of the movie without having to instiate the demuxer.  Maybe cached info?
         length = nil;
-        
+
         [self initInternalMediaDescriptor];
     }
     return self;
 }
 
-- (void)release
+- (void)setValue:(NSString *)value forMeta:(NSString *)meta
 {
-    @synchronized(self)
-    {
-        if([self retainCount] <= 1)
-        {
-            /* We must make sure we won't receive new event after an upcoming dealloc
-             * We also may receive a -retain in some event callback that may occcur
-             * Before libvlc_event_detach. So this can't happen in dealloc */
-            libvlc_event_manager_t * p_em = libvlc_media_descriptor_event_manager(p_md, NULL);
-            libvlc_event_detach(p_em, libvlc_MediaDescriptorMetaChanged,     HandleMediaMetaChanged,     self, NULL);
-//            libvlc_event_detach(p_em, libvlc_MediaDescriptorDurationChanged, HandleMediaDurationChanged, self, NULL);
-            libvlc_event_detach(p_em, libvlc_MediaDescriptorStateChanged,    HandleMediaStateChanged,    self, NULL);
-            libvlc_event_detach(p_em, libvlc_MediaDescriptorSubItemAdded,    HandleMediaSubItemAdded,    self, NULL);
-        }
-        [super release];
-    }
+    libvlc_meta_t metaName = [VLCMedia stringToMetaType:meta];
+    NSAssert(metaName >= 0, @"Invalid meta");
+    libvlc_media_set_meta(p_md, metaName, [value UTF8String]);
 }
 
 - (void)dealloc
 {
+    libvlc_event_manager_t * p_em = libvlc_media_event_manager(p_md);
+    libvlc_event_detach(p_em, libvlc_MediaMetaChanged,     HandleMediaMetaChanged,     self);
+    libvlc_event_detach(p_em, libvlc_MediaDurationChanged, HandleMediaDurationChanged, self);
+    libvlc_event_detach(p_em, libvlc_MediaStateChanged,    HandleMediaStateChanged,    self);
+    libvlc_event_detach(p_em, libvlc_MediaSubItemAdded,    HandleMediaSubItemAdded,    self);
+    libvlc_event_detach(p_em, libvlc_MediaParsedChanged,   HandleMediaParsedChanged,    self);
+    [[VLCEventManager sharedManager] cancelCallToObject:self];
+
     // Testing to see if the pointer exists is not required, if the pointer is null
     // then the release message is not sent to it.
     delegate = nil;
-    [self setLength:nil];
-
+    [length release];
     [url release];
     [subitems release];
     [metaDictionary release];
 
-    libvlc_media_descriptor_release( p_md );
+    libvlc_media_release( p_md );
 
     [super dealloc];
 }
@@ -259,7 +259,7 @@ static void HandleMediaSubItemAdded(const libvlc_event_t * event, void * self)
 - (NSString *)description
 {
     NSString * result = [metaDictionary objectForKey:VLCMetaInformationTitle];
-    return [NSString stringWithFormat:@"<%@ %p> %@", [self className], self, (result ? result : [url absoluteString])];
+    return [NSString stringWithFormat:@"<%@ %p> %@", [self class], self, (result ? result : [url absoluteString])];
 }
 
 - (NSComparisonResult)compare:(VLCMedia *)media
@@ -275,17 +275,18 @@ static void HandleMediaSubItemAdded(const libvlc_event_t * event, void * self)
 
 - (VLCTime *)length
 {
-    if (!length) 
+    if (!length)
     {
         // Try figuring out what the length is
-        long long duration = libvlc_media_descriptor_get_duration( p_md, NULL );
-        if (duration > -1) 
+        long long duration = libvlc_media_get_duration( p_md );
+        if (duration > -1)
         {
-            [self setLength:[VLCTime timeWithNumber:[NSNumber numberWithLongLong:duration]]];
+            length = [[VLCTime timeWithNumber:[NSNumber numberWithLongLong:duration]] retain];
             return [[length retain] autorelease];
-        } 
+        }
+        return [VLCTime nullTime];
     }
-    return [VLCTime nullTime];
+    return [[length retain] autorelease];
 }
 
 - (VLCTime *)lengthWaitUntilDate:(NSDate *)aDate
@@ -294,11 +295,15 @@ static void HandleMediaSubItemAdded(const libvlc_event_t * event, void * self)
 
     if (!length)
     {
-        while (!length && ![self isPreparsed] && [aDate timeIntervalSinceNow] > 0)
+        // Force parsing of this item.
+        [self parseIfNeeded];
+
+        // wait until we are preparsed
+        while (!length && !libvlc_media_is_parsed(p_md) && [aDate timeIntervalSinceNow] > 0)
         {
             usleep( thread_sleep );
         }
-        
+
         // So we're done waiting, but sometimes we trap the fact that the parsing
         // was done before the length gets assigned, so lets go ahead and assign
         // it ourselves.
@@ -309,11 +314,84 @@ static void HandleMediaSubItemAdded(const libvlc_event_t * event, void * self)
     return [[length retain] autorelease];
 }
 
-- (BOOL)isPreparsed
+- (BOOL)isParsed
 {
-    return libvlc_media_descriptor_is_preparsed( p_md, NULL );
+    return isParsed;
 }
 
+- (void)parse
+{
+    libvlc_media_parse_async(p_md);
+}
+
+NSString *VLCMediaTracksInformationCodec = @"codec"; // NSNumber
+NSString *VLCMediaTracksInformationId    = @"id";    // NSNumber
+NSString *VLCMediaTracksInformationType  = @"type";  // NSString
+
+NSString *VLCMediaTracksInformationTypeAudio    = @"audio";
+NSString *VLCMediaTracksInformationTypeVideo    = @"video";
+NSString *VLCMediaTracksInformationTypeText     = @"text";
+NSString *VLCMediaTracksInformationTypeUnknown  = @"unknown";
+
+NSString *VLCMediaTracksInformationCodecProfile  = @"profile"; // NSNumber
+NSString *VLCMediaTracksInformationCodecLevel    = @"level";   // NSNumber
+
+NSString *VLCMediaTracksInformationAudioChannelsNumber = @"channelsNumber"; // NSNumber
+NSString *VLCMediaTracksInformationAudioRate           = @"rate";           // NSNumber
+
+NSString *VLCMediaTracksInformationVideoHeight = @"height"; // NSNumber
+NSString *VLCMediaTracksInformationVideoWidth  = @"width";  // NSNumber
+
+- (NSArray *)tracksInformation
+{
+    // Trigger parsing if needed
+    [self parseIfNeeded];
+
+    libvlc_media_track_info_t *tracksInfo;
+    int count = libvlc_media_get_tracks_info(p_md, &tracksInfo);
+    NSMutableArray *array = [NSMutableArray array];
+    for (int i = 0; i < count; i++) {
+        NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                                           [NSNumber numberWithUnsignedInt:tracksInfo[i].i_codec], VLCMediaTracksInformationCodec,
+                                           [NSNumber numberWithInt:tracksInfo[i].i_id],            VLCMediaTracksInformationId,
+                                           [NSNumber numberWithInt:tracksInfo[i].i_profile],       VLCMediaTracksInformationCodecProfile,
+                                           [NSNumber numberWithInt:tracksInfo[i].i_level],         VLCMediaTracksInformationCodecLevel,
+                                           nil];
+
+        NSString *type;
+        switch (tracksInfo[i].i_type) {
+            case libvlc_track_audio:
+                type = VLCMediaTracksInformationTypeAudio;
+                NSNumber *level = [NSNumber numberWithUnsignedInt:tracksInfo[i].u.audio.i_channels];
+                NSNumber *rate =  [NSNumber numberWithUnsignedInt:tracksInfo[i].u.audio.i_rate];
+                [dictionary setObject:level forKey:VLCMediaTracksInformationAudioChannelsNumber];
+                [dictionary setObject:rate  forKey:VLCMediaTracksInformationAudioRate];
+                break;
+            case libvlc_track_video:
+                type = VLCMediaTracksInformationTypeVideo;
+                NSNumber *width =  [NSNumber numberWithUnsignedInt:tracksInfo[i].u.video.i_width];
+                NSNumber *height = [NSNumber numberWithUnsignedInt:tracksInfo[i].u.video.i_height];
+                [dictionary setObject:width  forKey:VLCMediaTracksInformationVideoWidth];
+                [dictionary setObject:height forKey:VLCMediaTracksInformationVideoHeight];
+                break;
+            case libvlc_track_text:
+                type = VLCMediaTracksInformationTypeText;
+                [dictionary setObject:VLCMediaTracksInformationTypeText forKey:VLCMediaTracksInformationType];
+                break;
+            case libvlc_track_unknown:
+            default:
+                type = VLCMediaTracksInformationTypeUnknown;
+                break;
+        }
+        [dictionary setValue:type forKey:VLCMediaTracksInformationType];
+
+        [array addObject:dictionary];
+    }
+    free(tracksInfo);
+    return array;
+}
+
+
 @synthesize url;
 @synthesize subitems;
 @synthesize metaDictionary;
@@ -335,31 +413,32 @@ static void HandleMediaSubItemAdded(const libvlc_event_t * event, void * self)
 {
     if (self = [super init])
     {
-        libvlc_exception_t ex;
-        libvlc_exception_init( &ex );
-                
-        libvlc_media_descriptor_retain( md );
+        libvlc_media_retain( md );
         p_md = md;
-        
+
         metaDictionary = [[NSMutableDictionary alloc] initWithCapacity:3];
         [self initInternalMediaDescriptor];
     }
     return self;
 }
 
-- (void *)libVLCMediaDescriptor 
+- (void *)libVLCMediaDescriptor
 {
     return p_md;
 }
 
 + (id)mediaWithMedia:(VLCMedia *)media andLibVLCOptions:(NSDictionary *)options
 {
-    libvlc_media_descriptor_t * p_md;
-    p_md = libvlc_media_descriptor_duplicate( [media libVLCMediaDescriptor] );
+    libvlc_media_t * p_md;
+    p_md = libvlc_media_duplicate( [media libVLCMediaDescriptor] );
+
     for( NSString * key in [options allKeys] )
     {
-        NSLog(@"Adding %@", [NSString stringWithFormat:@"--%@ %@", key, [options objectForKey:key]]);
-        libvlc_media_descriptor_add_option(p_md, [[NSString stringWithFormat:@"%@=#%@", key, [options objectForKey:key]] UTF8String], NULL);
+        if ( [options objectForKey:key] != [NSNull null] )
+            libvlc_media_add_option(p_md, [[NSString stringWithFormat:@"%@=%@", key, [options objectForKey:key]] UTF8String]);
+        else
+            libvlc_media_add_option(p_md, [[NSString stringWithFormat:@"%@", key] UTF8String]);
+
     }
     return [VLCMedia mediaWithLibVLCMediaDescriptor:p_md];
 }
@@ -428,28 +507,23 @@ static void HandleMediaSubItemAdded(const libvlc_event_t * event, void * self)
 
 - (void)initInternalMediaDescriptor
 {
-    libvlc_exception_t ex;
-    libvlc_exception_init( &ex );
-
-    char * p_url = libvlc_media_descriptor_get_mrl( p_md, &ex );
-    catch_exception( &ex );
+    char * p_url = libvlc_media_get_mrl( p_md );
 
     url = [[NSURL URLWithString:[NSString stringWithUTF8String:p_url]] retain];
     if( !url ) /* Attempt to interpret as a file path then */
         url = [[NSURL fileURLWithPath:[NSString stringWithUTF8String:p_url]] retain];
     free( p_url );
 
-    libvlc_media_descriptor_set_user_data( p_md, (void*)self, &ex );
-    catch_exception( &ex );
+    libvlc_media_set_user_data( p_md, (void*)self );
+
+    libvlc_event_manager_t * p_em = libvlc_media_event_manager( p_md );
+    libvlc_event_attach(p_em, libvlc_MediaMetaChanged,     HandleMediaMetaChanged,     self);
+    libvlc_event_attach(p_em, libvlc_MediaDurationChanged, HandleMediaDurationChanged, self);
+    libvlc_event_attach(p_em, libvlc_MediaStateChanged,    HandleMediaStateChanged,    self);
+    libvlc_event_attach(p_em, libvlc_MediaSubItemAdded,    HandleMediaSubItemAdded,    self);
+    libvlc_event_attach(p_em, libvlc_MediaParsedChanged,   HandleMediaParsedChanged,   self);
 
-    libvlc_event_manager_t * p_em = libvlc_media_descriptor_event_manager( p_md, &ex );
-    libvlc_event_attach(p_em, libvlc_MediaDescriptorMetaChanged,     HandleMediaMetaChanged,     self, &ex);
-//    libvlc_event_attach(p_em, libvlc_MediaDescriptorDurationChanged, HandleMediaDurationChanged, self, &ex);
-    libvlc_event_attach(p_em, libvlc_MediaDescriptorStateChanged,    HandleMediaStateChanged,    self, &ex);
-    libvlc_event_attach(p_em, libvlc_MediaDescriptorSubItemAdded,    HandleMediaSubItemAdded,    self, &ex);
-    catch_exception( &ex );
-    
-    libvlc_media_list_t * p_mlist = libvlc_media_descriptor_subitems( p_md, NULL );
+    libvlc_media_list_t * p_mlist = libvlc_media_subitems( p_md );
 
     if (!p_mlist)
         subitems = nil;
@@ -459,54 +533,69 @@ static void HandleMediaSubItemAdded(const libvlc_event_t * event, void * self)
         libvlc_media_list_release( p_mlist );
     }
 
-    state = LibVLCStateToMediaState(libvlc_media_descriptor_get_state( p_md, NULL ));
-    /* Force VLCMetaInformationTitle, that will trigger preparsing
-     * And all the other meta will be added through the libvlc event system */
-    [self fetchMetaInformationFromLibVLCWithType: VLCMetaInformationTitle];
+    isParsed = libvlc_media_is_parsed(p_md);
+    state = LibVLCStateToMediaState(libvlc_media_get_state( p_md ));
 }
 
 - (void)fetchMetaInformationFromLibVLCWithType:(NSString *)metaType
 {
-    char * psz_value = libvlc_media_descriptor_get_meta( p_md, [VLCMedia stringToMetaType:metaType], NULL);
+    char * psz_value = libvlc_media_get_meta( p_md, [VLCMedia stringToMetaType:metaType] );
     NSString * newValue = psz_value ? [NSString stringWithUTF8String: psz_value] : nil;
     NSString * oldValue = [metaDictionary valueForKey:metaType];
     free(psz_value);
 
-    if ( !(newValue && oldValue && [oldValue compare:newValue] == NSOrderedSame) )
+    if ( newValue != oldValue && !(oldValue && newValue && [oldValue compare:newValue] == NSOrderedSame) )
     {
-        if ([metaType isEqualToString:VLCMetaInformationArtworkURL])
+        // Only fetch the art if needed. (ie, create the NSImage, if it was requested before)
+        if (isArtFetched && [metaType isEqualToString:VLCMetaInformationArtworkURL])
         {
-            [NSThread detachNewThreadSelector:@selector(fetchMetaInformationForArtWorkWithURL:) 
+            [NSThread detachNewThreadSelector:@selector(fetchMetaInformationForArtWorkWithURL:)
                                          toTarget:self
                                        withObject:newValue];
-            return;
         }
 
         [metaDictionary setValue:newValue forKeyPath:metaType];
     }
 }
 
+#if !TARGET_OS_IPHONE
 - (void)fetchMetaInformationForArtWorkWithURL:(NSString *)anURL
 {
     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
-    
-    // Go ahead and load up the art work
-    NSURL * artUrl = [NSURL URLWithString:[anURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
-    NSImage * art  = [[[NSImage alloc] initWithContentsOfURL:artUrl] autorelease]; 
-        
-    // If anything was found, lets save it to the meta data dictionary
-    if (art)
+    NSImage * art = nil;
+
+    if( anURL )
     {
-        [self performSelectorOnMainThread:@selector(setArtwork:) withObject:art waitUntilDone:NO];
+        // Go ahead and load up the art work
+        NSURL * artUrl = [NSURL URLWithString:[anURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+        // Don't attempt to fetch artwork from remote. Core will do that alone
+        if ([artUrl isFileURL])
+            art  = [[[NSImage alloc] initWithContentsOfURL:artUrl] autorelease];
     }
 
+    // If anything was found, lets save it to the meta data dictionary
+    [self performSelectorOnMainThread:@selector(setArtwork:) withObject:art waitUntilDone:NO];
+
     [pool release];
 }
 
 - (void)setArtwork:(NSImage *)art
 {
+    if (!art)
+    {
+        [metaDictionary removeObjectForKey:@"artwork"];
+        return;
+    }
+
     [metaDictionary setObject:art forKey:@"artwork"];
 }
+#endif
+
+- (void)parseIfNeeded
+{
+    if (![self isParsed])
+        [self parse];
+}
 
 - (void)metaChanged:(NSString *)metaType
 {
@@ -518,7 +607,7 @@ static void HandleMediaSubItemAdded(const libvlc_event_t * event, void * self)
     if( subitems )
         return; /* Nothing to do */
 
-    libvlc_media_list_t * p_mlist = libvlc_media_descriptor_subitems( p_md, NULL );
+    libvlc_media_list_t * p_mlist = libvlc_media_subitems( p_md );
 
     NSAssert( p_mlist, @"The mlist shouldn't be nil, we are receiving a subItemAdded");
 
@@ -528,10 +617,74 @@ static void HandleMediaSubItemAdded(const libvlc_event_t * event, void * self)
     libvlc_media_list_release( p_mlist );
 }
 
+- (void)parsedChanged:(NSNumber *)isParsedAsNumber
+{
+    [self willChangeValueForKey:@"parsed"];
+    isParsed = [isParsedAsNumber boolValue];
+    [self didChangeValueForKey:@"parsed"];
+
+    // FIXME: Probably don't even call this if there is no delegate.
+    if (!delegate || !isParsed)
+        return;
+
+    if ([delegate respondsToSelector:@selector(mediaDidFinishParsing:)]) {
+        [delegate mediaDidFinishParsing:self];
+    }
+}
+
 - (void)setStateAsNumber:(NSNumber *)newStateAsNumber
 {
     [self setState: [newStateAsNumber intValue]];
 }
+
+#if TARGET_OS_IPHONE
+- (NSDictionary *)metaDictionary
+{
+    if (!areOthersMetaFetched) {
+        areOthersMetaFetched = YES;
+        /* Force VLCMetaInformationTitle, that will trigger preparsing
+         * And all the other meta will be added through the libvlc event system */
+        [self fetchMetaInformationFromLibVLCWithType: VLCMetaInformationTitle];
+
+    }
+    if (!isArtURLFetched)
+    {
+        isArtURLFetched = YES;
+        /* Force isArtURLFetched, that will trigger artwork download eventually
+         * And all the other meta will be added through the libvlc event system */
+        [self fetchMetaInformationFromLibVLCWithType: VLCMetaInformationArtworkURL];
+    }
+    return metaDictionary;
+}
+
+#else
+
+- (id)valueForKeyPath:(NSString *)keyPath
+{
+    if( !isArtFetched && [keyPath isEqualToString:@"metaDictionary.artwork"])
+    {
+        isArtFetched = YES;
+        /* Force the retrieval of the artwork now that someone asked for it */
+        [self fetchMetaInformationFromLibVLCWithType: VLCMetaInformationArtworkURL];
+    }
+    else if( !areOthersMetaFetched && [keyPath hasPrefix:@"metaDictionary."])
+    {
+        areOthersMetaFetched = YES;
+        /* Force VLCMetaInformationTitle, that will trigger preparsing
+         * And all the other meta will be added through the libvlc event system */
+        [self fetchMetaInformationFromLibVLCWithType: VLCMetaInformationTitle];
+
+    }
+    else if( !isArtURLFetched && [keyPath hasPrefix:@"metaDictionary.artworkURL"])
+    {
+        isArtURLFetched = YES;
+        /* Force isArtURLFetched, that will trigger artwork download eventually
+         * And all the other meta will be added through the libvlc event system */
+        [self fetchMetaInformationFromLibVLCWithType: VLCMetaInformationArtworkURL];
+    }
+    return [super valueForKeyPath:keyPath];
+}
+#endif
 @end
 
 /******************************************************************************
@@ -544,8 +697,8 @@ static void HandleMediaSubItemAdded(const libvlc_event_t * event, void * self)
 {
     if (length && value && [length compare:value] == NSOrderedSame)
         return;
-        
-    [length release];       
+
+    [length release];
     length = value ? [value retain] : nil;
 }