1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//
// VIResoureLoader.m
// VIMediaCacheDemo
//
// Created by Vito on 4/21/16.
// Copyright © 2016 Vito. All rights reserved.
//
#import "VIResourceLoader.h"
#import "VIMediaDownloader.h"
#import "VIResourceLoadingRequestWorker.h"
#import "VIContentInfo.h"
NSString * const MCResourceLoaderErrorDomain = @"LSFilePlayerResourceLoaderErrorDomain";
@interface VIResourceLoader () <VIResourceLoadingRequestWorkerDelegate>
@property (nonatomic, strong, readwrite) NSURL *url;
@property (nonatomic, strong) VIMediaDownloader *mediaDownloader;
@property (nonatomic, strong) NSMutableDictionary *pendingRequestWorkers;
@property (nonatomic, getter=isCancelled) BOOL cancelled;
@end
@implementation VIResourceLoader
- (void)dealloc {
[_mediaDownloader invalidateAndCancel];
}
- (instancetype)initWithURL:(NSURL *)url {
self = [super init];
if (self) {
_url = url;
_mediaDownloader = [[VIMediaDownloader alloc] initWithURL:url];
_pendingRequestWorkers = [NSMutableDictionary dictionary];
}
return self;
}
- (instancetype)init {
NSAssert(NO, @"Use - initWithURL: instead");
return nil;
}
- (void)addRequest:(AVAssetResourceLoadingRequest *)request {
[self.pendingRequestWorkers enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, VIResourceLoadingRequestWorker * _Nonnull obj, BOOL * _Nonnull stop) {
[obj cancel];
[obj finish];
}];
[self.pendingRequestWorkers removeAllObjects];
[self startWorkerWithRequest:request];
}
- (void)removeRequest:(AVAssetResourceLoadingRequest *)request {
NSString *key = [self keyForRequest:request];
VIResourceLoadingRequestWorker *requestWorker = self.pendingRequestWorkers[key];
[requestWorker finish];
[self.pendingRequestWorkers removeObjectForKey:key];
}
- (void)cancel {
[self.mediaDownloader cancel];
}
#pragma mark - VIResourceLoadingRequestWorkerDelegate
- (void)resourceLoadingRequestWorker:(VIResourceLoadingRequestWorker *)requestWorker didCompleteWithError:(NSError *)error {
[self removeRequest:requestWorker.request];
if (error && [self.delegate respondsToSelector:@selector(resourceLoader:didFailWithError:)]) {
[self.delegate resourceLoader:self didFailWithError:error];
}
}
#pragma mark - Helper
- (void)startWorkerWithRequest:(AVAssetResourceLoadingRequest *)request {
NSString *key = [self keyForRequest:request];
VIResourceLoadingRequestWorker *requestWorker = [[VIResourceLoadingRequestWorker alloc] initWithMediaDownloader:self.mediaDownloader
resourceLoadingRequest:request];
requestWorker.delegate = self;
self.pendingRequestWorkers[key] = requestWorker;
[requestWorker startWork];
}
- (NSString *)keyForRequest:(AVAssetResourceLoadingRequest *)request {
return [NSString stringWithFormat:@"%@%@", request.request.URL.absoluteString, request.request.allHTTPHeaderFields[@"Range"]];
}
- (NSError *)loaderCancelledError{
NSError *error = [[NSError alloc] initWithDomain:MCResourceLoaderErrorDomain
code:-3
userInfo:@{NSLocalizedDescriptionKey:@"Resource loader cancelled"}];
return error;
}
@end