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边下边播的实现代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!