Lumen help

CDN Load Balancer for iOS and tvOS

Project Setup

CocoaPods

                use_frameworks!
target '<Your Target Name> '
  pod 'LumenOrchestratorSDK', '~> 22.03.2'
end

            
                binary "https://sdk.streamroot.io/ios/LumenOrchestratorSDK.json"
            

Integrate Lumen SDK

                <key>DeliveryClient</key>
<dict>
  <key>Key</key>
  <string>customerkey</string>
</dict>
            
                <key>NSAppTransportSecurity</key>
<dict> 
    <key>NSAllowsLocalNetworking</key>
    <true/>
</dict>
            
                #import <LumenOrchestratorSDK/LumenOrchestratorSDK.h>
            

Integrating the Code

                LMDeliveryClient.initializeApp()
            
                import AVKit

class PlayerInteractor: LMPlayerInteractorBase {
  
  fileprivate var player: AVPlayer?
  fileprivate var playbackState: LMPlaybackState
  
  fileprivate var observer: Any?
  
  override init() {
    self.playbackState = .idle
    super.init()
  }
  
  func linkPlayer(_ player: AVPlayer)  {
    self.player = player
    guard let playerItem = player.currentItem else { return }
    
    NotificationCenter.default.addObserver(self, selector: #selector(handlePlayedToEndFail),
                                           name: NSNotification.Name.AVPlayerItemFailedToPlayToEndTime,
                                           object: playerItem)
    NotificationCenter.default.addObserver(self, selector: #selector(handlePlayToEndSucceded),
                                           name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
                                           object: playerItem)
    NotificationCenter.default.addObserver(self, selector: #selector(handleAccesLogEntry),
                                           name: NSNotification.Name.AVPlayerItemNewAccessLogEntry,
                                           object: playerItem)
    NotificationCenter.default.addObserver(self, selector: #selector(handleErrorLogEntry),
                                           name: NSNotification.Name.AVPlayerItemNewErrorLogEntry,
                                           object: playerItem)
    NotificationCenter.default.addObserver(self, selector: #selector(handleItemPlayBackJumped),
                                           name: NSNotification.Name.AVPlayerItemTimeJumped,
                                           object: playerItem)
    NotificationCenter.default.addObserver(self, selector: #selector(handleItemPlayBackStall),
                                           name: NSNotification.Name.AVPlayerItemPlaybackStalled,
                                           object: playerItem)
    player.addObserver(self, forKeyPath: "rate", options: NSKeyValueObservingOptions.new, context: nil)
    observePlayback()
  }
  
  deinit {
    if let observer = self.observer {
      player?.removeTimeObserver(observer)
      self.observer = nil
    }
    
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVPlayerItemFailedToPlayToEndTime,
                                              object: player?.currentItem)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
                                              object: player?.currentItem)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVPlayerItemNewAccessLogEntry,
                                              object: player?.currentItem)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVPlayerItemNewErrorLogEntry,
                                              object: player?.currentItem)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVPlayerItemTimeJumped,
                                              object: player?.currentItem)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVPlayerItemPlaybackStalled,
                                              object: player?.currentItem)
    player?.removeObserver(self, forKeyPath: "rate")
  }
  
  fileprivate func updateState(_ state: LMPlaybackState) {
    if playbackState != state {
      super.playerStateDidChange(state)
      playbackState = state
    }
  }
  
  public override func observeValue(forKeyPath keyPath: String?,
                                    of _: Any?,
                                    change _: [NSKeyValueChangeKey: Any]?,
                                    context _: UnsafeMutableRawPointer?) {
    if keyPath == "rate", player?.rate == 0.0 {
      updateState(.paused)
    }
  }
  
  fileprivate func observePlayback() {
    if let observer = self.observer {
      player?.removeTimeObserver(observer)
      self.observer = nil
    }
    
    // Invoke callback every half second
    let interval = CMTime(seconds: 1,
                          preferredTimescale: CMTimeScale(NSEC_PER_SEC))
    // Queue on which to invoke the callback
    let mainQueue = DispatchQueue.main
    // Add time observer
    observer = player?.addPeriodicTimeObserver(forInterval: interval, queue: mainQueue) { [weak self] _ in
      guard let self = self else { return }
      
      let playbackLikelyToKeepUp: Bool = self.player?.currentItem?.isPlaybackLikelyToKeepUp ?? false
      if !playbackLikelyToKeepUp {
        // rebuffering
        self.updateState(.buffering)
      } else {
        // playing
        let rate = self.player?.rate
        if rate == 1.0, self.player?.error == nil {
          self.updateState(.playing)
        }
      }
    }
  }
}


// MARK: - Handler
extension PlayerInteractor {
  @objc private func handlePlayedToEndFail(_: Notification) {
    super.playbackErrorOccurred()
  }
  
  @objc private func handlePlayToEndSucceded(_: Notification) {
    updateState(.ended)
  }
  
  @objc private func handleAccesLogEntry(_: Notification) {
    guard let playerEvents = player?.currentItem?.accessLog()?.events.first else {
      return
    }
    // trackswitch
    if playerEvents.switchBitrate > 0 {
      super.trackSwitchOccurred()
    }
    // dropframe
    if playerEvents.numberOfDroppedVideoFrames > 0 {
      super.updateDroppedFrameCount(playerEvents.numberOfDroppedVideoFrames)
    }
  }
  
  @objc private func handleErrorLogEntry(_: Notification) {
    super.playbackErrorOccurred()
  }
  
  @objc private func handleItemPlayBackJumped(_: Notification) {
    updateState(.seeking)
  }
  
  @objc private func handleItemPlayBackStall(_: Notification) {
    updateState(.buffering)
  }
}
            
                var deliveryClient: LMDeliveryClient?
            
                deliveryClient = LMDeliveryClientBuilder.clientBuilder()
     .playerInteractor(<#playerInteractor#>)
     .contentId(<#string#>)
     .meshProperty(<#string#>)   
     .build(<#manifestUrl#>)
     .latency(<#latency#>)
     .logLevel(<#level#>)
     .build(<#manifestUrl#>)

            
                super.playerStateDidChange(.playing)
            

4. Play the stream

                guard let deliveryUrl = deliveryClient?.localManifestURL else {
  print("Local Url manifets could not be generated")
  return
}
    
let playerItem = AVPlayerItem(asset: AVURLAsset(url: deliveryUrl))
player = AVPlayer(playerItem: playerItem)
/*
* 
* We are calling here linkPlayer to start the playerInteractor
* 
*/
playerInteractor.linkPlayer(player!)
// Call the player play() method
player?.play()
            
                self.deliveryClient?.stop()
            
                self.deliveryClient?.displayStatView(someView!)
            

Explore CDN services