MLThumbnailerQueue.m 5.98 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/*****************************************************************************
 * MLThumbnailerQueue.m
 * MobileMediaLibraryKit
 *****************************************************************************
 * Copyright (C) 2010 Pierre d'Herbemont
 * Copyright (C) 2010-2013 VLC authors and VideoLAN
 * $Id$
 *
 * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
 *          Felix Paul Kühne <fkuehne # videolan.org>
 *
12 13 14
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
15 16 17 18
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
21
 *
22 23 24
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25
 *****************************************************************************/
Pierre's avatar
Pierre committed
26 27 28

#import "MLThumbnailerQueue.h"
#import "MLFile.h"
29
#import "MLCrashPreventer.h"
Pierre's avatar
Pierre committed
30 31 32 33

@interface ThumbnailOperation : NSOperation <VLCMediaThumbnailerDelegate>
{
    MLFile *_file;
34
    VLCMedia *_media;
Pierre's avatar
Pierre committed
35 36 37 38
}
@property (retain,readwrite) MLFile *file;
@end

39 40 41 42
@interface MLThumbnailerQueue ()
- (void)didFinishOperation:(ThumbnailOperation *)op;
@end

Pierre's avatar
Pierre committed
43 44 45 46 47 48 49 50 51 52 53 54
@implementation ThumbnailOperation
@synthesize file=_file;
- (id)initWithFile:(MLFile *)file;
{
    if (!(self = [super init]))
        return nil;
    self.file = file;
    return self;
}

- (void)dealloc
{
55
    [_media release];
Pierre's avatar
Pierre committed
56 57 58 59 60 61
    [_file release];
    [super dealloc];
}

- (void)fetchThumbnail
{
62
    APLog(@"Starting THUMB %@", self.file);
63 64 65

    [[MLCrashPreventer sharedPreventer] willParseFile:self.file];

66 67
    _media = [[VLCMedia mediaWithURL:[NSURL URLWithString:self.file.url]] retain];
    VLCMediaThumbnailer *thumbnailer = [VLCMediaThumbnailer thumbnailerWithMedia:_media andDelegate:self];
68 69 70 71 72 73 74 75 76 77
    /* optimize thumbnails for the device */
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        if ([UIScreen mainScreen].scale==2.0) {
            thumbnailer.thumbnailWidth = 540.;
            thumbnailer.thumbnailHeight = 405.;
        } else {
            thumbnailer.thumbnailWidth = 272.;
            thumbnailer.thumbnailHeight = 204.;
        }
    } else {
78 79 80
        if (SYSTEM_RUNS_IOS7) {
            thumbnailer.thumbnailWidth = 480.;
            thumbnailer.thumbnailHeight = 270.;
81
        } else {
82 83 84 85 86 87 88
            if ([UIScreen mainScreen].scale==2.0) {
                thumbnailer.thumbnailWidth = 480.;
                thumbnailer.thumbnailHeight = 270.;
            } else {
                thumbnailer.thumbnailWidth = 240.;
                thumbnailer.thumbnailHeight = 135.;
            }
89 90
        }
    }
Pierre's avatar
Pierre committed
91
    [thumbnailer fetchThumbnail];
92
    [[MLThumbnailerQueue sharedThumbnailerQueue].queue setSuspended:YES]; // Balanced in -mediaThumbnailer:didFinishThumbnail
Pierre's avatar
Pierre committed
93 94 95 96 97 98 99
    [self retain]; // Balanced in -mediaThumbnailer:didFinishThumbnail:
}
- (void)main
{
    [self performSelectorOnMainThread:@selector(fetchThumbnail) withObject:nil waitUntilDone:YES];
}

Pierre's avatar
Pierre committed
100
- (void)endThumbnailing
Pierre's avatar
Pierre committed
101
{
102 103 104 105
    [[MLCrashPreventer sharedPreventer] didParseFile:self.file];
    MLThumbnailerQueue *thumbnailer = [MLThumbnailerQueue sharedThumbnailerQueue];
    [thumbnailer.queue setSuspended:NO];
    [thumbnailer didFinishOperation:self];
Pierre's avatar
Pierre committed
106 107
    [self release];
}
Pierre's avatar
Pierre committed
108 109 110
- (void)mediaThumbnailer:(VLCMediaThumbnailer *)mediaThumbnailer didFinishThumbnail:(CGImageRef)thumbnail
{
    mediaThumbnailer.delegate = nil;
111
    APLog(@"Finished thumbnail for %@", self.file.title);
112
    self.file.computedThumbnail = [UIImage imageWithCGImage:thumbnail];
Pierre's avatar
Pierre committed
113 114 115 116 117 118 119 120 121

    [self endThumbnailing];
}

- (void)mediaThumbnailerDidTimeOut:(VLCMediaThumbnailer *)mediaThumbnailer
{
    self.file.thumbnailTimeouted = YES;
    [self endThumbnailing];
}
Pierre's avatar
Pierre committed
122 123 124
@end

@implementation MLThumbnailerQueue
125
@synthesize queue=_queue;
Pierre's avatar
Pierre committed
126 127 128 129 130 131 132 133 134
+ (MLThumbnailerQueue *)sharedThumbnailerQueue
{
    static MLThumbnailerQueue *shared = nil;
    if (!shared) {
        shared = [[MLThumbnailerQueue alloc] init];
    }
    return shared;
}

135 136 137 138 139
- (id)init
{
    self = [super init];
    if (self != nil) {
        _fileDescriptionToOperation = [[NSMutableDictionary alloc] init];
140 141
        _queue = [[NSOperationQueue alloc] init];
        [_queue setMaxConcurrentOperationCount:1];
142 143 144 145 146 147
    }
    return self;
}

- (void)dealloc
{
148
    [_queue release];
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
    [_fileDescriptionToOperation release];
    [super dealloc];
}


static inline NSString *hashFromFile(MLFile *file)
{
    return [NSString stringWithFormat:@"%p", [[file objectID] URIRepresentation]];
}

- (void)didFinishOperation:(ThumbnailOperation *)op
{
    [_fileDescriptionToOperation setValue:nil forKey:hashFromFile(op.file)];
}

Pierre's avatar
Pierre committed
164 165
- (void)addFile:(MLFile *)file
{
166
    if (_fileDescriptionToOperation[hashFromFile(file)])
167 168
        return;
    if (![[MLCrashPreventer sharedPreventer] isFileSafe:file]) {
169
        APLog(@"'%@' is unsafe and will crash, ignoring", file.title);
170 171
        return;
    }
172 173 174 175
    if ([file isAlbumTrack]) {
        APLog(@"'%@' is an audio file, ignoring", file.title);
        return;
    }
176 177
    ThumbnailOperation *op = [[ThumbnailOperation alloc] initWithFile:file];
    [_fileDescriptionToOperation setValue:op forKey:hashFromFile(file)];
178
    [self.queue addOperation:op];
179
    [op autorelease];
Pierre's avatar
Pierre committed
180 181 182 183
}

- (void)stop
{
184
    [_queue setMaxConcurrentOperationCount:0];
Pierre's avatar
Pierre committed
185 186 187 188
}

- (void)resume
{
189
    [_queue setMaxConcurrentOperationCount:1];
Pierre's avatar
Pierre committed
190
}
191 192 193

- (void)setHighPriorityForFile:(MLFile *)file
{
194
    ThumbnailOperation *op = _fileDescriptionToOperation[hashFromFile(file)];
195 196 197 198 199
    [op setQueuePriority:NSOperationQueuePriorityHigh];
}

- (void)setDefaultPriorityForFile:(MLFile *)file
{
200
    ThumbnailOperation *op = _fileDescriptionToOperation[hashFromFile(file)];
201 202
    [op setQueuePriority:NSOperationQueuePriorityNormal];
}
Pierre's avatar
Pierre committed
203
@end