项目中之前使用的是AVPlayer直接播放URL地址,但是不知道是相机的wifi不够稳定还是代码的问题,app总是出现缓冲卡顿,就考虑改写成边下边播的模式,查过了许多资料,发现大部分都是用的同一种方法
AVAssetResourceLoaderDelegate 代理方法,来看看如何实现
首先要实现两个必须的代理方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
AVAssetResourceLoaderDelegateObjective-C
#pragma mark - AVAssetResourceLoaderDelegate
//开始加载
- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest {
[self addLoadingRequest:loadingRequest];
return YES;
}
//取消加载
- ( void )resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest {
[self removeLoadingRequest:loadingRequest];
}
#pragma mark - AVAssetResourceLoaderDelegate
//开始加载
- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest {
[self addLoadingRequest:loadingRequest];
return YES;
}
//取消加载
- ( void )resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest {
[self removeLoadingRequest:loadingRequest];
}
|
然后要定义一个下载类,其实就是分段下载数据的下载器
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
102
103
104
105
106
|
AVAssetResourceLoaderDelegateObjective-C
- ( void )start {
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[self.requestURL originalSchemeURL] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:RequestTimeout];
if (self.requestOffset > 0 ) {
[request addValue:[NSString stringWithFormat:@ "bytes=%ld-%ld" , self.requestOffset, self.fileLength - 1 ] forHTTPHeaderField:@ "Range" ];
}
self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
self.task = [self.session dataTaskWithRequest:request];
[self.task resume];
}
#pragma mark - NSURLSessionDataDelegate
//服务器响应
- ( void )URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:( void (^)(NSURLSessionResponseDisposition))completionHandler {
if (self.cancel) return ;
SRQLog(@ "response: %@" ,response);
completionHandler(NSURLSessionResponseAllow);
NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *)response;
NSString * contentRange = [[httpResponse allHeaderFields] objectForKey:@ "Content-Range" ];
NSString * fileLength = [[contentRange componentsSeparatedByString:@ "/" ] lastObject];
self.fileLength = fileLength.integerValue > 0 ? fileLength.integerValue : response.expectedContentLength;
if (self.delegate && [self.delegate respondsToSelector: @selector (requestTaskDidReceiveResponse)]) {
[self.delegate requestTaskDidReceiveResponse];
}
}
//服务器返回数据 可能会调用多次
- ( void )URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
if (self.cancel) return ;
//SRQLog(@"收到响应了: %@",data);
self.cacheLength += data.length;
if (self.delegate && [self.delegate respondsToSelector: @selector (requestTaskDidUpdateCache)]) {
[self.delegate requestTaskDidUpdateCache];
}
}
//请求完成会调用该方法,请求失败则error有值
- ( void )URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
if (self.cancel) {
SRQLog(@ "下载取消" );
} else {
if (error) {
if (self.delegate && [self.delegate respondsToSelector: @selector (requestTaskDidFailWithError:)]) {
[self.delegate requestTaskDidFailWithError:error];
}
} else {
//可以缓存则保存文件
if (self.cache) {
[FileHandle cacheTempFileWithFileName:[NSString fileNameWithURL:self.requestURL]];
}
if (self.delegate && [self.delegate respondsToSelector: @selector (requestTaskDidFinishLoadingWithCache:)]) {
[self.delegate requestTaskDidFinishLoadingWithCache:self.cache];
}
}
}
}
- ( void )start {
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[self.requestURL originalSchemeURL] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:RequestTimeout];
if (self.requestOffset > 0 ) {
[request addValue:[NSString stringWithFormat:@ "bytes=%ld-%ld" , self.requestOffset, self.fileLength - 1 ] forHTTPHeaderField:@ "Range" ];
}
self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
self.task = [self.session dataTaskWithRequest:request];
[self.task resume];
}
#pragma mark - NSURLSessionDataDelegate
//服务器响应
- ( void )URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:( void (^)(NSURLSessionResponseDisposition))completionHandler {
if (self.cancel) return ;
SRQLog(@ "response: %@" ,response);
completionHandler(NSURLSessionResponseAllow);
NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *)response;
NSString * contentRange = [[httpResponse allHeaderFields] objectForKey:@ "Content-Range" ];
NSString * fileLength = [[contentRange componentsSeparatedByString:@ "/" ] lastObject];
self.fileLength = fileLength.integerValue > 0 ? fileLength.integerValue : response.expectedContentLength;
if (self.delegate && [self.delegate respondsToSelector: @selector (requestTaskDidReceiveResponse)]) {
[self.delegate requestTaskDidReceiveResponse];
}
}
//服务器返回数据 可能会调用多次
- ( void )URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
if (self.cancel) return ;
//SRQLog(@"收到响应了: %@",data);
self.cacheLength += data.length;
if (self.delegate && [self.delegate respondsToSelector: @selector (requestTaskDidUpdateCache)]) {
[self.delegate requestTaskDidUpdateCache];
}
}
//请求完成会调用该方法,请求失败则error有值
- ( void )URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
if (self.cancel) {
SRQLog(@ "下载取消" );
} else {
if (error) {
if (self.delegate && [self.delegate respondsToSelector: @selector (requestTaskDidFailWithError:)]) {
[self.delegate requestTaskDidFailWithError:error];
}
} else {
//可以缓存则保存文件
if (self.cache) {
[FileHandle cacheTempFileWithFileName:[NSString fileNameWithURL:self.requestURL]];
}
if (self.delegate && [self.delegate respondsToSelector: @selector (requestTaskDidFinishLoadingWithCache:)]) {
[self.delegate requestTaskDidFinishLoadingWithCache:self.cache];
}
}
}
}
|
最后将拿到的数据塞进AVAssetResourceLoaderDelegate代理中,交还给AVPlayer,就可以播放了
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
|
AVAssetResourceLoaderDelegateObjective-C
- (BOOL)finishLoadingWithLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest {
//填充信息
CFStringRef contentType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef)(MimeType), NULL);
loadingRequest.contentInformationRequest.contentType = CFBridgingRelease(contentType);
loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
loadingRequest.contentInformationRequest.contentLength = self.requestTask.fileLength;
//读文件,填充数据
NSUInteger cacheLength = self.requestTask.cacheLength;
NSUInteger requestedOffset = loadingRequest.dataRequest.requestedOffset;
if (loadingRequest.dataRequest.currentOffset != 0 ) {
requestedOffset = loadingRequest.dataRequest.currentOffset;
}
NSUInteger canReadLength = cacheLength - (requestedOffset - self.requestTask.requestOffset);
NSUInteger respondLength = MIN(canReadLength, loadingRequest.dataRequest.requestedLength);
//SRQLog(@"好不容易填充一次");
[loadingRequest.dataRequest respondWithData:[FileHandle readTempFileDataWithOffset:requestedOffset - self.requestTask.requestOffset length:respondLength]];
//如果完全响应了所需要的数据,则完成
NSUInteger nowendOffset = requestedOffset + canReadLength;
NSUInteger reqEndOffset = loadingRequest.dataRequest.requestedOffset + loadingRequest.dataRequest.requestedLength;
if (nowendOffset >= reqEndOffset) {
[loadingRequest finishLoading];
return YES;
}
return NO;
}
- ( void )player{
self.resouerLoader = [[ResourceLoader alloc] init];
self.asset = [AVURLAsset URLAssetWithURL:[self.videoUrl customSchemeURL] options:nil];
[self.asset.resourceLoader setDelegate:self.resouerLoader queue:dispatch_get_main_queue()];
_playerItem = [AVPlayerItem playerItemWithAsset:self.asset];
_players = [AVPlayer playerWithPlayerItem:_playerItem];
}
- (BOOL)finishLoadingWithLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest {
//填充信息
CFStringRef contentType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef)(MimeType), NULL);
loadingRequest.contentInformationRequest.contentType = CFBridgingRelease(contentType);
loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
loadingRequest.contentInformationRequest.contentLength = self.requestTask.fileLength;
//读文件,填充数据
NSUInteger cacheLength = self.requestTask.cacheLength;
NSUInteger requestedOffset = loadingRequest.dataRequest.requestedOffset;
if (loadingRequest.dataRequest.currentOffset != 0 ) {
requestedOffset = loadingRequest.dataRequest.currentOffset;
}
NSUInteger canReadLength = cacheLength - (requestedOffset - self.requestTask.requestOffset);
NSUInteger respondLength = MIN(canReadLength, loadingRequest.dataRequest.requestedLength);
//SRQLog(@"好不容易填充一次");
[loadingRequest.dataRequest respondWithData:[FileHandle readTempFileDataWithOffset:requestedOffset - self.requestTask.requestOffset length:respondLength]];
//如果完全响应了所需要的数据,则完成
NSUInteger nowendOffset = requestedOffset + canReadLength;
NSUInteger reqEndOffset = loadingRequest.dataRequest.requestedOffset + loadingRequest.dataRequest.requestedLength;
if (nowendOffset >= reqEndOffset) {
[loadingRequest finishLoading];
return YES;
}
return NO;
}
- ( void )player{
self.resouerLoader = [[ResourceLoader alloc] init];
self.asset = [AVURLAsset URLAssetWithURL:[self.videoUrl customSchemeURL] options:nil];
[self.asset.resourceLoader setDelegate:self.resouerLoader queue:dispatch_get_main_queue()];
_playerItem = [AVPlayerItem playerItemWithAsset:self.asset];
_players = [AVPlayer playerWithPlayerItem:_playerItem];
}
|
注意:此方法服务器端最好支持Range头,这样才是分段下载。
总结
以上所述是小编给大家介绍的iOS 边下边播的实现代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对服务器之家网站的支持!
原文链接:https://blog.duicode.com/2526.html