實現AVPlayer離線快取

時光易逝發表於2019-03-01

系統播放器(AVPlayer)不支援快取,比較依賴網路,這段時間重新研究了下,為了以後方便學習和研究寫了份demo。功能:支援視訊的離線快取、邊下邊播、快進、斷網重連、資料自動儲存到本地、當下次重新播放時會優先使用本地資料等,專案 TTPlayerCache 支援了CocoaPods,用起來比較方便。

1、用法:

pod `TTPlayerCache`

#import <TTPlayerCache.h>
...
//把視訊播放地址轉成系統不能識別的URL
NSString *videoUrl = @"http://....";
videoUrl = TTResourceUrlFromOrigianllUrl(videoUrl);
...
...
//設定AVPlayer播放
//初始化代理
self.resourceLoaderDelegate = [TTResourceLoaderDelegate new];
self.urlAsset = [AVURLAsset assetWithURL:self.videoURL];
[self.urlAsset.resourceLoader setDelegate:self.resourceLoaderDelegate queue:TT_resourceLoader_delegate_queue()];
...
複製程式碼

2、視訊播放過程

通過對系統播放器抓包分析:
只測試了MP4視訊格式

  1. AVPlayer每次播放時第一次都會先請求bytes=0-2的資料,獲得到視訊的總位元組數、視訊型別等資訊
  2. 第二次請求全部資料bytes=x-,當資料響應填充後可能會有不同的反應,請求全部資料是requestsAllDataToEndOfResource == YES。
  3. 當視訊快進時,會取消先前的下載任務,如果快進區域沒有緩衝也會呼叫**resourceLoader: shouldWaitForLoadingOfRequestedResource:方法,這是會出現requestsAllDataToEndOfResource == NO/YES的loadingRequest ,然後繼續重複。

    當視訊快取到一定程度時系統會呼叫
    resourceLoader: didCancelLoadingRequest:**取消下載任務。

3、程式碼分析:

程式碼比較少,主要是TTResourceLoaderDelegate和TTResourceLoaderData這兩個類

  • TTResourceLoaderDelegate 實現AVAssetResourceLoaderDelegate的兩個代理方法。
- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest { 
//對loadingRequest進行處理,資料回填
...
return YES;
}
- (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest {
//取消請求處理
...
}
複製程式碼
  • TTResourceLoaderData實現對loadingRequest響應資料、快取、判斷是否繼續請求資料(還是使用本地資料)、請求任務取消等操作。
  • TTResourceLoaderData對下載資料的處理類。
  • TTResourceLoaderCache對本地快取資料的處理,可以獲取總快取大小、清空全部快取和刪除某個快取。
  • TTPlayerCacheMacro定義了TTPlayerCache的Scheme等常量、URL轉換方法。
  • TTReachability 是對Reachability原始碼加了字首方便使用,管理視訊播放過程中斷網處理。

- (void)handleAssetResourceLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest {...}
實現對loadingRequest的處理,根據loadingRequest.dataRequest.requestsAllDataToEndOfResource == YES與否分別處理(是否等待請求全部資料),封裝不同的NSURLRequest,判斷是否使用本地資料。requestsAllDataToEndOfResource 為YES的下載任務只有一個,在開始之前取消上一個等等。

  • 下載任務收到響應時- (void)TT_downloadTaskDataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response,先初始化一個TTResourceLoaderData儲存下視訊的總位元組數和型別,TTResourceLoaderData會建立一個和視訊位元組數一樣長度的NSMutableData用於填充資料。
  • 下載任務收到資料時- (void)TT_downloadTaskDataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data ->呼叫TTResourceLoaderData- (void)appendData:(NSData *)data taskId:(NSUInteger)taskIdentifier方法對資料進行處理:
    把data放到_data的正確位置、調整_receivedDataPointArray(儲存已經下載的資料的位置)、loadingRequest.dataReqeust響應資料 ,具體看程式碼。
  • 下載任務完成或出錯- (void)TT_downloadTaskTask:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error ->呼叫TTResourceLoaderData- (void)taskCompleteWithError:(NSError *)error taskId:(NSUInteger)taskIdentifier下載任務是否取消、網路錯誤、正常完成分別處理。
  • 網路恢復時呼叫- (void)reloadLoadingRequestWhenHasNetError重新整理播放器。
    對下載資料的處理都在TTResourceLoaderData類裡完成。

重點:

loadingRequest.dataRequest.requestsAllDataToEndOfResource == YES or NO 的處理
TTResourceLoaderData物件對視訊資料實際位置的表示…
第一次寫,寫的比較簡陋,不對、不好的地方歡迎指正,謝謝!

相關文章