这是我参与8月更文挑战的第12天,活动详情查看:8月更文挑战
前言
由于只是简单的音频播放,所以我们准备自己用AVFoundation
下的AVPlayer
自己封装一个,就不去找第三方的了
封装
1、新建一个AudioPlayer
类,在里面定义一个PlayState
枚举,用来记录播放状态
enum PlayState {
case stopped /// 停止播放
case playing /// 正在播放
case paused /// 暂停播放
case failed /// 播放错误
}
复制代码
2、增加一个AudioPlayerDelegate
协议
@objc protocol AudioPlayerDelegate: AnyObject {
/// 播放状态
@objc optional func playerStateDidChange(_ player: AudioPlayer)
/// 获取播放时间
@objc optional func playerCurrentTimeDidChange(_ player: AudioPlayer)
/// 播放结束
@objc optional func playerPlaybackDidEnd(_ player: AudioPlayer)
/// 播放错误
@objc optional func player(_ player: AudioPlayer, didFailWithError error: Error?)
}
复制代码
3、在AudioPlayer
里面暴露一些属性,方便外面获取与设置
weak var delegate: AudioPlayerDelegate?
/// Pauses playback automatically when resigning active.
var playbackPausesWhenResigningActive: Bool = true
/// Pauses playback automatically when backgrounded.
var playbackPausesWhenBackgrounded: Bool = true
/// Resumes playback when became active.
var playbackResumesWhenBecameActive: Bool = true
/// Resumes playback when entering foreground.
var playbackResumesWhenEnteringForeground: Bool = true
/// 最大时间
var maximumDuration: TimeInterval {
get {
if let playerItem = self.playerItem {
return CMTimeGetSeconds(playerItem.duration)
} else {
return CMTimeGetSeconds(CMTime.indefinite)
}
}
}
/// 当前时间
var currentTimeInterval: TimeInterval {
get {
if let playerItem = self.playerItem {
return CMTimeGetSeconds(playerItem.currentTime())
} else {
return CMTimeGetSeconds(CMTime.indefinite)
}
}
}
/// 播放状态
var playState: PlayState = .stopped {
didSet {
self.delegate?.playerStateDidChange?(self)
}
}
复制代码
4、添加一个AVPlayer
和AVPlayerItem
private var player: AVPlayer?
private var playerItem: AVPlayerItem?
复制代码
5、添加一些public
方法
/// 播放
func play() {
if let p = player {
p.play()
}
}
/// 暂停
func pause() {
if let p = player, playState == .playing {
p.pause()
}
}
/// 停止
func stop() {
if playState == .stopped {
return
}
self.player?.pause()
self.playState = .stopped
self.delegate?.playerPlaybackDidEnd?(self)
}
/// 快进/快退
func seek(to time: CMTime) {
if let playerItem = self.playerItem {
return playerItem.seek(to: time, completionHandler: nil)
}
}
/// 改变播放状态
func changePlay() {
switch playState {
case .playing:
pause()
case .paused:
play()
default:
pause()
}
}
复制代码
6、添加player
播放进度和状态的监听
private func addObserver() {
player?.addPeriodicTimeObserver(forInterval: CMTimeMake(value: 1, timescale: 100), queue: .main, using: { [weak self] (cmTime) in
guard let `self` = self else { return }
self.delegate?.playerCurrentTimeDidChange?(self)
})
player?.addObserver(self, forKeyPath: "timeControlStatus", options: .new, context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "timeControlStatus", let p = player {
switch p.timeControlStatus {
case .paused:
playState = .paused
case .playing:
playState = .playing
case .waitingToPlayAtSpecifiedRate:
fallthrough
default:
break
}
}
}
复制代码
7、由于我们app退到后台和回到前台要设置是否播放处理,所以要监听app的活跃状态来做一些处理
private func addApplicationObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(handleApplicationWillResignActive(_:)), name: UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleApplicationDidBecomeActive(_:)), name: UIApplication.didBecomeActiveNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleApplicationDidEnterBackground(_:)), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleApplicationWillEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
}
@objc private func handleApplicationWillResignActive(_ aNotification: Notification) {
if playState == .playing && playbackPausesWhenResigningActive {
pause()
}
}
@objc private func handleApplicationDidBecomeActive(_ aNotification: Notification) {
if playState == .paused && playbackResumesWhenBecameActive {
play()
}
}
@objc private func handleApplicationDidEnterBackground(_ aNotification: Notification) {
if playState == .playing && playbackPausesWhenBackgrounded {
pause()
}
}
@objc private func handleApplicationWillEnterForeground(_ aNoticiation: Notification) {
if playState != .playing && playbackResumesWhenEnteringForeground {
play()
}
}
复制代码
8、暴露一个设置url
的方法来播放
func player(_ url: String) {
guard let u = URL(string: url) else { return }
playerItem = AVPlayerItem(url: u)
if player == nil {
player = AVPlayer(playerItem: playerItem)
addObserver()
addApplicationObservers()
do {
/// 使用这个category的应用不会随着手机静音键打开而静音,可在手机静音下播放声音
try AVAudioSession.sharedInstance().setCategory(.playback)
} catch {}
} else {
/// 替换播放的 playerItem
player?.replaceCurrentItem(with: playerItem)
}
addPlayToEndTimeObserver()
play()
}
复制代码
音频播放封装就结束了,下一篇就可以开始使用它来播放
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END