From 061a9e55a2e22b06b0c5f28ff3c0041a02e585d9 Mon Sep 17 00:00:00 2001 From: Guy Hadash Date: Sun, 10 Oct 2021 11:55:49 +0300 Subject: [PATCH 1/6] preload & cancelPreload --- .../AVPlayerWrapper/AVPlayerWrapper.swift | 85 +++++++++++++++---- .../AVPlayerWrapperProtocol.swift | 5 +- SwiftAudioEx/Classes/AudioPlayer.swift | 14 +++ 3 files changed, 87 insertions(+), 17 deletions(-) diff --git a/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift b/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift index c9a35c9..38a3fd5 100755 --- a/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift +++ b/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift @@ -31,7 +31,9 @@ class AVPlayerWrapper: AVPlayerWrapperProtocol { let playerTimeObserver: AVPlayerTimeObserver let playerItemNotificationObserver: AVPlayerItemNotificationObserver let playerItemObserver: AVPlayerItemObserver - + let additionalAVPlayer: AVPlayer + public var preloadedAssets: [String: AVAsset] + /** True if the last call to load(from:playWhenReady) had playWhenReady=true. */ @@ -55,6 +57,9 @@ class AVPlayerWrapper: AVPlayerWrapperProtocol { self.playerItemNotificationObserver = AVPlayerItemNotificationObserver() self.playerItemObserver = AVPlayerItemObserver() + self.additionalAVPlayer = AVPlayer(); + self.preloadedAssets = [String: AVAsset](); + self.playerObserver.delegate = self self.playerTimeObserver.delegate = self self.playerItemNotificationObserver.delegate = self @@ -183,7 +188,13 @@ class AVPlayerWrapper: AVPlayerWrapperProtocol { recreateAVPlayer() } - self._pendingAsset = AVURLAsset(url: url, options: options) + if (self.preloadedAssets[url.absoluteString] != nil){ + self._pendingAsset = self.preloadedAssets[url.absoluteString] + self.loadAssetIntoPlayer(); + return + } else { + self._pendingAsset = AVURLAsset(url: url, options: options) + } if let pendingAsset = _pendingAsset { self._state = .loading @@ -200,20 +211,7 @@ class AVPlayerWrapper: AVPlayerWrapperProtocol { let isPendingAsset = (self._pendingAsset != nil && pendingAsset.isEqual(self._pendingAsset)) switch status { case .loaded: - if isPendingAsset { - let currentItem = AVPlayerItem(asset: pendingAsset, automaticallyLoadedAssetKeys: [Constants.assetPlayableKey]) - currentItem.preferredForwardBufferDuration = self.bufferDuration - self.avPlayer.replaceCurrentItem(with: currentItem) - - // Register for events - self.playerTimeObserver.registerForBoundaryTimeEvents() - self.playerObserver.startObserving() - self.playerItemNotificationObserver.startObserving(item: currentItem) - self.playerItemObserver.startObserving(item: currentItem) - for format in pendingAsset.availableMetadataFormats { - self.delegate?.AVWrapper(didReceiveMetadata: pendingAsset.metadata(forFormat: format)) - } - } + self.loadAssetIntoPlayer(); break case .failed: @@ -233,6 +231,25 @@ class AVPlayerWrapper: AVPlayerWrapperProtocol { }) } } + + func loadAssetIntoPlayer() { + if let pendingAsset = _pendingAsset { + + let isPendingAsset = (self._pendingAsset != nil && pendingAsset.isEqual(self._pendingAsset)) + + if isPendingAsset { + let currentItem = AVPlayerItem(asset: pendingAsset, automaticallyLoadedAssetKeys: [Constants.assetPlayableKey]) + currentItem.preferredForwardBufferDuration = self.bufferDuration + self.avPlayer.replaceCurrentItem(with: currentItem) + + // Register for events + self.playerTimeObserver.registerForBoundaryTimeEvents() + self.playerObserver.startObserving() + self.playerItemNotificationObserver.startObserving(item: currentItem) + self.playerItemObserver.startObserving(item: currentItem) + } + } + } func load(from url: URL, playWhenReady: Bool, initialTime: TimeInterval? = nil, options: [String : Any]? = nil) { _initialTime = initialTime @@ -311,6 +328,42 @@ extension AVPlayerWrapper: AVPlayerObserverDelegate { break } } + + func cancelPreload(item: AudioItem) { + let url = item.getSourceUrl(); + self.preloadedAssets[url]?.cancelLoading(); + self.preloadedAssets[url] = nil; + } + + func preload(item: AudioItem) { + let urlString = item.getSourceUrl(); + let url = URL(string: urlString); + + let asset = AVURLAsset(url:url!); + let keys = ["playable", "tracks", "duration"]; + + asset.loadValuesAsynchronously(forKeys: keys, completionHandler: { + var _: NSError? = nil + + for key in keys { + let status = asset.statusOfValue(forKey: key, error: nil) + if status == AVKeyValueStatus.failed { + return + } + } + + if( self.preloadedAssets[urlString] == nil){ + let playerItem = AVPlayerItem(asset: asset); + self.additionalAVPlayer.replaceCurrentItem(with: playerItem) + + self.additionalAVPlayer.play(); + self.additionalAVPlayer.pause(); + DispatchQueue.main.async { + self.preloadedAssets[urlString] = asset; + } + } + }); + } } diff --git a/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapperProtocol.swift b/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapperProtocol.swift index d541ddf..c107715 100755 --- a/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapperProtocol.swift +++ b/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapperProtocol.swift @@ -52,5 +52,8 @@ protocol AVPlayerWrapperProtocol: class { func load(from url: URL, playWhenReady: Bool, options: [String: Any]?) func load(from url: URL, playWhenReady: Bool, initialTime: TimeInterval?, options: [String: Any]?) - + + func preload(item: AudioItem) + + func cancelPreload(item: AudioItem) } diff --git a/SwiftAudioEx/Classes/AudioPlayer.swift b/SwiftAudioEx/Classes/AudioPlayer.swift index 6504825..5784a1a 100755 --- a/SwiftAudioEx/Classes/AudioPlayer.swift +++ b/SwiftAudioEx/Classes/AudioPlayer.swift @@ -185,6 +185,20 @@ public class AudioPlayer: AVPlayerWrapperDelegate { } enableRemoteCommands(forItem: item) } + + /** + Preload item. + */ + public func preload(item: AudioItem) { + self.wrapper.preload(item: item); + } + + /** + cancel preload item. + */ + public func cancelPreload(item: AudioItem) { + self.wrapper.cancelPreload(item: item); + } /** Toggle playback status. From 20f47f2d0ebe5a7374296316c6b2163feec9d900 Mon Sep 17 00:00:00 2001 From: Guy Hadash Date: Sun, 10 Oct 2021 14:34:57 +0300 Subject: [PATCH 2/6] adding preloadNext & cancelAllPreloads --- .../AVPlayerWrapper/AVPlayerWrapper.swift | 9 +++++++++ SwiftAudioEx/Classes/AudioPlayer.swift | 16 ++++++++++------ SwiftAudioEx/Classes/QueuedAudioPlayer.swift | 8 ++++++++ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift b/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift index 38a3fd5..014f4f9 100755 --- a/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift +++ b/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift @@ -335,6 +335,15 @@ extension AVPlayerWrapper: AVPlayerObserverDelegate { self.preloadedAssets[url] = nil; } + func cancelAllPreloads() { + for asset in self.preloadedAssets { + if (self.preloadedAssets[asset.key] != nil){ + self.preloadedAssets[asset.key]?.cancelLoading(); + self.preloadedAssets[asset.key] = nil; + } + } + } + func preload(item: AudioItem) { let urlString = item.getSourceUrl(); let url = URL(string: urlString); diff --git a/SwiftAudioEx/Classes/AudioPlayer.swift b/SwiftAudioEx/Classes/AudioPlayer.swift index 5784a1a..c1f5ebf 100755 --- a/SwiftAudioEx/Classes/AudioPlayer.swift +++ b/SwiftAudioEx/Classes/AudioPlayer.swift @@ -190,16 +190,20 @@ public class AudioPlayer: AVPlayerWrapperDelegate { Preload item. */ public func preload(item: AudioItem) { - self.wrapper.preload(item: item); - } + self.wrapper.preload(item: item); + } /** cancel preload item. */ - public func cancelPreload(item: AudioItem) { - self.wrapper.cancelPreload(item: item); - } - + public func cancelPreload(item: AudioItem) { + self.wrapper.cancelPreload(item: item); + } + + public func cancelAllPreloads() { + self.wrapper.cancelAllPreloads(); + } + /** Toggle playback status. */ diff --git a/SwiftAudioEx/Classes/QueuedAudioPlayer.swift b/SwiftAudioEx/Classes/QueuedAudioPlayer.swift index ad67c6c..85bee8e 100755 --- a/SwiftAudioEx/Classes/QueuedAudioPlayer.swift +++ b/SwiftAudioEx/Classes/QueuedAudioPlayer.swift @@ -231,4 +231,12 @@ public class QueuedAudioPlayer: AudioPlayer, QueueManagerDelegate { func onReceivedFirstItem() { self.event.queueIndex.emit(data: (nil, 0)) } + + func preloadNext() { + let nextItems = queueManager.nextItems + + if nextItems.count > 0 { + self.preload(item: nextItems[0]) + } + } } From ccb17046a5f4af1901b3d776a6bd2d5ebf983eae Mon Sep 17 00:00:00 2001 From: Guy Hadash Date: Sun, 10 Oct 2021 14:37:39 +0300 Subject: [PATCH 3/6] minor fix --- .../Classes/AVPlayerWrapper/AVPlayerWrapperProtocol.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapperProtocol.swift b/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapperProtocol.swift index c107715..c696693 100755 --- a/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapperProtocol.swift +++ b/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapperProtocol.swift @@ -9,7 +9,7 @@ import Foundation import AVFoundation -protocol AVPlayerWrapperProtocol: class { +protocol AVPlayerWrapperProtocol: AnyObject { var state: AVPlayerWrapperState { get } @@ -53,7 +53,9 @@ protocol AVPlayerWrapperProtocol: class { func load(from url: URL, playWhenReady: Bool, initialTime: TimeInterval?, options: [String: Any]?) - func preload(item: AudioItem) + func preload(item: AudioItem) - func cancelPreload(item: AudioItem) + func cancelAllPreloads() + + func cancelPreload(item: AudioItem) } From bfcf199affdf55ae635f37d3d3e24bfeae2b6b5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Neme=C4=8Dek?= Date: Wed, 10 Nov 2021 01:40:16 +0100 Subject: [PATCH 4/6] Use AssetOptions when preloading --- .../Classes/AVPlayerWrapper/AVPlayerWrapper.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift b/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift index 014f4f9..8c87ec7 100755 --- a/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift +++ b/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift @@ -345,11 +345,12 @@ extension AVPlayerWrapper: AVPlayerObserverDelegate { } func preload(item: AudioItem) { - let urlString = item.getSourceUrl(); - let url = URL(string: urlString); + guard let url = URL(string: item.getSourceUrl()) else { return } - let asset = AVURLAsset(url:url!); - let keys = ["playable", "tracks", "duration"]; + let options = (item as? AssetOptionsProviding)?.getAssetOptions() + let asset = AVURLAsset(url: url, options: options) + + let keys = ["playable", "tracks", "duration"] asset.loadValuesAsynchronously(forKeys: keys, completionHandler: { var _: NSError? = nil From 38756e25f953ba5029f56da900e6983250506bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Neme=C4=8Dek?= Date: Wed, 10 Nov 2021 01:44:14 +0100 Subject: [PATCH 5/6] Fix syntax issue --- SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift b/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift index 8c87ec7..d045fb7 100755 --- a/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift +++ b/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift @@ -345,7 +345,8 @@ extension AVPlayerWrapper: AVPlayerObserverDelegate { } func preload(item: AudioItem) { - guard let url = URL(string: item.getSourceUrl()) else { return } + let urlString = item.getSourceUrl() + guard let url = URL(string: urlString) else { return } let options = (item as? AssetOptionsProviding)?.getAssetOptions() let asset = AVURLAsset(url: url, options: options) From 0fdb4d5de0a9e36cd551db03b9f476e64b225b8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Neme=C4=8Dek?= Date: Wed, 10 Nov 2021 01:45:59 +0100 Subject: [PATCH 6/6] Remove unneeded space --- SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift b/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift index d045fb7..5482749 100755 --- a/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift +++ b/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift @@ -346,7 +346,7 @@ extension AVPlayerWrapper: AVPlayerObserverDelegate { func preload(item: AudioItem) { let urlString = item.getSourceUrl() - guard let url = URL(string: urlString) else { return } + guard let url = URL(string: urlString) else { return } let options = (item as? AssetOptionsProviding)?.getAssetOptions() let asset = AVURLAsset(url: url, options: options)