iOS 邊下邊播的實(shí)現(xiàn)代碼
項(xiàng)目中之前使用的是AVPlayer直接播放URL地址,但是不知道是相機(jī)的wifi不夠穩(wěn)定還是代碼的問(wèn)題,app總是出現(xiàn)緩沖卡頓,就考慮改寫成邊下邊播的模式,查過(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è)下載類,其實(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-08IOS中無(wú)限滾動(dòng)Scrollview效果
這篇文章主要為大家詳細(xì)介紹了IOS中無(wú)限滾動(dòng)Scrollview效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-02-02iOS 11 AppIcon不顯示問(wèn)題小結(jié)
小編在更新xcode9后,在運(yùn)行老項(xiàng)目時(shí)遇到iOS 11 AppIcon不顯示問(wèn)題,下面小編大家分享一下我的思路,需要的朋友參考下吧2017-10-10IOS用AFN發(fā)送字符串形式的Json數(shù)據(jù)給服務(wù)器實(shí)例
本篇文章主要介紹了IOS用AFN發(fā)送字符串形式的Json數(shù)據(jù)給服務(wù)器實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-04-04IOS 開(kāi)發(fā)之UITextField的光標(biāo)操作擴(kuò)展
這篇文章主要介紹了IOS 開(kāi)發(fā)之UITextField的光標(biāo)操作擴(kuò)展的相關(guān)資料,需要的朋友可以參考下2017-06-06iOS中修改UITextField占位符字體顏色的方法總結(jié)
這篇文章給大家分享了iOS中修改UITextField占位符字體顏色的三個(gè)方法,分別是使用attributedPlaceholder屬性、重寫drawPlaceholderInRect方法和修改UITextField內(nèi)部placeholderLaber的顏色,下面我們一起來(lái)看看詳細(xì)的方法介紹。2016-09-09iOS開(kāi)發(fā)之?dāng)?shù)字每隔3位用逗號(hào)分隔
以前在做電商app時(shí)經(jīng)常會(huì)針對(duì)稍大的金額展示出來(lái),需要每隔千位添加逗號(hào)便于用戶識(shí)別,下面通過(guò)本文給大家分享ios中數(shù)字每隔3位用逗號(hào)分隔的實(shí)例代碼,需要的朋友參考下吧2017-09-09iOS實(shí)現(xiàn)點(diǎn)贊動(dòng)畫特效
這篇文章主要為大家詳細(xì)介紹了iOS實(shí)現(xiàn)點(diǎn)贊動(dòng)畫特效,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-01-01