如果你要做一个类似网易云音乐,豆瓣电台的在线音乐类APP,你会这么做?
音频播放的需求
- 离线播放:播放本地音频文件,包括先下载完成音频文件再进行播放,这种使用AVFoundation里的AVAudioPlayer就可以满足
- 在线播放:使用AVFoundation的AVPlayer可以满足
- 在线播放同时存储文件:使用AudioFileStreamer+AudioQueue可以满足
- 在线播放且带有音效处理:使用AudioFileStreamer+AudioQueue+音效处理(系统自带或者自行开发)来满足
AVPlayer
AVPlayer存在于AVFoundation中,其实它是一个视频播放器,但是用它来播放音乐是没问题的。
AVAsset是抽象类,不能直接使用,其子类AVURLAsset可以根据URL生成包含媒体信息的Asset对象。
AVPlayerItem:和媒体资源存在对应关系,管理媒体资源的信息和状态。
功能实现
- 网络链接播放音乐
1 | NSURl* url = [NSURL URLWithString:self.currentSong.url]; |
- 上一首,下一首
两种实现,一种是自行控制下一首歌曲的item,将其替换到当前播放的item一种是使用AVPlayer的子类AVQueuePlayer来播放多个item,调用advanceToNextItem来播放下一首1
[player replaceCurrentItemWithPlayerItem:songItem];
1 | NSArray * items = @[item1, item2, item3 ....]; |
AVPlayer缓存实现
AVPlayer使用过程中,有很多局限性,比如播放音乐时,不能控制其内部播放逻辑,比如播放时seek会失败,数据加载完毕后不能获取到数据文件进行其他操作,因此需要其他方法弥补其不足,AVAssetResourceLoader
.
AVAssetResourceLoader
:可以自行掌握AVPlayer数据的加载,包括获取AVPlaer需要的数据信息,以及传递多少数据给AVPlayer
AVAssetResourceLoaderDelegate
使用AVAssetResourceLoader需要实现AVAssetResourceLoaderDelegate方法
1 | AVURLAsset *urlAsset = ... |
找一个对象实现AVAssetResourceLoaderDelegate这个协议的方法,丢给asset,再把asset丢给AVPlayer,AVPlayer在执行播放的时候就会去访问delegate,能否播放这个url,然后会触发下面方法
1 | - (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader |
此时可以保存loadingRequest并对其所指定的数据进行读取或下载操作,当数据读取或下载完成,我们可以对loadingRequest进行完成操作。
1 | - (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader |
这是我们需要取消loadingRequest所指定的数据的读取或下载操作
- 如何请求数据
shouldWaitForLoadingOfRequestedResource
代理方法会得到一个AVAssetResourceLoadingRequest
对象
1 | @interface AVAssetResourceLoadingRequest : NSObject |
request代表原始的请求,AVPlayer是会触发分片下载的策略,dataRequest中可以得到请求范围的信息,有了请求地址和请求范围,可以重新创建一个设置了请求Range头的NSURLRequest对象,让下载器去下载这个文件的Range范围内的数据。