Music_Player3/MusicPlayer/MP/Common/Tool(工具封装)/MP_PlayerManager.swift
2024-05-29 17:31:45 +08:00

567 lines
20 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.

//
// MP_PlayerManager.swift
// MusicPlayer
//
// Created by Mr.Zhou on 2024/5/10.
//
import UIKit
import AVFoundation
import MediaPlayer
import AVKit
import FreeStreamer
///
enum MP_PlayerStateType:Int {
///
case Null = 0
///
case Playing = 1
///
case Pause = 2
}
///
enum MP_PlayerPlayType:Int {
///
case normal = 0
///
case random = 1
///
case single = 2
}
///
typealias MP_PlayTimerStartAction = () -> Void
///()
typealias MP_PlayTimerRunAction = (_ currentTime:TimeInterval, _ duration:TimeInterval) -> Void
///
typealias MP_PlayTimerEndAction = () -> Void
///
typealias MP_PlayTimerPauseAction = () -> Void
///
typealias MP_PlayTimerResumeAction = () -> Void
///
typealias MP_PlayTimerStopAction = () -> Void
///
typealias MP_PlayTimerEditEndAction = () -> Void
///
typealias MP_PlayCacheValueAction = (Float) -> Void
///
class MP_PlayerManager:NSObject{
///
static let shared = MP_PlayerManager()
///
// private var player:AVPlayer = AVPlayer()
///
private var player:FSAudioStream!
// ///
private var next:FSAudioStream!
///
private var timer:DispatchSourceTimer!
///load
var loadPlayer:MPPositive_PlayerLoadViewModel!{
didSet{
if loadPlayer != nil {
//load
NotificationCenter.notificationKey.post(notificationName: .pup_bottom_show)
}else {
//load
NotificationCenter.notificationKey.post(notificationName: .player_delete_list)
playState = .Null
}
}
}
//
private var playState:MP_PlayerStateType = .Null{
didSet{
//
NotificationCenter.notificationKey.post(notificationName: .switch_player_status, object: playState)
}
}
///
func getPlayState() -> MP_PlayerStateType {
return playState
}
///
private var playType:MP_PlayerPlayType = .normal{
didSet{
//
NotificationCenter.notificationKey.post(notificationName: .player_type_switch)
}
}
///
func getPlayType() -> MP_PlayerPlayType {
return playType
}
///
/// - Parameter type:
func setPlayType(_ type:MP_PlayerPlayType) {
playType = type
if playType == .random {
}
}
///
private var startActionBlock:MP_PlayTimerStartAction!
///
var runActionBlock:MP_PlayTimerRunAction!
///
var cacheValueBlock:MP_PlayCacheValueAction!
private override init() {
super.init()
// player.automaticallyWaitsToMinimizeStalling = false
//// player.delegate = self
// //
// NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying(_ :)), name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem)
//
NotificationCenter.notificationKey.add(observer: self, selector: #selector(userSwitchCurrentVideoAction(_ :)), notificationName: .positive_player_reload)
//
// NotificationCenter.notificationKey.add(observer: self, selector: #selector(netWorkReachableAction(_ :)), notificationName: .net_switch_reachable)
//
let queue = DispatchQueue(label: "com.MPPlayerTimer.queue")
//
timer = DispatchSource.makeTimerSource(queue: queue)
//
timer!.schedule(deadline: .now(), repeating: .seconds(1))
//
timer.setEventHandler {
[weak self] in
guard let self = self else {return}
timerAction()
}
//
timer.resume()
}
deinit {
NotificationCenter.default.removeObserver(self)
player = nil
timer.cancel()
timer = nil
}
///
private func timerAction() {
//
guard let player = player, playState == .Playing else {return}
DispatchQueue.main.async {[weak self] in
guard let self = self, findTurePlayer(player) else {return}
//
let duration = player.duration.playbackTimeInSeconds
let currentTime = player.currentTimePlayed.playbackTimeInSeconds
//
if self.runActionBlock != nil {
self.runActionBlock!(TimeInterval(currentTime), TimeInterval(duration))
}
//
if player.cached == true || loadPlayer.currentVideo.isDlownd == true {
if self.cacheValueBlock != nil {
self.cacheValueBlock!(1)
}
}else {
//
let byte = currentTime + 30
let rate = Float(byte)/duration
if self.cacheValueBlock != nil {
self.cacheValueBlock!(rate)
}
}
}
}
///
/// - Parameters:
/// - startAction:
/// - runAction:
/// - endAction:
func play(startAction:MP_PlayTimerStartAction? = nil) {
guard loadPlayer != nil, loadPlayer.currentVideo != nil else {
//
print("Player No Data")
return
}
//
stopAndReleaseStream(&player)
//
if startAction != nil {
startActionBlock = startAction
}
if next != nil, (next.url == (loadPlayer.currentVideo.resourcePlayerURL! as NSURL)) {
player = next
}else {
//
player = .init(url: loadPlayer.currentVideo.resourcePlayerURL!)
player.maxRetryCount = 2
}
//
let index = loadPlayer.listViewVideos.firstIndex(of: loadPlayer.currentVideo) ?? 0
if (loadPlayer.listViewVideos.count-1) > index {
stopAndReleaseStream(&next)
//URL
let nextURL = loadPlayer.listViewVideos[index + 1].resourcePlayerURL
next = preloadNext(nextURL!)
}
//
player.play()
//
player.onStateChange = {
[weak self] status in
guard let self = self, findTurePlayer(player) else {return}
switch status {
case .fsAudioStreamFailed://
print("\(loadPlayer.currentVideo?.title ?? "")加载失败")
playState = .Null
case .fsAudioStreamRetryingFailed://
print("\(loadPlayer.currentVideo?.title ?? "")重试失败")
print("失败URL:\(String(describing: loadPlayer.currentVideo?.resourcePlayerURL))")
//
loadPlayer.remakeImproveData { [weak self] in
guard let self = self else {return}
//
player?.url = loadPlayer.currentVideo.resourcePlayerURL! as NSURL
}
case .fsAudioStreamPlaying://
///
print("\(loadPlayer.currentVideo?.title ?? "")开始播放")
if playState != .Playing {
playState = .Playing
if startAction != nil {
startAction!()
}
}
case .fsAudioStreamPlaybackCompleted://
playerDidFinishPlaying()
case .fsAudioStreamEndOfFile://
print("\(loadPlayer.currentVideo?.title ?? "")加载完毕")
default:
break
}
}
}
///
private func findTurePlayer(_ stream:FSAudioStream) -> Bool {
guard let currentVideoURL = loadPlayer?.currentVideo?.resourcePlayerURL as? NSURL else {
return false
}
let streamURL = stream.url
if streamURL == currentVideoURL {
return true
}else {
return false
}
}
///
private func preloadNext(_ url:URL) -> FSAudioStream{
let stream = FSAudioStream(url: url)
stream?.maxRetryCount = 1
//
stream!.preload()
print("下一首已经在预加载")
return stream!
}
//MARK: -
//
@objc private func playerDidFinishPlaying() {
//
guard playState == .Playing else {
return
}
switch playType {
case .single:
var postion = FSStreamPosition()
postion.position = 0
//
player.seek(to: postion)
player.play()
default:
//
nextEvent()
}
}
//MARK: -
///
private func pause() {
//
guard playState == .Playing else {
//
print("Player is not in playing")
return
}
//
player.pause()
//
playState = .Pause
}
///
/// - Parameter pauseAction:
func pause(_ pauseAction:MP_PlayTimerPauseAction? = nil) {
//
guard playState == .Playing else {
//
print("Player is not in playing")
return
}
if pauseAction != nil {
pauseAction!()
}
//
player.pause()
//
playState = .Pause
}
//MARK: -
///
/// - Parameter resumeAction:
func resume(_ resumeAction:MP_PlayTimerResumeAction? = nil) {
//
guard playState == .Pause else {
//
print("Player is not paused")
return
}
if resumeAction != nil {
resumeAction!()
}
//
// player.play()
player.pause()
//
playState = .Playing
}
///
private func resume() {
//
guard playState == .Pause else {
//
print("Player is not paused")
return
}
//
// player.play()
player.pause()
//
playState = .Playing
}
//MARK: -
//
func stop() {
//
guard playState != .Null else {
//
print("Player is not started")
return
}
player.stop()
playState = .Null
}
//MARK: - /
///
func previousEvent() {
//
playState = .Null
var nextIndex:Int = 0
//
switch playType {
case .random://
for (index, item) in loadPlayer.randomVideos.enumerated() {
if item.videoId == loadPlayer.currentVideo?.song.videoId {
//
nextIndex = index - 1
}
}
//next
if nextIndex < 0 {
//
let last = loadPlayer.randomVideos.last
loadPlayer.improveData(last?.videoId ?? "", isRandom: true)
}else {
//
let song = loadPlayer.randomVideos[nextIndex]
loadPlayer.improveData(song.videoId ?? "", isRandom: true)
}
default://
for (index, item) in loadPlayer.songVideos.enumerated() {
if item.videoId == loadPlayer.currentVideo?.song.videoId {
//
nextIndex = index - 1
}
}
//next
if nextIndex < 0 {
//
let last = loadPlayer.songVideos.last
loadPlayer.improveData(last?.videoId ?? "")
}else {
//
let song = loadPlayer.songVideos[nextIndex]
loadPlayer.improveData(song.videoId ?? "")
}
}
}
///
func nextEvent() {
//
playState = .Null
var nextIndex:Int = 0
switch playType {
case .random:
for (index, item) in loadPlayer.randomVideos.enumerated() {
if item.videoId == loadPlayer.currentVideo?.song.videoId {
//
nextIndex = index + 1
}
}
//
if nextIndex > (loadPlayer.randomVideos.count-1) {
//
let first = loadPlayer.randomVideos.first
loadPlayer.improveData(first?.videoId ?? "", isRandom: true)
}else {
//,ID
let song = loadPlayer.randomVideos[nextIndex]
loadPlayer.improveData(song.videoId ?? "", isRandom: true)
}
default:
for (index, item) in loadPlayer.songVideos.enumerated() {
if item.videoId == loadPlayer.currentVideo?.song.videoId {
//
nextIndex = index + 1
}
}
//
if nextIndex > (loadPlayer.songVideos.count-1) {
//
let first = loadPlayer.songVideos.first
loadPlayer.improveData(first?.videoId ?? "")
}else {
//,ID
let song = loadPlayer.songVideos[nextIndex]
loadPlayer.improveData(song.videoId ?? "")
}
}
}
///
@objc private func userSwitchCurrentVideoAction(_ sender:Notification) {
//
playState = .Null
//
if player != nil {
//
stopAndReleaseStream(&player)
}
if cacheValueBlock != nil {
cacheValueBlock!(0)
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
[weak self] in
guard let self = self else {return}
if loadPlayer.currentVideo != nil {
//
play(startAction: startActionBlock)
}
}
}
///
func setEditPorgressStatu() {
guard playState != .Null else {
return
}
//
pause()
}
/// setEditPorgressStatu()使
/// - Parameters:
/// - progress: 0-1
func setEditProgressEnd(_ progress:Float, endAction:MP_PlayTimerEditEndAction? = nil) {
guard playState != .Null, let player = player, findTurePlayer(player) else {
return
}
guard progress >= 0, progress <= 1 else {
return
}
//
var time:FSStreamPosition = .init()
time.position = progress
//
let currentTime = player.currentTimePlayed.playbackTimeInSeconds
if progress != currentTime {
//
player.seek(to: time)
//
resume()
if endAction != nil {
endAction!()
}
}
}
///
private func stopAndReleaseStream(_ stream: inout FSAudioStream?) {
stream?.stop() //
stream?.onStateChange = nil //
stream?.onFailure = nil
stream = nil
}
}
////MARK: -
//extension MP_PlayerManager: MP_AVPlayerItemDelegate {
// ///
// func playerItemReadyToPlay(_ playerItem: MP_AVPlayerItem) {
// DispatchQueue.main.async {
// [weak self] in
// guard let self = self else {return}
// if playState != .Playing {
// //
// player.play()
// playState = .Playing
// //
// if startActionBlock != nil {
// startActionBlock!()
// }
//
// }
// }
// }
// ///
// func playerItem(_ playerItem: MP_AVPlayerItem, progress:Float) {
// DispatchQueue.main.async {
// [weak self] in
// guard let self = self else {return}
// //
// if cacheValueBlock != nil {
// cacheValueBlock!(progress)
// }
// }
// }
// ///
// func playerItem(_ playerItem: MP_AVPlayerItem, didFinishLoadingData data: Data) {
// print("\(loadPlayer.currentVideo.title ?? "") ")
// DispatchQueue.main.async {
// [weak self] in
// guard let self = self else {return}
// //
// if cacheValueBlock != nil {
// cacheValueBlock!(1)
// }
// }
// }
// ///,
// func playerItemPlaybackStalled(_ playerItem: MP_AVPlayerItem) {
// print("")
// }
// ///
// func playerItem(_ playerItem: MP_AVPlayerItem, loadingError error: any Error) {
// print("\(loadPlayer.currentVideo.title ?? "") ,---\(error)")
// }
//
//
//}