iOS 边下边播的实现代码

 更新时间:2017年11月13日 11:52:58   投稿:mrr  
在之前项目中使用的是AVPlayer直接播放URL地址,但是不知道是相机的wifi不够稳定还是代码的问题,app总是出现缓冲卡顿。后来考虑改成边下边播模式,下面小编给大家带来了iOS 边下边播的实现代码,需要的朋友参考下吧

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

相关文章

  • 学习iOS开关按钮UISwitch控件

    学习iOS开关按钮UISwitch控件

    这篇文章主要为大家详细介绍了iOS开关按钮UISwitch控件,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-08-08
  • IOS中无限滚动Scrollview效果

    IOS中无限滚动Scrollview效果

    这篇文章主要为大家详细介绍了IOS中无限滚动Scrollview效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-02-02
  • iOS 11 AppIcon不显示问题小结

    iOS 11 AppIcon不显示问题小结

    小编在更新xcode9后,在运行老项目时遇到iOS 11 AppIcon不显示问题,下面小编大家分享一下我的思路,需要的朋友参考下吧
    2017-10-10
  • iOS 适配iPhone X的方法

    iOS 适配iPhone X的方法

    下面小编就为大家分享一篇iOS 适配iPhone X的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-01-01
  • IOS用AFN发送字符串形式的Json数据给服务器实例

    IOS用AFN发送字符串形式的Json数据给服务器实例

    本篇文章主要介绍了IOS用AFN发送字符串形式的Json数据给服务器实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2017-04-04
  • IOS 开发之UITextField的光标操作扩展

    IOS 开发之UITextField的光标操作扩展

    这篇文章主要介绍了IOS 开发之UITextField的光标操作扩展的相关资料,需要的朋友可以参考下
    2017-06-06
  • iOS中修改UITextField占位符字体颜色的方法总结

    iOS中修改UITextField占位符字体颜色的方法总结

    这篇文章给大家分享了iOS中修改UITextField占位符字体颜色的三个方法,分别是使用attributedPlaceholder属性、重写drawPlaceholderInRect方法和修改UITextField内部placeholderLaber的颜色,下面我们一起来看看详细的方法介绍。
    2016-09-09
  • iOS开发之数字每隔3位用逗号分隔

    iOS开发之数字每隔3位用逗号分隔

    以前在做电商app时经常会针对稍大的金额展示出来,需要每隔千位添加逗号便于用户识别,下面通过本文给大家分享ios中数字每隔3位用逗号分隔的实例代码,需要的朋友参考下吧
    2017-09-09
  • iOS实现点赞动画特效

    iOS实现点赞动画特效

    这篇文章主要为大家详细介绍了iOS实现点赞动画特效,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-01-01
  • iOS WKWebView适配实战篇

    iOS WKWebView适配实战篇

    这篇文章主要介绍了iOS WKWebView适配实战篇,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06

最新评论