/* 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;
[[VLCEventManager sharedManager] callOnMainThreadObject:self
withMethod:@selector(metaChanged:)
withArgumentAsObject:[VLCMedia metaTypeToString:event->u.media_meta_changed.meta_type]];
- [pool release];
+ [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_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_state_changed.new_state)]];
- [pool release];
+ [pool drain];
}
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
*/
}
- (id)initWithURL:(NSURL *)anURL
-{
+{
if (self = [super init])
{
- libvlc_exception_t ex;
- libvlc_exception_init(&ex);
-
- p_md = libvlc_media_new([VLCLibrary sharedInstance],
- [[anURL absoluteString] 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;
}
- (id)initAsNodeWithName:(NSString *)aName
-{
+{
if (self = [super init])
{
- libvlc_exception_t ex;
- libvlc_exception_init(&ex);
-
p_md = libvlc_media_new_as_node([VLCLibrary sharedInstance],
- [aName UTF8String],
- &ex);
- catch_exception(&ex);
+ [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_event_manager(p_md);
- libvlc_event_detach(p_em, libvlc_MediaMetaChanged, HandleMediaMetaChanged, self, NULL);
-// libvlc_event_detach(p_em, libvlc_MediaDurationChanged, HandleMediaDurationChanged, self, NULL);
- libvlc_event_detach(p_em, libvlc_MediaStateChanged, HandleMediaStateChanged, self, NULL);
- libvlc_event_detach(p_em, libvlc_MediaSubItemAdded, 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];
- (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
- (VLCTime *)length
{
- if (!length)
+ if (!length)
{
// Try figuring out what the length is
- long long duration = libvlc_media_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];
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.
return [[length retain] autorelease];
}
-- (BOOL)isPreparsed
+- (BOOL)isParsed
+{
+ return isParsed;
+}
+
+- (void)parse
{
- return libvlc_media_is_preparsed( p_md );
+ 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;
{
libvlc_media_retain( md );
p_md = md;
-
+
metaDictionary = [[NSMutableDictionary alloc] initWithCapacity:3];
[self initInternalMediaDescriptor];
}
return self;
}
-- (void *)libVLCMediaDescriptor
+- (void *)libVLCMediaDescriptor
{
return p_md;
}
for( NSString * key in [options allKeys] )
{
- libvlc_media_add_option(p_md, [[NSString stringWithFormat:@"%@=#%@", key, [options objectForKey:key]] UTF8String]);
+ 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];
}
- (void)initInternalMediaDescriptor
{
- libvlc_exception_t ex;
- libvlc_exception_init( &ex );
-
char * p_url = libvlc_media_get_mrl( p_md );
url = [[NSURL URLWithString:[NSString stringWithUTF8String:p_url]] retain];
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, &ex);
-// libvlc_event_attach(p_em, libvlc_MediaDurationChanged, HandleMediaDurationChanged, self, &ex);
- libvlc_event_attach(p_em, libvlc_MediaStateChanged, HandleMediaStateChanged, self, &ex);
- libvlc_event_attach(p_em, libvlc_MediaSubItemAdded, HandleMediaSubItemAdded, self, &ex);
-
+ 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_media_list_t * p_mlist = libvlc_media_subitems( p_md );
if (!p_mlist)
libvlc_media_list_release( p_mlist );
}
+ isParsed = libvlc_media_is_parsed(p_md);
state = LibVLCStateToMediaState(libvlc_media_get_state( p_md ));
}
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]];
-
// Don't attempt to fetch artwork from remote. Core will do that alone
if ([artUrl isFileURL])
- art = [[[NSImage alloc] initWithContentsOfURL:artUrl] autorelease];
+ art = [[[NSImage alloc] initWithContentsOfURL:artUrl] autorelease];
}
// If anything was found, lets save it to the meta data dictionary
[metaDictionary setObject:art forKey:@"artwork"];
}
+#endif
+
+- (void)parseIfNeeded
+{
+ if (![self isParsed])
+ [self parse];
+}
- (void)metaChanged:(NSString *)metaType
{
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"])
/* 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
/******************************************************************************
{
if (length && value && [length compare:value] == NSOrderedSame)
return;
-
- [length release];
+
+ [length release];
length = value ? [value retain] : nil;
}