VLCMedia.m 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. /*****************************************************************************
  2. * VLCMedia.m: VLC.framework VLCMedia 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. *
  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.
  14. *
  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.
  19. *
  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. *****************************************************************************/
  24. #import "VLCMedia.h"
  25. #import "VLCMediaList.h"
  26. #import "VLCEventManager.h"
  27. #import "VLCLibrary.h"
  28. #import "VLCLibVLCBridging.h"
  29. #include <vlc/libvlc.h>
  30. /* Meta Dictionary Keys */
  31. NSString *VLCMetaInformationTitle = @"title";
  32. NSString *VLCMetaInformationArtist = @"artist";
  33. NSString *VLCMetaInformationGenre = @"genre";
  34. NSString *VLCMetaInformationCopyright = @"copyright";
  35. NSString *VLCMetaInformationAlbum = @"album";
  36. NSString *VLCMetaInformationTrackNumber = @"trackNumber";
  37. NSString *VLCMetaInformationDescription = @"description";
  38. NSString *VLCMetaInformationRating = @"rating";
  39. NSString *VLCMetaInformationDate = @"date";
  40. NSString *VLCMetaInformationSetting = @"setting";
  41. NSString *VLCMetaInformationURL = @"url";
  42. NSString *VLCMetaInformationLanguage = @"language";
  43. NSString *VLCMetaInformationNowPlaying = @"nowPlaying";
  44. NSString *VLCMetaInformationPublisher = @"publisher";
  45. NSString *VLCMetaInformationEncodedBy = @"encodedBy";
  46. NSString *VLCMetaInformationArtworkURL = @"artworkURL";
  47. NSString *VLCMetaInformationArtwork = @"artwork";
  48. NSString *VLCMetaInformationTrackID = @"trackID";
  49. /* Notification Messages */
  50. NSString *VLCMediaMetaChanged = @"VLCMediaMetaChanged";
  51. /* libvlc event callback */
  52. static void HandleMediaMetaChanged(const libvlc_event_t *event, void *self)
  53. {
  54. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  55. [[VLCEventManager sharedManager] callOnMainThreadObject:self
  56. withMethod:@selector(metaChanged:)
  57. withArgumentAsObject:[NSNumber numberWithInt:(int)event->u.media_descriptor_meta_changed.meta_type]];
  58. [pool release];
  59. }
  60. static void HandleMediaDurationChanged(const libvlc_event_t *event, void *self)
  61. {
  62. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  63. //[[VLCEventManager sharedManager] callOnMainThreadObject:self
  64. // withMethod:@selector(setLength:)
  65. // withArgumentAsObject:[VLCTime timeWithNumber:
  66. // [NSNumber numberWithLongLong:event->u.media_descriptor_duration_changed.new_duration]]];
  67. [pool release];
  68. }
  69. // TODO: Documentation
  70. @interface VLCMedia (Private)
  71. /* Statics */
  72. + (libvlc_meta_t)stringToMetaType:(NSString *)string;
  73. + (NSString *)metaTypeToString:(libvlc_meta_t)type;
  74. /* Initializers */
  75. - (void)initInternalMediaDescriptor;
  76. /* Operations */
  77. - (BOOL)setMetaValue:(char *)value forKey:(NSString *)key;
  78. - (void)fetchMetaInformation;
  79. - (void)fetchMetaInformationForArtWorkWithURL:(NSString *)anURL;
  80. - (void)notifyChangeForKey:(NSString *)key withOldValue:(id)oldValue;
  81. /* Callback Methods */
  82. - (void)metaChanged:(NSNumber *)metaType;
  83. @end
  84. @implementation VLCMedia
  85. + (id)mediaWithPath:(NSString *)aPath;
  86. {
  87. return [[[VLCMedia alloc] initWithPath:(id)aPath] autorelease];
  88. }
  89. + (id)mediaWithURL:(NSURL *)aURL;
  90. {
  91. return [[[VLCMedia alloc] initWithPath:(id)[aURL path] autorelease];
  92. }
  93. - (id)initWithPath:(NSString *)aPath
  94. {
  95. if (self = [super init])
  96. {
  97. libvlc_exception_t ex;
  98. libvlc_exception_init(&ex);
  99. p_md = libvlc_media_descriptor_new([VLCLibrary sharedInstance],
  100. [aURL cString],
  101. &ex);
  102. quit_on_exception(&ex);
  103. url = [aURL copy];
  104. delegate = nil;
  105. metaDictionary = [[NSMutableDictionary alloc] initWithCapacity:3];
  106. // This value is set whenever the demuxer figures out what the length is.
  107. // TODO: Easy way to tell the length of the movie without having to instiate the demuxer. Maybe cached info?
  108. length = nil;
  109. [self initInternalMediaDescriptor];
  110. }
  111. return self;
  112. }
  113. - (void)release
  114. {
  115. @synchronized(self)
  116. {
  117. if([self retainCount] <= 1)
  118. {
  119. /* We must make sure we won't receive new event after an upcoming dealloc
  120. * We also may receive a -retain in some event callback that may occcur
  121. * Before libvlc_event_detach. So this can't happen in dealloc */
  122. libvlc_event_manager_t *p_em = libvlc_media_descriptor_event_manager(p_md, NULL);
  123. libvlc_event_detach(p_em, libvlc_MediaDescriptorMetaChanged, HandleMediaMetaChanged, self, NULL);
  124. libvlc_event_detach(p_em, libvlc_MediaDescriptorDurationChanged, HandleMediaDurationChanged, self, NULL);
  125. }
  126. [super release];
  127. }
  128. }
  129. - (void)dealloc
  130. {
  131. // Testing to see if the pointer exists is not required, if the pointer is null
  132. // then the release message is not sent to it.
  133. [self setDelegate:nil];
  134. [self setLength:nil];
  135. [url release];
  136. [subitems release];
  137. [metaDictionary release];
  138. libvlc_media_descriptor_release( p_md );
  139. [super dealloc];
  140. }
  141. - (NSString *)description
  142. {
  143. NSString *result = nil;
  144. if (metaDictionary)
  145. result = [metaDictionary objectForKey:VLCMetaInformationTitle];
  146. return (result ? result : url);
  147. }
  148. - (NSComparisonResult)compare:(VLCMedia *)media
  149. {
  150. if (self == media)
  151. return NSOrderedSame;
  152. else if (!media)
  153. return NSOrderedDescending;
  154. else
  155. return [[self url] compare:[media url]];
  156. }
  157. - (NSString *)url
  158. {
  159. return [[url copy] autorelease];
  160. }
  161. - (VLCMediaList *)subitems
  162. {
  163. return subitems;
  164. }
  165. - (VLCTime *)length
  166. {
  167. if (!length)
  168. {
  169. // Try figuring out what the length is
  170. long long duration = libvlc_media_descriptor_get_duration( p_md, NULL );
  171. if (duration > -1)
  172. {
  173. [self setLength:[VLCTime timeWithNumber:[NSNumber numberWithLongLong:duration]]];
  174. return [[length retain] autorelease];
  175. }
  176. }
  177. return [VLCTime nullTime];
  178. }
  179. - (VLCTime *)lengthWaitUntilDate:(NSDate *)aDate
  180. {
  181. #define CLOCK_FREQ 1000000
  182. #define THREAD_SLEEP ((long long)(0.010*CLOCK_FREQ))
  183. if (![url hasPrefix:@"file://"])
  184. return [self length];
  185. else if (!length)
  186. {
  187. while (!length && ![self isPreparsed] && [aDate timeIntervalSinceNow] > 0)
  188. {
  189. usleep( THREAD_SLEEP );
  190. }
  191. // So we're done waiting, but sometimes we trap the fact that the parsing
  192. // was done before the length gets assigned, so lets go ahead and assign
  193. // it ourselves.
  194. if (!length)
  195. return [self length];
  196. }
  197. #undef CLOCK_FREQ
  198. #undef THREAD_SLEEP
  199. return [[length retain] autorelease];
  200. }
  201. - (BOOL)isPreparsed
  202. {
  203. return libvlc_media_descriptor_is_preparsed( p_md, NULL );
  204. }
  205. - (NSDictionary *)metaDictionary
  206. {
  207. return metaDictionary;
  208. }
  209. - (void)setDelegate:(id)value
  210. {
  211. delegate = value;
  212. }
  213. - (id)delegate
  214. {
  215. return delegate;
  216. }
  217. @end
  218. @implementation VLCMedia (LibVLCBridging)
  219. + (id)mediaWithLibVLCMediaDescriptor:(void *)md
  220. {
  221. return [[[VLCMedia alloc] initWithLibVLCMediaDescriptor:md] autorelease];
  222. }
  223. - (id)initWithLibVLCMediaDescriptor:(void *)md
  224. {
  225. if (self = [super init])
  226. {
  227. libvlc_exception_t ex;
  228. libvlc_exception_init( &ex );
  229. char * p_url;
  230. p_url = libvlc_media_descriptor_get_mrl( md, &ex );
  231. quit_on_exception( &ex );
  232. url = [[NSString stringWithCString:p_url] retain];
  233. libvlc_media_descriptor_retain( md );
  234. p_md = md;
  235. [self initInternalMediaDescriptor];
  236. }
  237. return self;
  238. }
  239. - (void *)libVLCMediaDescriptor
  240. {
  241. return p_md;
  242. }
  243. @end
  244. @implementation VLCMedia (Private)
  245. + (libvlc_meta_t)stringToMetaType:(NSString *)string
  246. {
  247. #define VLCStringToMeta( name, string ) if ([VLCMetaInformation##name compare:string] == NSOrderedSame) return libvlc_meta_##name;
  248. VLCStringToMeta(Title, string);
  249. VLCStringToMeta(Artist, string);
  250. VLCStringToMeta(Genre, string);
  251. VLCStringToMeta(Copyright, string);
  252. VLCStringToMeta(Album, string);
  253. VLCStringToMeta(TrackNumber, string);
  254. VLCStringToMeta(Description, string);
  255. VLCStringToMeta(Rating, string);
  256. VLCStringToMeta(Date, string);
  257. VLCStringToMeta(Setting, string);
  258. VLCStringToMeta(URL, string);
  259. VLCStringToMeta(Language, string);
  260. VLCStringToMeta(NowPlaying, string);
  261. VLCStringToMeta(Publisher, string);
  262. VLCStringToMeta(ArtworkURL, string);
  263. VLCStringToMeta(TrackID, string);
  264. #undef VLCStringToMeta
  265. return -1;
  266. }
  267. + (NSString *)metaTypeToString:(libvlc_meta_t)type
  268. {
  269. #define VLCMetaToString( name, type ) if (libvlc_meta_##name == type) return VLCMetaInformation##name;
  270. VLCMetaToString(Title, type);
  271. VLCMetaToString(Artist, type);
  272. VLCMetaToString(Genre, type);
  273. VLCMetaToString(Copyright, type);
  274. VLCMetaToString(Album, type);
  275. VLCMetaToString(TrackNumber, type);
  276. VLCMetaToString(Description, type);
  277. VLCMetaToString(Rating, type);
  278. VLCMetaToString(Date, type);
  279. VLCMetaToString(Setting, type);
  280. VLCMetaToString(URL, type);
  281. VLCMetaToString(Language, type);
  282. VLCMetaToString(NowPlaying, type);
  283. VLCMetaToString(Publisher, type);
  284. VLCMetaToString(ArtworkURL, type);
  285. VLCMetaToString(TrackID, type);
  286. #undef VLCMetaToString
  287. return nil;
  288. }
  289. - (void)initInternalMediaDescriptor
  290. {
  291. libvlc_exception_t ex;
  292. libvlc_exception_init( &ex );
  293. libvlc_media_descriptor_set_user_data( p_md, (void*)self, &ex );
  294. quit_on_exception( &ex );
  295. libvlc_event_manager_t *p_em = libvlc_media_descriptor_event_manager( p_md, &ex );
  296. libvlc_event_attach(p_em, libvlc_MediaDescriptorMetaChanged, HandleMediaMetaChanged, self, &ex);
  297. libvlc_event_attach(p_em, libvlc_MediaDescriptorDurationChanged, HandleMediaDurationChanged, self, &ex);
  298. quit_on_exception( &ex );
  299. libvlc_media_list_t *p_mlist = libvlc_media_descriptor_subitems( p_md, NULL );
  300. if (!p_mlist)
  301. subitems = nil;
  302. else
  303. {
  304. [subitems release];
  305. subitems = [[VLCMediaList mediaListWithLibVLCMediaList:p_mlist] retain];
  306. libvlc_media_list_release( p_mlist );
  307. }
  308. [self fetchMetaInformation];
  309. }
  310. - (BOOL)setMetaValue:(char *)value forKey:(NSString *)key
  311. {
  312. BOOL result = NO;
  313. NSString *oldValue = [metaDictionary valueForKey:key];
  314. if ((!oldValue && value) || (oldValue && !value) || (oldValue && value && [oldValue compare:[NSString stringWithCString:value]] != NSOrderedSame))
  315. {
  316. if (!metaDictionary)
  317. metaDictionary = [[NSMutableDictionary alloc] initWithCapacity:3];
  318. if (value)
  319. [metaDictionary setValue:[NSString stringWithCString:value] forKeyPath:key];
  320. else
  321. [metaDictionary setValue:nil forKeyPath:key];
  322. if ([key compare:VLCMetaInformationArtworkURL] == NSOrderedSame)
  323. {
  324. if ([metaDictionary valueForKey:VLCMetaInformationArtworkURL])
  325. {
  326. // Initialize a new thread
  327. [NSThread detachNewThreadSelector:@selector(fetchMetaInformationForArtWorkWithURL:)
  328. toTarget:self
  329. withObject:[metaDictionary valueForKey:VLCMetaInformationArtworkURL]];
  330. }
  331. }
  332. result = YES;
  333. }
  334. free( value );
  335. return result;
  336. }
  337. - (void)fetchMetaInformation
  338. {
  339. // TODO: Only fetch meta data that has been requested. Just don't fetch
  340. // it, just because.
  341. [self setMetaValue:libvlc_media_descriptor_get_meta( p_md, libvlc_meta_Title, NULL ) forKey:[VLCMedia metaTypeToString:libvlc_meta_Title]];
  342. [self setMetaValue:libvlc_media_descriptor_get_meta( p_md, libvlc_meta_Artist, NULL ) forKey:[VLCMedia metaTypeToString:libvlc_meta_Artist]];
  343. [self setMetaValue:libvlc_media_descriptor_get_meta( p_md, libvlc_meta_ArtworkURL, NULL ) forKey:[VLCMedia metaTypeToString:libvlc_meta_ArtworkURL]];
  344. }
  345. - (void)fetchMetaInformationForArtWorkWithURL:(NSString *)anURL
  346. {
  347. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  348. @try
  349. {
  350. // Go ahead and load up the art work
  351. NSURL *artUrl = [NSURL URLWithString:[anURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
  352. NSImage *art = [[[NSImage alloc] initWithContentsOfURL:artUrl] autorelease];
  353. // If anything was found, lets save it to the meta data dictionary
  354. if (art)
  355. {
  356. @synchronized(metaDictionary)
  357. {
  358. [metaDictionary setObject:art forKey:VLCMetaInformationArtwork];
  359. }
  360. // Let the world know that there is new art work available
  361. [self notifyChangeForKey:VLCMetaInformationArtwork withOldValue:nil];
  362. }
  363. }
  364. @finally
  365. {
  366. [pool release];
  367. }
  368. }
  369. - (void)notifyChangeForKey:(NSString *)key withOldValue:(id)oldValue
  370. {
  371. // Send out a formal notification
  372. [[NSNotificationCenter defaultCenter] postNotificationName:VLCMediaMetaChanged
  373. object:self
  374. userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
  375. key, @"key",
  376. oldValue, @"oldValue",
  377. nil]];
  378. // Now notify the delegate
  379. if (delegate && [delegate respondsToSelector:@selector(media:metaValueChangedFrom:forKey:)])
  380. [delegate media:self metaValueChangedFrom:oldValue forKey:key];
  381. }
  382. - (void)metaChanged:(NSNumber *)metaType
  383. {
  384. // TODO: Only retrieve the meta that was changed
  385. // Can we figure out what piece was changed instead of retreiving everything?
  386. NSString *key = [VLCMedia metaTypeToString:[metaType intValue]];
  387. id oldValue = (metaDictionary ? [metaDictionary valueForKey:key] : nil);
  388. // Update the meta data
  389. if ([self setMetaValue:libvlc_media_descriptor_get_meta(p_md, [metaType intValue], NULL) forKey:key])
  390. // There was actually a change, send out the notifications
  391. [self notifyChangeForKey:key withOldValue:oldValue];
  392. }
  393. @end
  394. @implementation VLCMedia (VLCMediaPlayerBridging)
  395. - (void)setLength:(VLCTime *)value
  396. {
  397. if (length != value)
  398. {
  399. if (length && value && [length compare:value] == NSOrderedSame)
  400. return;
  401. [self willChangeValueForKey:@"length"];
  402. if (length) {
  403. [length release];
  404. length = nil;
  405. }
  406. if (value)
  407. length = [value retain];
  408. [self didChangeValueForKey:@"length"];
  409. }
  410. }
  411. @end