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_tpthread_cond_t 等来线程同步。如果要修改、添加功能,会很容易出错,导致死锁。

于是在一年多以前开始着手构建更现代化的音频播放器,使用 GCD 来控制线程同步。

中途放弃了好几次(对 iOS 音频处理理解不充分导致),但是保持思考着问题的话,总会在某个时间能突然灵感一现,得到启发。

背景

先看下 DOUAudioStreamerFreeStreamer 的简单分析。

DOUAudioStreamer 简要流程/核心方法

批量解码,导致 CPU 瞬时峰值比较高

FreeStreamer/FreePlayer 简要流程/核心方法

每次解码单个 packet,CPU 平稳。但是歌曲切换时候,会有残留 buffer 等,延迟大。不支持 Wave 格式播放

Code Name: APlay

APlay 简要流程/核心方法

使用全新 GCD 实现流控制,简化线程同步,避免出现死锁,更低的延迟,更低的 CPU 和内存占用

FreeStreamerFreePlayer 播放器对 WAVE 格式支持不好,这也是新框架 APlay 需要解决的问题

Next

APlay 正式开源啦

学习资料

雷霄骅-视音频编解码技术零基础学习方法

Learning Core Audio A Hands-On Guide to Audio

KKBOX iOS/Mac OS X 基礎開發教材