本文演示如何使用第三方的
StreamingKit 库,来实现网络流音频的播放。
一、StreamingKit介绍和配置
1,基本介绍
(1)
StreamingKit 是一个适用于 iOS 和 Mac OSX 的音频播放流媒体库。StreamingKit 提供了一个简洁的面向对象 API,用于在 CoreAudio 框架下进行音频的解压和播放(采用硬件或软件编解码器)处理。
(2)
StreamingKit 的主要机制是对从播放器输入的数据源进行解耦,从而使高级定制的数据源可以进行诸如基于流媒体的渐进式下载、编码解码、自动恢复、动态缓冲之类的处理。StreamingKit 是唯一支持不同格式音频文件无缝播放的音频播放流媒体库。
(3)
Github 主页:https://github.com/tumtumtum/StreamingKit
2,主要特点
- 免费开源
- 简洁的 API
- 可读性很强的源代码
- 精心使用多线程提供了一个快速响应的 API,既能防止线程阻塞,又能保证缓冲流畅
- 缓冲并无缝播放所有不同格式的音频文件
- 容易实现的音频数据源(支持本地、HTTP、AutoRecovering HTTP 作为数据源)
- 容易 kuo 扩展数据源以支持自动缓冲、编码等
- 低耗电和低 CPU 使用率(CPU 使用率 0%,流式处理时使用率为 1%)
- 优化线性数据源,仅随机访问数据源需要搜索
- StreamingKit0.2.0 使用 AudioUnit API 而不是速度较慢的音频队列 API,允许对原始 PCM 数据进行实时截取以获得并行测量、EQ 等特征
- 电能计量
- 内置的均衡器(iOS5.0 及以上版本、OSX10.9 及以上版本)支持音频播放的同时动态改变、启用、禁用均衡器
- 提供了 iOS 和 Mac OSX 应用实例
3,安装配置
(1)将源码包下载下来后,将其中的
StreamingKit/StreamingKit 文件夹复制到项目中来。
(2)创建桥接头文件,内容如下:
1
|
#
import
"STKAudioPlayer.h"
|
二、制作一个网络音频播放器
1,效果图
(1)程序运行后自动开始播放音乐(整个队列一个有
3 首歌曲,默认先播放第一首)
(2)点击“
上一曲”“下一曲”按钮可以切换当前播放歌曲。
(3)歌曲播放过程中进度条会随之变化,进度条右侧会显示出当前歌曲播放时间。
(4)进度条可以拖动,拖动结束后自动播放该时间点的音乐。
(5)点击“
暂停”按钮可以交替切换播放器暂停、继续状态。
(6)点击“
结束”按钮,结束整个播放器的音乐播放。
(2)为了让播放器能在后台持续播放,我们需要将 Targets -> Capabilities -> BackgroundModes 设为 ON,同时勾选“Audio, AirPlay, and Picture in Picture”。
(3)主视图代码(ViewController.swift)
源码下载:
hangge_1667.zip
2,实现步骤
(1)在
info.plist 中添加如下配置以支持 http 传输。
1
2
3
4
5
|
<key>
NSAppTransportSecurity
</key>
<dict>
<key>
NSAllowsArbitraryLoads
</key>
<
true
/>
</dict>
|
(2)为了让播放器能在后台持续播放,我们需要将 Targets -> Capabilities -> BackgroundModes 设为 ON,同时勾选“Audio, AirPlay, and Picture in Picture”。
同时还要在
AppDelegate.swift 中注册后台播放。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
import
UIKit
import
AVFoundation
@UIApplicationMain
class
AppDelegate
:
UIResponder
,
UIApplicationDelegate
{
var
window:
UIWindow
?
func
application(_ application:
UIApplication
, didFinishLaunchingWithOptions
launchOptions: [
UIApplicationLaunchOptionsKey
:
Any
]?) ->
Bool
{
// 注册后台播放
let
session =
AVAudioSession
.sharedInstance()
do {
try session.setActive(
true
)
try session.setCategory(
AVAudioSessionCategoryPlayback
)
} catch {
print
(error)
}
return
true
}
func
applicationWillResignActive(_ application:
UIApplication
) {
}
func
applicationDidEnterBackground(_ application:
UIApplication
) {
}
func
applicationWillEnterForeground(_ application:
UIApplication
) {
}
func
applicationDidBecomeActive(_ application:
UIApplication
) {
}
func
applicationWillTerminate(_ application:
UIApplication
) {
}
}
|
(3)主视图代码(ViewController.swift)
import
UIKit
class
ViewController
:
UIViewController
{
//显示歌曲标题
@IBOutlet
weak
var
titleLabel:
UILabel
!
//暂停按钮
@IBOutlet
weak
var
pauseBtn:
UIButton
!
//可拖动的进度条
@IBOutlet
weak
var
playbackSlider:
UISlider
!
//当前播放时间标签
@IBOutlet
weak
var
playTime:
UILabel
!
//更新进度条定时器
var
timer:
Timer
!
//音频播放器
var
audioPlayer:
STKAudioPlayer
!
//播放列表
var
queue = [
Music
(name:
"歌曲1"
,
Music
(name:
"歌曲2"
,
Music
(name:
"歌曲3"
,
//当前播放音乐索引
var
currentIndex:
Int
= -1
//是否循环播放
var
loop:
Bool
=
false
//当前播放状态
var
state:
STKAudioPlayerState
= []
override
func
viewDidLoad() {
super
.viewDidLoad()
//设置进度条相关属性
playbackSlider!.minimumValue = 0
playbackSlider!.isContinuous =
false
//重置播放器
resetAudioPlayer()
//开始播放歌曲列表
playWithQueue(queue: queue)
//设置一个定时器,每三秒钟滚动一次
timer =
Timer
.scheduledTimer(timeInterval: 0.1, target:
self
,
selector: #selector(tick), userInfo:
nil
, repeats:
true
)
}
//重置播放器
func
resetAudioPlayer() {
var
options =
STKAudioPlayerOptions
()
options.flushQueueOnSeek =
true
options.enableVolumeMixer =
true
audioPlayer =
STKAudioPlayer
(options: options)
audioPlayer.meteringEnabled =
true
audioPlayer.volume = 1
audioPlayer.delegate =
self
}
//开始播放歌曲列表(默认从第一首歌曲开始播放)
func
playWithQueue(queue: [
Music
], index:
Int
= 0) {
guard index >= 0 && index < queue.count
else
{
return
}
self
.queue = queue
audioPlayer.clearQueue()
let
url = queue[index].url
audioPlayer.play(url)
for
i
in
1 ..< queue.count {
audioPlayer.queue(queue[
Int
((index + i) % queue.count)].url)
}
currentIndex = index
loop =
false
}
//停止播放
func
stop() {
audioPlayer.stop()
queue = []
currentIndex = -1
}
//单独播放某个歌曲
func
play(file:
Music
) {
audioPlayer.play(file.url)
}
//下一曲
func
next() {
guard queue.count > 0
else
{
return
}
currentIndex = (currentIndex + 1) % queue.count
playWithQueue(queue: queue, index: currentIndex)
}
//上一曲
func
prev() {
currentIndex =
max
(0, currentIndex - 1)
playWithQueue(queue: queue, index: currentIndex)
}
//下一曲按钮点击
@IBAction
func
nextBtnTapped(_ sender:
Any
) {
next()
}
//上一曲按钮点击
@IBAction
func
prevBtnTapped(_ sender:
Any
) {
prev()
}
//暂停继续按钮点击
@IBAction
func
pauseBtnTapped(_ sender:
Any
) {
//在暂停和继续两个状态间切换
if
self
.state == .paused {
audioPlayer.resume()
}
else
{
audioPlayer.pause()
}
}
//结束按钮点击
@IBAction
func
stopBtnTapped(_ sender:
Any
) {
stop()
}
//定时器响应,更新进度条和时间
func
tick() {
if
state == .playing {
//更新进度条进度值
self
.playbackSlider!.value =
Float
(audioPlayer.progress)
//一个小算法,来实现00:00这种格式的播放时间
let
all:
Int
=
Int
(audioPlayer.progress)
let
m:
Int
=all % 60
let
f:
Int
=
Int
(all/60)
var
time:
String
=
""
if
f<10{
time=
"0\(f):"
}
else
{
time=
"\(f)"
}
if
m<10{
time+=
"0\(m)"
}
else
{
time+=
"\(m)"
}
//更新播放时间
self
.playTime!.text=time
}
}
//拖动进度条改变值时触发
@IBAction
func
playbackSliderValueChanged(_ sender:
Any
) {
//播放器定位到对应的位置
audioPlayer.seek(toTime:
Double
(playbackSlider.value))
//如果当前时暂停状态,则继续播放
if
state == .paused
{
audioPlayer.resume()
}
}
override
func
didReceiveMemoryWarning() {
super
.didReceiveMemoryWarning()
}
}
//Audio Player相关代理方法
extension
ViewController
:
STKAudioPlayerDelegate
{
//开始播放歌曲
func
audioPlayer(_ audioPlayer:
STKAudioPlayer
,
didStartPlayingQueueItemId queueItemId:
NSObject
) {
if
let
index = (queue.index { $0.url == queueItemId
as
!
URL
}) {
currentIndex = index
}
}
//缓冲完毕
func
audioPlayer(_ audioPlayer:
STKAudioPlayer
,
didFinishBufferingSourceWithQueueItemId queueItemId:
NSObject
) {
updateNowPlayingInfoCenter()
}
//播放状态变化
func
audioPlayer(_ audioPlayer:
STKAudioPlayer
,
stateChanged state:
STKAudioPlayerState
,
previousState:
STKAudioPlayerState
) {
self
.state = state
if
state != .stopped && state != .error && state != .disposed {
}
updateNowPlayingInfoCenter()
}
//播放结束
func
audioPlayer(_ audioPlayer:
STKAudioPlayer
,
didFinishPlayingQueueItemId queueItemId:
NSObject
,
with stopReason:
STKAudioPlayerStopReason
,
andProgress progress:
Double
, andDuration duration:
Double
) {
if
let
index = (queue.index {
$0.url == audioPlayer.currentlyPlayingQueueItemId()
as
!
URL
}) {
currentIndex = index
}
//自动播放下一曲
if
stopReason == .eof {
next()
}
else
if
stopReason == .error {
stop()
resetAudioPlayer()
}
}
//发生错误
func
audioPlayer(_ audioPlayer:
STKAudioPlayer
,
unexpectedError errorCode:
STKAudioPlayerErrorCode
) {
print
(
"Error when playing music \(errorCode)"
)
resetAudioPlayer()
playWithQueue(queue: queue, index: currentIndex)
}
//更新当前播放信息
func
updateNowPlayingInfoCenter() {
if
currentIndex >= 0 {
let
music = queue[currentIndex]
//更新标题
titleLabel.text =
"当前播放:\(music.name)"
//更新暂停按钮名字
let
pauseBtnTitle =
self
.state == .playing ?
"暂停"
:
"继续"
pauseBtn.setTitle(pauseBtnTitle,
for
: .normal)
//设置进度条相关属性
playbackSlider!.maximumValue =
Float
(audioPlayer.duration)
}
else
{
//停止播放
titleLabel.text =
"播放停止!"
//更新进度条和时间标签
playbackSlider.value = 0
playTime.text =
"--:--"
}
}
}
//歌曲类
class
Music
{
var
name:
String
var
url:
URL
//类构造函数
init
(name:
String
, url:
URL
){
self
.name = name
self
.url = url
}
}
|
![](https://i-blog.csdnimg.cn/blog_migrate/b21d9ae556170caacaaa2b785159716c.gif)
原文出自: www.hangge.com 转载请保留原文链接: http://www.hangge.com/blog/cache/detail_1667.html