Swift - 製做一個在線流媒體音樂播放器(使用StreamingKit庫)

 

 

在以前的文章中,我介紹瞭如何使用 AVPlayer 製做一個簡單的音樂播放器(點擊查看1點擊查看2)。雖然這個播放器也能夠播放網絡音頻,但其其實是將音頻文件下載到本地後再播放的。
本文演示如何使用第三方的 StreamingKit 庫,來實現網絡流音頻的播放。

1、StreamingKit介紹和配置

1,基本介紹

(1) StreamingKit 是一個適用於 iOS 和 Mac OSX 的音頻播放流媒體庫。StreamingKit 提供了一個簡潔的面向對象 API,用於在 CoreAudio 框架下進行音頻的解壓和播放(採用硬件或軟件編解碼器)處理。
(2) StreamingKit 的主要機制是對從播放器輸入的數據源進行解耦,從而使高級定製的數據源能夠進行諸如基於流媒體的漸進式下載、編碼解碼、自動恢復、動態緩衝之類的處理。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 文件夾複製到項目中來。
原文:Swift - 製做一個在線流媒體音樂播放器(使用StreamingKit庫)

(2)建立橋接頭文件,內容以下:
1
# import "STKAudioPlayer.h"

2、製做一個網絡音頻播放器

1,效果圖

(1)程序運行後自動開始播放音樂(整個隊列一個有 3 首歌曲,默認先播放第一首)
(2)點擊「 上一曲」「下一曲」按鈕能夠切換當前播放歌曲。
(3)歌曲播放過程當中進度條會隨之變化,進度條右側會顯示出當前歌曲播放時間。
(4)進度條能夠拖動,拖動結束後自動播放該時間點的音樂。
(5)點擊「 暫停」按鈕能夠交替切換播放器暫停、繼續狀態。
(6)點擊「 結束」按鈕,結束整個播放器的音樂播放。
 
原文:Swift - 製做一個在線流媒體音樂播放器(使用StreamingKit庫)

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」。
原文:Swift - 製做一個在線流媒體音樂播放器(使用StreamingKit庫)

同時還要在 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" ,
                        url: URL (string: "http://mxd.766.com/sdo/music/data/3/m10.mp3" )!),
                  Music (name: "歌曲2" ,
                        url: URL (string: "http://mxd.766.com/sdo/music/data/3/m12.mp3" )!),
                  Music (name: "歌曲3" ,
                        url: URL (string: "http://mxd.766.com/sdo/music/data/3/m13.mp3" )!)]
     
     //當前播放音樂索引
     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
     }
}
 
源碼下載:hangge_1667.zip

原文出自: www.hangge.com  轉載請保留原文連接: http://www.hangge.com/blog/cache/detail_1667.html
相關文章
相關標籤/搜索