Music_Player3/relax.offline.mp3.music/MP/Common/Tool(工具封装)/MP_PlayerManager.swift
2024-09-26 14:07:13 +08:00

1009 lines
41 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 Kingfisher
///
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
var title:String{
switch self {
case .normal:
return "顺序播放"
case .random:
return "随机播放"
case .single:
return "单曲循环"
}
}
}
///
enum MP_TimerStateType:Int {
///
case Resume = 0
///
case Suspend = 1
}
///
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 = (_ currentValue:TimeInterval, _ duration:TimeInterval) -> Void
///
class MP_PlayerManager:NSObject{
///
static let shared = MP_PlayerManager()
///
lazy var player:AVPlayer = {
let player = AVPlayer()
player.rate = 1
return player
}()
///
lazy var videoLayer:AVPlayerLayer = {
let layer:AVPlayerLayer = .init(player: player)
layer.videoGravity = .resizeAspect
layer.backgroundColor = UIColor.black.cgColor
return layer
}()
//
var center:MPRemoteCommandCenter?{
didSet{
print("更新了远程控制中心")
}
}
///
private var timer:DispatchSourceTimer?
///
private var times:TimeInterval = 0
///
private var queue:DispatchQueue?
///load
var loadPlayer:MPPositive_PlayerLoadViewModel!{
willSet{
DispatchQueue.main.async {
[weak self] in
guard let self = self else {return}
guard loadPlayer != nil else {
if newValue != nil {
//load
NotificationCenter.notificationKey.post(notificationName: .pup_bottom_show)
}
return
}
if newValue != nil {
//load
NotificationCenter.notificationKey.post(notificationName: .pup_bottom_show)
}else {
//load
NotificationCenter.notificationKey.post(notificationName: .player_delete_list)
playState = .Null
player.pause()
center?.playCommand.removeTarget(self)
center?.pauseCommand.removeTarget(self)
center?.nextTrackCommand.removeTarget(self)
center?.previousTrackCommand.removeTarget(self)
center?.changePlaybackPositionCommand.removeTarget(self)
center = nil
do {
try AVAudioSession.sharedInstance().setActive(false)
} catch {
print("Error deactivating audio session: \(error)")
}
}
}
}
}
//
private var currentInfo:[String:Any]?
//
private var isLast:Bool = false
//
func setLastStatus(bool:Bool) {
isLast = bool
}
//
private var playState:MP_PlayerStateType = .Null{
didSet{
//
NotificationCenter.notificationKey.post(notificationName: .switch_player_status, object: playState)
}
}
//广广
// var isAdLate:Bool? = false{
// didSet{
// DispatchQueue.main.asyncAfter(deadline: .now()) {
// [weak self] in
// guard let self = self else {return}
// guard let ad = isAdLate, loadPlayer?.currentVideo != nil else {return}
// //广
// if ad == true {
// //广
// switch playState {
// case .Null://
// //
// player.pause()
// case .Playing://
// //
// player.pause()
// case .Pause://
// //
// player.pause()
// }
// }else {
// //广
// switch playState {
// case .Null://
// //
// if loadPlayer?.currentVideo?.resourcePlayerItem?.isPlaybackLikelyToKeepUp == true {
// //
// playState = .Playing
// player.play()
// }else {
// //
// playState = .Null
// player.pause()
// }
// case .Playing://
// playState = .Playing
// //
// player.play()
// case .Pause://
// playState = .Pause
// //
// player.pause()
// }
// }
// }
// }
// }
///
func getPlayState() -> MP_PlayerStateType {
return playState
}
///
private var playType:MP_PlayerPlayType = .normal{
didSet{
DispatchQueue.main.async {
//
NotificationCenter.notificationKey.post(notificationName: .player_type_switch)
}
}
}
///
func getPlayType() -> MP_PlayerPlayType {
return playType
}
///
/// - Parameter type:
func setPlayType(_ type:MP_PlayerPlayType) {
playType = type
print("播放器播放方法已经改为\(type.title)")
}
///
private var timerType:MP_TimerStateType = .Suspend
///
private var startActionBlock:MP_PlayTimerStartAction?
///
var runActionBlock:MP_PlayTimerRunAction?
///
var bottomProgressBlock:MP_PlayTimerRunAction?
///
var cacheValueBlock:MP_PlayCacheValueAction?
///
private var statusObservation:NSKeyValueObservation?
///
private var loadedTimeRangesObservation:NSKeyValueObservation?
///
private var playbackLikelyToKeepUpObservation:NSKeyValueObservation?
///
private var errorObservation:NSKeyValueObservation?
///
private var playEntitlementObservation:NSKeyValueObservation?
private override init() {
super.init()
//
timer?.cancel()
queue = DispatchQueue(label: "com.playerTimer.timer",attributes: .concurrent)
timer = DispatchSource.makeTimerSource(queue: queue)
//0.1,0.01
timer?.schedule(deadline: .now(), repeating: .milliseconds(100), leeway: .milliseconds(10))
//
timer?.setEventHandler(handler: { [weak self] in
//0.1
self?.times += 0.1
})
//
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)
//
NotificationCenter.default.addObserver(self, selector: #selector(handleAudioSessionRouteChangeAction(_ :)), name: AVAudioSession.routeChangeNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleAppStateChange(_ :)), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleAppStateChange(_ :)), name: UIApplication.willEnterForegroundNotification, object: nil)
//
let interval:CMTime = .init(seconds: 1, preferredTimescale: .init(1))
//线
player.addPeriodicTimeObserver(forInterval: interval, queue: .main, using: { [weak self] (time) in
guard let self = self else { return }
cacheLoadTimes()
//
let currentDuration = CMTimeGetSeconds(time)
//
updateNowPlayingInfo()
//
let maxDuration = getMusicDuration()
if maxDuration.isNaN == false {
//
if currentDuration <= maxDuration {
//
if runActionBlock != nil {
runActionBlock!(currentDuration, maxDuration)
}
if bottomProgressBlock != nil {
bottomProgressBlock!(currentDuration, maxDuration)
}
}
}
})
}
deinit {
NotificationCenter.default.removeObserver(self)
center?.playCommand.removeTarget(self)
center?.pauseCommand.removeTarget(self)
center?.nextTrackCommand.removeTarget(self)
center?.previousTrackCommand.removeTarget(self)
timer?.cancel()
timer = nil
}
///
/// - Parameters:
/// - startAction:
/// - runAction:
/// - endAction:
func play(startAction:MP_PlayTimerStartAction? = nil) {
guard loadPlayer != nil, loadPlayer?.currentVideo != nil else {
//
print("Player No Data")
return
}
//
switch playState {
case .Null://
break
case .Playing://
player.pause()
case .Pause://
break
}
//
if startAction != nil {
startActionBlock = startAction
}
//
guard loadPlayer?.currentVideo?.isPlayEntitlement != false else {
//,
if MP_NetWorkManager.shared.netWorkStatu == .reachable {
nextEvent()
}
return
}
if let currentVideo = loadPlayer?.currentVideo {
//playerItem
player.replaceCurrentItem(with: currentVideo.resourcePlayerItem)
if center == nil {
setCommandCenter()
}
//
startTimer()
//PlayerItem
if currentVideo.isKVO == false {
//
statusObservation?.invalidate()
statusObservation = currentVideo.resourcePlayerItem?.observe(\.status, options: [.old,.new], changeHandler: { [weak self] item, change in
guard let self = self else {return}
if item.status == .readyToPlay {
//
if playState != .Playing {
//statuVlaueplayerItem
print("当前音乐-\(currentVideo.title ?? "") 已经准备好播放")
}
}else {
print("当前音乐-\(currentVideo.title ?? "") 未做好准备播放,失败原因是\(currentVideo.resourcePlayerItem?.error?.localizedDescription ?? "")")
MP_AnalyticsManager.shared.player_b_failure_errorAction(currentVideo.song.videoId ?? "", videoname: currentVideo.title ?? "", artistname: currentVideo.song.shortBylineText ?? "", error: currentVideo.resourcePlayerItem?.error?.localizedDescription ?? "Failed to buffer data")
if currentVideo.isKVO == true {
suspendTimer()
currentVideo.isKVO = false
statusObservation?.invalidate()
loadedTimeRangesObservation?.invalidate()
playbackLikelyToKeepUpObservation?.invalidate()
errorObservation?.invalidate()
//
loadPlayer?.remakeImproveData {
[weak self] in
self?.play()
}
}
}
})
//
loadedTimeRangesObservation?.invalidate()
loadedTimeRangesObservation = currentVideo.resourcePlayerItem?.observe(\.loadedTimeRanges, options: [.old,.new], changeHandler: { [weak self] item, change in
guard let self = self else {return}
cacheLoadTimes()
})
//
playbackLikelyToKeepUpObservation?.invalidate()
playbackLikelyToKeepUpObservation = currentVideo.resourcePlayerItem?.observe(\.isPlaybackLikelyToKeepUp, options: [.old,.new], changeHandler: { [weak self] item, change in
guard let self = self else {return}
if let playbackLikelyToKeepUp = change.newValue, playbackLikelyToKeepUp == true {
if playState != .Playing && playState != .Pause {
//
playState = .Playing
// //广
// if isAdLate != true {
// //
// if isLast {
// pause()
// isLast = false
// }else {
// player.play()
// }
// }else {
//
// }
//
if isLast {
pause()
isLast = false
}else {
player.play()
}
//
suspendTimer()
if let currentVideo = loadPlayer?.currentVideo {
MP_AnalyticsManager.shared.player_b_success_actionAction(currentVideo.song.videoId ?? "", videoname: currentVideo.title ?? "", artistname: currentVideo.song.shortBylineText ?? "")
}
//
if startActionBlock != nil {
startActionBlock!()
}
}
}else {
//
player.pause()
playState = .Null
}
})
//
errorObservation?.invalidate()
errorObservation = currentVideo.resourcePlayerItem?.observe(\.error, options: [.old,.new], changeHandler: { [weak self] item, change in
guard let self = self else {return}
if let error = change.newValue, let nsError = error {
print("当前音乐-\(currentVideo.title ?? "") 未做好准备播放,失败原因是\(nsError.localizedDescription)")
MP_AnalyticsManager.shared.player_b_failure_errorAction(currentVideo.song.videoId ?? "", videoname: currentVideo.title ?? "", artistname: currentVideo.song.shortBylineText ?? "", error: nsError.localizedDescription)
}
})
//
playEntitlementObservation?.invalidate()
playEntitlementObservation = currentVideo.observe(\.isPlayEntitlement, options: [.old,.new], changeHandler: { [weak self] item, change in
guard let self = self, MP_NetWorkManager.shared.netWorkStatu == .reachable else {return}
if change.newValue == 0 {
//
nextEvent()
}
})
currentVideo.isKVO = true
//0
player.seek(to: .zero)
updateNowPlayingInfo()
}
}
}
///
func startTimer() {
guard timerType == .Suspend else {
return
}
times = 0
//
timer?.resume()
timerType = .Resume
}
///
func suspendTimer() {
guard timerType == .Resume else {
return
}
//
timer?.suspend()
timerType = .Suspend
guard times != 0 else {
return
}
let times = Int(self.times)
let msTimes = times*1000
MP_AnalyticsManager.shared.player_b_delay_actionAction(loadPlayer?.currentVideo?.song.videoId ?? "", videoname: loadPlayer?.currentVideo?.title ?? "", artistname: loadPlayer?.currentVideo?.song.shortBylineText ?? "", delay: "\(msTimes)ms")
}
///
@objc private func handleAudioSessionRouteChangeAction(_ notification: Notification) {
guard let info = notification.userInfo, let reasonValue = info[AVAudioSessionRouteChangeReasonKey] as? UInt, let reason = AVAudioSession.RouteChangeReason(rawValue:reasonValue) else {
return
}
switch reason {
case .newDeviceAvailable://
print("设备已连接")
//
case .oldDeviceUnavailable://
print("设备已断开")
//
pause()
default:
break
}
}
///退
@objc private func handleAppStateChange(_ notification: Notification) {
if notification.name == UIApplication.didEnterBackgroundNotification {
//AVPlayer
videoLayer.player = nil
}else if notification.name == UIApplication.willEnterForegroundNotification {
videoLayer.player = player
}
}
///
@objc private func netWorkReachableAction(_ sender:Notification) {
//
if loadPlayer?.currentVideo != nil {
if playState == .Playing, let currentItem = loadPlayer?.currentVideo?.resourcePlayerItem {
//
let currentTime = currentItem.currentTime()
//
player.seek(to: currentTime)
player.play()
playState = .Playing
}
}
}
//
func playerStatuTimerAction() {
MPPositive_Debouncer.shared.playCall {
[weak self] in
guard let self = self else {return}
//10
if playState != .Playing {
if isLast == true {
MP_HUD.text("Failed to obtain resource, please try again later".localizableString(), delay: 2.0, completion: nil)
}
}
}
}
//
private func cacheLoadTimes() {
//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)
}
}
}
//MARK: -
///
private func getMusicDuration() -> TimeInterval {
return CMTimeGetSeconds(player.currentItem?.duration ?? .zero)
}
//MARK: -
//
@objc private func playerDidFinishPlaying(_ sender:Notification) {
//
guard playState == .Playing else {
return
}
//
if let song = loadPlayer?.currentVideo?.song {
createRecentData(3, song: song)
}
switch playType {
case .single:
//
player.seek(to: CMTime.zero)
player.play()
default:
//
nextEvent()
}
}
///
private func createRecentData(_ level:Int16, song:MPPositive_SongItemModel) {
//
MPPositive_RecentlyModel.fetch(predicate: .init(format: "videoId == %@", (song.videoId ?? ""))) { results in
if results.isEmpty {
//,
let item = try? MPPositive_RecentlyModel.create()
item?.coverImage = song.coverUrls?.last
item?.reviewImage = song.reviewUrls?.last
item?.title = song.title
item?.subtitle = (song.longBylineText ?? "")+(song.shortBylineText ?? "")
item?.videoId = song.videoId
item?.lyricsID = song.lyricsID
item?.relatedID = song.relatedID
item?.addTime = Date()
item?.level = level
item?.artistID = song.artistID
item?.albumID = song.albumID
item?.playListID = song.playListID
}else {
//
if let item = results.first {
if (item.level ?? 0) < level {
item.level = level
}
item.addTime = Date()
}
}
MPPositive_RecentlyModel.save()
}
}
//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
}
///广
func adToPause() {
//
playState = .Null
player.pause()
}
///广
func adToResume() {
}
//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()
//
playState = .Playing
}
///
private func resume() {
//
guard playState == .Pause else {
//
print("Player is not paused")
return
}
//
player.play()
//
playState = .Playing
}
//MARK: -
//
func stop() {
//
guard playState != .Null else {
//
print("Player is not started")
return
}
player.pause()
playState = .Null
}
//MARK: - /
///
func previousEvent() {
//
playState = .Null
var nextIndex:Int = 0
//
switch playType {
case .random://
//
guard (loadPlayer?.randomVideos?.count ?? 0) != 1 else {
player.seek(to: .zero)
playState = .Playing
player.play()
return
}
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 {
//
if let song = loadPlayer?.randomVideos?[nextIndex] {
loadPlayer?.improveData(song.videoId ?? "", isRandom: true)
}
}
default://
guard (loadPlayer?.songVideos?.count ?? 0) != 1 else {
player.seek(to: .zero)
playState = .Playing
player.play()
return
}
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 {
//
if let song = loadPlayer?.songVideos?[nextIndex] {
loadPlayer?.improveData(song.videoId ?? "")
}
}
}
}
///
func nextEvent() {
//
playState = .Null
var nextIndex:Int = 0
switch playType {
case .random:
guard let randomVideos = loadPlayer?.randomVideos, randomVideos.count != 1 else {
player.seek(to: .zero)
playState = .Playing
player.play()
return
}
for (index, item) in randomVideos.enumerated() {
if item.videoId == loadPlayer?.currentVideo?.song.videoId {
//
nextIndex = index + 1
}
}
//
if nextIndex > (randomVideos.count-1) {
//
let first = randomVideos.first
loadPlayer?.improveData(first?.videoId ?? "", isRandom: true)
}else {
//,ID
if let song = loadPlayer?.randomVideos?[nextIndex] {
loadPlayer?.improveData(song.videoId ?? "", isRandom: true)
}
}
default:
guard let songVideos = loadPlayer?.songVideos, songVideos.count != 1 else {
player.seek(to: .zero)
playState = .Playing
player.play()
return
}
for (index, item) in songVideos.enumerated() {
if item.videoId == loadPlayer?.currentVideo?.song.videoId {
//
nextIndex = index + 1
}
}
//
if nextIndex > (songVideos.count-1) {
//
let first = songVideos.first
loadPlayer?.improveData(first?.videoId ?? "")
}else {
//,ID
if let song = loadPlayer?.songVideos?[nextIndex] {
loadPlayer?.improveData(song.videoId ?? "")
}
}
}
}
///
private func switchEvent() {
//
guard let currentVideo = loadPlayer?.currentVideo else {return}
if let currentDruation = player.currentItem?.duration {
let times = CMTimeGetSeconds(currentDruation)
if times >= 60 {
//
createRecentData(2, song: currentVideo.song)
}else {
//
createRecentData(1, song: currentVideo.song)
}
}else {
//
}
}
///
@objc private func userSwitchCurrentVideoAction(_ sender:Notification) {
//
playState = .Null
//
player.pause()
//
if let video = sender.object as? MPPositive_SongViewModel {
if video.isKVO == true {
//KVO
statusObservation?.invalidate()
playbackLikelyToKeepUpObservation?.invalidate()
errorObservation?.invalidate()
loadedTimeRangesObservation?.invalidate()
video.isKVO = false
}
}
if cacheValueBlock != nil {
cacheValueBlock!(0, 1)
}
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 else {
return
}
guard progress >= 0, progress <= 1 else {
return
}
//
let timePoint:Double = Double(progress)*getMusicDuration()
//
let time:CMTime = .init(seconds: timePoint, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
//
player.seek(to: time)
//
resume()
if endAction != nil {
endAction!()
}
}
//MARK: -
func setCommandCenter() {
//
center = MPRemoteCommandCenter.shared()
//
//
center!.playCommand.addTarget(handler: { [weak self] (event) in
guard let self = self else { return .noActionableNowPlayingItem}
if loadPlayer?.currentVideo != nil && playState == .Pause {
resume()
return .success
}else {
return .noActionableNowPlayingItem
}
})
//
center!.pauseCommand.addTarget(handler: { [weak self] (event) in
guard let self = self else { return .noActionableNowPlayingItem}
if loadPlayer?.currentVideo != nil && playState == .Playing {
pause()
return .success
}else {
return .noActionableNowPlayingItem
}
})
//
center!.previousTrackCommand.addTarget { [weak self] (event) in
guard let self = self else { return .noActionableNowPlayingItem}
if loadPlayer?.currentVideo != nil {
previousEvent()
return .success
}else {
return .noActionableNowPlayingItem
}
}
//
center!.nextTrackCommand.addTarget { [weak self] (event) in
guard let self = self else { return .noActionableNowPlayingItem}
if loadPlayer?.currentVideo != nil {
nextEvent()
return .success
}else {
return .noActionableNowPlayingItem
}
}
//
center?.changePlaybackPositionCommand.addTarget(handler: { [weak self] event in
guard let self = self else { return .noActionableNowPlayingItem}
guard let positionEvent = event as? MPChangePlaybackPositionCommandEvent else {
return .commandFailed
}
if loadPlayer?.currentVideo != nil {
self.player.seek(to: CMTime(seconds: positionEvent.positionTime, preferredTimescale: 1))
return .success
}else {
return .noActionableNowPlayingItem
}
})
}
//
func updateNowPlayingInfo() {
guard loadPlayer?.currentVideo != nil else {return}
//info
currentInfo = [:]
//
currentInfo![MPMediaItemPropertyTitle] = loadPlayer?.currentVideo?.title ?? ""
//
currentInfo![MPMediaItemPropertyArtist] = loadPlayer?.currentVideo?.song?.shortBylineText ?? ""
//
currentInfo![MPMediaItemPropertyAlbumTitle] = loadPlayer?.currentVideo?.song?.longBylineText
//
currentInfo![MPNowPlayingInfoPropertyElapsedPlaybackTime] = CMTimeGetSeconds(player.currentItem?.currentTime() ?? .zero)
//
currentInfo![MPMediaItemPropertyPlaybackDuration] = CMTimeGetSeconds(player.currentItem?.duration ?? .zero)
currentInfo![MPNowPlayingInfoPropertyPlaybackRate] = 1.0
let reviewURL = URL(string: loadPlayer?.currentVideo?.song?.reviewUrls?.last ?? "")!
KingfisherManager.shared.retrieveImage(with: reviewURL) { [weak self]result in
switch result {
case .success(let imageResult):
let image = imageResult.image
self?.currentInfo?[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: image.size, requestHandler: { size in
return image
})
// MPNowPlayingInfoCenter线
DispatchQueue.main.async {
//
MPNowPlayingInfoCenter.default().nowPlayingInfo = self?.currentInfo
}
case .failure(_):
self?.currentInfo?[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: placeholderImage.size, requestHandler: { size in
return placeholderImage
})
// MPNowPlayingInfoCenter线
DispatchQueue.main.async {
//
MPNowPlayingInfoCenter.default().nowPlayingInfo = self?.currentInfo
}
}
}
}
func getVideoDimensions(from playerItem: AVPlayerItem, completion: @escaping (CGFloat, CGFloat) -> Void) {
guard let asset = playerItem.asset as? AVURLAsset else {
completion(0, 0)
return
}
asset.loadValuesAsynchronously(forKeys: ["tracks"]) {
var error: NSError? = nil
let status = asset.statusOfValue(forKey: "tracks", error: &error)
if status == .loaded {
let videoTracks = asset.tracks(withMediaType: .video)
guard let videoTrack = videoTracks.first else {
completion(0, 0)
return
}
let size = videoTrack.naturalSize.applying(videoTrack.preferredTransform)
completion(abs(size.width), abs(size.height))
} else {
completion(0, 0)
}
}
}
///
func isCoverVideo(playerItem: AVPlayerItem, completion: @escaping (Bool) -> Void) {
getVideoDimensions(from: playerItem) { width, height in
if width == 0 || height == 0 {
//0
completion(true)
} else {
completion(width <= 1.5 * height)
}
}
}
}