iOS 音視頻播放

按公司需求須要對音頻文件進行後臺播放,藉此機會對音頻播放作了個總結.主要針對 AVPlayer 進行詳細說明.git

iOS 各播放器比較

名稱 使用環境 優勢 缺點
System Sound Services AVFoundation C語言的底層寫法,節省內存 支持的格式有限,音量沒法經過音量鍵控制,並且播放方式單一。
AVAudioPlayer AVFoundation 抒寫效率更高,基本上支持全部的音頻格式,對播放的控制,如循環播放,聲音大小,暫停等比較方便。 對內存的消耗會多些。不支持流式,即沒法播放在線音樂。
AVPlayer AVFoundation 能夠播放音視頻,可播放在線音樂,使用靈活
MPMoviePlayerController MediaPlayer 簡單易用 不可定製
AVPlayerViewController AVKit 簡單易用 不可定製
IJKPlayer IJKMediaFramework 定製度高,支持流媒體播放 使用稍複雜

AVPlayer 使用

簡介

AVPlayer 是iOS上經常使用的視頻播放器組件,支持常見的音視頻格式,支持流播放,能夠播放在線音樂.
支持視頻格式: WMV,AVI,MKV,RMVB,RM,XVID,MP4,3GP,MPG等。
支持音頻格式:MP3,WMA,RM,ACC,OGG,APE,FLAC,FLV等。github

相關類

  • AVPlayer:播放器,控制播放器的播放,暫停,播放速度.
  • AVURLAsset : AVAsset 的一個子類,使用 URL 進行實例化,實例化對象包換 URL 對應視頻資源的全部信息.
  • AVPlayerItem:管理資源對象,提供播放數據源.
  • AVPlayerLayer:負責顯示視頻,若是沒有添加該類,只有聲音沒有畫面.

簡單使用

使用 url 建立 AVPlayer

let player = AVPlayer(url: URL(string: "http://www.xxxx.mp3"))
複製代碼

使用 AVPlayerItem 建立 AVPlayer

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()
}
複製代碼

AVPlayer 控制播放

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)
複製代碼

AVPlayerItem 建立

// 使用 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)
}
複製代碼

監聽 AVPlayerItem 狀態和緩存進度

// 監聽 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)

開啓遠程控制

// 聲明接收Remote Control事件
UIApplication.shared.beginReceivingRemoteControlEvents()
複製代碼

設置 Remote Control 響應

// 響應 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))
複製代碼

移除 Remote Control 響應

// 在關閉播放頁面時記得移除
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 取值:網絡

    • UIEventSubtypeRemoteControlTogglePlayPause // 暫停
    • UIEventSubtypeRemoteControlPreviousTrack // 上一首
    • UIEventSubtypeRemoteControlNextTrack // 下一首
    • UIEventSubtypeRemoteControlPlay // 播放
    • UIEventSubtypeRemoteControlPause // 暫停
  • 關閉接受遠程控制session

鎖屏頁面顯示播放信息(Now Playing Center)

使用 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
    }
    
}
複製代碼

其餘

相關文章
相關標籤/搜索