0

I have a custom media player, and I wish to use it universally within my app; however I do not wish to keep copying and pasting the code from page to page, as there are multiple areas that I need this video player to work. How would I go about making this solely in one file and being able to just call it in each individual file that it is needed in? This is strictly for iOS

Here is the code:

let avPlayer = AVPlayer()
var avPlayerLayer: AVPlayerLayer!

let playerButton = UIButton()

var timeWatcher : AnyObject!

var timeRemainingLabel = UILabel()

let seekSlider = UISlider()
var playerRateBeforeSeek : Float = 0

var loadingIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)


override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = .black

    // An AVPlayerLayer is a CALayer instance to which the AVPlayer can
    // direct its visual output. Without it, the user will see nothing.
    avPlayerLayer = AVPlayerLayer(player: avPlayer)
    view.layer.insertSublayer(avPlayerLayer, at: 0)

    //this is for the button, we will do it through storyboard
    view.addSubview(playerButton)
    playerButton.addTarget(self, action: #selector(playerButtonTapped), for: .touchUpInside)


    let url = NSURL(string: "https://content.jwplatform.com/manifests/vM7nH0Kl.m3u8")
    let playerItem = AVPlayerItem(url: url! as URL)
    avPlayer.replaceCurrentItem(with: playerItem)

    //timer info
    let timeInterval : CMTime = CMTimeMakeWithSeconds(1.0, 10)
    timeWatcher = avPlayer.addPeriodicTimeObserver(forInterval: timeInterval, queue: DispatchQueue.main, using: { (elapsedTime:CMTime) in
       // print("the time has now been:", CMTimeGetSeconds(elapsedTime))
        self.observeTime(elapsedTime: elapsedTime)
        self.timeRemainingLabel.textColor = .white
        self.view.addSubview(self.timeRemainingLabel)

    //buffer indicator
        self.loadingIndicatorView.hidesWhenStopped = true
        self.view.addSubview(self.loadingIndicatorView)
        self.avPlayer.addObserver(self, forKeyPath: "currentItem.playbackLikelyToKeepUp", options: .new, context: &playbackLikelyToKeepUpContext)

    }) as AnyObject!
}


//buffer
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if context == &playbackLikelyToKeepUpContext {
        if avPlayer.currentItem!.isPlaybackLikelyToKeepUp {
            loadingIndicatorView.stopAnimating()
        } else {
            loadingIndicatorView.startAnimating()
        }
    }
}

deinit {
    //timer
    avPlayer.removeTimeObserver(timeWatcher)

    //buffer
    avPlayer.removeObserver(self, forKeyPath: "currentItem.playbackLikelyToKeepUp")
}


private func updateTimeLabel(elapsedTime: Float64, duration: Float64) {
    let timeRemaining: Float64 = CMTimeGetSeconds(avPlayer.currentItem!.duration) - elapsedTime

    timeRemainingLabel.text = String(format: "%02d:%02d", ((lround(timeRemaining) / 60) % 60), lround(timeRemaining) % 60)


}


private func observeTime(elapsedTime: CMTime) {
    let duration = CMTimeGetSeconds(avPlayer.currentItem!.duration)
  //  if isFinite(duration)
   // {
        let elapsedTime = CMTimeGetSeconds(elapsedTime)
        updateTimeLabel(elapsedTime: elapsedTime, duration: duration)
   //}
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    loadingIndicatorView.startAnimating() //animating the buffer
    avPlayer.play() // Start the playback

}

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()

    // Layout subviews manually - replace w/ storyboard
    avPlayerLayer.frame = view.bounds
    playerButton.frame = view.bounds

    //time remaining - replace w/ story board
    let controlsHeight: CGFloat = 30
    let controlsY: CGFloat = view.bounds.size.height - controlsHeight
    timeRemainingLabel.frame = CGRect(x: 5, y: controlsY, width: 60, height: controlsHeight)

    //buffer - replace w/ storyboard
    loadingIndicatorView.center = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
}

func playerButtonTapped(sender: UIButton) {
    let playerIsPlayer = avPlayer.rate > 0
    if playerIsPlayer {
        avPlayer.pause()
    } else {
        avPlayer.play()
    }
}

// Force the view into landscape mode (which is how most video media is consumed.
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.landscape
}

override var shouldAutorotate: Bool {
    return true
}
rmaddy
  • 314,917
  • 42
  • 532
  • 579
Andrew Saxoni
  • 77
  • 1
  • 7
  • 1
    Move the player to a separate class and initialize that class whenever you need the player. – Lefteris Jan 12 '17 at 15:42
  • Does that track if a video is currently playing within the media player because it is separated within its own class? Or do I need to set up a tracker for that as well; because I don't want to be able for someone to be playing two videos at once. – Andrew Saxoni Jan 12 '17 at 15:45
  • You should deinit the class when you don't need it. If you want to use it globally through the app in the same instance, you should make it a singleton – Lefteris Jan 12 '17 at 15:48
  • I've never heard of a singleton before; I just looked it up and it looks promising for it; do you have any resources/references I could look up in relation to singleton for swift 3? because it appears there was a difference for singletons b/w swift 2 and swift 3 – Andrew Saxoni Jan 12 '17 at 16:02
  • On swift 3 it's just a single line. See here: http://stackoverflow.com/a/39628384/312312 – Lefteris Jan 12 '17 at 16:06
  • Awesome thank you very much! I'll take a look into this. – Andrew Saxoni Jan 12 '17 at 16:10

0 Answers0