Commit 0381d383 authored by Pierre d'Herbemont's avatar Pierre d'Herbemont

VLCKit: Protect against file that takes forever to thumbnail.

parent a8b37fbd
...@@ -21,9 +21,12 @@ ...@@ -21,9 +21,12 @@
CGImageRef _thumbnail; CGImageRef _thumbnail;
void *_data; void *_data;
NSTimer *_parsingTimeoutTimer; NSTimer *_parsingTimeoutTimer;
NSTimer *_thumbnailingTimeoutTimer;
CGFloat _thumbnailHeight,_thumbnailWidth; CGFloat _thumbnailHeight,_thumbnailWidth;
CGFloat _effectiveThumbnailHeight,_effectiveThumbnailWidth; CGFloat _effectiveThumbnailHeight,_effectiveThumbnailWidth;
int _numberOfReceivedFrames;
BOOL _shouldRejectFrames;
} }
+ (VLCMediaThumbnailer *)thumbnailerWithMedia:(VLCMedia *)media andDelegate:(id<VLCMediaThumbnailerDelegate>)delegate; + (VLCMediaThumbnailer *)thumbnailerWithMedia:(VLCMedia *)media andDelegate:(id<VLCMediaThumbnailerDelegate>)delegate;
...@@ -52,5 +55,6 @@ ...@@ -52,5 +55,6 @@
@protocol VLCMediaThumbnailerDelegate @protocol VLCMediaThumbnailerDelegate
@required @required
- (void)mediaThumbnailerDidTimeOut:(VLCMediaThumbnailer *)mediaThumbnailer;
- (void)mediaThumbnailer:(VLCMediaThumbnailer *)mediaThumbnailer didFinishThumbnail:(CGImageRef)thumbnail; - (void)mediaThumbnailer:(VLCMediaThumbnailer *)mediaThumbnailer didFinishThumbnail:(CGImageRef)thumbnail;
@end @end
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
- (void)fetchThumbnail; - (void)fetchThumbnail;
- (void)startFetchingThumbnail; - (void)startFetchingThumbnail;
@property (readonly, assign) void *dataPointer; @property (readonly, assign) void *dataPointer;
@property (readonly, assign) BOOL shouldRejectFrames;
@end @end
static void *lock(void *opaque, void **pixels) static void *lock(void *opaque, void **pixels)
...@@ -42,7 +43,7 @@ void unlock(void *opaque, void *picture, void *const *p_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. // We may already have a thumbnail if we are receiving picture after the first one.
// Just ignore. // Just ignore.
if ([thumbnailer thumbnail]) if ([thumbnailer thumbnail] || [thumbnailer shouldRejectFrames])
return; return;
[thumbnailer performSelectorOnMainThread:@selector(didFetchThumbnail) withObject:nil waitUntilDone:YES]; [thumbnailer performSelectorOnMainThread:@selector(didFetchThumbnail) withObject:nil waitUntilDone:YES];
...@@ -59,6 +60,7 @@ void display(void *opaque, void *picture) ...@@ -59,6 +60,7 @@ void display(void *opaque, void *picture)
@synthesize dataPointer=_data; @synthesize dataPointer=_data;
@synthesize thumbnailWidth=_thumbnailWidth; @synthesize thumbnailWidth=_thumbnailWidth;
@synthesize thumbnailHeight=_thumbnailHeight; @synthesize thumbnailHeight=_thumbnailHeight;
@synthesize shouldRejectFrames=_shouldRejectFrames;
+ (VLCMediaThumbnailer *)thumbnailerWithMedia:(VLCMedia *)media andDelegate:(id<VLCMediaThumbnailerDelegate>)delegate + (VLCMediaThumbnailer *)thumbnailerWithMedia:(VLCMedia *)media andDelegate:(id<VLCMediaThumbnailerDelegate>)delegate
{ {
...@@ -70,6 +72,8 @@ void display(void *opaque, void *picture) ...@@ -70,6 +72,8 @@ void display(void *opaque, void *picture)
- (void)dealloc - (void)dealloc
{ {
NSAssert(!_thumbnailingTimeoutTimer, @"Timer not released");
NSAssert(!_parsingTimeoutTimer, @"Timer not released");
NSAssert(!_data, @"Data not released"); NSAssert(!_data, @"Data not released");
NSAssert(!_mp, @"Not properly retained"); NSAssert(!_mp, @"Not properly retained");
if (_thumbnail) if (_thumbnail)
...@@ -101,6 +105,7 @@ void display(void *opaque, void *picture) ...@@ -101,6 +105,7 @@ void display(void *opaque, void *picture)
{ {
NSArray *tracks = [_media tracksInformation]; NSArray *tracks = [_media tracksInformation];
// Find the video track // Find the video track
NSDictionary *videoTrack = nil; NSDictionary *videoTrack = nil;
for (NSDictionary *track in tracks) { for (NSDictionary *track in tracks) {
...@@ -129,11 +134,14 @@ void display(void *opaque, void *picture) ...@@ -129,11 +134,14 @@ void display(void *opaque, void *picture)
int newWidth = round(videoWidth * ratio); int newWidth = round(videoWidth * ratio);
int newHeight = round(videoHeight * 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; imageWidth = newWidth > 0 ? newWidth : imageWidth;
imageHeight = newHeight > 0 ? newHeight : imageHeight; imageHeight = newHeight > 0 ? newHeight : imageHeight;
} }
_numberOfReceivedFrames = 0;
NSAssert(!_shouldRejectFrames, @"Are we still running?");
_effectiveThumbnailHeight = imageHeight; _effectiveThumbnailHeight = imageHeight;
_effectiveThumbnailWidth = imageWidth; _effectiveThumbnailWidth = imageWidth;
...@@ -150,6 +158,9 @@ void display(void *opaque, void *picture) ...@@ -150,6 +158,9 @@ void display(void *opaque, void *picture)
libvlc_video_set_callbacks(_mp, lock, unlock, display, self); libvlc_video_set_callbacks(_mp, lock, unlock, display, self);
libvlc_media_player_play(_mp); libvlc_media_player_play(_mp);
libvlc_media_player_set_position(_mp, kSnapshotPosition); 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 - (void)mediaParsingTimedOut
...@@ -177,13 +188,21 @@ void display(void *opaque, void *picture) ...@@ -177,13 +188,21 @@ void display(void *opaque, void *picture)
- (void)didFetchThumbnail - (void)didFetchThumbnail
{ {
if (_shouldRejectFrames)
return;
// The video thread is blocking on us. Beware not to do too much work. // The video thread is blocking on us. Beware not to do too much work.
_numberOfReceivedFrames++;
// Make sure we are getting the right frame // 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. // Arbitrary choice to work around broken files.
libvlc_media_player_get_length(_mp) > 1000) libvlc_media_player_get_length(_mp) > 1000 &&
_numberOfReceivedFrames < 10)
{
return; return;
}
NSAssert(_data, @"We have no data"); NSAssert(_data, @"We have no data");
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
...@@ -214,10 +233,8 @@ void display(void *opaque, void *picture) ...@@ -214,10 +233,8 @@ void display(void *opaque, void *picture)
[self performSelector:@selector(notifyDelegate) withObject:nil afterDelay:0]; [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_stop(_mp);
libvlc_media_player_release(_mp); libvlc_media_player_release(_mp);
_mp = NULL; _mp = NULL;
...@@ -226,10 +243,39 @@ void display(void *opaque, void *picture) ...@@ -226,10 +243,39 @@ void display(void *opaque, void *picture)
free(_data); free(_data);
_data = NULL; _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 // Call delegate
[_delegate mediaThumbnailer:self didFinishThumbnail:_thumbnail]; [_delegate mediaThumbnailer:self didFinishThumbnail:_thumbnail];
[self release]; // Balancing -fetchThumbnail
} }
- (void)mediaThumbnailingTimedOut
{
[self endThumbnailing];
// Call delegate
[_delegate mediaThumbnailerDidTimeOut:self];
}
@end @end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment