MLThumbnailerQueue.m 6.91 KB
Newer Older
1 2 3 4 5
/*****************************************************************************
 * MLThumbnailerQueue.m
 * MobileMediaLibraryKit
 *****************************************************************************
 * Copyright (C) 2010 Pierre d'Herbemont
6
 * Copyright (C) 2010-2015 VLC authors and VideoLAN
7 8 9 10 11
 * $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"
30
#import "MLMediaLibrary.h"
31
#import "MLFileParserQueue.h"
Pierre's avatar
Pierre committed
32

Felix Paul Kühne's avatar
Felix Paul Kühne committed
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
#ifdef MLKIT_READONLY_TARGET

@implementation MLThumbnailerQueue

+ (MLThumbnailerQueue *)sharedThumbnailerQueue
{
    static MLThumbnailerQueue *shared = nil;
    if (!shared) {
        shared = [[MLThumbnailerQueue alloc] init];
    }
    return shared;
}

- (void)addFile:(MLFile *)file
{
}

- (void)setHighPriorityForFile:(MLFile *)file
{
}

- (void)setDefaultPriorityForFile:(MLFile *)file
{
}

- (void)stop
{
}

- (void)resume
{
}

@end

#else

Pierre's avatar
Pierre committed
70 71 72
@interface ThumbnailOperation : NSOperation <VLCMediaThumbnailerDelegate>
{
    MLFile *_file;
73
    VLCMedia *_media;
74
    VLCLibrary *_internalLibrary;
Pierre's avatar
Pierre committed
75
}
76
@property (strong,readwrite) MLFile *file;
Pierre's avatar
Pierre committed
77 78
@end

79
@interface MLThumbnailerQueue ()
80 81
{
    VLCLibrary *_internalLibrary;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
82 83
    NSDictionary *_fileDescriptionToOperation;
    NSOperationQueue *_queue;
84
}
85 86 87
- (void)didFinishOperation:(ThumbnailOperation *)op;
@end

Pierre's avatar
Pierre committed
88 89
@implementation ThumbnailOperation
@synthesize file=_file;
90
- (id)initWithFile:(MLFile *)file andVLCLibrary:(VLCLibrary *)library;
Pierre's avatar
Pierre committed
91 92 93 94
{
    if (!(self = [super init]))
        return nil;
    self.file = file;
95
    _internalLibrary = library;
Pierre's avatar
Pierre committed
96 97 98 99 100
    return self;
}

- (void)fetchThumbnail
{
101
    APLog(@"Starting THUMB %@", self.file);
102 103 104

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

105
    _media = [VLCMedia mediaWithURL:self.file.url];
106
    VLCMediaThumbnailer *thumbnailer = [VLCMediaThumbnailer thumbnailerWithMedia:_media delegate:self andVLCLibrary:_internalLibrary];
107 108
    MLThumbnailerQueue *thumbnailerQueue = [MLThumbnailerQueue sharedThumbnailerQueue];

109
    CGSize thumbSize = [UIImage preferredThumbnailSizeForDevice];
110 111
    thumbnailer.thumbnailWidth = thumbSize.width;
    thumbnailer.thumbnailHeight = thumbSize.height;
Pierre's avatar
Pierre committed
112
    [thumbnailer fetchThumbnail];
113
    [thumbnailerQueue.queue setSuspended:YES]; // Balanced in -mediaThumbnailer:didFinishThumbnail
114
     // Balanced in -mediaThumbnailer:didFinishThumbnail:
Pierre's avatar
Pierre committed
115 116 117 118 119 120
}
- (void)main
{
    [self performSelectorOnMainThread:@selector(fetchThumbnail) withObject:nil waitUntilDone:YES];
}

Pierre's avatar
Pierre committed
121
- (void)endThumbnailing
Pierre's avatar
Pierre committed
122
{
123
    [[MLCrashPreventer sharedPreventer] didParseFile:self.file];
124 125 126
    MLThumbnailerQueue *thumbnailerQueue = [MLThumbnailerQueue sharedThumbnailerQueue];
    [thumbnailerQueue.queue setSuspended:NO];
    [thumbnailerQueue didFinishOperation:self];
Pierre's avatar
Pierre committed
127
}
Pierre's avatar
Pierre committed
128 129 130
- (void)mediaThumbnailer:(VLCMediaThumbnailer *)mediaThumbnailer didFinishThumbnail:(CGImageRef)thumbnail
{
    mediaThumbnailer.delegate = nil;
131 132 133 134
    MLFile *file = self.file;
    APLog(@"Finished thumbnail for %@", file.title);
    if (thumbnail) {
        UIImage *thumbnailImage = [UIImage imageWithCGImage:thumbnail];
135
        if (thumbnailImage) {
136
            file.computedThumbnail = [UIImage imageWithCGImage:thumbnail];
137
#if CS_ENABLED
138
            [file updateCoreSpotlightEntry];
139
#endif
140
        }
141
    }
Pierre's avatar
Pierre committed
142 143 144 145 146 147 148 149 150

    [self endThumbnailing];
}

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

@implementation MLThumbnailerQueue
154
@synthesize queue=_queue;
Pierre's avatar
Pierre committed
155 156 157 158 159 160 161 162 163
+ (MLThumbnailerQueue *)sharedThumbnailerQueue
{
    static MLThumbnailerQueue *shared = nil;
    if (!shared) {
        shared = [[MLThumbnailerQueue alloc] init];
    }
    return shared;
}

164 165 166 167
- (id)init
{
    self = [super init];
    if (self != nil) {
168
        int speedCategory = [[MLMediaLibrary sharedMediaLibrary] deviceSpeedCategory];
169 170 171 172
        APLog(@"running on a category %i device", speedCategory);
        if (speedCategory < 2)
            _internalLibrary = [VLCLibrary sharedLibrary];
        else
173
            _internalLibrary = [[VLCLibrary alloc] initWithOptions:@[@"--avcodec-threads=1", @"--avcodec-skip-idct=4", @"--deinterlace=-1", @"--avcodec-skiploopfilter=3", @"--no-interact", @"--avi-index=3"]];
174
        _fileDescriptionToOperation = [[NSMutableDictionary alloc] init];
175 176
        _queue = [[NSOperationQueue alloc] init];
        [_queue setMaxConcurrentOperationCount:1];
177 178 179 180 181 182 183 184 185 186 187 188 189 190
    }
    return self;
}

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
191 192
- (void)addFile:(MLFile *)file
{
193
    if (_fileDescriptionToOperation[hashFromFile(file)])
194 195
        return;
    if (![[MLCrashPreventer sharedPreventer] isFileSafe:file]) {
196
        APLog(@"'%@' is unsafe and will crash, ignoring", file.title);
197 198
        return;
    }
199

200 201 202 203 204 205
    if (file.albumTrack) {
        APLog(@"'%@' is part of a music album, ignoring", file.title);
        return;
    }

    if ([file isKindOfType:kMLFileTypeAudio]) {
206 207 208
        APLog(@"'%@' is an audio file, ignoring", file.title);
        return;
    }
209 210 211 212 213 214 215

    if (file.hasFetchedInfo.boolValue != YES) {
        APLog(@"'%@' still awaits parsing, ignoring", file.title);
        [[MLFileParserQueue sharedFileParserQueue] addFile:file];
        return;
    }

216
    ThumbnailOperation *op = [[ThumbnailOperation alloc] initWithFile:file andVLCLibrary:_internalLibrary];
217
    [_fileDescriptionToOperation setValue:op forKey:hashFromFile(file)];
218
    [self.queue addOperation:op];
Pierre's avatar
Pierre committed
219 220 221 222
}

- (void)stop
{
223
    [_queue setMaxConcurrentOperationCount:0];
Pierre's avatar
Pierre committed
224 225 226 227
}

- (void)resume
{
228
    [_queue setMaxConcurrentOperationCount:1];
Pierre's avatar
Pierre committed
229
}
230 231 232

- (void)setHighPriorityForFile:(MLFile *)file
{
233
    ThumbnailOperation *op = _fileDescriptionToOperation[hashFromFile(file)];
234 235 236 237 238
    [op setQueuePriority:NSOperationQueuePriorityHigh];
}

- (void)setDefaultPriorityForFile:(MLFile *)file
{
239
    ThumbnailOperation *op = _fileDescriptionToOperation[hashFromFile(file)];
240 241
    [op setQueuePriority:NSOperationQueuePriorityNormal];
}
242

Pierre's avatar
Pierre committed
243
@end
Felix Paul Kühne's avatar
Felix Paul Kühne committed
244 245

#endif