一:基础功能的实现:
1.基于AVFoundation/AVFoundation.h来实现功能.
1.1创建一个播放器,并且传一个url过来就播放,是否考虑缓存
- (void)playWithURL:(NSURL*)url isCache:(BOOL)isCache;
播放需要2个类来辅助,AVURLAsset,AVPlayerItem.
AVURLAsset:用来处理资源的请求
AVPlayerItem:用来组织资源, 当资源的组织者, 告诉我们资源准备好了之后, 我们再播放(后续会在个里面做缓存相关).使用KVO监听他的两个状态 ,来做播放的相应的操作.item 是他的实例对象.
//AVPlayerItemStatus statu
[itemaddObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
[itemaddObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
这里给播放器设置资源组织者,让他来管理资源.
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(@"状态未知");
self.playState = MLRemotePlayerStateFailed;
}
}
else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) {
BOOLisKeepUp = [change[NSKeyValueChangeNewKey]boolValue];
if(isKeepUp) {
NSLog(@"当前的资源, 准备的已经足够播放了");
if(!_isUserPause) {
[selfresume];
}
}else{
NSLog(@"资源还不够, 正在加载过程当中");
self.playState = MLRemotePlayerStateLoading;
}
}
}
2.基础功能包括播放,暂停,继续播放,快进,进度条拖动,倍速,静音功能,
基础的功能大部分都是都有直接调用的方法.直接自己封装下就好了,几个需要自己计算的功能.




二:状态控制,播放事件
比较烦的状态控制,需要不断调试,写一个枚举来记录.

在播放,暂停,加载数据等时候去赋值就好,需要自己去看在哪里加合适.
播放事件就是上面说到的监听的两个属性,AVPlayerItemStatus status ,self.player.currentItem.playbackLikelyToKeepUp
status :来监听状态是否资源准备好了,好了就可以播放
playbackLikelyToKeepUp:这个也是用来准备资源,看准备的资源是否足够这一段的播放,是一段一段的,所以写一个if else.
注意:要注意用户重复点击播放按钮,重复监听这两个属性,写一个移除监听的方法,判断当前播放任务已经存在并且是同一个url地址,是的话就继续播放,还要判断当前self.palyer.currentItem是否存在 存在就移除,然重新去监听.

三:拦截播放请求,准备动态下载,缓存数据
播放请求其实走的是资源组织者对象的代理方法, AVURLAsset *asset = [AVURLAsset assetWithURL:url]; 所以以这个为切入点,来拦截他的请求.
自己实现一个代理的类MLRemoteResourceLoaderDelegate,并且设置asset的代理为 这个类的实例. [asset.resourceLoader setDelegate:self.resourceLoaderDelegate queue:dispatch_get_main_queue()];
实现这两个代理:
/**当外界, 需要播放一段音频资源是, 会跑一个请求, 给这个对象
这个对象, 到时候, 只需要根据请求信息, 抛数据给外界
在这个里面做3件事情
*/
- (BOOL)resourceLoader:(AVAssetResourceLoader*)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest*)loadingRequest{
// 1. 填充相应的信息头信息
// 计算总大小
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. 相应数据给外界
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. 完成本次请求(一旦,所有的数据都给完了, 才能调用完成请求方法)
[loadingRequestfinishLoading];
}
/** 取消请求*/
- (void)resourceLoader:(AVAssetResourceLoader*)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest*)loadingRequest
四:动态下载,缓存数据
4.1创建一个管理文件的类,方便文件的操作,要先把下载中的文件放在temp文件夹,当他的下载大小和响应头中返回给我们的资源总大小一致的时候我们要把他移动到cache文件夹下保存.


文件夹操作实现就不写了
4.2创建下载的类,主要用于下载文件,并且将文件以输出流的方式输出到指定目录下,写一个代理,将告诉外界已经在下载了,然后由资源的代理在下载代理的视线中开始处理音频播放的相关操作.从而实现了下载和播放的操作,自己要弄一个可变数组来存储不断返回来的loadingRequest,因为loadingRequest是一段一段的数据流.

1>创建session对象

2>实现-(void)downloadWithUrl:(NSURL*)url offset:(longlong)offset方法

3>实现session的3个代理方法

在MLRemoteResourceLoaderDelegate中创建一个下载器downloader,delegate = self.实现代理方法,在代理方法中调用处理资源handeAllLoading的方法.
拿到资源的URL地址
NSURL*url = [loadingRequest.request.URLHTTPUrl];

1>在资源返回的代理方法中判断是否有本地数据;

2>判断是否有正在下载的数据

3>判断当前的资源是否需要重新下载
3.1>当资源请求, 开始点 < 下载的开始点
3.2> 当资源的请求, 开始点 > 下载的开始点 + 下载的长度 + 一个你自己想要的缓存值

4>开始处理资源请求 (在下载过程当中, 也要不断的判断)

4.3注意事项:假如在创建player的时候需要缓存, NSURLComponents *components 的scheme需要修改成sreaming的流模式.自己写一个URL分类,修改一下.
网友评论