首页 > 系统 > iOS > 正文

iOS 边下边播的实现代码

2019-10-21 18:43:47
字体:
来源:转载
供稿:网友

项目中之前使用的是AVPlayer直接播放URL地址,但是不知道是相机的wifi不够稳定还是代码的问题,app总是出现缓冲卡顿,就考虑改写成边下边播的模式,查过了许多资料,发现大部分都是用的同一种方法

AVAssetResourceLoaderDelegate 代理方法,来看看如何实现

首先要实现两个必须的代理方法

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];}

然后要定义一个下载类,其实就是分段下载数据的下载器

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,就可以播放了

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

 

注:相关教程知识阅读请移步到IOS开发频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表