按公司需求須要對音頻文件進行後臺播放,藉此機會對音頻播放作了個總結.主要針對 AVPlayer 進行詳細說明.git
名稱 | 使用環境 | 優勢 | 缺點 |
---|---|---|---|
System Sound Services | AVFoundation | C語言的底層寫法,節省內存 | 支持的格式有限,音量沒法經過音量鍵控制,並且播放方式單一。 |
AVAudioPlayer | AVFoundation | 抒寫效率更高,基本上支持全部的音頻格式,對播放的控制,如循環播放,聲音大小,暫停等比較方便。 | 對內存的消耗會多些。不支持流式,即沒法播放在線音樂。 |
AVPlayer | AVFoundation | 能夠播放音視頻,可播放在線音樂,使用靈活 | |
MPMoviePlayerController | MediaPlayer | 簡單易用 | 不可定製 |
AVPlayerViewController | AVKit | 簡單易用 | 不可定製 |
IJKPlayer | IJKMediaFramework | 定製度高,支持流媒體播放 | 使用稍複雜 |
AVPlayer 是iOS上經常使用的視頻播放器組件,支持常見的音視頻格式,支持流播放,能夠播放在線音樂.
支持視頻格式: WMV,AVI,MKV,RMVB,RM,XVID,MP4,3GP,MPG等。
支持音頻格式:MP3,WMA,RM,ACC,OGG,APE,FLAC,FLV等。github
let player = AVPlayer(url: URL(string: "http://www.xxxx.mp3"))
複製代碼
if let url = URL(string: "http://www.***.mp3") {
let asset = AVAsset(url: url)
guard asset.isPlayable else{
// 檢測文件是否可播放
return
}
let playItem = AVPlayerItem(asset: asset)
let player = AVPlayer(playerItem: playItem)
player.play()
}
複製代碼
player.play() // 播放
player.pause() //暫停
player.rate = 1.0 // 播放速度
複製代碼
//播放完成
AVPlayerItemDidPlayToEndTimeNotification
//播放失敗
AVPlayerItemFailedToPlayToEndTimeNotification
//異常中斷
AVPlayerItemPlaybackStalledNotification
// eg: 播放結束通知
NotificationCenter.default.addObserver(self, selector: #selector(finish(_:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
複製代碼
// 添加週期時間觀察者 一秒執行一次 block
let timeObserver = player.addPeriodicTimeObserver(forInterval: CMTime(seconds: 1, preferredTimescale: 1), queue: DispatchQueue.main, using: { [weak self] (cmTime) in
if let totalTime = self?.currentPlayItem?.duration {
self?.delegate?.player(self!, currentTime: cmTime.seconds, totalTime: totalTime.seconds)
}
})
// 不要忘記移除
player.removeTimeObserver(observer)
複製代碼
// 使用 AVAsset 建立
if let url = URL(string: "http://www.***.mp3") {
let asset = AVAsset(url: url)
guard asset.isPlayable else{
// 檢測文件是否可播放
return
}
let playItem = AVPlayerItem(asset: asset)
}
// 使用 URL 建立
if let url = URL(string: "http://www.***.mp3") {
let playItem = AVPlayerItem(url: url)
}
複製代碼
// 監聽 playerItem 狀態變化
playItem.addObserver(self, forKeyPath: "status", options: .new, context: nil)
// 監聽緩存時間
playItem.addObserver(self, forKeyPath: "loadedTimeRanges", options: .new, context: nil)
// 移除監聽
currentPlayItem?.removeObserver(self, forKeyPath: "status")
currentPlayItem?.removeObserver(self, forKeyPath: "loadedTimeRanges")
複製代碼
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if object is AVPlayerItem {
if keyPath == "status" {
if let playerItem = object as? AVPlayerItem {
switch playerItem.status {
case .readyToPlay:
// 準備播放
case .failed:
// 加載失敗
default:
// 未知狀態
}
}
}
if keyPath == "loadedTimeRanges" {
if let playerItem = object as? AVPlayerItem {
if let timeRange = playerItem.loadedTimeRanges.first as? CMTimeRange {
let cache = timeRange.start.seconds + timeRange.duration.seconds // 緩存總時長
}
}
}
}
複製代碼
利用 AVAudioSession 申請後臺播放權限swift
let session = AVAudioSession.sharedInstance()
do {
try session.setActive(true)
try session.setCategory(AVAudioSessionCategoryPlayback)
} catch {
print(error)
}
複製代碼
// 聲明接收Remote Control事件
UIApplication.shared.beginReceivingRemoteControlEvents()
複製代碼
// 響應 Remote Control事件
MPRemoteCommandCenter.shared().playCommand.addTarget(self, action: #selector(play))
MPRemoteCommandCenter.shared().nextTrackCommand.addTarget(self, action: #selector(next))
MPRemoteCommandCenter.shared().pauseCommand.addTarget(self, action: #selector(pause))
MPRemoteCommandCenter.shared().previousTrackCommand.addTarget(self, action: #selector(previous))
複製代碼
// 在關閉播放頁面時記得移除
MPRemoteCommandCenter.shared().playCommand.removeTarget(self, action: #selector(play))
MPRemoteCommandCenter.shared().nextTrackCommand.removeTarget(self, action: #selector(next))
MPRemoteCommandCenter.shared().pauseCommand.removeTarget(self, action: #selector(pause))
MPRemoteCommandCenter.shared().previousTrackCommand.removeTarget(self, action: #selector(previous))
// 中止響應 Remote Control
UIApplication.shared.endReceivingRemoteControlEvents()
複製代碼
開啓接受遠程控制緩存
使當前頁面成爲第一響應者bash
重寫 remoteControlReceivedWithEvent 方法. UIEvent Type 取值:網絡
關閉接受遠程控制session
使用 MPNowPlayingInfoCenter 設置鎖屏頁面音樂信息.app
func setLockScreenPlayingInfo(_ info: YTTMediaInfo) {
// Now Playing Center能夠在鎖屏界面展現音樂的信息,也達到加強用戶體驗的做用。
// https://www.jianshu.com/p/458b67f84f27
var infoDic: [String : Any] = [:]
infoDic[MPMediaItemPropertyTitle] = info.title // 歌曲名
infoDic[MPMediaItemPropertyArtist] = info.singer // 歌手
if let img = info.image {
infoDic[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(image: img) // 專輯圖片
}
infoDic[MPMediaItemPropertyPlaybackDuration] = info.totalTime // 歌曲總時長
infoDic[MPNowPlayingInfoPropertyElapsedPlaybackTime] = info.currentTime // 當前播放時間
infoDic[MPNowPlayingInfoPropertyPlaybackRate] = 1.0 // 播放速度
MPNowPlayingInfoCenter.default().nowPlayingInfo = infoDic
}
複製代碼
注意: MPNowPlayingInfoPropertyElapsedPlaybackTime 設置的並非時時的,他是根據你設置的值進行計時的,若是想要在鎖屏頁面獲得準確的時間,請及時刷新 MPNowPlayingInfoPropertyElapsedPlaybackTime 的值.當暫停時要暫停播放時間,只需將 MPNowPlayingInfoPropertyPlaybackRate 設置爲 0.播放時設置回 1.ide
iOS 對後臺管理十分嚴格,任何 app 都有大約3分鐘或者10分鐘的後臺執行時間.3分鐘或者10分鐘後, app 就會被強制掛起.使用 AVAudioSession 申請後臺權限時,能夠保證播放本地音樂能在後臺長久播放,當播放網絡音樂時就會出現不能播放狀況,針對這狀況使用了 beginBackgroundTask 設置後臺任務 ID,經過這種方式咱們大約能夠得到額外的 10 分鐘來執行後臺任務.爲了能無限後臺播放網絡音樂添加計時器,立即將掛起時再次申請後臺任務 ID.ui
func applicationDidEnterBackground(_ application: UIApplication) {
// 這樣作,能夠在按home鍵進入後臺後 ,播放一段時間,幾分鐘吧。可是不能持續播放網絡歌曲,若須要持續播放網絡歌曲,還須要申請後臺任務id
bgTask = application.beginBackgroundTask(expirationHandler: nil)
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
}
@objc func timerAction() {
timerCount = timerCount + 1
if timerCount < 500 {
return
}
timerCount = 0
let newTask = UIApplication.shared.beginBackgroundTask(expirationHandler: nil)
if bgTask != UIBackgroundTaskInvalid && newTask != UIBackgroundTaskInvalid {
UIApplication.shared.endBackgroundTask(bgTask)
bgTask = newTask
}
}
複製代碼