iOS 邊下邊播的實(shí)現(xiàn)代碼
項(xiàng)目中之前使用的是AVPlayer直接播放URL地址,但是不知道是相機(jī)的wifi不夠穩(wěn)定還是代碼的問(wèn)題,app總是出現(xiàn)緩沖卡頓,就考慮改寫(xiě)成邊下邊播的模式,查過(guò)了許多資料,發(fā)現(xiàn)大部分都是用的同一種方法
AVAssetResourceLoaderDelegate 代理方法,來(lái)看看如何實(shí)現(xiàn)
首先要實(shí)現(xiàn)兩個(gè)必須的代理方法
AVAssetResourceLoaderDelegateObjective-C
#pragma mark - AVAssetResourceLoaderDelegate
//開(kāi)始加載
- (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
//開(kāi)始加載
- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest {
[self addLoadingRequest:loadingRequest];
return YES;
}
//取消加載
- (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest {
[self removeLoadingRequest:loadingRequest];
}
然后要定義一個(gè)下載類(lèi),其實(shí)就是分段下載數(shù)據(jù)的下載器
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
//服務(wù)器響應(yīng)
- (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];
}
}
//服務(wù)器返回?cái)?shù)據(jù) 可能會(huì)調(diào)用多次
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
if (self.cancel) return;
//SRQLog(@"收到響應(yīng)了: %@",data);
self.cacheLength += data.length;
if (self.delegate && [self.delegate respondsToSelector:@selector(requestTaskDidUpdateCache)]) {
[self.delegate requestTaskDidUpdateCache];
}
}
//請(qǐng)求完成會(huì)調(diào)用該方法,請(qǐng)求失敗則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
//服務(wù)器響應(yīng)
- (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];
}
}
//服務(wù)器返回?cái)?shù)據(jù) 可能會(huì)調(diào)用多次
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
if (self.cancel) return;
//SRQLog(@"收到響應(yīng)了: %@",data);
self.cacheLength += data.length;
if (self.delegate && [self.delegate respondsToSelector:@selector(requestTaskDidUpdateCache)]) {
[self.delegate requestTaskDidUpdateCache];
}
}
//請(qǐng)求完成會(huì)調(diào)用該方法,請(qǐng)求失敗則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];
}
}
}
}
最后將拿到的數(shù)據(jù)塞進(jìn)AVAssetResourceLoaderDelegate代理中,交還給AVPlayer,就可以播放了
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;
//讀文件,填充數(shù)據(jù)
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]];
//如果完全響應(yīng)了所需要的數(shù)據(jù),則完成
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;
//讀文件,填充數(shù)據(jù)
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]];
//如果完全響應(yīng)了所需要的數(shù)據(jù),則完成
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];
}
注意:此方法服務(wù)器端最好支持Range頭,這樣才是分段下載。
總結(jié)
以上所述是小編給大家介紹的iOS 邊下邊播的實(shí)現(xiàn)代碼,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
學(xué)習(xí)iOS開(kāi)關(guān)按鈕UISwitch控件
這篇文章主要為大家詳細(xì)介紹了iOS開(kāi)關(guān)按鈕UISwitch控件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08
IOS中無(wú)限滾動(dòng)Scrollview效果
這篇文章主要為大家詳細(xì)介紹了IOS中無(wú)限滾動(dòng)Scrollview效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-02-02
iOS 11 AppIcon不顯示問(wèn)題小結(jié)
小編在更新xcode9后,在運(yùn)行老項(xiàng)目時(shí)遇到iOS 11 AppIcon不顯示問(wèn)題,下面小編大家分享一下我的思路,需要的朋友參考下吧2017-10-10
IOS用AFN發(fā)送字符串形式的Json數(shù)據(jù)給服務(wù)器實(shí)例
本篇文章主要介紹了IOS用AFN發(fā)送字符串形式的Json數(shù)據(jù)給服務(wù)器實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-04-04
IOS 開(kāi)發(fā)之UITextField的光標(biāo)操作擴(kuò)展
這篇文章主要介紹了IOS 開(kāi)發(fā)之UITextField的光標(biāo)操作擴(kuò)展的相關(guān)資料,需要的朋友可以參考下2017-06-06
iOS中修改UITextField占位符字體顏色的方法總結(jié)
這篇文章給大家分享了iOS中修改UITextField占位符字體顏色的三個(gè)方法,分別是使用attributedPlaceholder屬性、重寫(xiě)drawPlaceholderInRect方法和修改UITextField內(nèi)部placeholderLaber的顏色,下面我們一起來(lái)看看詳細(xì)的方法介紹。2016-09-09
iOS開(kāi)發(fā)之?dāng)?shù)字每隔3位用逗號(hào)分隔
以前在做電商app時(shí)經(jīng)常會(huì)針對(duì)稍大的金額展示出來(lái),需要每隔千位添加逗號(hào)便于用戶(hù)識(shí)別,下面通過(guò)本文給大家分享ios中數(shù)字每隔3位用逗號(hào)分隔的實(shí)例代碼,需要的朋友參考下吧2017-09-09
iOS實(shí)現(xiàn)點(diǎn)贊動(dòng)畫(huà)特效
這篇文章主要為大家詳細(xì)介紹了iOS實(shí)現(xiàn)點(diǎn)贊動(dòng)畫(huà)特效,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-01-01

