我们实现了AVAssetResourceLoaderDelegate协议来拦截视频加载请求,并将其缓存起来。通过在resourceLoader:shouldWaitForLoadingOfRequestedResource:方法中处理请求,我们可以检查是否已经存在缓存数据,如果是,则直接使用缓存数据,否则发起下载请求。
在startDataRequest方法中,我们使用NSURLSessionDataTask来异步下载视频数据,并在下载完成后// Player.h
import <UIKit/UIKit.h>
import <AVFoundation/AVFoundation.h>
@interface Player : NSObject<AVAssetResourceLoaderDelegate>
@property (nonatomic, strong) NSURL *videoURL;
@property (nonatomic, strong) AVPlayer *player;
@property (nonatomic, strong) AVPlayerLayer *playerLayer;
- (instancetype)initWithVideoURL:(NSURL*)videoURL;
- (void)play;
- (void)pause;
- (void)stop;
- (void)downloadVideoWithCompletion:(void (^)(BOOL success, NSError *error))completion;
@end
// Player.m
import "Player.h"
@implementation Player {
AVAssetResourceLoadingRequest *_loadingRequest;
NSMutableData *_cachedData;
}
(instancetype)initWithVideoURL:(NSURL*)videoURL {
self = [super init];
if (self) {
self.videoURL = videoURL;
[self setupPlayer];
}
return self;
}(void)setupPlayer {
AVURLAsset asset = [AVURLAsset URLAssetWithURL:self.videoURL options:nil];
[asset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()];
AVPlayerItem playerItem = [AVPlayerItem playerItemWithAsset:asset];
self.player = [AVPlayer playerWithPlayerItem:playerItem];
self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
}(void)play {
[self.player play];
}(void)pause {
[self.player pause];
}(void)stop {
[self.player pause];
[self.player seekToTime:kCMTimeZero];
}-
(void)downloadVideoWithCompletion:(void (^)(BOOL success, NSError *error))completion {
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:self.videoURL completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
completion(NO, error);
} else {
NSString *documentDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *destinationPath = [documentDirectory stringByAppendingPathComponent:@"downloadedVideo.mp4"];
NSURL *destinationURL = [NSURL fileURLWithPath:destinationPath];NSError *moveError = nil; [[NSFileManager defaultManager] moveItemAtURL:location toURL:destinationURL error:&moveError]; if (moveError) { completion(NO, moveError); } else { completion(YES, nil); } }
}];
[downloadTask resume];
}
pragma mark - AVAssetResourceLoaderDelegate
-
(BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest {
if (!_loadingRequest) {
NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithURL:self.videoURL resolvingAgainstBaseURL:NO];
urlComponents.scheme = @"cached";
NSURL *customURL = [urlComponents URL];if (customURL && _cachedData) { NSData *requestedData = [_cachedData copy]; [self processRequest:loadingRequest withCachedData:requestedData]; return YES; } else { _loadingRequest = loadingRequest; [self startDataRequest]; }
} else {
// Another loading request is already in progress, so we cannot process the new request concurrently
[loadingRequest finishLoadingWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:nil]];
return YES;
}
return YES;
} -
(void)startDataRequest {
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:self.videoURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
[_loadingRequest finishLoadingWithError:error];
} else {
[_loadingRequest
dataRequestDidReceiveResponse:[[NSHTTPURLResponse alloc] initWithURL:_loadingRequest.request.URL statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:response.allHeaderFields]];
[_loadingRequest.dataRequest
respondWithData:data];
[_loadingRequest finishLoading];
_loadingRequest = nil;// Cache the loaded data _cachedData = [NSMutableData dataWithData:data]; }
}];
[dataTask resume];
} -
(void)processRequest:(AVAssetResourceLoadingRequest *)loadingRequest withCachedData:(NSData *)cachedData {
[loadingRequest
dataRequestDidReceiveResponse:[[NSHTTPURLResponse alloc] initWithURL:loadingRequest.request.URL statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:nil]];NSUInteger offset = (NSUInteger)loadingRequest.dataRequest.requestedOffset;
NSUInteger length = (NSUInteger)loadingRequest.dataRequest.requestedLength;
NSRange requestedRange = NSMakeRange(offset, length);
NSData *requestedData = [cachedData subdataWithRange:requestedRange];[loadingRequest.dataRequest respondWithData:requestedData];
[loadingRequest finishLoading];
}
@end