防止 AVPlayer 一次播放多个曲目的最佳方法

Posted

技术标签:

【中文标题】防止 AVPlayer 一次播放多个曲目的最佳方法【英文标题】:Best way to prevent AVPlayer playing more than one track at a time 【发布时间】:2017-07-28 08:55:11 【问题描述】:

我正在使用 AVPlayer 播放流式音频,并希望阻止它一次播放多个音轨。最好的方法是什么?我的方法完全是程序化的——我不使用情节提要。当一个 url 被发送到 ViewController(VC,委托)时,它会设置一个新的播放器实例——我认为这是问题所在,但我不确定。谢谢。

 //this routine returns the url from control to the VC (delegate)
  func selectedAudio(_ audioUrl:String?) 
    if let urlString = audioUrl 
      setupPlayer(urlString)
    
  

    //single button which toggles between play/pause
  func playOrPauseTouched() 
    if isPlaying  
      pausePlayer()
     else 
      playPlayer()
    
  

  func playPlayer() 
    if player == nil || selectedTrackName != currentTrackLabel.text
      if let urlString = selectedAudioUrl 
        setupPlayer(urlString)
        currentTrackLabel.text = "Playing: \(selectedTrackName ?? "")"
      
    
    player?.play()
    isPlaying = true
    playPauseButton.setImage(UIImage(named: "pause"), for: .normal)
    activityIndicatorView.startAnimating()
  

  func pausePlayer() 
    player?.pause()
    isPlaying = false
    playPauseButton.setImage(UIImage(named: "play"), for: .normal)
  

  func setupPlayer(_ urlString:String) 
    if let url = URL(string:urlString) 
      player = AVPlayer(url: url)
      let playerLayer = AVPlayerLayer(player: player)
      player?.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: nil)
      let interval = CMTime(value: 1, timescale: 5) //gives smooth thumb movement w/short clips
      player?.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main, using:  (progressTime) in
        let seconds = CMTimeGetSeconds(progressTime)
        let secondsString = String(format: "%02d", Int(seconds.truncatingRemainder(dividingBy: 60)))
        let minutesString = String(format: "%02d", Int(seconds / 60))
        self.currentTimeLabel.text = "\(minutesString):\(secondsString)"
        if let duration = self.player?.currentItem?.duration 
          let durationSeconds = CMTimeGetSeconds(duration)
          self.audioslider.value = Float(seconds / durationSeconds)
        
      )
    
  

【问题讨论】:

您没有尝试在创建新实例之前停止player 吗? 我不知道 AVPlayer 的停止方法(虽然我见过 AVAudioPlayer 的停止方法)。但我可能会遗漏一些东西。我知道的唯一文档是 Objective-C 而不是 Swift,这对我来说很困难 确实,没有停止方法。试试pause() 是否有助于了解它是否是以前的AVPlayer 为了将来帮助其他人,我更新了代码以反映我找到的解决方案。正如 Tofaani Kaanudo 建议的那样,我已经在使用 KVO,但需要改进我的等效“playerDidFinishPlaying”以在曲目完成后设置“player = nil”(并推进到下一个曲目)。完成此操作后,playPlayer() 中的“if player == nil”行现在可以防止同时播放多个曲目。谢谢 【参考方案1】:

我的意见是使用KVO,它会通知您AVPlayerItem 的当前状态。

意味着您将获得state,这将帮助您识别当前项目正在播放、推送、缓冲或结束播放。

所以你应该使用一个flag 来处理你当前的项目state

例如

在播放项目时设置标志为真 当项目结束播放时设置标志为 false。 当您点击按钮播放另一个项目时,然后检查 if flag == false 等标志,然后开始播放另一个项目,否则不会。

第二种方式:

使用NotificationCenter,如下所示

NotificationCenter.default.addObserver(self, selector: Selector(("playerDidFinishPlaying:")), 
       name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem)

及以下项目播放完毕后自动调用

func playerDidFinishPlaying(note: NSNotification) 
    print("Item finished playing ")
   // Set flag here 

和托管标志一样。

【讨论】:

谢谢你,你的指导很清楚,虽然我对KVO了解不多,所以我用过的地方都是抄别人的sn-ps代码。我需要进一步研究它以了解如何编写该代码。 @TonyM - 你可以使用第二种方式

以上是关于防止 AVPlayer 一次播放多个曲目的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 AVPlayer 从数组中选择后播放下一首曲目

iOS AVPlayer 后台播放问题自动停止问题 防止应用被后台挂起方法

获取 Spotify 用户当前播放的曲目名称 [Web API]

使用 AVPlayer 时防止背景音频静音

如何使用 MediaPlayer 同时播放多个曲目?

防止 AVPlayer 取消背景音频