From 69dab0bb7ac02709421b58cc4b9890f3e2812482 Mon Sep 17 00:00:00 2001 From: Guy Hadash Date: Sun, 10 Oct 2021 11:55:49 +0300 Subject: [PATCH 1/4] 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 fa680732..b81d559b 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. */ @@ -58,6 +60,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 @@ -186,7 +191,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 @@ -203,20 +214,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: @@ -236,6 +234,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 @@ -319,6 +336,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 d541ddff..c107715b 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 6504825b..5784a1a5 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 7b9abe7a7cd968ee2abcd538e735646d182edc81 Mon Sep 17 00:00:00 2001 From: Guy Hadash Date: Sun, 10 Oct 2021 14:34:57 +0300 Subject: [PATCH 2/4] 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 b81d559b..49e67c1a 100755 --- a/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift +++ b/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift @@ -343,6 +343,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 5784a1a5..c1f5ebfa 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 ad67c6c8..85bee8ee 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 06d881f1ce57e35e8788df931276b53560a9b143 Mon Sep 17 00:00:00 2001 From: Guy Hadash Date: Sun, 10 Oct 2021 14:37:39 +0300 Subject: [PATCH 3/4] 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 c107715b..c696693c 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 72e3a88420ce453746d806ecc0ccf5a04322fd85 Mon Sep 17 00:00:00 2001 From: Guy Hadash Date: Wed, 17 Nov 2021 11:25:00 +0200 Subject: [PATCH 4/4] setting options when preloading making preloadNext public --- SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift | 3 ++- SwiftAudioEx/Classes/QueuedAudioPlayer.swift | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift b/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift index 49e67c1a..4b026e3e 100755 --- a/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift +++ b/SwiftAudioEx/Classes/AVPlayerWrapper/AVPlayerWrapper.swift @@ -356,7 +356,8 @@ extension AVPlayerWrapper: AVPlayerObserverDelegate { let urlString = item.getSourceUrl(); let url = URL(string: urlString); - let asset = AVURLAsset(url:url!); + let options = (item as? AssetOptionsProviding)?.getAssetOptions() + let asset = AVURLAsset(url:url!, options: options) let keys = ["playable", "tracks", "duration"]; asset.loadValuesAsynchronously(forKeys: keys, completionHandler: { diff --git a/SwiftAudioEx/Classes/QueuedAudioPlayer.swift b/SwiftAudioEx/Classes/QueuedAudioPlayer.swift index 85bee8ee..3f62e613 100755 --- a/SwiftAudioEx/Classes/QueuedAudioPlayer.swift +++ b/SwiftAudioEx/Classes/QueuedAudioPlayer.swift @@ -232,7 +232,7 @@ public class QueuedAudioPlayer: AudioPlayer, QueueManagerDelegate { self.event.queueIndex.emit(data: (nil, 0)) } - func preloadNext() { + public func preloadNext() { let nextItems = queueManager.nextItems if nextItems.count > 0 {