Compare commits

..

2 Commits

Author SHA1 Message Date
tanhakabir 52ea5a21b1 Merge branch 'issue-106' of https://github.com/tanhakabir/SwiftAudioPlayer into issue-106 2021-05-07 13:18:17 -07:00
tanhakabir c2cab7b272 pass lockscreen info to player in example app 2021-05-07 13:15:48 -07:00
3 changed files with 50 additions and 52 deletions
+24 -23
View File
@@ -183,7 +183,7 @@ public class SAPlayer {
public var audioQueued: [URL] {
get {
return presenter.audioQueue.map { (queued) -> URL in
return queued.url
return queued.1
}
}
}
@@ -233,7 +233,11 @@ public class SAPlayer {
- Note: Setting this to nil clears the information displayed on the lockscreen media player.
*/
public var mediaInfo: SALockScreenInfo? = nil
public var mediaInfo: SALockScreenInfo? = nil {
didSet {
presenter.handleLockscreenInfo(info: mediaInfo)
}
}
private init() {
presenter = SAPlayerPresenter(delegate: self)
@@ -410,14 +414,14 @@ extension SAPlayer {
- Parameter mediaInfo: The media information of the audio to show on the lockscreen media player (optional).
*/
public func startSavedAudio(withSavedUrl url: URL, mediaInfo: SALockScreenInfo? = nil) {
// Because we support queueing, we want to clear off any existing players.
// Therefore, instantiate new player every time, destroy any existing ones.
// This prevents a crash where an owning engine already exists.
presenter.handleClear()
presenter.handlePlaySavedAudio(withSavedUrl: url)
self.mediaInfo = mediaInfo
presenter.handlePlaySavedAudio(withSavedUrl: url)
}
@available(*, deprecated, renamed: "startSavedAudio")
public func initializeSavedAudio(withSavedUrl url: URL, mediaInfo: SALockScreenInfo? = nil) {
self.mediaInfo = mediaInfo
presenter.handlePlaySavedAudio(withSavedUrl: url)
}
/**
@@ -449,14 +453,14 @@ extension SAPlayer {
- Parameter mediaInfo: The media information of the audio to show on the lockscreen media player (optional).
*/
public func startRemoteAudio(withRemoteUrl url: URL, bitrate: SAPlayerBitrate = .high, mediaInfo: SALockScreenInfo? = nil) {
// Because we support queueing, we want to clear off any existing players.
// Therefore, instantiate new player every time, destroy any existing ones.
// This prevents a crash where an owning engine already exists.
presenter.handleClear()
presenter.handlePlayStreamedAudio(withRemoteUrl: url, bitrate: bitrate)
self.mediaInfo = mediaInfo
presenter.handlePlayStreamedAudio(withRemoteUrl: url, bitrate: bitrate)
}
@available(*, deprecated, renamed: "startRemoteAudio")
public func initializeRemoteAudio(withRemoteUrl url: URL, mediaInfo: SALockScreenInfo? = nil) {
self.mediaInfo = mediaInfo
presenter.handlePlayStreamedAudio(withRemoteUrl: url, bitrate: .high)
}
/**
@@ -470,21 +474,18 @@ extension SAPlayer {
Queues remote audio to be played next. The URLs in the queue can be both remote or on disk but once the queued audio starts playing it will start buffering and loading then. This means no guarantee for a 'gapless' playback where there might be several moments in between one audio ending and another starting due to buffering remote audio.
- Parameter withRemoteUrl: The URL of the remote audio.
- Parameter bitrate: The bitrate of the streamed audio. By default the bitrate is set to high for streaming saved audio files. If you want to stream radios then you should use the `low` bitrate option.
- Parameter mediaInfo: The media information of the audio to show on the lockscreen media player (optional).
*/
public func queueRemoteAudio(withRemoteUrl url: URL, bitrate: SAPlayerBitrate = .high, mediaInfo: SALockScreenInfo? = nil) {
presenter.handleQueueStreamedAudio(withRemoteUrl: url, mediaInfo: mediaInfo, bitrate: bitrate)
public func queueRemoteAudio(withRemoteUrl url: URL) {
presenter.handleQueueStreamedAudio(withRemoteUrl: url)
}
/**
Queues saved audio to be played next. The URLs in the queuecan be both remote or on disk but once the queued audio starts playing it will start buffering and loading then. This means no guarantee for a 'gapless' playback where there might be several moments in between one audio ending and another starting due to buffering remote audio.
- Parameter withSavedUrl: The URL of the audio saved on the device.
- Parameter mediaInfo: The media information of the audio to show on the lockscreen media player (optional).
*/
public func queueSavedAudio(withSavedUrl url: URL, mediaInfo: SALockScreenInfo? = nil) {
presenter.handleQueueSavedAudio(withSavedUrl: url, mediaInfo: mediaInfo)
public func queueSavedAudio(withSavedUrl url: URL) {
presenter.handleQueueSavedAudio(withSavedUrl: url)
}
/**
-1
View File
@@ -27,7 +27,6 @@ import Foundation
import CoreMedia
protocol SAPlayerDelegate: AnyObject, LockScreenViewProtocol {
var mediaInfo: SALockScreenInfo? { get set }
var skipForwardSeconds: Double { get set }
var skipBackwardSeconds: Double { get set }
+26 -28
View File
@@ -28,20 +28,6 @@ import AVFoundation
import MediaPlayer
class SAPlayerPresenter {
struct QueueItem {
var loc: Location
var url: URL
var mediaInfo: SALockScreenInfo?
var bitrate: SAPlayerBitrate
init(loc: Location, url: URL, mediaInfo: SALockScreenInfo?, bitrate: SAPlayerBitrate = .high) {
self.loc = loc
self.url = url
self.mediaInfo = mediaInfo
self.bitrate = bitrate
}
}
enum Location {
case remote
case disk
@@ -55,13 +41,14 @@ class SAPlayerPresenter {
private var key: String?
private var isPlaying: SAPlayingStatus = .buffering
private var mediaInfo: SALockScreenInfo?
private var urlKeyMap: [Key: URL] = [:]
var durationRef:UInt = 0
var needleRef:UInt = 0
var playingStatusRef:UInt = 0
var audioQueue: [QueueItem] = []
var audioQueue: [(Location, URL)] = []
init(delegate: SAPlayerDelegate?) {
self.delegate = delegate
@@ -83,7 +70,7 @@ class SAPlayerPresenter {
needle = nil
duration = nil
key = nil
delegate?.mediaInfo = nil
mediaInfo = nil
delegate?.clearLockScreenInfo()
AudioClockDirector.shared.detachFromChangesInDuration(withID: durationRef)
@@ -92,21 +79,29 @@ class SAPlayerPresenter {
}
func handlePlaySavedAudio(withSavedUrl url: URL) {
// Because we support queueing, we want to clear off any existing players.
// Therefore, instantiate new player every time, destroy any existing ones.
// This prevents a crash where an owning engine already exists.
handleClear()
attachForUpdates(url: url)
delegate?.startAudioDownloaded(withSavedUrl: url)
}
func handlePlayStreamedAudio(withRemoteUrl url: URL, bitrate: SAPlayerBitrate) {
// Because we support queueing, we want to clear off any existing players.
// Therefore, instantiate new player every time, destroy any existing ones.
// This prevents a crash where an owning engine already exists.
handleClear()
attachForUpdates(url: url)
delegate?.startAudioStreamed(withRemoteUrl: url, bitrate: bitrate)
}
func handleQueueStreamedAudio(withRemoteUrl url: URL, mediaInfo: SALockScreenInfo?, bitrate: SAPlayerBitrate) {
audioQueue.append(QueueItem(loc: .remote, url: url, mediaInfo: mediaInfo, bitrate: bitrate))
func handleQueueStreamedAudio(withRemoteUrl url: URL) {
audioQueue.append((.remote, url))
}
func handleQueueSavedAudio(withSavedUrl url: URL, mediaInfo: SALockScreenInfo?) {
audioQueue.append(QueueItem(loc: .disk, url: url, mediaInfo: mediaInfo))
func handleQueueSavedAudio(withSavedUrl url: URL) {
audioQueue.append((.disk, url))
}
private func attachForUpdates(url: URL) {
@@ -125,7 +120,7 @@ class SAPlayerPresenter {
self.delegate?.updateLockscreenPlaybackDuration(duration: duration)
self.duration = duration
self.delegate?.setLockScreenInfo(withMediaInfo: self.delegate?.mediaInfo, duration: duration)
self.delegate?.setLockScreenInfo(withMediaInfo: self.mediaInfo, duration: duration)
})
needleRef = AudioClockDirector.shared.attachToChangesInNeedle(closure: { [weak self] (key, needle) in
@@ -169,6 +164,11 @@ class SAPlayerPresenter {
delegate?.clearEngine()
detachFromUpdates()
}
@available(iOS 10.0, *)
func handleLockscreenInfo(info: SALockScreenInfo?) {
self.mediaInfo = info
}
}
//MARK:- Used by outside world including:
@@ -238,26 +238,24 @@ extension SAPlayerPresenter {
return
}
let nextAudioURL = audioQueue.removeFirst()
let key = nextAudioURL.url.key
let key = nextAudioURL.1.key
Log.info("getting ready to play \(nextAudioURL)")
AudioQueueDirector.shared.changeInQueue(key, url: nextAudioURL.url)
AudioQueueDirector.shared.changeInQueue(key, url: nextAudioURL.1)
handleClear()
delegate?.mediaInfo = nextAudioURL.mediaInfo
// We need to give a second to clean up the previous engine properly. Deinit takes some time.
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { [weak self] (_) in
guard let self = self else { return }
switch nextAudioURL.loc {
switch nextAudioURL.0 {
case .remote:
self.handlePlayStreamedAudio(withRemoteUrl: nextAudioURL.url, bitrate: nextAudioURL.bitrate)
self.handlePlayStreamedAudio(withRemoteUrl: nextAudioURL.1, bitrate: .high) // TODO fix to add option for low birate
break
case .disk:
self.handlePlaySavedAudio(withSavedUrl: nextAudioURL.url)
self.handlePlaySavedAudio(withSavedUrl: nextAudioURL.1)
}
self.shouldPlayImmediately = true