import UIKit
import Foundation
class PublicFunction: NSObject {
//MARK:播放時長
static func convertTime(totalSeconds:Int) -> (String) {
let seconds:Int = totalSeconds % 60
let minutes:Int = (totalSeconds / 60) % 60
let time:String = "\(String(format: "%02d", minutes)):\(String(format: "%02d", seconds))"
return time
}
import UIKit
import AVFoundation
class NetAudioPlayerTool: NSObject {
//正在播放回調
//currentTime 播放時間
//currentProgress 播放進度
typealias OnPlayingBlock = (_ currentTime:Float64,_ currentProgress:Float) -> ()
//準備播放回調
//totalDuration 音頻總時長
typealias PrepareToPlayBlock = (_ totalDuration:Float) -> ()
//正在緩沖回調
//bufferDuration 已緩沖的時長
typealias OnBufferingBlock = (_ bufferDuration:Float) -> ()
//播放完成回調
//flag YES播放完成, NO播放失敗
typealias CompletePlayingBlock = (_ flag:Bool) -> ()
//緩存自動暫停回調, 用于更改播放按鈕的樣式
//bufferDuration isPlaying 是否正在播放, 如果沒有播放表示正在緩沖
typealias IsPlayingBlock = (_ isPlaying:Float) -> ()
var playingBlock:OnPlayingBlock?
var prepareToPlayBlock:PrepareToPlayBlock?
var bufferingBlock:OnBufferingBlock?
var completePlayBlock:CompletePlayingBlock?
var isPlayingBlock:IsPlayingBlock?
var palyerItem:AVPlayerItem?
//播放器
var player:AVPlayer = AVPlayer()
//播放路徑
var audioPath:String = ""
//音量大小
var volume:Float = 1.0
// 只有當播放器狀態為`ReadyToPlay`,才可以執行拖拽操作,否則crash.
var canDraggingFlag:Bool = false
//之前播放的進度
var timeOffset:Float64 = 0
//播放時間
var currentTime:Float64 = 0
//音頻總時長
var totalDuration:Float64 = 0
override init() {
super.init()
self.addObserverForPlayer()
}
//MARK:如果在播放音頻前有錄音操作,需要重新設置音頻會話,否則聲音極小.
func setPlaybackSession() {
let session = AVAudioSession.sharedInstance()
do {
try session.setActive(true)
try session.setCategory(AVAudioSession.Category.playback)
} catch {
print(error)
}
}
//MARK:創建播放器
func createAudioPlayer() {
//創建媒體資源管理對象
let Url = URL(string: self.audioPath)
self.palyerItem = AVPlayerItem.init(url: Url!)
//創建ACplayer:負責視頻播放
self.player = AVPlayer.init(playerItem: self.palyerItem)
self.player.rate = 1.0
self.player.volume = self.volume
//與播放緩存相關的觀測屬性
self.addObserverForPlayItem()
}
//MARK:開始播放
func playAudio() {
self.player.play()
}
//MARK:暫停播放
func pauseAudio() {
self.player.pause()
}
//MARK:停止播放
func stopAudio() {
self.destroyPlayer()
}
//MARK:改變播放進度
func changeAudio(currentTime:Float64) {
if currentTime>0 {
let time:CMTime = CMTimeMakeWithSeconds(currentTime, preferredTimescale: 1 * Int32(NSEC_PER_SEC))
self.player.seek(to: time, toleranceBefore: CMTime.zero, toleranceAfter:CMTime.zero)
self.player.play()
}
}
//MARK:Notification
//播放完畢
func addObserverForPlayer() {
NotificationCenter.default.addObserver(self, selector: #selector(audioPlayCompletion), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
}
//MARK:Observer
//觀察 加載狀態 加載進度
func addObserverForPlayItem() {
self.player.currentItem?.addObserver(self, forKeyPath: "status", options: .new, context: nil)
self.player.currentItem?.addObserver(self, forKeyPath: "loadedTimeRanges", options: .new, context: nil)
self.player.currentItem?.addObserver(self, forKeyPath: "playbackBufferEmpty", options: .new, context: nil)
self.player.currentItem?.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: .new, context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
let playerItem:AVPlayerItem = object as! AVPlayerItem
if keyPath == "status" {
switch self.palyerItem?.status {
case .readyToPlay ://準備播放
self.canDraggingFlag = true
self.totalDuration = CMTimeGetSeconds(playerItem.duration)
self.prepareToPlayBlock?(Float(self.totalDuration))
if self.timeOffset>0 {
let time:CMTime = CMTimeMakeWithSeconds(self.timeOffset, preferredTimescale: 1 * Int32(NSEC_PER_SEC))
self.player.seek(to: time, toleranceBefore: CMTime.zero, toleranceAfter:CMTime.zero)
}
self.updatePlayProgress()//播放進度
//self.playAudio()
break
case .failed:
self.completePlayBlock?(false)
break
case .unknown:
break
default :
break
}
} else if keyPath == "loadedTimeRanges"{//獲取最新緩存的區間
let bufferInterval:TimeInterval = self.bufferedDuration()
self.bufferingBlock?(Float(bufferInterval))
} else if keyPath == "playbackBufferEmpty"{//正在緩存視頻請稍等
} else if keyPath == "playbackLikelyToKeepUp"{//緩存好了繼續播放
}
}
//播放進度
func updatePlayProgress() {
self.player.addPeriodicTimeObserver(forInterval: CMTimeMake(value: 1, timescale: 1), queue: DispatchQueue.main) { (time) in
//當前正在播放的時間
self.currentTime = CMTimeGetSeconds(time)
self.playingBlock?(self.currentTime,Float(self.currentTime))
}
}
//MARK:播放完畢
@objc func audioPlayCompletion() {
if self.player.currentItem?.status == AVPlayerItem.Status.readyToPlay {
self.player.seek(to: CMTime.zero) { finished in
self.completePlayBlock?(true)
}
}
}
//MARK:計算緩沖進度
func bufferedDuration() -> (TimeInterval) {
let loadTimeArray = self.palyerItem!.loadedTimeRanges
//獲取最新緩存的區間
let newTimeRange : CMTimeRange = loadTimeArray.first as! CMTimeRange
let startSeconds = CMTimeGetSeconds(newTimeRange.start);
let durationSeconds = CMTimeGetSeconds(newTimeRange.duration);
let totalBuffer = startSeconds + durationSeconds;//緩沖總長度
//print("當前緩沖時間:\(totalBuffer)")
return totalBuffer
}
//MARK:釋放 播放器
func destroyPlayer() {
self.player.pause()
self.currentTime = 0.0
self.player.currentItem?.cancelPendingSeeks()
self.player.currentItem?.asset.cancelLoading()
self.player.replaceCurrentItem(with: nil)
}
//MARK:移除通知
func removeObserverFromPlayer() {
self.player.currentItem?.removeObserver(self, forKeyPath: "status")
self.player.currentItem?.removeObserver(self, forKeyPath: "loadedTimeRanges")
self.player.currentItem?.removeObserver(self, forKeyPath: "playbackBufferEmpty")
self.player.currentItem?.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp")
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
}
}
let netPlayer = NetAudioPlayerTool()
//線上音頻 播放時間
var currentTime:Float = 0
//線上音頻 音頻總時長
var totalDuration:Float = 0
//MARK:創建線上播放器
func createOnlineAudioPlayer() {
self.netPlayer.audioPath = "https://webfs.ali.kugou.com/202108021358/f8e446476833e4c950f58d07762c4827/KGTX/CLTX001/84ea1667187279849794a9cc96fa0d27.mp3"
self.netPlayer.createAudioPlayer()
}
//MARK:Player Block
func addNetPlayerBlock() {
//網絡音頻 準備播放時更新UI
netPlayer.prepareToPlayBlock = { (totalDuration) in
print("音頻總時長:\(PublicFunction.convertTime(totalSeconds: Int(totalDuration)))")
self.totalDuration = totalDuration
}
//網絡音頻 緩沖時更新UI
netPlayer.bufferingBlock = { [weak self] (bufferInterval) in
print("當前緩沖時間:\(PublicFunction.convertTime(totalSeconds: Int(bufferInterval)))")
print("當前緩沖進度:\(Float(bufferInterval / self!.totalDuration))")
}
//網絡音頻 播放時更新UI
netPlayer.playingBlock = { (currentTime,currentProgress) in
print("當前播放時長---\(PublicFunction.convertTime(totalSeconds: Int(currentTime)))")
print("當前播放進度:\(currentProgress)")
}
//網絡音頻 播放完成更新UI
netPlayer.completePlayBlock = { (isSuccess) in
print("播放完畢")
}
}
//開始播放
netPlayer.playAudio()
//暫停播放
netPlayer.pauseAudio()
//拖動修改進度
netPlayer.changeAudio(currentTime: Float64(sender.value))