Advertisement
Guest User

Untitled

a guest
Oct 3rd, 2017
136
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 2.69 KB | None | 0 0
  1. import AVFoundation
  2.  
  3. /// The `PlayerLoader` class provides a callback mechanism for consumers of `AVPlayer` when loading a resource.
  4. /// AVFoundation works by an inconvenient series of APIs that rely on KVO for asynchronous operations. This class
  5. /// eliminates the boilerplate that KVO imposes and makes the callsite much more clean.
  6. public final class PlayerLoader: NSObject {
  7. public typealias Callback = (AVPlayer) -> ()
  8.  
  9. /// The callback will initialize to a non-nil value. The callback is set to nil once it's invoked, at which time the
  10. /// KVO observation is removed. This is necessary to avoid double-removing the KVO observation.
  11. private var callback: Callback?
  12.  
  13. private let player: AVPlayer
  14.  
  15. // This is declared `var` because we have to take its pointer value
  16. private static var observationContext = NSObject()
  17. private let observationKeypath = #keyPath(AVPlayer.currentItem.status)
  18.  
  19. init(player: AVPlayer, callback: @escaping Callback) {
  20. self.callback = callback
  21. self.player = player
  22.  
  23. super.init()
  24.  
  25. if self.player.currentItem?.status == AVPlayerItemStatus.readyToPlay {
  26. self.callback?(self.player)
  27. } else {
  28. self.player.addObserver(
  29. self,
  30. forKeyPath: self.observationKeypath,
  31. options: [NSKeyValueObservingOptions.new],
  32. context: &PlayerLoader.observationContext
  33. )
  34. }
  35. }
  36.  
  37. public convenience init(url: URL, callback: @escaping Callback) {
  38. self.init(player: AVPlayer(url: url), callback: callback)
  39. }
  40.  
  41. override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
  42. guard let context = context, context == &PlayerLoader.observationContext else {
  43. return
  44. }
  45.  
  46. guard let keyPath = keyPath, keyPath == self.observationKeypath else {
  47. return
  48. }
  49.  
  50. guard let currentItem = self.player.currentItem else {
  51. return
  52. }
  53.  
  54. guard !CMTimeGetSeconds(currentItem.duration).isNaN else {
  55. return
  56. }
  57.  
  58. guard currentItem.status == AVPlayerItemStatus.readyToPlay else {
  59. return
  60. }
  61.  
  62. self.player.removeObserver(self, forKeyPath: self.observationKeypath)
  63.  
  64. self.player.preroll(atRate: 1.0) { _ in
  65. self.callback?(self.player)
  66. self.callback = nil
  67. }
  68. }
  69.  
  70. deinit {
  71. // This is really awkward but is one of the reasons why this class exists in the first place.
  72. if self.callback != nil {
  73. self.player.removeObserver(self, forKeyPath: self.observationKeypath)
  74. }
  75. }
  76. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement