查看: 449|回复: 0

[IOS开发教程] iOS 边下边播的实现代码

发表于 2017-11-29 08:00:04

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

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

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

  1. AVAssetResourceLoaderDelegateObjective-C
  2. #pragma mark - AVAssetResourceLoaderDelegate
  3. //开始加载
  4. - (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest {
  5. [self addLoadingRequest:loadingRequest];
  6. return YES;
  7. }
  8. //取消加载
  9. - (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest {
  10. [self removeLoadingRequest:loadingRequest];
  11. }
  12. #pragma mark - AVAssetResourceLoaderDelegate
  13. //开始加载
  14. - (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest {
  15. [self addLoadingRequest:loadingRequest];
  16. return YES;
  17. }
  18. //取消加载
  19. - (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest {
  20. [self removeLoadingRequest:loadingRequest];
  21. }
复制代码

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

  1. AVAssetResourceLoaderDelegateObjective-C
  2. - (void)start {
  3. NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[self.requestURL originalSchemeURL] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:RequestTimeout];
  4. if (self.requestOffset > 0) {
  5. [request addValue:[NSString stringWithFormat:@"bytes=%ld-%ld", self.requestOffset, self.fileLength - 1] forHTTPHeaderField:@"Range"];
  6. }
  7. self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
  8. self.task = [self.session dataTaskWithRequest:request];
  9. [self.task resume];
  10. }
  11. #pragma mark - NSURLSessionDataDelegate
  12. //服务器响应
  13. - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
  14. if (self.cancel) return;
  15. SRQLog(@"response: %@",response);
  16. completionHandler(NSURLSessionResponseAllow);
  17. NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *)response;
  18. NSString * contentRange = [[httpResponse allHeaderFields] objectForKey:@"Content-Range"];
  19. NSString * fileLength = [[contentRange componentsSeparatedByString:@"/"] lastObject];
  20. self.fileLength = fileLength.integerValue > 0 ? fileLength.integerValue : response.expectedContentLength;
  21. if (self.delegate && [self.delegate respondsToSelector:@selector(requestTaskDidReceiveResponse)]) {
  22. [self.delegate requestTaskDidReceiveResponse];
  23. }
  24. }
  25. //服务器返回数据 可能会调用多次
  26. - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
  27. if (self.cancel) return;
  28. //SRQLog(@"收到响应了: %@",data);
  29. self.cacheLength += data.length;
  30. if (self.delegate && [self.delegate respondsToSelector:@selector(requestTaskDidUpdateCache)]) {
  31. [self.delegate requestTaskDidUpdateCache];
  32. }
  33. }
  34. //请求完成会调用该方法,请求失败则error有值
  35. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
  36. if (self.cancel) {
  37. SRQLog(@"下载取消");
  38. }else {
  39. if (error) {
  40. if (self.delegate && [self.delegate respondsToSelector:@selector(requestTaskDidFailWithError:)]) {
  41. [self.delegate requestTaskDidFailWithError:error];
  42. }
  43. }else {
  44. //可以缓存则保存文件
  45. if (self.cache) {
  46. [FileHandle cacheTempFileWithFileName:[NSString fileNameWithURL:self.requestURL]];
  47. }
  48. if (self.delegate && [self.delegate respondsToSelector:@selector(requestTaskDidFinishLoadingWithCache:)]) {
  49. [self.delegate requestTaskDidFinishLoadingWithCache:self.cache];
  50. }
  51. }
  52. }
  53. }
  54. - (void)start {
  55. NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[self.requestURL originalSchemeURL] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:RequestTimeout];
  56. if (self.requestOffset > 0) {
  57. [request addValue:[NSString stringWithFormat:@"bytes=%ld-%ld", self.requestOffset, self.fileLength - 1] forHTTPHeaderField:@"Range"];
  58. }
  59. self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
  60. self.task = [self.session dataTaskWithRequest:request];
  61. [self.task resume];
  62. }
  63. #pragma mark - NSURLSessionDataDelegate
  64. //服务器响应
  65. - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
  66. if (self.cancel) return;
  67. SRQLog(@"response: %@",response);
  68. completionHandler(NSURLSessionResponseAllow);
  69. NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *)response;
  70. NSString * contentRange = [[httpResponse allHeaderFields] objectForKey:@"Content-Range"];
  71. NSString * fileLength = [[contentRange componentsSeparatedByString:@"/"] lastObject];
  72. self.fileLength = fileLength.integerValue > 0 ? fileLength.integerValue : response.expectedContentLength;
  73. if (self.delegate && [self.delegate respondsToSelector:@selector(requestTaskDidReceiveResponse)]) {
  74. [self.delegate requestTaskDidReceiveResponse];
  75. }
  76. }
  77. //服务器返回数据 可能会调用多次
  78. - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
  79. if (self.cancel) return;
  80. //SRQLog(@"收到响应了: %@",data);
  81. self.cacheLength += data.length;
  82. if (self.delegate && [self.delegate respondsToSelector:@selector(requestTaskDidUpdateCache)]) {
  83. [self.delegate requestTaskDidUpdateCache];
  84. }
  85. }
  86. //请求完成会调用该方法,请求失败则error有值
  87. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
  88. if (self.cancel) {
  89. SRQLog(@"下载取消");
  90. }else {
  91. if (error) {
  92. if (self.delegate && [self.delegate respondsToSelector:@selector(requestTaskDidFailWithError:)]) {
  93. [self.delegate requestTaskDidFailWithError:error];
  94. }
  95. }else {
  96. //可以缓存则保存文件
  97. if (self.cache) {
  98. [FileHandle cacheTempFileWithFileName:[NSString fileNameWithURL:self.requestURL]];
  99. }
  100. if (self.delegate && [self.delegate respondsToSelector:@selector(requestTaskDidFinishLoadingWithCache:)]) {
  101. [self.delegate requestTaskDidFinishLoadingWithCache:self.cache];
  102. }
  103. }
  104. }
  105. }
复制代码

最后将拿到的数据塞进AVAssetResourceLoaderDelegate代理中,交还给AVPlayer,就可以播放了

  1. AVAssetResourceLoaderDelegateObjective-C
  2. - (BOOL)finishLoadingWithLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest {
  3. //填充信息
  4. CFStringRef contentType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef)(MimeType), NULL);
  5. loadingRequest.contentInformationRequest.contentType = CFBridgingRelease(contentType);
  6. loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
  7. loadingRequest.contentInformationRequest.contentLength = self.requestTask.fileLength;
  8. //读文件,填充数据
  9. NSUInteger cacheLength = self.requestTask.cacheLength;
  10. NSUInteger requestedOffset = loadingRequest.dataRequest.requestedOffset;
  11. if (loadingRequest.dataRequest.currentOffset != 0) {
  12. requestedOffset = loadingRequest.dataRequest.currentOffset;
  13. }
  14. NSUInteger canReadLength = cacheLength - (requestedOffset - self.requestTask.requestOffset);
  15. NSUInteger respondLength = MIN(canReadLength, loadingRequest.dataRequest.requestedLength);
  16. //SRQLog(@"好不容易填充一次");
  17. [loadingRequest.dataRequest respondWithData:[FileHandle readTempFileDataWithOffset:requestedOffset - self.requestTask.requestOffset length:respondLength]];
  18. //如果完全响应了所需要的数据,则完成
  19. NSUInteger nowendOffset = requestedOffset + canReadLength;
  20. NSUInteger reqEndOffset = loadingRequest.dataRequest.requestedOffset + loadingRequest.dataRequest.requestedLength;
  21. if (nowendOffset >= reqEndOffset) {
  22. [loadingRequest finishLoading];
  23. return YES;
  24. }
  25. return NO;
  26. }
  27. - (void)player{
  28. self.resouerLoader = [[ResourceLoader alloc] init];
  29. self.asset = [AVURLAsset URLAssetWithURL:[self.videoUrl customSchemeURL] options:nil];
  30. [self.asset.resourceLoader setDelegate:self.resouerLoader queue:dispatch_get_main_queue()];
  31. _playerItem = [AVPlayerItem playerItemWithAsset:self.asset];
  32. _players = [AVPlayer playerWithPlayerItem:_playerItem];
  33. }
  34. - (BOOL)finishLoadingWithLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest {
  35. //填充信息
  36. CFStringRef contentType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef)(MimeType), NULL);
  37. loadingRequest.contentInformationRequest.contentType = CFBridgingRelease(contentType);
  38. loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
  39. loadingRequest.contentInformationRequest.contentLength = self.requestTask.fileLength;
  40. //读文件,填充数据
  41. NSUInteger cacheLength = self.requestTask.cacheLength;
  42. NSUInteger requestedOffset = loadingRequest.dataRequest.requestedOffset;
  43. if (loadingRequest.dataRequest.currentOffset != 0) {
  44. requestedOffset = loadingRequest.dataRequest.currentOffset;
  45. }
  46. NSUInteger canReadLength = cacheLength - (requestedOffset - self.requestTask.requestOffset);
  47. NSUInteger respondLength = MIN(canReadLength, loadingRequest.dataRequest.requestedLength);
  48. //SRQLog(@"好不容易填充一次");
  49. [loadingRequest.dataRequest respondWithData:[FileHandle readTempFileDataWithOffset:requestedOffset - self.requestTask.requestOffset length:respondLength]];
  50. //如果完全响应了所需要的数据,则完成
  51. NSUInteger nowendOffset = requestedOffset + canReadLength;
  52. NSUInteger reqEndOffset = loadingRequest.dataRequest.requestedOffset + loadingRequest.dataRequest.requestedLength;
  53. if (nowendOffset >= reqEndOffset) {
  54. [loadingRequest finishLoading];
  55. return YES;
  56. }
  57. return NO;
  58. }
  59. - (void)player{
  60. self.resouerLoader = [[ResourceLoader alloc] init];
  61. self.asset = [AVURLAsset URLAssetWithURL:[self.videoUrl customSchemeURL] options:nil];
  62. [self.asset.resourceLoader setDelegate:self.resouerLoader queue:dispatch_get_main_queue()];
  63. _playerItem = [AVPlayerItem playerItemWithAsset:self.asset];
  64. _players = [AVPlayer playerWithPlayerItem:_playerItem];
  65. }
复制代码

注意:此方法服务器端最好支持Range头,这样才是分段下载。

总结

以上所述是小编给大家介绍的iOS 边下边播的实现代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对程序员之家网站的支持!



回复

使用道具 举报

关闭

站长推荐上一条 /1 下一条