MoviaBox/Thimra/Libs/Player/SPPlayer.swift
2025-04-24 11:14:21 +08:00

263 lines
7.3 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// SPPlayer.swift
// Thimra
//
// Created by on 2025/4/9.
//
import UIKit
import ZFPlayer
@objc protocol SPPlayerDelegate {
///
// @objc optional func sp_onDurationUpdate(_ player: SPPlayer, duration: Int)
//
// ///
// @objc optional func sp_onCurrentPositionUpdate(_ player: SPPlayer, position: Int)
///
@objc optional func sp_player(_ player: SPPlayer, playStateDidChanged state: SPPlayer.PlayState)
///
@objc optional func sp_playTimeChanged(_ player: SPPlayer, currentTime: Int, duration: Int)
///
@objc optional func sp_firstRenderedStart(_ player: SPPlayer)
///
@objc optional func sp_playCompletion(_ player: SPPlayer)
///
@objc optional func sp_playLoadingEnd(_ player: SPPlayer)
}
class SPPlayer: NSObject {
@objc enum PlayState: Int {
case unknown
case playing
case paused
case failed
case stopped
}
weak var delegate: SPPlayerDelegate?
private(set) lazy var isPlaying = false
private(set) lazy var playState: PlayState = .unknown
/**
*/
private var isAddIdleTimerDisabledObserver = false
///
var duration: Int {
return Int(self.player.totalTime)
}
///
var currentPosition: Int {
return Int(self.player.currentTime)
}
///0.5 - 2
var rate: Float {
set {
player.rate = newValue
}
get {
return player.rate
}
}
var playerView: UIView? {
didSet {
playerView?.addSubview(player.view)
player.view.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
}
private lazy var player: ZFAVPlayerManager = {
let player = ZFAVPlayerManager()
player.shouldAutoPlay = false
return player
}()
var isLoop = true
deinit {
self.stop()
}
override init() {
super.init()
player.scalingMode = .aspectFill
sp_addAction()
}
/**
*/
private func addIdleTimerDisabledObserver() {
if !isAddIdleTimerDisabledObserver {
isAddIdleTimerDisabledObserver = true
UIApplication.shared.addObserver(self, forKeyPath: "idleTimerDisabled", options: NSKeyValueObservingOptions.new, context: nil)
}
}
/**
*/
private func removeIdleTimerDisabledObserver() {
if isAddIdleTimerDisabledObserver {
isAddIdleTimerDisabledObserver = false
UIApplication.shared.removeObserver(self, forKeyPath: "idleTimerDisabled")
}
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if !UIApplication.shared.isIdleTimerDisabled {
UIApplication.shared.isIdleTimerDisabled = true
}
}
func setPlayUrl(url: String) {
let proxyURL = KTVHTTPCache.proxyURL(withOriginalURL: URL(string: url))
if proxyURL != self.player.assetURL {
self.player.assetURL = proxyURL
}
// self.prepare()
}
///
func prepare() {
// self.player.prepareToPlay()
}
func stop() {
self.isPlaying = false
player.stop()
self.removeIdleTimerDisabledObserver()
UIApplication.shared.isIdleTimerDisabled = false
}
func start() {
self.isPlaying = true
player.play()
UIApplication.shared.isIdleTimerDisabled = true
self.addIdleTimerDisabledObserver()
}
///
func pause() {
self.isPlaying = false
player.pause()
self.removeIdleTimerDisabledObserver()
UIApplication.shared.isIdleTimerDisabled = false
}
///
func replay() {
self.isPlaying = true
self.player.replay()
UIApplication.shared.isIdleTimerDisabled = true
self.addIdleTimerDisabledObserver()
}
func seekToTime(toTime: Int) {
var time = toTime
if time < 0 {
time = 0
}
if time > self.duration {
time = self.duration
}
self.player.seek(toTime: TimeInterval(time), completionHandler: nil)
}
}
extension SPPlayer {
private func sp_addAction() {
//
player.playerPlayTimeChanged = { [weak self] (asset, currentTime, duration) in
guard let self = self else { return }
self.delegate?.sp_playTimeChanged?(self, currentTime: Int(currentTime), duration: Int(duration))
}
//
player.playerPlayStateChanged = { [weak self] (asset, playState) in
guard let self = self else { return }
if playState == .playStatePlaying, !isPlaying {
self.pause()
} else if playState == .playStatePaused, isPlaying {
self.start()
}
switch playState {
case .playStateUnknown:
self.playState = .unknown
case .playStatePlaying:
self.playState = .playing
case .playStatePaused:
self.playState = .paused
case .playStatePlayStopped:
self.playState = .stopped
case .playStatePlayFailed:
self.playState = .failed
default:
self.playState = .unknown
}
self.delegate?.sp_player?(self, playStateDidChanged: self.playState)
spLog(message: "播放状态====\(playState)")
}
//
player.playerLoadStateChanged = { [weak self] (asset, loadState) in
guard let self = self else { return }
if loadState == .playable, !isPlaying {
self.pause()
} else if loadState == .playable, isPlaying, self.player.playState != .playStatePlaying {
self.start()
}
// switch loadState {
// case .prepare:
// spLog(message: "====")
// case .playable:
// spLog(message: "====")
// case .playthroughOK:
// spLog(message: "====")
// case .stalled:
// spLog(message: "====")
// default:
// break
// }
}
//
player.playerPlayFailed = { [weak self] (asset, error) in
spLog(message: "错误信息====\(error)")
}
//
player.playerDidToEnd = { [weak self] (asset) in
guard let self = self else { return }
if isLoop {
self.replay()
} else {
self.isPlaying = false
self.delegate?.sp_playCompletion?(self)
}
}
}
}