播放器更改
This commit is contained in:
parent
4621f95204
commit
3c20413986
@ -44,20 +44,13 @@ typealias MP_PlayTimerStopAction = () -> Void
|
|||||||
///播放器调整进度时执行事件
|
///播放器调整进度时执行事件
|
||||||
typealias MP_PlayTimerEditEndAction = () -> Void
|
typealias MP_PlayTimerEditEndAction = () -> Void
|
||||||
///播放器缓存值执行事件
|
///播放器缓存值执行事件
|
||||||
typealias MP_PlayCacheValueAction = (Float) -> Void
|
typealias MP_PlayCacheValueAction = (_ currentValue:TimeInterval, _ duration:TimeInterval) -> Void
|
||||||
///播放器
|
///播放器
|
||||||
class MP_PlayerManager:NSObject{
|
class MP_PlayerManager:NSObject{
|
||||||
///控制器单例
|
///控制器单例
|
||||||
static let shared = MP_PlayerManager()
|
static let shared = MP_PlayerManager()
|
||||||
|
|
||||||
///播放器
|
///播放器
|
||||||
// private var player:AVPlayer = AVPlayer()
|
private var player:AVPlayer = AVPlayer()
|
||||||
///当前播放流
|
|
||||||
private var player:FSAudioStream!
|
|
||||||
// ///预加载下一首流
|
|
||||||
private var next:FSAudioStream!
|
|
||||||
///计时器
|
|
||||||
private var timer:DispatchSourceTimer!
|
|
||||||
///load模块
|
///load模块
|
||||||
var loadPlayer:MPPositive_PlayerLoadViewModel!{
|
var loadPlayer:MPPositive_PlayerLoadViewModel!{
|
||||||
didSet{
|
didSet{
|
||||||
@ -76,7 +69,6 @@ class MP_PlayerManager:NSObject{
|
|||||||
didSet{
|
didSet{
|
||||||
//当播放器状态发生变化时,对播放器按钮状态进行切换
|
//当播放器状态发生变化时,对播放器按钮状态进行切换
|
||||||
NotificationCenter.notificationKey.post(notificationName: .switch_player_status, object: playState)
|
NotificationCenter.notificationKey.post(notificationName: .switch_player_status, object: playState)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
///获取播放器播放状态
|
///获取播放器播放状态
|
||||||
@ -104,73 +96,19 @@ class MP_PlayerManager:NSObject{
|
|||||||
}
|
}
|
||||||
///播放器启动时执行事件记录
|
///播放器启动时执行事件记录
|
||||||
private var startActionBlock:MP_PlayTimerStartAction!
|
private var startActionBlock:MP_PlayTimerStartAction!
|
||||||
|
|
||||||
///播放器运行时执行事件记录
|
///播放器运行时执行事件记录
|
||||||
var runActionBlock:MP_PlayTimerRunAction!
|
var runActionBlock:MP_PlayTimerRunAction!
|
||||||
///播放器缓存值闭包
|
///播放器缓存值闭包
|
||||||
var cacheValueBlock:MP_PlayCacheValueAction!
|
var cacheValueBlock:MP_PlayCacheValueAction!
|
||||||
private override init() {
|
private override init() {
|
||||||
super.init()
|
super.init()
|
||||||
// player.automaticallyWaitsToMinimizeStalling = false
|
// 添加观察者,监听播放结束事件
|
||||||
//// player.delegate = self
|
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying(_ :)), name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem)
|
||||||
// // 添加观察者,监听播放结束事件
|
|
||||||
// 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(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 {
|
deinit {
|
||||||
NotificationCenter.default.removeObserver(self)
|
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:
|
/// - Parameters:
|
||||||
/// - startAction: 开始播放时需要执行的事件
|
/// - startAction: 开始播放时需要执行的事件
|
||||||
@ -182,103 +120,120 @@ class MP_PlayerManager:NSObject{
|
|||||||
print("Player No Data")
|
print("Player No Data")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//清除旧流媒体
|
//检索播放器状态
|
||||||
stopAndReleaseStream(&player)
|
switch playState {
|
||||||
|
case .Null://未启动
|
||||||
|
break
|
||||||
|
case .Playing://启动中
|
||||||
|
player.pause()
|
||||||
|
case .Pause://暂停中
|
||||||
|
break
|
||||||
|
}
|
||||||
//记录事件
|
//记录事件
|
||||||
if startAction != nil {
|
if startAction != nil {
|
||||||
startActionBlock = startAction
|
startActionBlock = startAction
|
||||||
}
|
}
|
||||||
if next != nil, (next.url == (loadPlayer.currentVideo.resourcePlayerURL! as NSURL)) {
|
//覆盖播放器原有的playerItem
|
||||||
player = next
|
player.replaceCurrentItem(with: loadPlayer.currentVideo.resourcePlayerItem)
|
||||||
}else {
|
//将进度回归为0
|
||||||
//配置当前播放音乐
|
player.seek(to: .zero)
|
||||||
player = .init(url: loadPlayer.currentVideo.resourcePlayerURL!)
|
//设置一个秒为刻度的时间值
|
||||||
player.maxRetryCount = 2
|
let interval:CMTime = .init(seconds: 1, preferredTimescale: .init(1))
|
||||||
}
|
//为播放器添加运行时主线程每秒触发事件
|
||||||
//预加载下一首(假如有的话)
|
player.addPeriodicTimeObserver(forInterval: interval, queue: .main, using: { [weak self] (time) in
|
||||||
let index = loadPlayer.listViewVideos.firstIndex(of: loadPlayer.currentVideo) ?? 0
|
guard let self = self else { return }
|
||||||
if (loadPlayer.listViewVideos.count-1) > index {
|
//转化为当前播放进度秒值
|
||||||
stopAndReleaseStream(&next)
|
let currentDuration = CMTimeGetSeconds(time)
|
||||||
//纯在下一首,获取下一位的URL
|
//获取当前播放音乐资源的最大时间值
|
||||||
let nextURL = loadPlayer.listViewVideos[index + 1].resourcePlayerURL
|
let maxDuration = getMusicDuration()
|
||||||
next = preloadNext(nextURL!)
|
if maxDuration.isNaN == false {
|
||||||
}
|
//判断当值进度是否超越最大时间值
|
||||||
//开始播放
|
if currentDuration <= maxDuration {
|
||||||
player.play()
|
//没有,执行运行时时间
|
||||||
//获取播放器状态
|
if runActionBlock != nil {
|
||||||
player.onStateChange = {
|
runActionBlock!(currentDuration, maxDuration)
|
||||||
[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
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
//对当前播放PlayerItem设置监听状态
|
||||||
|
//准备状态
|
||||||
|
loadPlayer.currentVideo?.resourcePlayerItem?.addObserver(self, forKeyPath: "status", options: [.old,.new], context: nil)
|
||||||
|
//当前缓冲值
|
||||||
|
loadPlayer.currentVideo?.resourcePlayerItem.addObserver(self, forKeyPath: "loadedTimeRanges", options: [.old,.new], context: nil)
|
||||||
}
|
}
|
||||||
///对流的检查,判断当前调用流是否是播放流
|
//实现KVO监听
|
||||||
private func findTurePlayer(_ stream:FSAudioStream) -> Bool {
|
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||||
guard let currentVideoURL = loadPlayer?.currentVideo?.resourcePlayerURL as? NSURL else {
|
guard let keyPath = keyPath else {
|
||||||
return false
|
return
|
||||||
}
|
}
|
||||||
let streamURL = stream.url
|
//根据keyPath检索
|
||||||
if streamURL == currentVideoURL {
|
switch keyPath {
|
||||||
return true
|
case "status"://playerItem状态
|
||||||
}else {
|
if let statuValue = change?[.newKey] as? Int, statuValue == AVPlayerItem.Status.readyToPlay.rawValue {
|
||||||
return false
|
//判断当前播放器是否在播放当前音乐中
|
||||||
|
if playState != .Playing {
|
||||||
|
//当statuVlaue值等于playerItem准备播放的值,说明已经准备好播放
|
||||||
|
print("当前音乐-\(loadPlayer.currentVideo?.title ?? "") 已经准备好播放")
|
||||||
|
//还未播放当前音乐,启动播放
|
||||||
|
print("开始播放音乐-\(loadPlayer.currentVideo?.title ?? "")")
|
||||||
|
player.play()
|
||||||
|
playState = .Playing
|
||||||
|
//执行开始播放闭包
|
||||||
|
if startActionBlock != nil {
|
||||||
|
startActionBlock!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
print("当前音乐-\(loadPlayer.currentVideo?.title ?? "") 未做好准备播放,失败原因是\(loadPlayer.currentVideo?.resourcePlayerItem.error?.localizedDescription ?? "")")
|
||||||
|
//资源更新,重新配置一下相关内容
|
||||||
|
loadPlayer.remakeImproveData {
|
||||||
|
[weak self] in
|
||||||
|
guard let self = self else {return}
|
||||||
|
//重新播放
|
||||||
|
play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "loadedTimeRanges"://当前缓冲进度
|
||||||
|
//获取当前播放Item的缓冲值组
|
||||||
|
if let timeRanges = loadPlayer.currentVideo?.resourcePlayerItem.loadedTimeRanges.map({$0.timeRangeValue}), let first = timeRanges.first {
|
||||||
|
//获取开始时间的秒数
|
||||||
|
let startSeconds = first.start.seconds
|
||||||
|
//获取缓冲区的持续时间
|
||||||
|
let durationSeconds = first.duration.seconds
|
||||||
|
//计算当前缓冲总时间
|
||||||
|
let bufferedSeconds = startSeconds + durationSeconds
|
||||||
|
//获取当前播放音乐资源的最大时间值
|
||||||
|
let maxDuration = getMusicDuration()
|
||||||
|
//传递缓存值
|
||||||
|
if cacheValueBlock != nil {
|
||||||
|
cacheValueBlock!(bufferedSeconds, maxDuration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case "playbackLikelyToKeepUp"://是否存在足够的数据开始播放
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//MARK: - 获取当前音乐总长度
|
||||||
///预加载下一首流
|
///获取音乐资源总时长
|
||||||
private func preloadNext(_ url:URL) -> FSAudioStream{
|
private func getMusicDuration() -> TimeInterval {
|
||||||
let stream = FSAudioStream(url: url)
|
return CMTimeGetSeconds(player.currentItem?.duration ?? .zero)
|
||||||
stream?.maxRetryCount = 1
|
|
||||||
// 开始预加载数据
|
|
||||||
stream!.preload()
|
|
||||||
print("下一首已经在预加载")
|
|
||||||
return stream!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//MARK: - 音乐播放结束
|
//MARK: - 音乐播放结束
|
||||||
//当前音乐播放结束时
|
//当前音乐播放结束时
|
||||||
@objc private func playerDidFinishPlaying() {
|
@objc private func playerDidFinishPlaying(_ sender:Notification) {
|
||||||
//检索播放器对象
|
//检索播放器对象
|
||||||
guard playState == .Playing else {
|
guard playState == .Playing else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch playType {
|
switch playType {
|
||||||
case .single:
|
case .single:
|
||||||
var postion = FSStreamPosition()
|
|
||||||
postion.position = 0
|
|
||||||
//重播
|
//重播
|
||||||
player.seek(to: postion)
|
player.seek(to: CMTime.zero)
|
||||||
player.play()
|
player.play()
|
||||||
default:
|
default:
|
||||||
//当前音乐播放器正在播放中,下一首
|
//当前音乐播放器正在播放中,下一首
|
||||||
@ -330,8 +285,7 @@ class MP_PlayerManager:NSObject{
|
|||||||
resumeAction!()
|
resumeAction!()
|
||||||
}
|
}
|
||||||
//继续播放器
|
//继续播放器
|
||||||
// player.play()
|
player.play()
|
||||||
player.pause()
|
|
||||||
//切换播放器状态
|
//切换播放器状态
|
||||||
playState = .Playing
|
playState = .Playing
|
||||||
}
|
}
|
||||||
@ -344,8 +298,7 @@ class MP_PlayerManager:NSObject{
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
//继续播放器
|
//继续播放器
|
||||||
// player.play()
|
player.play()
|
||||||
player.pause()
|
|
||||||
//切换播放器状态
|
//切换播放器状态
|
||||||
playState = .Playing
|
playState = .Playing
|
||||||
}
|
}
|
||||||
@ -359,7 +312,7 @@ class MP_PlayerManager:NSObject{
|
|||||||
print("Player is not started")
|
print("Player is not started")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
player.stop()
|
player.pause()
|
||||||
playState = .Null
|
playState = .Null
|
||||||
}
|
}
|
||||||
//MARK: - 切歌(上一首/下一首)
|
//MARK: - 切歌(上一首/下一首)
|
||||||
@ -372,7 +325,7 @@ class MP_PlayerManager:NSObject{
|
|||||||
switch playType {
|
switch playType {
|
||||||
case .random://随机,播放随机列表内容
|
case .random://随机,播放随机列表内容
|
||||||
for (index, item) in loadPlayer.randomVideos.enumerated() {
|
for (index, item) in loadPlayer.randomVideos.enumerated() {
|
||||||
if item.videoId == loadPlayer.currentVideo?.song.videoId {
|
if item.videoId == loadPlayer.currentVideo.song.videoId {
|
||||||
//找到播放音乐的索引
|
//找到播放音乐的索引
|
||||||
nextIndex = index - 1
|
nextIndex = index - 1
|
||||||
}
|
}
|
||||||
@ -381,15 +334,15 @@ class MP_PlayerManager:NSObject{
|
|||||||
if nextIndex < 0 {
|
if nextIndex < 0 {
|
||||||
//播放列表最后一首
|
//播放列表最后一首
|
||||||
let last = loadPlayer.randomVideos.last
|
let last = loadPlayer.randomVideos.last
|
||||||
loadPlayer.improveData(last?.videoId ?? "", isRandom: true)
|
loadPlayer.improveData(last?.videoId ?? "")
|
||||||
}else {
|
}else {
|
||||||
//查询列表对应单曲
|
//查询列表对应单曲
|
||||||
let song = loadPlayer.randomVideos[nextIndex]
|
let song = loadPlayer.randomVideos[nextIndex]
|
||||||
loadPlayer.improveData(song.videoId ?? "", isRandom: true)
|
loadPlayer.improveData(song.videoId ?? "")
|
||||||
}
|
}
|
||||||
default://常规播放或者单曲播放
|
default://常规播放或者单曲播放
|
||||||
for (index, item) in loadPlayer.songVideos.enumerated() {
|
for (index, item) in loadPlayer.songVideos.enumerated() {
|
||||||
if item.videoId == loadPlayer.currentVideo?.song.videoId {
|
if item.videoId == loadPlayer.currentVideo.song.videoId {
|
||||||
//找到播放音乐的索引
|
//找到播放音乐的索引
|
||||||
nextIndex = index - 1
|
nextIndex = index - 1
|
||||||
}
|
}
|
||||||
@ -414,7 +367,7 @@ class MP_PlayerManager:NSObject{
|
|||||||
switch playType {
|
switch playType {
|
||||||
case .random:
|
case .random:
|
||||||
for (index, item) in loadPlayer.randomVideos.enumerated() {
|
for (index, item) in loadPlayer.randomVideos.enumerated() {
|
||||||
if item.videoId == loadPlayer.currentVideo?.song.videoId {
|
if item.videoId == loadPlayer.currentVideo.song.videoId {
|
||||||
//找到播放音乐的索引
|
//找到播放音乐的索引
|
||||||
nextIndex = index + 1
|
nextIndex = index + 1
|
||||||
}
|
}
|
||||||
@ -423,15 +376,15 @@ class MP_PlayerManager:NSObject{
|
|||||||
if nextIndex > (loadPlayer.randomVideos.count-1) {
|
if nextIndex > (loadPlayer.randomVideos.count-1) {
|
||||||
//播放列表第一首
|
//播放列表第一首
|
||||||
let first = loadPlayer.randomVideos.first
|
let first = loadPlayer.randomVideos.first
|
||||||
loadPlayer.improveData(first?.videoId ?? "", isRandom: true)
|
loadPlayer.improveData(first?.videoId ?? "")
|
||||||
}else {
|
}else {
|
||||||
//存在下一首,获取下一首ID,并播放
|
//存在下一首,获取下一首ID,并播放
|
||||||
let song = loadPlayer.randomVideos[nextIndex]
|
let song = loadPlayer.randomVideos[nextIndex]
|
||||||
loadPlayer.improveData(song.videoId ?? "", isRandom: true)
|
loadPlayer.improveData(song.videoId ?? "")
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
for (index, item) in loadPlayer.songVideos.enumerated() {
|
for (index, item) in loadPlayer.songVideos.enumerated() {
|
||||||
if item.videoId == loadPlayer.currentVideo?.song.videoId {
|
if item.videoId == loadPlayer.currentVideo.song.videoId {
|
||||||
//找到播放音乐的索引
|
//找到播放音乐的索引
|
||||||
nextIndex = index + 1
|
nextIndex = index + 1
|
||||||
}
|
}
|
||||||
@ -452,21 +405,18 @@ class MP_PlayerManager:NSObject{
|
|||||||
@objc private func userSwitchCurrentVideoAction(_ sender:Notification) {
|
@objc private func userSwitchCurrentVideoAction(_ sender:Notification) {
|
||||||
//将播放器状态调整未播放
|
//将播放器状态调整未播放
|
||||||
playState = .Null
|
playState = .Null
|
||||||
//清理所有的流
|
//暂停播放
|
||||||
if player != nil {
|
player.pause()
|
||||||
//清除所有流
|
//优先获取传递的值
|
||||||
stopAndReleaseStream(&player)
|
if let video = sender.object as? MPPositive_SongViewModel {
|
||||||
|
//切歌时移除KVO监听
|
||||||
|
video.resourcePlayerItem.removeObserver(self, forKeyPath: "status")
|
||||||
|
video.resourcePlayerItem.removeObserver(self, forKeyPath: "loadedTimeRanges")
|
||||||
|
// video.resourcePlayerItem.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp")
|
||||||
}
|
}
|
||||||
if cacheValueBlock != nil {
|
if loadPlayer.currentVideo != nil {
|
||||||
cacheValueBlock!(0)
|
//开始播放
|
||||||
}
|
play(startAction: startActionBlock)
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
|
||||||
[weak self] in
|
|
||||||
guard let self = self else {return}
|
|
||||||
if loadPlayer.currentVideo != nil {
|
|
||||||
//开始播放
|
|
||||||
play(startAction: startActionBlock)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -482,85 +432,22 @@ class MP_PlayerManager:NSObject{
|
|||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - progress: 要调整进度值(保证在0-1范围内,超出该方法不会响应)
|
/// - progress: 要调整进度值(保证在0-1范围内,超出该方法不会响应)
|
||||||
func setEditProgressEnd(_ progress:Float, endAction:MP_PlayTimerEditEndAction? = nil) {
|
func setEditProgressEnd(_ progress:Float, endAction:MP_PlayTimerEditEndAction? = nil) {
|
||||||
guard playState != .Null, let player = player, findTurePlayer(player) else {
|
guard playState != .Null else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard progress >= 0, progress <= 1 else {
|
guard progress >= 0, progress <= 1 else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
//根据当前进度值设置时间节点
|
||||||
|
let timePoint:Double = Double(progress)*getMusicDuration()
|
||||||
//设置对应的时间值
|
//设置对应的时间值
|
||||||
var time:FSStreamPosition = .init()
|
let time:CMTime = .init(seconds: timePoint, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
|
||||||
time.position = progress
|
//调整播放器时间
|
||||||
//获取当前值的大小
|
player.seek(to: time)
|
||||||
let currentTime = player.currentTimePlayed.playbackTimeInSeconds
|
//恢复播放
|
||||||
if progress != currentTime {
|
resume()
|
||||||
//调整播放器时间
|
if endAction != nil {
|
||||||
player.seek(to: time)
|
endAction!()
|
||||||
//恢复播放
|
|
||||||
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)")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//}
|
|
||||||
|
|||||||
@ -8,20 +8,16 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
import AVKit
|
import AVKit
|
||||||
import AVFoundation
|
import AVFoundation
|
||||||
import FreeStreamer
|
|
||||||
class MPPositive_SongViewModel: NSObject {
|
class MPPositive_SongViewModel: NSObject {
|
||||||
///排序号
|
///排序号
|
||||||
var index:Int!
|
var index:Int!
|
||||||
///音乐资源路径
|
|
||||||
// var resourcePlayerItem:AVPlayerItem!
|
|
||||||
// var resourcePlayerItem:MP_AVPlayerItem!
|
|
||||||
// var resourcePlayerItem:CachingPlayerItem!
|
|
||||||
///播放实例
|
///播放实例
|
||||||
// var resourcePlayerItem:FSAudioStream?
|
var resourcePlayerItem:AVPlayerItem!
|
||||||
|
///播放媒体
|
||||||
|
var resourcePlayerAsset:AVURLAsset!
|
||||||
///播放路径
|
///播放路径
|
||||||
var resourcePlayerURL:URL?
|
var resourcePlayerURL:URL!
|
||||||
///资源加载路径
|
|
||||||
// var resourceAsset:MP_AVURLAsset!
|
|
||||||
///封面
|
///封面
|
||||||
var coverUrl:URL?
|
var coverUrl:URL?
|
||||||
///标题
|
///标题
|
||||||
@ -41,10 +37,11 @@ class MPPositive_SongViewModel: NSObject {
|
|||||||
init(_ song:MPPositive_SongItemModel) {
|
init(_ song:MPPositive_SongItemModel) {
|
||||||
super.init()
|
super.init()
|
||||||
self.song = song
|
self.song = song
|
||||||
// resourcePlayerItem = nil
|
|
||||||
configure()
|
configure()
|
||||||
}
|
}
|
||||||
deinit {
|
deinit {
|
||||||
|
resourcePlayerItem = nil
|
||||||
|
resourcePlayerAsset = nil
|
||||||
resourcePlayerURL = nil
|
resourcePlayerURL = nil
|
||||||
}
|
}
|
||||||
//数据配置
|
//数据配置
|
||||||
@ -53,14 +50,12 @@ class MPPositive_SongViewModel: NSObject {
|
|||||||
index = song.index
|
index = song.index
|
||||||
|
|
||||||
if let first = song.resourceUrls?.first {
|
if let first = song.resourceUrls?.first {
|
||||||
//判断是否下载
|
resourcePlayerURL = .init(string:first)
|
||||||
if isDlownd == true {
|
resourcePlayerAsset = .init(url: resourcePlayerURL)
|
||||||
resourcePlayerURL = .init(string:first)
|
preloadAsset(resourcePlayerAsset)
|
||||||
}else {
|
resourcePlayerItem = .init(asset: resourcePlayerAsset)
|
||||||
//没有完成下载,使用网络路径
|
|
||||||
resourcePlayerURL = .init(string: first)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//封面路径默认取最后一条
|
//封面路径默认取最后一条
|
||||||
if song.reviewUrls?.first != nil {
|
if song.reviewUrls?.first != nil {
|
||||||
coverUrl = .init(string: song.reviewUrls!.last!)
|
coverUrl = .init(string: song.reviewUrls!.last!)
|
||||||
@ -98,4 +93,53 @@ class MPPositive_SongViewModel: NSObject {
|
|||||||
//检索是否下载
|
//检索是否下载
|
||||||
isDlownd = MPPositive_DownloadItemModel.fetch(.init(format: "videoId == %@", song.videoId)).count != 0
|
isDlownd = MPPositive_DownloadItemModel.fetch(.init(format: "videoId == %@", song.videoId)).count != 0
|
||||||
}
|
}
|
||||||
|
//执行预加载
|
||||||
|
func preloadAsset(_ asset:AVURLAsset) {
|
||||||
|
//执行预加载
|
||||||
|
if #available(iOS 16, *) {
|
||||||
|
//ios16以上的情况
|
||||||
|
Task{
|
||||||
|
do{
|
||||||
|
let playable = try await asset.load(.isPlayable)
|
||||||
|
if playable == true {
|
||||||
|
print("\(self.title ?? "")预加载成功")
|
||||||
|
}else {
|
||||||
|
//检索预加载失败原因
|
||||||
|
switch asset.status(of: .isPlayable) {
|
||||||
|
case .failed(let erro):
|
||||||
|
print("\(title ?? "")预加载失败,失败原因:\(erro.localizedDescription)")
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch{
|
||||||
|
print("预加载失败:\(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
//ios16以下的情况
|
||||||
|
let keys = ["playable"]
|
||||||
|
asset.loadValuesAsynchronously(forKeys: keys) {
|
||||||
|
[weak self] in
|
||||||
|
guard let self = self else {return}
|
||||||
|
for key in keys {
|
||||||
|
var error: NSError? = nil
|
||||||
|
let status = asset.statusOfValue(forKey: key, error: &error)
|
||||||
|
switch status {
|
||||||
|
case .loaded:
|
||||||
|
// key成功加载,资源准备就绪
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
print("\(self.title ?? "")预加载成功")
|
||||||
|
}
|
||||||
|
case .failed:
|
||||||
|
print("\(title ?? "")预加载失败,失败原因:\(error?.localizedDescription ?? "")")
|
||||||
|
case .cancelled:
|
||||||
|
print("\(title ?? "")预加载被取消了")
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -133,11 +133,14 @@ class MPPositive_PlayerViewController: MPPositive_BaseViewController, UIViewCont
|
|||||||
coverView.sliderView.value = Float(value)
|
coverView.sliderView.value = Float(value)
|
||||||
}
|
}
|
||||||
//当缓存变化时
|
//当缓存变化时
|
||||||
MP_PlayerManager.shared.cacheValueBlock = { [weak self] progress in
|
MP_PlayerManager.shared.cacheValueBlock = { [weak self] (value, duration) in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
if progress <= 1 {
|
if value < duration {
|
||||||
coverView.progressView.setProgress(progress, animated: false)
|
//进度缓存中
|
||||||
|
let float = value/duration
|
||||||
|
coverView.progressView.setProgress(Float(float), animated: false)
|
||||||
}else {
|
}else {
|
||||||
|
//进度缓存满了
|
||||||
coverView.progressView.setProgress(1, animated: false)
|
coverView.progressView.setProgress(1, animated: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user