Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import AVFoundation
- private let notificationNames: [Notification.Name] = [.AVPlayerItemTimeJumped,
- .AVPlayerItemPlaybackStalled,
- .AVPlayerItemDidPlayToEndTime,
- .AVPlayerItemFailedToPlayToEndTime]
- extension AVPlayer {
- private struct AssociatedKey {
- static var stateHandlerKey: UInt8 = 0
- static var currentItem: UInt8 = 0
- static var rate: UInt8 = 0
- }
- public enum PlayerNotifications {
- case DidStartPlayback
- case DidPausePlayback
- case DidChangeCurrentItem(item: AVPlayerItem?)
- case ItemTimeJumped(item: AVPlayerItem)
- case ItemPlaybackStalled(item: AVPlayerItem)
- case ItemDidPlayToEndTime(item: AVPlayerItem)
- case ItemPlayToEndTimeFailed(item: AVPlayerItem)
- }
- var stateHandler: ((PlayerNotifications) -> ())? {
- get {
- return objc_getAssociatedObject(self, &AssociatedKey.stateHandlerKey) as? ((PlayerNotifications) -> ())
- }
- set {
- objc_setAssociatedObject(self, &AssociatedKey.stateHandlerKey, newValue, .OBJC_ASSOCIATION_RETAIN)
- }
- }
- public convenience init(url URL: URL, _ stateHandler:@escaping (PlayerNotifications)->()) {
- self.init(url: URL)
- self.stateHandler = stateHandler
- setupObservers()
- }
- public convenience init(playerItem item: AVPlayerItem?, _ stateHandler:@escaping (PlayerNotifications)->()) {
- self.init(playerItem: item)
- self.stateHandler = stateHandler
- setupObservers()
- }
- private func setupObservers() {
- let notificationCenter = NotificationCenter.default
- notificationNames.forEach {
- guard stateHandler != nil else {
- notificationCenter.removeObserver(self, name: $0, object: nil)
- return
- }
- notificationCenter.addObserver(self,
- selector: #selector(handleNotification(_:)),
- name: $0,
- object: nil)
- }
- switch stateHandler {
- case .some:
- currentItemObserver = observe(\.currentItem, options: [.new]) { [weak self] _, change in
- guard let item = change.newValue else { return }
- self?.stateHandler?(PlayerNotifications.DidChangeCurrentItem(item: item))
- }
- rateObserver = observe(\.rate, options: [.old, .new]) { [weak self] _, change in
- guard let oldRate = change.oldValue else { return }
- guard let newRate = change.newValue else { return }
- if oldRate != 0 && newRate == 0 {
- self?.stateHandler?(PlayerNotifications.DidPausePlayback)
- } else if oldRate == 0 && newRate != 0 {
- self?.stateHandler?(PlayerNotifications.DidStartPlayback)
- }
- }
- case .none:
- currentItemObserver = nil
- rateObserver = nil
- }
- }
- private var currentItemObserver: NSKeyValueObservation? {
- get {
- return objc_getAssociatedObject(self, &AssociatedKey.currentItem) as? NSKeyValueObservation
- }
- set {
- currentItemObserver.flatMap { $0.invalidate() }
- objc_setAssociatedObject(self, &AssociatedKey.currentItem, newValue, .OBJC_ASSOCIATION_RETAIN)
- }
- }
- private var rateObserver: NSKeyValueObservation? {
- get {
- return objc_getAssociatedObject(self, &AssociatedKey.rate) as? NSKeyValueObservation
- }
- set {
- rateObserver.flatMap { $0.invalidate() }
- objc_setAssociatedObject(self, &AssociatedKey.rate, newValue, .OBJC_ASSOCIATION_RETAIN)
- }
- }
- @objc private func handleNotification(_ notification: Notification) {
- guard let item = notification.object as? AVPlayerItem else { return }
- guard item == currentItem else { return }
- switch notification.name {
- case .AVPlayerItemTimeJumped:
- stateHandler?(PlayerNotifications.ItemTimeJumped(item: item))
- case .AVPlayerItemPlaybackStalled:
- stateHandler?(PlayerNotifications.ItemPlaybackStalled(item: item))
- case .AVPlayerItemDidPlayToEndTime:
- stateHandler?(PlayerNotifications.ItemDidPlayToEndTime(item: item))
- case .AVPlayerItemFailedToPlayToEndTime:
- stateHandler?(PlayerNotifications.ItemPlayToEndTimeFailed(item: item))
- default:
- return
- }
- }
- func cleanupObservers() {
- notificationNames.forEach { name in
- NotificationCenter.default.removeObserver(self, name: name, object: nil)
- }
- currentItemObserver = nil
- rateObserver = nil
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement