好久没写过博客了,在这期间有很多同学通过博客评论、微博私信、邮件和我交流iOS音频方面的相关问题,其中被问到最多的是如何实现“边播边缓存”,这篇就来说一说这个话题。顺便一提,本文的题目虽然为“iOS音频播放”,但其中所涉及的部分技术方案在OSX平台或者在流播放视频下同样适用。
我能想到的方案
这类的技术方案其实有不少(其实在第一篇的末尾也略微有所涉及):
思路1. 最直接的方式,自行实现音频数据的请求在请求的过程中把数据缓存到磁盘,然后基于磁盘的数据自己实现解码、播放等功能;这个方法作为直接也最为复杂,开发者需要对音频播放的原理、操作系统等知识有一定程度的理解。如果能够实现这种方式所达到的效果也将会是最好的,整个过程都由开发者掌控,出现问题也可以对症下药。开源播放器FreeStreamer就是一个很好的例子,使用带有cache功能开源播放器或在其基础上进行二次开发也是不错的选择;
思路2. 请求拦截的方式,首先你需要一个能够进行流播放的播放器(如Apple提供的AVPlayer),通过拦截播放器发送的请求可以知道需要下载哪一段数据,于是就可以根据本地缓存文件的情况分段为播放器提供数据,如遇到已缓存的数据段直接从缓存中获取数据塞回给播放器,如遇到未缓存的数据段就发送请求获取数据,得到response和数据后保存到磁盘同时塞回给播放器。这种思路下有三个分支:
思路2.1 流播放器 + LocalServer,首先在搭建一个LocalServer(例如使用GCDWebServer),然后将URL组织成类似这种形式:
把组织好的URL交给播放器播放,播放器把请求发送到LocalServer上,LocalServer解析到实际的音频地址后发送请求或者读取已缓存的数据。
思路2.2 流播放器 + NSURLProtocol,大家都知道NSURLProtocol可以拦截Cocoa平台下URL Loading System中的请求,如果播放器的请求是运行在URL Loading System下的话使用这个方法可以轻松的拦截到播放器所发送的请求然后自己再进行请求或者读取缓存数据。这里需要注意如果使用AVPlayer作为播放器的话这种方法只在模拟器上才work,真机上并不能拦截到任何请求。这也证明AVPlayer在真机上并没有运行在URL Loading System下,但模拟器上却是(不知道在OSX下是否能work,有兴趣的同学可以尝试一下)。
注:如果播放器使用的是CFNetwork,也可以尝试拦截,例如使用FB的fishhook,这hook方法应该会遇上不少坑,请做好心理准备。。</