Swift - 制作一个在线流媒体音乐播放器(使用StreamingKit库)
本文演示如何使用第三方的 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 } } |

原文出自:www.hangge.com 转载请保留原文链接:http://www.hangge.com/blog/cache/detail_1667.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
2015-07-25 BLE简介和Android BLE编程
2015-07-25 Android BLE开发之Android手机与BLE终端通信
2014-07-25 关于小米官网宣传照片真伪鉴定