使用AudioQueue实现一个音频播放(二)--边播边缓存

前一篇文章中介绍了使用AuidoQueue实现一个音频播放器的功能,最近我又把这个项目完善了一下,做了一个边播边缓存的功能。

因为播放部分的逻辑我们已经完成,这里主要是怎么缓存数据的问题,实现之前我想到了2种方案。

方案一 在didReceiveDataBlock中缓存文件

直接在拿到音频数据进行播放的同时就将数据写入文件,这种方案的优点是实时性,播放过的音频数据都会被缓存起来,缺点就是缓存的文件可能并不是一个完整的音频,比如播了1分钟后seek到2分钟的地方继续播,中间1分钟的数据可能就没有收到,这就导致缓存的文件是有问题的,如果下次直接播这个本地文件可能就凉凉了。。。

方案二 在success的回调中缓存数据

在success中回调回来的数据就是完整的音频数据,缓存这个数据不会出现数据不完整的情况,但是这个回调并不是实时返回的,会在整个音频的数据都获取到以后才会返回,有一个延迟,如果网络不太好,可能直到音频播放完了才会返回。

考虑到方案的可行性和数据的可用性,这里我选择了方案二,毕竟完整的数据比缓存的实时性更重要一些。当然,这个方案一我觉得还是有优化的空间,如果能够优化到保证了数据的完整性,会比方案二更优。

增加的模块

这里我增加了2个模块,一个是文件的操作模块,还有一个是网络模块。

LLYFileManager

文件操作模块是对音频数据的存储相关操作。

1
2
3
4
5
6
7
8
9
10
11
12
@interface LLYFileManager : NSObject
//网络地址转本地地址
+ (NSString *)pathWithUrl:(NSString *)fileUrl;
//将数据保存到本地目录下
+ (BOOL)saveFileWithPath:(NSString *)path fileObject:(id)fileObject;
//判断当前url对应的文件是否已经缓存
+ (BOOL)isFileExit:(NSString *)url;
//获取文件大小
+ (unsigned long long)fileSizeWithFilePath:(NSString *)filePath;
@end

LLYHttpSessionManager

网络模块是基于AFNetworking做的一个二次封装(参考这篇文章),主要就是请求音频数据。

播放优化

在播放前,我们需判断一下当前url对应的文件是否已经下载到本地了,如果已经下载就直接播本地的文件,否则还是走网络播放的逻辑。

这个判断逻辑我是这样处理的,先发送一个http head请求,拿到对应url文件的countOfBytesExpectedToReceive,然后将countOfBytesExpectedToReceive和本地文件大小(fileSize)进行比较,如果fileSize >= contentExceptLength,则说明本地文件是完整的音频文件,可以直接播放,否则的话还是走网络播放的逻辑,并在success回调中覆盖之前的不完整文件。

我的demo