一:基礎(chǔ)功能的實現(xiàn):
? ? 1.基于AVFoundation/AVFoundation.h來實現(xiàn)功能.
1.1創(chuàng)建一個播放器,并且傳一個url過來就播放,是否考慮緩存
- (void)playWithURL:(NSURL*)url isCache:(BOOL)isCache;
播放需要2個類來輔助,AVURLAsset,AVPlayerItem.
AVURLAsset:用來處理資源的請求
AVPlayerItem:用來組織資源,? 當資源的組織者, 告訴我們資源準備好了之后, 我們再播放(后續(xù)會在個里面做緩存相關(guān)).使用KVO監(jiān)聽他的兩個狀態(tài)? ,來做播放的相應(yīng)的操作.item 是他的實例對象.
//AVPlayerItemStatus statu
? ? [itemaddObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
? ? [itemaddObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
這里給播放器設(shè)置資源組織者,讓他來管理資源.
? ? self.player = [AVPlayer playerWithPlayerItem:item];
#pragma mark - KVO
-(void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context {
? ? if([keyPathisEqualToString:@"status"]) {
? ? ? ? AVPlayerItemStatus status = [change[NSKeyValueChangeNewKey] integerValue];
? ? ? ? if (status == AVPlayerItemStatusReadyToPlay) {
? ? ? ? ? ? NSLog(@"資源準備好了, 這時候播放就沒有問題");
? ? ? ? ? ? [selfresume];
//? ? ? ? ? ? [self.player play];
? ? ? ? }else{
? ? ? ? ? ? NSLog(@"狀態(tài)未知");
? ? ? ? ? ? self.playState = MLRemotePlayerStateFailed;
? ? ? ? }
? ? }
? ? else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) {
? ? ? ? BOOLisKeepUp = [change[NSKeyValueChangeNewKey]boolValue];
? ? ? ? if(isKeepUp) {
? ? ? ? ? ? NSLog(@"當前的資源, 準備的已經(jīng)足夠播放了");
? ? ? ? ? ? if(!_isUserPause) {
? ? ? ? ? ? ? ? [selfresume];
? ? ? ? ? ? }
? ? ? ? }else{
? ? ? ? ? ? NSLog(@"資源還不夠, 正在加載過程當中");
? ? ? ? ? ? self.playState = MLRemotePlayerStateLoading;
? ? ? ? }
? ? }
}
? ? 2.基礎(chǔ)功能包括播放,暫停,繼續(xù)播放,快進,進度條拖動,倍速,靜音功能,
基礎(chǔ)的功能大部分都是都有直接調(diào)用的方法.直接自己封裝下就好了,幾個需要自己計算的功能.
二:狀態(tài)控制,播放事件
比較煩的狀態(tài)控制,需要不斷調(diào)試,寫一個枚舉來記錄.
在播放,暫停,加載數(shù)據(jù)等時候去賦值就好,需要自己去看在哪里加合適.
播放事件就是上面說到的監(jiān)聽的兩個屬性,AVPlayerItemStatus status ,self.player.currentItem.playbackLikelyToKeepUp
status :來監(jiān)聽狀態(tài)是否資源準備好了,好了就可以播放
playbackLikelyToKeepUp:這個也是用來準備資源,看準備的資源是否足夠這一段的播放,是一段一段的,所以寫一個if else.
注意:要注意用戶重復(fù)點擊播放按鈕,重復(fù)監(jiān)聽這兩個屬性,寫一個移除監(jiān)聽的方法,判斷當前播放任務(wù)已經(jīng)存在并且是同一個url地址,是的話就繼續(xù)播放,還要判斷當前self.palyer.currentItem是否存在 存在就移除,然重新去監(jiān)聽.
三:攔截播放請求,準備動態(tài)下載,緩存數(shù)據(jù)
播放請求其實走的是資源組織者對象的代理方法,?AVURLAsset *asset = [AVURLAsset assetWithURL:url]; 所以以這個為切入點,來攔截他的請求.
自己實現(xiàn)一個代理的類MLRemoteResourceLoaderDelegate,并且設(shè)置asset的代理為 這個類的實例.? ? [asset.resourceLoader setDelegate:self.resourceLoaderDelegate queue:dispatch_get_main_queue()];
實現(xiàn)這兩個代理:
/**當外界, 需要播放一段音頻資源是, 會跑一個請求, 給這個對象
?? 這個對象, 到時候, 只需要根據(jù)請求信息, 拋數(shù)據(jù)給外界
在這個里面做3件事情
?*/
- (BOOL)resourceLoader:(AVAssetResourceLoader*)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest*)loadingRequest{
? ? // 1. 填充相應(yīng)的信息頭信息
? ? // 計算總大小
? ? NSURL*url = loadingRequest.request.URL;
? ? long long totalSize = [XMGRemoteAudioFile cacheFileSize:url];
? ? loadingRequest.contentInformationRequest.contentLength = totalSize;
? ? NSString *contentType = [XMGRemoteAudioFile contentType:url];
? ? loadingRequest.contentInformationRequest.contentType = contentType;
? ? loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
? ? // 2. 相應(yīng)數(shù)據(jù)給外界
? ? NSData *data = [NSData dataWithContentsOfFile:[XMGRemoteAudioFile cacheFilePath:url] options:NSDataReadingMappedIfSafe error:nil];
? ? longlongrequestOffset = loadingRequest.dataRequest.requestedOffset;
? ? NSIntegerrequestLength = loadingRequest.dataRequest.requestedLength;
? ? NSData*subData = [datasubdataWithRange:NSMakeRange(requestOffset, requestLength)];
? ? [loadingRequest.dataRequestrespondWithData:subData];
? ? // 3. 完成本次請求(一旦,所有的數(shù)據(jù)都給完了, 才能調(diào)用完成請求方法)
? ? [loadingRequestfinishLoading];
}
/** 取消請求*/
- (void)resourceLoader:(AVAssetResourceLoader*)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest*)loadingRequest
四:動態(tài)下載,緩存數(shù)據(jù)
4.1創(chuàng)建一個管理文件的類,方便文件的操作,要先把下載中的文件放在temp文件夾,當他的下載大小和響應(yīng)頭中返回給我們的資源總大小一致的時候我們要把他移動到cache文件夾下保存.
文件夾操作實現(xiàn)就不寫了
4.2創(chuàng)建下載的類,主要用于下載文件,并且將文件以輸出流的方式輸出到指定目錄下,寫一個代理,將告訴外界已經(jīng)在下載了,然后由資源的代理在下載代理的視線中開始處理音頻播放的相關(guān)操作.從而實現(xiàn)了下載和播放的操作,自己要弄一個可變數(shù)組來存儲不斷返回來的loadingRequest,因為loadingRequest是一段一段的數(shù)據(jù)流.
1>創(chuàng)建session對象
2>實現(xiàn)-(void)downloadWithUrl:(NSURL*)url offset:(longlong)offset方法
3>實現(xiàn)session的3個代理方法
? ?在MLRemoteResourceLoaderDelegate中創(chuàng)建一個下載器downloader,delegate = self.實現(xiàn)代理方法,在代理方法中調(diào)用處理資源handeAllLoading的方法.
? 拿到資源的URL地址?
? NSURL*url = [loadingRequest.request.URLHTTPUrl];
1>在資源返回的代理方法中判斷是否有本地數(shù)據(jù);
2>判斷是否有正在下載的數(shù)據(jù)
3>判斷當前的資源是否需要重新下載
3.1>當資源請求, 開始點 < 下載的開始點
3.2>?當資源的請求, 開始點 > 下載的開始點 + 下載的長度 + 一個你自己想要的緩存值
4>開始處理資源請求 (在下載過程當中, 也要不斷的判斷)
4.3注意事項:假如在創(chuàng)建player的時候需要緩存,?NSURLComponents *components 的scheme需要修改成sreaming的流模式.自己寫一個URL分類,修改一下.