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))