Skip to content

Instantly share code, notes, and snippets.

@JoeyBodnar
Created March 28, 2016 05:51
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save JoeyBodnar/7be323b066058667851b to your computer and use it in GitHub Desktop.
Save JoeyBodnar/7be323b066058667851b to your computer and use it in GitHub Desktop.
Video Loading/Caching with AVAssetResourceLoader
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
VideoCell *videoCell = (VideoCell *)[self.tableView dequeueReusableCellWithIdentifier:@"VideoCell"];
NSString *urlString = @"streaming://download.wavetlan.com/SVV/Media/HTTP/MOV/ConvertedFiles/MediaCoder/MediaCoder_test11_36s_H263_VBR_590kbps_176x144_25fps_MPEG1Layer3_CBR_160kbps_Stereo_22050Hz.mov";
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
// Configure Video Cell Here:
NSURL *url = [NSURL URLWithString:urlString];
videoCell.contentView.backgroundColor = [UIColor clearColor];
videoCell.backgroundColor = [UIColor clearColor];
dispatch_async(queue, ^{
AVURLAsset *asset = [AVURLAsset assetWithURL:url];
[asset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()];
self.pendingRequests = [NSMutableArray array];
videoCell.item = [AVPlayerItem playerItemWithAsset:asset];
dispatch_sync(dispatch_get_main_queue(), ^{
self.player = [[AVPlayer alloc] initWithPlayerItem:videoCell.item];
self.player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
playerLayer.frame = CGRectMake(0, 0, videoCell.contentView.frame.size.width, videoCell.contentView.frame.size.height);
playerLayer.backgroundColor = [UIColor grayColor].CGColor;
[videoCell.contentView.layer addSublayer:playerLayer];
return videoCell;
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
self.videoData = [NSMutableData data];
self.response = (NSHTTPURLResponse *)response;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.videoData appendData:data];
NSLog(@"Size of End Image(bytes):%d",[self.videoData length]);
[self processPendingRequests];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self processPendingRequests];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:@"BlackberryVideo"];
if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath])
[[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:nil];
NSString *videopath= [[NSString alloc] initWithString:[NSString stringWithFormat:@"%@/challenge.mov",documentsDirectory]];
[self.videoData writeToFile:videopath atomically:NO];
}
- (void)processPendingRequests {
NSMutableArray *requestsCompleted = [NSMutableArray array];
for (AVAssetResourceLoadingRequest *loadingRequest in self.pendingRequests) {
[self fillInContentInformation:loadingRequest.contentInformationRequest];
BOOL didRespondCompletely = [self respondWithDataForRequest:loadingRequest.dataRequest];
if (didRespondCompletely) {
[requestsCompleted addObject:loadingRequest];
[loadingRequest finishLoading];
}
}
[self.pendingRequests removeObjectsInArray:requestsCompleted];
}
- (void)fillInContentInformation:(AVAssetResourceLoadingContentInformationRequest *)contentInformationRequest {
if (contentInformationRequest == nil || self.response == nil) {
return;
}
NSString *mimeType = [self.response MIMEType];
CFStringRef contentType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef)(mimeType), NULL);
contentInformationRequest.byteRangeAccessSupported = YES;
contentInformationRequest.contentType = CFBridgingRelease(contentType);
contentInformationRequest.contentLength = [self.response expectedContentLength];
}
- (BOOL)respondWithDataForRequest:(AVAssetResourceLoadingDataRequest *)dataRequest {
long long startOffset = dataRequest.requestedOffset;
if (dataRequest.currentOffset != 0) {
startOffset = dataRequest.currentOffset;
}
// Don't have any data at all for this request
if (self.videoData.length < startOffset) {
return NO;
}
// This is the total data we have from startOffset to whatever has been downloaded so far
NSUInteger unreadBytes = self.videoData.length - (NSUInteger)startOffset;
// Respond with whatever is available if we can't satisfy the request fully yet
NSUInteger numberOfBytesToRespondWith = MIN((NSUInteger)dataRequest.requestedLength, unreadBytes);
[dataRequest respondWithData:[self.videoData subdataWithRange:NSMakeRange((NSUInteger)startOffset, numberOfBytesToRespondWith)]];
long long endOffset = startOffset + dataRequest.requestedLength;
BOOL didRespondFully = self.videoData.length >= endOffset;
return didRespondFully;
}
- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest {
if (self.connection == nil) {
NSString *urlString = @"http://download.wavetlan.com/SVV/Media/HTTP/MOV/ConvertedFiles/MediaCoder/MediaCoder_test11_36s_H263_VBR_590kbps_176x144_25fps_MPEG1Layer3_CBR_160kbps_Stereo_22050Hz.mov";
NSURL *realURL = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:realURL];
self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
NSOperationQueue *getDataQueue = [[NSOperationQueue alloc] init];
[self.connection setDelegateQueue:[NSOperationQueue mainQueue]];
[self.connection start];
}
[self.pendingRequests addObject:loadingRequest];
return YES;
}
- (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest {
[self.pendingRequests removeObject:loadingRequest];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment