From 0381d38320edc33cd7bff46fa4adcb6236f5f7fa Mon Sep 17 00:00:00 2001 From: Pierre d'Herbemont Date: Sat, 25 Sep 2010 20:56:33 +0200 Subject: [PATCH] VLCKit: Protect against file that takes forever to thumbnail. --- .../Headers/Public/VLCMediaThumbnailer.h | 4 ++ .../framework/Sources/VLCMediaThumbnailer.m | 62 ++++++++++++++++--- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/projects/macosx/framework/Headers/Public/VLCMediaThumbnailer.h b/projects/macosx/framework/Headers/Public/VLCMediaThumbnailer.h index e27c344989..a18b4fcdca 100644 --- a/projects/macosx/framework/Headers/Public/VLCMediaThumbnailer.h +++ b/projects/macosx/framework/Headers/Public/VLCMediaThumbnailer.h @@ -21,9 +21,12 @@ CGImageRef _thumbnail; void *_data; NSTimer *_parsingTimeoutTimer; + NSTimer *_thumbnailingTimeoutTimer; CGFloat _thumbnailHeight,_thumbnailWidth; CGFloat _effectiveThumbnailHeight,_effectiveThumbnailWidth; + int _numberOfReceivedFrames; + BOOL _shouldRejectFrames; } + (VLCMediaThumbnailer *)thumbnailerWithMedia:(VLCMedia *)media andDelegate:(id)delegate; @@ -52,5 +55,6 @@ @protocol VLCMediaThumbnailerDelegate @required +- (void)mediaThumbnailerDidTimeOut:(VLCMediaThumbnailer *)mediaThumbnailer; - (void)mediaThumbnailer:(VLCMediaThumbnailer *)mediaThumbnailer didFinishThumbnail:(CGImageRef)thumbnail; @end diff --git a/projects/macosx/framework/Sources/VLCMediaThumbnailer.m b/projects/macosx/framework/Sources/VLCMediaThumbnailer.m index 71ad3d1cb0..133dfc9b5b 100644 --- a/projects/macosx/framework/Sources/VLCMediaThumbnailer.m +++ b/projects/macosx/framework/Sources/VLCMediaThumbnailer.m @@ -18,6 +18,7 @@ - (void)fetchThumbnail; - (void)startFetchingThumbnail; @property (readonly, assign) void *dataPointer; +@property (readonly, assign) BOOL shouldRejectFrames; @end static void *lock(void *opaque, void **pixels) @@ -42,7 +43,7 @@ void unlock(void *opaque, void *picture, void *const *p_pixels) // We may already have a thumbnail if we are receiving picture after the first one. // Just ignore. - if ([thumbnailer thumbnail]) + if ([thumbnailer thumbnail] || [thumbnailer shouldRejectFrames]) return; [thumbnailer performSelectorOnMainThread:@selector(didFetchThumbnail) withObject:nil waitUntilDone:YES]; @@ -59,6 +60,7 @@ void display(void *opaque, void *picture) @synthesize dataPointer=_data; @synthesize thumbnailWidth=_thumbnailWidth; @synthesize thumbnailHeight=_thumbnailHeight; +@synthesize shouldRejectFrames=_shouldRejectFrames; + (VLCMediaThumbnailer *)thumbnailerWithMedia:(VLCMedia *)media andDelegate:(id)delegate { @@ -70,6 +72,8 @@ void display(void *opaque, void *picture) - (void)dealloc { + NSAssert(!_thumbnailingTimeoutTimer, @"Timer not released"); + NSAssert(!_parsingTimeoutTimer, @"Timer not released"); NSAssert(!_data, @"Data not released"); NSAssert(!_mp, @"Not properly retained"); if (_thumbnail) @@ -101,6 +105,7 @@ void display(void *opaque, void *picture) { NSArray *tracks = [_media tracksInformation]; + // Find the video track NSDictionary *videoTrack = nil; for (NSDictionary *track in tracks) { @@ -129,11 +134,14 @@ void display(void *opaque, void *picture) int newWidth = round(videoWidth * ratio); int newHeight = round(videoHeight * ratio); - NSLog(@"video %dx%d from %dx%d or %dx%d", newWidth, newHeight, videoWidth, videoHeight, imageWidth, imageHeight); + imageWidth = newWidth > 0 ? newWidth : imageWidth; imageHeight = newHeight > 0 ? newHeight : imageHeight; } + _numberOfReceivedFrames = 0; + NSAssert(!_shouldRejectFrames, @"Are we still running?"); + _effectiveThumbnailHeight = imageHeight; _effectiveThumbnailWidth = imageWidth; @@ -150,6 +158,9 @@ void display(void *opaque, void *picture) libvlc_video_set_callbacks(_mp, lock, unlock, display, self); libvlc_media_player_play(_mp); libvlc_media_player_set_position(_mp, kSnapshotPosition); + + NSAssert(!_thumbnailingTimeoutTimer, @"We already have a timer around"); + _thumbnailingTimeoutTimer = [[NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(mediaThumbnailingTimedOut) userInfo:nil repeats:NO] retain]; } - (void)mediaParsingTimedOut @@ -177,13 +188,21 @@ void display(void *opaque, void *picture) - (void)didFetchThumbnail { + if (_shouldRejectFrames) + return; + // The video thread is blocking on us. Beware not to do too much work. + _numberOfReceivedFrames++; + // Make sure we are getting the right frame - if (libvlc_media_player_get_position(_mp) < kSnapshotPosition && + if (libvlc_media_player_get_position(_mp) < kSnapshotPosition / 2 && // Arbitrary choice to work around broken files. - libvlc_media_player_get_length(_mp) > 1000) + libvlc_media_player_get_length(_mp) > 1000 && + _numberOfReceivedFrames < 10) + { return; + } NSAssert(_data, @"We have no data"); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); @@ -214,10 +233,8 @@ void display(void *opaque, void *picture) [self performSelector:@selector(notifyDelegate) withObject:nil afterDelay:0]; } -- (void)notifyDelegate +- (void)stopAsync { - // Stop the media player - NSAssert(_mp, @"We have already destroyed mp"); libvlc_media_player_stop(_mp); libvlc_media_player_release(_mp); _mp = NULL; @@ -226,10 +243,39 @@ void display(void *opaque, void *picture) free(_data); _data = NULL; + _shouldRejectFrames = NO; +} + +- (void)endThumbnailing +{ + _shouldRejectFrames = YES; + + [_thumbnailingTimeoutTimer invalidate]; + [_thumbnailingTimeoutTimer release]; + _thumbnailingTimeoutTimer = nil; + + // Stop the media player + NSAssert(_mp, @"We have already destroyed mp"); + + [self performSelectorInBackground:@selector(stopAsync) withObject:nil]; + + [self autorelease]; // Balancing -fetchThumbnail +} + +- (void)notifyDelegate +{ + [self endThumbnailing]; + // Call delegate [_delegate mediaThumbnailer:self didFinishThumbnail:_thumbnail]; - [self release]; // Balancing -fetchThumbnail } +- (void)mediaThumbnailingTimedOut +{ + [self endThumbnailing]; + + // Call delegate + [_delegate mediaThumbnailerDidTimeOut:self]; +} @end -- 2.39.2