├── AssetLoaderDelegate.h ├── AssetLoaderDelegate.m └── README.md /AssetLoaderDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AssetLoaderDelegate.h 3 | // TimeTag 4 | // 5 | // Created by Renjith N on 23/02/15. 6 | // Copyright (c) 2015 MBP1. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AssetLoaderDelegate : NSObject 12 | 13 | @property (nonatomic, strong) NSMutableData *movieData; 14 | @property (nonatomic, strong) NSURLConnection *connection; 15 | 16 | @property (nonatomic, strong) NSHTTPURLResponse *response; 17 | @property (nonatomic, strong) NSMutableArray *pendingRequests; 18 | 19 | @property (nonatomic,strong) NSString *cacheDir; 20 | @property (nonatomic,strong) NSString *fileName; 21 | @property (nonatomic,strong) NSString *fileUrl; 22 | 23 | + (NSString*) preViewFoundInCacheDirectory:(NSString*) url; 24 | 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /AssetLoaderDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AssetLoaderDelegate.m 3 | // TimeTag 4 | // 5 | // Created by Renjith N on 23/02/15. 6 | // Copyright (c) 2015 MBP1. All rights reserved. 7 | // 8 | 9 | #import "AssetLoaderDelegate.h" 10 | #import 11 | #import 12 | 13 | 14 | @implementation AssetLoaderDelegate 15 | 16 | 17 | - (id)init{ 18 | if (self = [super init]) { 19 | self.cacheDir = [AssetLoaderDelegate cacheDirectory]; 20 | self.pendingRequests = [NSMutableArray array]; 21 | } 22 | return self; 23 | } 24 | 25 | #pragma mark - NSURLConnection delegate 26 | 27 | - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{ 28 | self.movieData = [NSMutableData data]; 29 | self.response = (NSHTTPURLResponse *)response; 30 | [self processPendingRequests]; 31 | } 32 | 33 | - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{ 34 | [self.movieData appendData:data]; 35 | [self processPendingRequests]; 36 | } 37 | 38 | - (void)connectionDidFinishLoading:(NSURLConnection *)connection{ 39 | [self processPendingRequests]; 40 | NSLog(@"Download complete"); 41 | NSString *fileName = [NSURL URLWithString:self.fileUrl].absoluteString.lastPathComponent; 42 | NSString *cachedFilePath = [[NSString alloc] initWithFormat:@"%@/%@",self.cacheDir,[fileName componentsSeparatedByString:@"?"].firstObject]; 43 | BOOL writen = [self.movieData writeToFile:cachedFilePath atomically:YES]; 44 | if(!writen){ 45 | NSLog(@"Error"); 46 | 47 | } 48 | } 49 | 50 | #pragma mark - AVURLAsset resource loading 51 | 52 | - (void)processPendingRequests{ 53 | NSLog(@"processPendingRequests:%lu",(unsigned long)self.pendingRequests.count); 54 | NSMutableArray *requestsCompleted = [NSMutableArray array]; 55 | 56 | for (AVAssetResourceLoadingRequest *loadingRequest in self.pendingRequests){ 57 | [self fillInContentInformation:loadingRequest.contentInformationRequest]; 58 | 59 | BOOL didRespondCompletely = [self respondWithDataForRequest:loadingRequest.dataRequest]; 60 | 61 | if (didRespondCompletely){ 62 | [requestsCompleted addObject:loadingRequest]; 63 | 64 | [loadingRequest finishLoading]; 65 | } 66 | } 67 | 68 | [self.pendingRequests removeObjectsInArray:requestsCompleted]; 69 | } 70 | 71 | - (void)fillInContentInformation:(AVAssetResourceLoadingContentInformationRequest *)contentInformationRequest{ 72 | if (contentInformationRequest == nil || self.response == nil){ 73 | return; 74 | } 75 | 76 | NSString *mimeType = [self.response MIMEType]; 77 | CFStringRef contentType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef)(mimeType), NULL); 78 | 79 | contentInformationRequest.byteRangeAccessSupported = YES; 80 | contentInformationRequest.contentType = CFBridgingRelease(contentType); 81 | contentInformationRequest.contentLength = [self.response expectedContentLength]; 82 | } 83 | 84 | - (BOOL)respondWithDataForRequest:(AVAssetResourceLoadingDataRequest *)dataRequest{ 85 | long long startOffset = dataRequest.requestedOffset; 86 | if (dataRequest.currentOffset != 0){ 87 | startOffset = dataRequest.currentOffset; 88 | } 89 | 90 | // Don't have any data at all for this request 91 | if (self.movieData.length < startOffset){ 92 | return NO; 93 | } 94 | 95 | // This is the total data we have from startOffset to whatever has been downloaded so far 96 | NSUInteger unreadBytes = self.movieData.length - (NSUInteger)startOffset; 97 | // Respond with whatever is available if we can't satisfy the request fully yet 98 | NSUInteger numberOfBytesToRespondWith = MIN((NSUInteger)dataRequest.requestedLength, unreadBytes); 99 | 100 | NSLog(@"data:%lu,,,(%lld,%lu)",(unsigned long)self.movieData.length,startOffset,(unsigned long)numberOfBytesToRespondWith); 101 | [dataRequest respondWithData:[self.movieData subdataWithRange:NSMakeRange((NSUInteger)startOffset, numberOfBytesToRespondWith)]]; 102 | 103 | long long endOffset = startOffset + dataRequest.requestedLength; 104 | BOOL didRespondFully = self.movieData.length >= endOffset; 105 | 106 | return didRespondFully; 107 | } 108 | 109 | 110 | - (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest{ 111 | if (self.connection == nil){ 112 | NSURL *interceptedURL = [loadingRequest.request URL]; 113 | NSURLComponents *actualURLComponents = [[NSURLComponents alloc] initWithURL:interceptedURL resolvingAgainstBaseURL:NO]; 114 | actualURLComponents.scheme = @"https"; 115 | NSURLRequest *request = [NSURLRequest requestWithURL:[actualURLComponents URL]]; 116 | self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; 117 | [self.connection setDelegateQueue:[NSOperationQueue mainQueue]]; 118 | [self.connection start]; 119 | } 120 | 121 | NSLog(@"pendingRequests:%@",loadingRequest); 122 | [self.pendingRequests addObject:loadingRequest]; 123 | [self processPendingRequests]; 124 | return YES; 125 | } 126 | 127 | - (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest{ 128 | [self.pendingRequests removeObject:loadingRequest]; 129 | } 130 | 131 | + (NSString*) preViewFoundInCacheDirectory:(NSString*) url{ 132 | 133 | NSString *fileName = [NSURL URLWithString:url].absoluteString.lastPathComponent; 134 | 135 | NSString *cachedFilePath = [[NSString alloc] initWithFormat:@"%@/%@",[AssetLoaderDelegate cacheDirectory],[fileName componentsSeparatedByString:@"?"].firstObject]; 136 | if([[NSFileManager defaultManager] fileExistsAtPath:cachedFilePath]){ 137 | return cachedFilePath; 138 | } 139 | else{ 140 | return nil; 141 | } 142 | } 143 | 144 | + (NSString *)cacheDirectory{ 145 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); 146 | NSString *cacheDir = [paths objectAtIndex:0]; 147 | NSString *videoCacheDir = [NSString stringWithFormat:@"%@/%@",cacheDir,@"Previews"]; 148 | 149 | BOOL isDir = NO; 150 | NSError *error; 151 | if (! [[NSFileManager defaultManager] fileExistsAtPath:videoCacheDir isDirectory:&isDir] && isDir == NO) { 152 | [[NSFileManager defaultManager] createDirectoryAtPath:videoCacheDir withIntermediateDirectories:YES attributes:nil error:&error]; 153 | } 154 | 155 | return videoCacheDir; 156 | } 157 | 158 | @end 159 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AVAssetResourceLoader-Video-Example 2 | Implementation of AVAssetResourceLoader custom class which can be useful while caching videos while streaming 3 | 4 | ## Usage 5 | 6 | ``` 7 | AVURLAsset *asset ; 8 | assetLoader = [[AssetLoaderDelegate alloc] init]; 9 | assetLoader.fileUrl = self.videoURL ; //S3 url in this case 10 | asset = [AVURLAsset URLAssetWithURL:[self url:self.videoURL WithCustomScheme:@"streaming"] options:nil]; 11 | [asset.resourceLoader setDelegate:assetLoader queue:dispatch_get_main_queue()]; 12 | 13 | 14 | - (NSURL *) url:(NSURL*) url WithCustomScheme:(NSString *)scheme{ 15 | NSURLComponents *components = [[NSURLComponents alloc] initWithURL:url resolvingAgainstBaseURL:NO]; 16 | components.scheme = scheme; 17 | return [components URL]; 18 | } 19 | 20 | ``` 21 | --------------------------------------------------------------------------------