学计算机的那个

不是我觉到、悟到,你给不了我,给了也拿不住;只有我觉到、悟到,才有可能做到,能做到的才是我的.

0%

iOS音频边加载边播放

如果你要做一个类似网易云音乐,豆瓣电台的在线音乐类APP,你会这么做?

音频播放的需求

  1. 离线播放:播放本地音频文件,包括先下载完成音频文件再进行播放,这种使用AVFoundation里的AVAudioPlayer就可以满足
  2. 在线播放:使用AVFoundation的AVPlayer可以满足
  3. 在线播放同时存储文件:使用AudioFileStreamer+AudioQueue可以满足
  4. 在线播放且带有音效处理:使用AudioFileStreamer+AudioQueue+音效处理(系统自带或者自行开发)来满足

AVPlayer

AVPlayer存在于AVFoundation中,其实它是一个视频播放器,但是用它来播放音乐是没问题的。

AVAsset是抽象类,不能直接使用,其子类AVURLAsset可以根据URL生成包含媒体信息的Asset对象。

AVPlayerItem:和媒体资源存在对应关系,管理媒体资源的信息和状态。

功能实现

  1. 网络链接播放音乐
1
2
3
NSURl* url  = [NSURL URLWithString:self.currentSong.url];
AVPleryItem* songItem=[[AVPlayerItem alloc]initWithURL:url];
AVPlayer* player=[[AVPlayer alloc]initWithPlayerItem:songItem];
  1. 上一首,下一首
    两种实现,一种是自行控制下一首歌曲的item,将其替换到当前播放的item
    1
    [player replaceCurrentItemWithPlayerItem:songItem];
    一种是使用AVPlayer的子类AVQueuePlayer来播放多个item,调用advanceToNextItem来播放下一首
1
2
NSArray * items = @[item1, item2, item3 ....];
AVQueuePlayer * queuePlayer = [[AVQueuePlayer alloc]initWithItems:items];

AVPlayer缓存实现

AVPlayer使用过程中,有很多局限性,比如播放音乐时,不能控制其内部播放逻辑,比如播放时seek会失败,数据加载完毕后不能获取到数据文件进行其他操作,因此需要其他方法弥补其不足,AVAssetResourceLoader.

AVAssetResourceLoader:可以自行掌握AVPlayer数据的加载,包括获取AVPlaer需要的数据信息,以及传递多少数据给AVPlayer

AVAssetResourceLoaderDelegate

使用AVAssetResourceLoader需要实现AVAssetResourceLoaderDelegate方法

1
2
AVURLAsset *urlAsset = ...
[urlAsset.resourceLoader setDelegate:<AVAssetResourceLoaderDelegate> queue:dispatch_get_main_queue()];

找一个对象实现AVAssetResourceLoaderDelegate这个协议的方法,丢给asset,再把asset丢给AVPlayer,AVPlayer在执行播放的时候就会去访问delegate,能否播放这个url,然后会触发下面方法

1
2
- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader 
shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest; //要求加载资源的代理方法

此时可以保存loadingRequest并对其所指定的数据进行读取或下载操作,当数据读取或下载完成,我们可以对loadingRequest进行完成操作。

1
2
- (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader 
didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest; //取消加载资源的代理方法

这是我们需要取消loadingRequest所指定的数据的读取或下载操作

  • 如何请求数据
    shouldWaitForLoadingOfRequestedResource代理方法会得到一个AVAssetResourceLoadingRequest对象
1
2
3
4
5
6
7
8
9
10
11
12
13
@interface AVAssetResourceLoadingRequest : NSObject 

@property (nonatomic, readonly) NSURLRequest *request;

@property (nonatomic, readonly, nullable) AVAssetResourceLoadingContentInformationRequest *contentInformationRequest NS_AVAILABLE(10_9, 7_0);

@property (nonatomic, readonly, nullable) AVAssetResourceLoadingDataRequest *dataRequest NS_AVAILABLE(10_9, 7_0);

- (void)finishLoading NS_AVAILABLE(10_9, 7_0);

- (void)finishLoadingWithError:(nullable NSError *)error;

@end

request代表原始的请求,AVPlayer是会触发分片下载的策略,dataRequest中可以得到请求范围的信息,有了请求地址和请求范围,可以重新创建一个设置了请求Range头的NSURLRequest对象,让下载器去下载这个文件的Range范围内的数据。

参考

1. AVPlayer 音视频缓存方案
2.VPlayer的缓存实现