Skip to content

Commit 631c28e

Browse files
committed
added events
1 parent 1329d94 commit 631c28e

File tree

7 files changed

+98
-7
lines changed

7 files changed

+98
-7
lines changed

Sources/swiftui-loop-videoplayer/enum/PlayerEvent.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,22 @@ public enum PlayerEvent: Equatable {
2020
/// - currentTime: The time (in seconds) to which the player is seeking.
2121
case seek(Bool, currentTime: Double)
2222

23+
/// Indicates that the player's playback is currently paused.
24+
///
25+
/// This state occurs when the player has been manually paused by the user or programmatically
26+
/// through a method like `pause()`. The player is not playing any content while in this state.
27+
case paused
28+
29+
/// Indicates that the player is currently waiting to play at the specified rate.
30+
///
31+
/// This state generally occurs when the player is buffering or waiting for sufficient data
32+
/// to continue playback. It can also occur if the playback rate is temporarily reduced to zero
33+
/// due to external factors, such as network conditions or system resource limitations.
34+
case waitingToPlayAtSpecifiedRate
35+
36+
/// Indicates that the player is actively playing content.
37+
///
38+
/// This state occurs when the player is currently playing video or audio content at the
39+
/// specified playback rate. This is the active state where media is being rendered to the user.
40+
case playing
2341
}

Sources/swiftui-loop-videoplayer/protocol/helpers/PlayerDelegateProtocol.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,22 @@ public protocol PlayerDelegateProtocol: AnyObject {
3232
/// - currentTime: The current time of the player after seeking, in seconds.
3333
@MainActor
3434
func didSeek(value: Bool, currentTime: Double)
35+
36+
/// Called when the player has paused playback.
37+
///
38+
/// This method is triggered when the player's `timeControlStatus` changes to `.paused`.
39+
@MainActor
40+
func didPausePlayback()
41+
42+
/// Called when the player is waiting to play at the specified rate.
43+
///
44+
/// This method is triggered when the player's `timeControlStatus` changes to `.waitingToPlayAtSpecifiedRate`.
45+
@MainActor
46+
func isWaitingToPlay()
47+
48+
/// Called when the player starts or resumes playing.
49+
///
50+
/// This method is triggered when the player's `timeControlStatus` changes to `.playing`.
51+
@MainActor
52+
func didStartPlaying()
3553
}

Sources/swiftui-loop-videoplayer/protocol/player/AbstractPlayer.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,9 @@ extension AbstractPlayer{
153153
return
154154
}
155155

156-
guard player.currentItem?.status == .readyToPlay else{
157-
if let asset = currentAsset{
158-
update(asset: asset , loop: false, autoPlay: false){ [weak self] status in
156+
guard currentItem?.status == .readyToPlay else{
157+
if let currentAsset{
158+
update(asset: currentAsset , loop: false, autoPlay: false){ [weak self] status in
159159
if status == .readyToPlay{
160160
self?.seek(to: time)
161161
}else {
@@ -416,13 +416,17 @@ internal func cleanUp(
416416
player: inout AVQueuePlayer?,
417417
playerLooper: inout AVPlayerLooper?,
418418
errorObserver: inout NSKeyValueObservation?,
419+
timeControlObserver: inout NSKeyValueObservation?,
419420
statusObserver: inout NSKeyValueObservation?,
420421
timeObserver: inout Any?
421422
) {
422423

423424
errorObserver?.invalidate()
424425
errorObserver = nil
425-
426+
427+
timeControlObserver?.invalidate()
428+
timeControlObserver = nil
429+
426430
statusObserver?.invalidate()
427431
statusObserver = nil
428432

Sources/swiftui-loop-videoplayer/protocol/player/LoopingPlayerProtocol.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ public protocol LoopingPlayerProtocol: AbstractPlayer, LayerMakerProtocol{
3434
/// ensuring that all playback errors are managed and reported appropriately.
3535
var errorObserver: NSKeyValueObservation? { get set }
3636

37+
/// An optional observer for monitoring changes to the player's `timeControlStatus` property.
38+
var timeControlObserver: NSKeyValueObservation? { get set }
39+
3740
/// Declare a variable to hold the time observer token outside the if statement
3841
var timeObserver: Any? { get set }
3942

@@ -207,6 +210,22 @@ internal extension LoopingPlayerProtocol {
207210
errorObserver = player.observe(\.error, options: [.new]) { [weak self] player, _ in
208211
self?.handlePlayerError(player)
209212
}
213+
214+
timeControlObserver = player.observe(\.timeControlStatus, options: [.new, .old]) { [weak self] player, change in
215+
switch player.timeControlStatus {
216+
case .paused:
217+
// This could mean playback has stopped, but it's not specific to end of playback
218+
self?.delegate?.didPausePlayback()
219+
case .waitingToPlayAtSpecifiedRate:
220+
// Player is waiting to play (e.g., buffering)
221+
self?.delegate?.isWaitingToPlay()
222+
case .playing:
223+
// Player is currently playing
224+
self?.delegate?.didStartPlaying()
225+
@unknown default:
226+
break
227+
}
228+
}
210229
}
211230

212231
/// Removes observers for handling errors.

Sources/swiftui-loop-videoplayer/view/helpers/PlayerCoordinator.swift

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Combine
1010

1111
@MainActor
1212
internal class PlayerCoordinator: NSObject, PlayerDelegateProtocol {
13-
13+
1414
let eventPublisher: PassthroughSubject<PlayerEvent, Never>
1515

1616
let timePublisher: PassthroughSubject<Double, Never>
@@ -72,4 +72,28 @@ internal class PlayerCoordinator: NSObject, PlayerDelegateProtocol {
7272
func didSeek(value: Bool, currentTime : Double) {
7373
eventPublisher.send(.seek(value, currentTime: currentTime))
7474
}
75+
76+
/// Called when the player has paused playback.
77+
///
78+
/// This method is triggered when the player's `timeControlStatus` changes to `.paused`.
79+
@MainActor
80+
func didPausePlayback(){
81+
eventPublisher.send(.paused)
82+
}
83+
84+
/// Called when the player is waiting to play at the specified rate.
85+
///
86+
/// This method is triggered when the player's `timeControlStatus` changes to `.waitingToPlayAtSpecifiedRate`.
87+
@MainActor
88+
func isWaitingToPlay(){
89+
eventPublisher.send(.waitingToPlayAtSpecifiedRate)
90+
}
91+
92+
/// Called when the player starts or resumes playing.
93+
///
94+
/// This method is triggered when the player's `timeControlStatus` changes to `.playing`.
95+
@MainActor
96+
func didStartPlaying(){
97+
eventPublisher.send(.playing)
98+
}
7599
}

Sources/swiftui-loop-videoplayer/view/player/ios/LoopingPlayerUIView.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ class LoopingPlayerUIView: UIView, LoopingPlayerProtocol {
4545
/// Observer for errors from the AVQueuePlayer.
4646
internal var errorObserver: NSKeyValueObservation?
4747

48+
/// An optional observer for monitoring changes to the player's `timeControlStatus` property.
49+
internal var timeControlObserver: NSKeyValueObservation?
50+
4851
/// Observes the status property of the new player item.
4952
internal var statusObserver: NSKeyValueObservation?
5053

@@ -80,7 +83,8 @@ class LoopingPlayerUIView: UIView, LoopingPlayerProtocol {
8083
/// This method invalidates the status and error observers to prevent memory leaks,
8184
/// pauses the player, and clears out player-related references to assist in clean deinitialization.
8285
deinit {
83-
cleanUp(player: &player, playerLooper: &playerLooper, errorObserver: &errorObserver, statusObserver: &statusObserver, timeObserver: &timeObserver)
86+
cleanUp(player: &player, playerLooper: &playerLooper, errorObserver: &errorObserver,
87+
timeControlObserver : &timeControlObserver, statusObserver: &statusObserver, timeObserver: &timeObserver)
8488
}
8589
}
8690
#endif

Sources/swiftui-loop-videoplayer/view/player/mac/LoopingPlayerNSView.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ class LoopingPlayerNSView: NSView, LoopingPlayerProtocol {
4747
/// Observer for errors from the AVQueuePlayer.
4848
internal var errorObserver: NSKeyValueObservation?
4949

50+
/// An optional observer for monitoring changes to the player's `timeControlStatus` property.
51+
internal var timeControlObserver: NSKeyValueObservation?
52+
5053
/// Observes the status property of the new player item.
5154
internal var statusObserver: NSKeyValueObservation?
5255

@@ -82,7 +85,8 @@ class LoopingPlayerNSView: NSView, LoopingPlayerProtocol {
8285
/// This method invalidates the status and error observers to prevent memory leaks,
8386
/// pauses the player, and clears out player-related references to assist in clean deinitialization.
8487
deinit {
85-
cleanUp(player: &player, playerLooper: &playerLooper, errorObserver: &errorObserver, statusObserver: &statusObserver, timeObserver: &timeObserver)
88+
cleanUp(player: &player, playerLooper: &playerLooper, errorObserver: &errorObserver,
89+
timeControlObserver : &timeControlObserver, statusObserver: &statusObserver, timeObserver: &timeObserver)
8690
}
8791
}
8892
#endif

0 commit comments

Comments
 (0)