iOS 边下边播的实现代码
项目中之前使用的是AVPlayer直接播放URL地址,但是不知道是相机的wifi不够稳定还是代码的问题,app总是出现缓冲卡顿,就考虑改写成边下边播的模式,查过了许多资料,发现大部分都是用的同一种方法
AVAssetResourceLoaderDelegate代理方法,来看看如何实现
首先要实现两个必须的代理方法
AVAssetResourceLoaderDelegateObjective-C #pragmamark-AVAssetResourceLoaderDelegate //开始加载 -(BOOL)resourceLoader:(AVAssetResourceLoader*)resourceLoadershouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest*)loadingRequest{ [selfaddLoadingRequest:loadingRequest]; returnYES; } //取消加载 -(void)resourceLoader:(AVAssetResourceLoader*)resourceLoaderdidCancelLoadingRequest:(AVAssetResourceLoadingRequest*)loadingRequest{ [selfremoveLoadingRequest:loadingRequest]; } #pragmamark-AVAssetResourceLoaderDelegate //开始加载 -(BOOL)resourceLoader:(AVAssetResourceLoader*)resourceLoadershouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest*)loadingRequest{ [selfaddLoadingRequest:loadingRequest]; returnYES; } //取消加载 -(void)resourceLoader:(AVAssetResourceLoader*)resourceLoaderdidCancelLoadingRequest:(AVAssetResourceLoadingRequest*)loadingRequest{ [selfremoveLoadingRequest:loadingRequest]; }
然后要定义一个下载类,其实就是分段下载数据的下载器
AVAssetResourceLoaderDelegateObjective-C -(void)start{ NSMutableURLRequest*request=[NSMutableURLRequestrequestWithURL:[self.requestURLoriginalSchemeURL]cachePolicy:NSURLRequestReloadIgnoringCacheDatatimeoutInterval:RequestTimeout]; if(self.requestOffset>0){ [requestaddValue:[NSStringstringWithFormat:@"bytes=%ld-%ld",self.requestOffset,self.fileLength-1]forHTTPHeaderField:@"Range"]; } self.session=[NSURLSessionsessionWithConfiguration:[NSURLSessionConfigurationdefaultSessionConfiguration]delegate:selfdelegateQueue:[NSOperationQueuemainQueue]]; self.task=[self.sessiondataTaskWithRequest:request]; [self.taskresume]; } #pragmamark-NSURLSessionDataDelegate //服务器响应 -(void)URLSession:(NSURLSession*)sessiondataTask:(NSURLSessionDataTask*)dataTaskdidReceiveResponse:(NSURLResponse*)responsecompletionHandler:(void(^)(NSURLSessionResponseDisposition))completionHandler{ if(self.cancel)return; SRQLog(@"response:%@",response); completionHandler(NSURLSessionResponseAllow); NSHTTPURLResponse*httpResponse=(NSHTTPURLResponse*)response; NSString*contentRange=[[httpResponseallHeaderFields]objectForKey:@"Content-Range"]; NSString*fileLength=[[contentRangecomponentsSeparatedByString:@"/"]lastObject]; self.fileLength=fileLength.integerValue>0?fileLength.integerValue:response.expectedContentLength; if(self.delegate&&[self.delegaterespondsToSelector:@selector(requestTaskDidReceiveResponse)]){ [self.delegaterequestTaskDidReceiveResponse]; } } //服务器返回数据可能会调用多次 -(void)URLSession:(NSURLSession*)sessiondataTask:(NSURLSessionDataTask*)dataTaskdidReceiveData:(NSData*)data{ if(self.cancel)return; //SRQLog(@"收到响应了:%@",data); self.cacheLength+=data.length; if(self.delegate&&[self.delegaterespondsToSelector:@selector(requestTaskDidUpdateCache)]){ [self.delegaterequestTaskDidUpdateCache]; } } //请求完成会调用该方法,请求失败则error有值 -(void)URLSession:(NSURLSession*)sessiontask:(NSURLSessionTask*)taskdidCompleteWithError:(NSError*)error{ if(self.cancel){ SRQLog(@"下载取消"); }else{ if(error){ if(self.delegate&&[self.delegaterespondsToSelector:@selector(requestTaskDidFailWithError:)]){ [self.delegaterequestTaskDidFailWithError:error]; } }else{ //可以缓存则保存文件 if(self.cache){ [FileHandlecacheTempFileWithFileName:[NSStringfileNameWithURL:self.requestURL]]; } if(self.delegate&&[self.delegaterespondsToSelector:@selector(requestTaskDidFinishLoadingWithCache:)]){ [self.delegaterequestTaskDidFinishLoadingWithCache:self.cache]; } } } } -(void)start{ NSMutableURLRequest*request=[NSMutableURLRequestrequestWithURL:[self.requestURLoriginalSchemeURL]cachePolicy:NSURLRequestReloadIgnoringCacheDatatimeoutInterval:RequestTimeout]; if(self.requestOffset>0){ [requestaddValue:[NSStringstringWithFormat:@"bytes=%ld-%ld",self.requestOffset,self.fileLength-1]forHTTPHeaderField:@"Range"]; } self.session=[NSURLSessionsessionWithConfiguration:[NSURLSessionConfigurationdefaultSessionConfiguration]delegate:selfdelegateQueue:[NSOperationQueuemainQueue]]; self.task=[self.sessiondataTaskWithRequest:request]; [self.taskresume]; } #pragmamark-NSURLSessionDataDelegate //服务器响应 -(void)URLSession:(NSURLSession*)sessiondataTask:(NSURLSessionDataTask*)dataTaskdidReceiveResponse:(NSURLResponse*)responsecompletionHandler:(void(^)(NSURLSessionResponseDisposition))completionHandler{ if(self.cancel)return; SRQLog(@"response:%@",response); completionHandler(NSURLSessionResponseAllow); NSHTTPURLResponse*httpResponse=(NSHTTPURLResponse*)response; NSString*contentRange=[[httpResponseallHeaderFields]objectForKey:@"Content-Range"]; NSString*fileLength=[[contentRangecomponentsSeparatedByString:@"/"]lastObject]; self.fileLength=fileLength.integerValue>0?fileLength.integerValue:response.expectedContentLength; if(self.delegate&&[self.delegaterespondsToSelector:@selector(requestTaskDidReceiveResponse)]){ [self.delegaterequestTaskDidReceiveResponse]; } } //服务器返回数据可能会调用多次 -(void)URLSession:(NSURLSession*)sessiondataTask:(NSURLSessionDataTask*)dataTaskdidReceiveData:(NSData*)data{ if(self.cancel)return; //SRQLog(@"收到响应了:%@",data); self.cacheLength+=data.length; if(self.delegate&&[self.delegaterespondsToSelector:@selector(requestTaskDidUpdateCache)]){ [self.delegaterequestTaskDidUpdateCache]; } } //请求完成会调用该方法,请求失败则error有值 -(void)URLSession:(NSURLSession*)sessiontask:(NSURLSessionTask*)taskdidCompleteWithError:(NSError*)error{ if(self.cancel){ SRQLog(@"下载取消"); }else{ if(error){ if(self.delegate&&[self.delegaterespondsToSelector:@selector(requestTaskDidFailWithError:)]){ [self.delegaterequestTaskDidFailWithError:error]; } }else{ //可以缓存则保存文件 if(self.cache){ [FileHandlecacheTempFileWithFileName:[NSStringfileNameWithURL:self.requestURL]]; } if(self.delegate&&[self.delegaterespondsToSelector:@selector(requestTaskDidFinishLoadingWithCache:)]){ [self.delegaterequestTaskDidFinishLoadingWithCache:self.cache]; } } } }
最后将拿到的数据塞进AVAssetResourceLoaderDelegate代理中,交还给AVPlayer,就可以播放了
AVAssetResourceLoaderDelegateObjective-C -(BOOL)finishLoadingWithLoadingRequest:(AVAssetResourceLoadingRequest*)loadingRequest{ //填充信息 CFStringRefcontentType=UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType,(__bridgeCFStringRef)(MimeType),NULL); loadingRequest.contentInformationRequest.contentType=CFBridgingRelease(contentType); loadingRequest.contentInformationRequest.byteRangeAccessSupported=YES; loadingRequest.contentInformationRequest.contentLength=self.requestTask.fileLength; //读文件,填充数据 NSUIntegercacheLength=self.requestTask.cacheLength; NSUIntegerrequestedOffset=loadingRequest.dataRequest.requestedOffset; if(loadingRequest.dataRequest.currentOffset!=0){ requestedOffset=loadingRequest.dataRequest.currentOffset; } NSUIntegercanReadLength=cacheLength-(requestedOffset-self.requestTask.requestOffset); NSUIntegerrespondLength=MIN(canReadLength,loadingRequest.dataRequest.requestedLength); //SRQLog(@"好不容易填充一次"); [loadingRequest.dataRequestrespondWithData:[FileHandlereadTempFileDataWithOffset:requestedOffset-self.requestTask.requestOffsetlength:respondLength]]; //如果完全响应了所需要的数据,则完成 NSUIntegernowendOffset=requestedOffset+canReadLength; NSUIntegerreqEndOffset=loadingRequest.dataRequest.requestedOffset+loadingRequest.dataRequest.requestedLength; if(nowendOffset>=reqEndOffset){ [loadingRequestfinishLoading]; returnYES; } returnNO; } -(void)player{ self.resouerLoader=[[ResourceLoaderalloc]init]; self.asset=[AVURLAssetURLAssetWithURL:[self.videoUrlcustomSchemeURL]options:nil]; [self.asset.resourceLoadersetDelegate:self.resouerLoaderqueue:dispatch_get_main_queue()]; _playerItem=[AVPlayerItemplayerItemWithAsset:self.asset]; _players=[AVPlayerplayerWithPlayerItem:_playerItem]; } -(BOOL)finishLoadingWithLoadingRequest:(AVAssetResourceLoadingRequest*)loadingRequest{ //填充信息 CFStringRefcontentType=UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType,(__bridgeCFStringRef)(MimeType),NULL); loadingRequest.contentInformationRequest.contentType=CFBridgingRelease(contentType); loadingRequest.contentInformationRequest.byteRangeAccessSupported=YES; loadingRequest.contentInformationRequest.contentLength=self.requestTask.fileLength; //读文件,填充数据 NSUIntegercacheLength=self.requestTask.cacheLength; NSUIntegerrequestedOffset=loadingRequest.dataRequest.requestedOffset; if(loadingRequest.dataRequest.currentOffset!=0){ requestedOffset=loadingRequest.dataRequest.currentOffset; } NSUIntegercanReadLength=cacheLength-(requestedOffset-self.requestTask.requestOffset); NSUIntegerrespondLength=MIN(canReadLength,loadingRequest.dataRequest.requestedLength); //SRQLog(@"好不容易填充一次"); [loadingRequest.dataRequestrespondWithData:[FileHandlereadTempFileDataWithOffset:requestedOffset-self.requestTask.requestOffsetlength:respondLength]]; //如果完全响应了所需要的数据,则完成 NSUIntegernowendOffset=requestedOffset+canReadLength; NSUIntegerreqEndOffset=loadingRequest.dataRequest.requestedOffset+loadingRequest.dataRequest.requestedLength; if(nowendOffset>=reqEndOffset){ [loadingRequestfinishLoading]; returnYES; } returnNO; } -(void)player{ self.resouerLoader=[[ResourceLoaderalloc]init]; self.asset=[AVURLAssetURLAssetWithURL:[self.videoUrlcustomSchemeURL]options:nil]; [self.asset.resourceLoadersetDelegate:self.resouerLoaderqueue:dispatch_get_main_queue()]; _playerItem=[AVPlayerItemplayerItemWithAsset:self.asset]; _players=[AVPlayerplayerWithPlayerItem:_playerItem]; }
注意:此方法服务器端最好支持Range头,这样才是分段下载。
总结
以上所述是小编给大家介绍的iOS边下边播的实现代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!