使用 Swift 从零构建一个音乐播放器
2018年5月17日Code Name: APlay 01
这是一个系列专题,我们将学习如何使用 Swift 语言从零开始一步一步构建你自己的音乐播放器。
本人水平有限,如有错漏,欢迎指导,谢谢。
系列文章列表
- 构建通用的文件流提供器(Stream Provider)
- 构建 ID3, Flac Metadata 解析器
- 构建环形缓冲区(Circle Buffer)
- 构建 iOS 系统支持音频解码器(Decoder)
- 构建管理器(称之为 Composer),将 Streamer 和 Decoder,Circle Buffer 串联起来
前言
由于之前工作的内容是负责某小众音乐平台的 App (落网),及由此机遇开始接触到音频处理这一块,摸爬滚打了几年,算是了解了 iOS 系统下音频处理的一些大概。
一开始使用的是豆瓣开源的 DOUAudioStreamer,但是该项目缺于维护,并不能满足日益增长的实用需求。
于是乎,寻找新的替代品。考察了多个开源库,FreeStreamer 是一个相对来说功能挺完善的的第三方开源库。实现也没有很复杂的逻辑和技术,修改起来也比较简单。
在 FreeStreamer 的基础上,删除了部分无关的类,增加了适合项目的一些新功能。因为 FreeStreamer 是 Objective-C/C++ 实现的,对于使用 Swift 来说,不太友好。本着熟悉这个项目的目的使用 Swift 重写一遍,就有了现在的 FreePlayer。
得益于重写的工作,对于 FreeStreamer 的架构设计、功能实现有了一定的了解,为实现此文的播放器打下基础。
动机
由于都是比较有年代感的项目,大牛们都是用 pthread_mutex_t
、pthread_cond_t
等来线程同步。如果要修改、添加功能,会很容易出错,导致死锁。
于是在一年多以前开始着手构建更现代化的音频播放器,使用 GCD 来控制线程同步。
中途放弃了好几次(对 iOS 音频处理理解不充分导致),但是保持思考着问题的话,总会在某个时间能突然灵感一现,得到启发。
背景
先看下 DOUAudioStreamer 和 FreeStreamer 的简单分析。
DOUAudioStreamer 简要流程/核心方法
批量解码,导致 CPU 瞬时峰值比较高
FreeStreamer/FreePlayer 简要流程/核心方法
每次解码单个 packet,CPU 平稳。但是歌曲切换时候,会有残留 buffer 等,延迟大。不支持 Wave 格式播放
Code Name: APlay
APlay 简要流程/核心方法
使用全新 GCD 实现流控制,简化线程同步,避免出现死锁,更低的延迟,更低的 CPU 和内存占用
FreeStreamer,FreePlayer 播放器对 WAVE
格式支持不好,这也是新框架 APlay
需要解决的问题