Compare commits

...

5 Commits

Author SHA1 Message Date
tanhakabir 2acbde2efa Release 2.10.0 2020-09-19 15:52:25 -07:00
Moises Inzunza acbdf05d4f Fixed bug in swift 5.3 (#46) 2020-09-19 12:19:50 -07:00
Tanha c325caa914 Release 2.9.0 2020-03-06 23:53:42 -08:00
tanhakabir dd54d81573 implement streaming cancellation (#32)
* cancels streaming initally, but player enters weird state trying to stream again

* calling superclass invalidate from engines for good measure

* Fix UI to fix seemingly weird state

* Fix error warning
2020-03-06 23:52:22 -08:00
tanhakabir ebc282d5c2 fix crash on cancel streaming pressed (#30) 2020-03-06 23:28:30 -08:00
9 changed files with 52 additions and 11 deletions
@@ -151,6 +151,8 @@ class ViewController: UIViewController {
if buffer.bufferingProgress >= 0.99 {
self.streamButton.isEnabled = false
} else {
self.streamButton.isEnabled = true
}
self.isPlayable = buffer.isReadyForPlaying
@@ -272,8 +274,12 @@ class ViewController: UIViewController {
SAPlayer.shared.startRemoteAudio(withRemoteUrl: selectedAudio.url)
streamButton.setTitle("Cancel streaming", for: .normal)
downloadButton.isEnabled = false
isStreaming = true
} else {
// TODO
SAPlayer.shared.stopStreamingRemoteAudio()
streamButton.setTitle("Stream", for: .normal)
downloadButton.isEnabled = true
isStreaming = false
}
}
+1
View File
@@ -136,6 +136,7 @@ class AudioDiskEngine: AudioEngine {
}
override func invalidate() {
super.invalidate()
//Nothing to invalidate for disk
}
}
+1
View File
@@ -291,6 +291,7 @@ class AudioStreamEngine: AudioEngine {
}
override func invalidate() {
super.invalidate()
converter.invalidate()
}
}
@@ -3,7 +3,7 @@
// SwiftAudioPlayer
//
// Created by Tanha Kabir on 2019-01-29.
// Copyright © 2019 Tanha Kabir, Jon Mercer
// Copyright © 2019 Tanha Kabir, Jon Mercer, Moy Inzunza
//
// This file was modified and adapted from https://github.com/syedhali/AudioStreamer
// which was released under Apache License 2.0. Apache License 2.0 requires explicit
@@ -32,7 +32,19 @@
import Foundation
import AVFoundation
func ParserPacketListener(_ context: UnsafeMutableRawPointer, _ byteCount: UInt32, _ packetCount: UInt32, _ streamData: UnsafeRawPointer, _ packetDescriptions: UnsafeMutablePointer<AudioStreamPacketDescription>) {
#if swift(>=5.3)
func ParserPacketListener (_ context: UnsafeMutableRawPointer, _ byteCount: UInt32, _ packetCount: UInt32, _ streamData: UnsafeRawPointer, _ packetDescriptions: UnsafeMutablePointer<AudioStreamPacketDescription>?) {
parserPacket(context, byteCount, packetCount, streamData, packetDescriptions!)
}
#else
func ParserPacketListener (_ context: UnsafeMutableRawPointer, _ byteCount: UInt32, _ packetCount: UInt32, _ streamData: UnsafeRawPointer, _ packetDescriptions: UnsafeMutablePointer<AudioStreamPacketDescription>) {
parserPacket(context, byteCount, packetCount, streamData, packetDescriptions)
}
#endif
func parserPacket(_ context: UnsafeMutableRawPointer, _ byteCount: UInt32, _ packetCount: UInt32, _ streamData: UnsafeRawPointer, _ packetDescriptions: UnsafeMutablePointer<AudioStreamPacketDescription>){
let selfAudioParser = Unmanaged<AudioParser>.fromOpaque(context).takeUnretainedValue()
//bug in core audio where this could be nil
@@ -68,4 +80,5 @@ func ParserPacketListener(_ context: UnsafeMutableRawPointer, _ byteCount: UInt3
selfAudioParser.audioPackets.append((nil, audioPacketData))
}
}
}
@@ -243,7 +243,7 @@ extension AudioStreamWorker: URLSessionDataDelegate {
}
guard self.task == dataTask else {
Log.error("stream_error not the same task") //Probably because of seek
Log.error("stream_error not the same task 638283") //Probably because of seek
return
}
@@ -271,7 +271,7 @@ extension AudioStreamWorker: URLSessionDataDelegate {
}
guard self.task == dataTask else {
Log.error("stream_error not the same task")
Log.error("stream_error not the same task 517253")
return
}
@@ -293,8 +293,8 @@ extension AudioStreamWorker: URLSessionDataDelegate {
return
}
guard self.task == task else {
Log.error("stream_error not the same task")
if self.task != task && self.task != nil {
Log.error("stream_error not the same task 3901833")
return
}
+10
View File
@@ -312,6 +312,10 @@ extension SAPlayer {
presenter.handlePlayStreamedAudio(withRemoteUrl: url)
}
public func stopStreamingRemoteAudio() {
presenter.handleStopStreamingAudio()
}
/**
Resets the player to the state before initializing audio and setting media info.
*/
@@ -336,6 +340,12 @@ extension SAPlayer: SAPlayerDelegate {
player = AudioStreamEngine(withRemoteUrl: url, delegate: presenter)
}
func clearEngine() {
player?.pause()
player?.invalidate()
player = nil
}
func playEngine() {
becomeDeviceAudioPlayer()
player?.play()
+1
View File
@@ -32,6 +32,7 @@ protocol SAPlayerDelegate: AnyObject, LockScreenViewProtocol {
func startAudioDownloaded(withSavedUrl url: AudioURL)
func startAudioStreamed(withRemoteUrl url: AudioURL)
func clearEngine()
func playEngine()
func pauseEngine()
func seekEngine(toNeedle needle: Needle) //TODO ensure that engine cleans up out of bounds
+12 -3
View File
@@ -83,9 +83,7 @@ class SAPlayerPresenter {
}
private func attachForUpdates(url: URL) {
AudioClockDirector.shared.detachFromChangesInDuration(withID: durationRef)
AudioClockDirector.shared.detachFromChangesInNeedle(withID: needleRef)
AudioClockDirector.shared.detachFromChangesInPlayingStatus(withID: playingStatusRef)
detachFromUpdates()
self.key = url.key
urlKeyMap[url.key] = url
@@ -125,6 +123,17 @@ class SAPlayerPresenter {
})
}
private func detachFromUpdates() {
AudioClockDirector.shared.detachFromChangesInDuration(withID: durationRef)
AudioClockDirector.shared.detachFromChangesInNeedle(withID: needleRef)
AudioClockDirector.shared.detachFromChangesInPlayingStatus(withID: playingStatusRef)
}
func handleStopStreamingAudio() {
delegate?.clearEngine()
detachFromUpdates()
}
@available(iOS 10.0, *)
func handleLockscreenInfo(info: SALockScreenInfo?) {
self.mediaInfo = info
+1 -1
View File
@@ -8,7 +8,7 @@
Pod::Spec.new do |s|
s.name = 'SwiftAudioPlayer'
s.version = '2.8.2'
s.version = '2.10.0'
s.summary = 'SwiftAudioPlayer is a Swift based audio player that can handle streaming from a remote location and audio manipulation.'
# This description is used to generate tags and improve search results.