首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >当应用程序从后台返回时,iOS -AVPlayer视频冻结/暂停

当应用程序从后台返回时,iOS -AVPlayer视频冻结/暂停
EN

Stack Overflow用户
提问于 2018-01-27 06:11:53
回答 5查看 14K关注 0票数 12

我的应用程序的登录页面上有一个循环播放的视频。我遵循了Youtube的教程,让它在视环视频控制器上工作

问题是,当应用程序转到后台时,如果我不马上回来,当我回来的时候,视频就会被冻结。

根据苹果文档的说法,这是应该发生的。

我试着使用NotificationCenter的Notification.Name.UIApplicationWillResignActive,但这不起作用。

,当应用程序从后台返回后,我如何让视频继续播放?

代码语言:javascript
运行
复制
var player: AVPlayer!
var playerLayer: AVPlayerLayer!

override func viewDidLoad() {
        super.viewDidLoad()

        configurePlayer()
}


@objc fileprivate func configurePlayer(){

        let url = Bundle.main.url(forResource: "myVideo", withExtension: ".mov")

        player = AVPlayer.init(url: url!)
        playerLayer = AVPlayerLayer(player: player!)
        playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
        playerLayer.frame = view.layer.frame


        player.actionAtItemEnd = AVPlayerActionAtItemEnd.none

        player.play()

        view.layer.insertSublayer(playerLayer, at: 0)

        NotificationCenter.default.addObserver(self, selector: #selector(playerItemReachedEnd), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem)

        NotificationCenter.default.addObserver(self, selector: #selector(playerItemReachedEnd), name: Notification.Name.UIApplicationWillResignActive, object: player.currentItem)

    }

@objc fileprivate func playerItemReachedEnd(){
        player.seek(to: kCMTimeZero)
    }
EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2018-01-28 14:18:19

根据苹果文档的说法,当播放视频并将应用程序发送到后台时,播放机会自动暂停:

他们要做的是在应用程序到后台时删除AVPlayerLayer (设置为零),然后在前景时重新初始化它:

他们说最好的处理方法是在applicationDidEnterBackgroundapplicationDidBecomeActive

我使用NSNotification侦听背景和前景事件,并设置函数来暂停播放机&将playerLayer设置为零(都用于背景事件),然后重新初始化playerLayer,并为前台事件播放播放机。这些是我使用的.UIApplicationWillEnterForeground.UIApplicationDidEnterBackground通知

我发现,由于某种原因,如果你长时间按“主页”按钮,弹出的屏幕上会显示“我能帮你什么”,如果你再次按“主页”按钮回到你的应用程序,视频就会被冻结,使用上面的2份通知并不能阻止它的发生。我发现防止这种情况的唯一方法是也使用通知.UIApplicationWillResignActive.UIApplicationDidBecomeActive。如果您不添加这些除了上述通知,那么您的视频将冻结在主页按钮,长按和后退。我发现防止所有冻结方案的最佳方法是使用所有4种通知。

与上面的代码不同的是,我必须将player和playerLayer类变量设置为选项,而不是隐式地展开选项,我还向AVPlayer类添加了一个扩展,以检查它是否在iOS 9或更低版本中运行。在iOS 10和更高版本中,有一个内置方法.timeControlStatus AVPlayer定时器状态

我上面的代码:

代码语言:javascript
运行
复制
var player: AVPlayer?
var playerLayer: AVPlayerLayer?

在AVPlayer 9或更低版本中向检查AVPlayer的状态添加一个扩展:

代码语言:javascript
运行
复制
import AVFoundation

extension AVPlayer{

    var isPlaying: Bool{
        return rate != 0 && error == nil
    }
}

以下是完整的代码:

代码语言:javascript
运行
复制
var player: AVPlayer?
var playerLayer: AVPlayerLayer? //must be optional because it will get set to nil on background event

override func viewDidLoad() {
    super.viewDidLoad()

    // background event
    NotificationCenter.default.addObserver(self, selector: #selector(setPlayerLayerToNil), name: UIApplication.didEnterBackgroundNotification, object: nil)

    // foreground event
    NotificationCenter.default.addObserver(self, selector: #selector(reinitializePlayerLayer), name: UIApplication.willEnterForegroundNotification, object: nil)

   // add these 2 notifications to prevent freeze on long Home button press and back
    NotificationCenter.default.addObserver(self, selector: #selector(setPlayerLayerToNil), name: UIApplication.willResignActiveNotification, object: nil)

    NotificationCenter.default.addObserver(self, selector: #selector(reinitializePlayerLayer), name: UIApplication.didBecomeActiveNotification, object: nil)

    configurePlayer()
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    // this is also for the long Home button press
    if let player = player{
        if #available(iOS 10.0, *) {
            if player.timeControlStatus == .paused{
                player.play()
            }
        } else {
            if player.isPlaying == false{
                player.play()
            }
        }
    }
}

@objc fileprivate func configurePlayer(){

    let url = Bundle.main.url(forResource: "myVideo", withExtension: ".mov")

    player = AVPlayer.init(url: url!)
    playerLayer = AVPlayerLayer(player: player!)
    playerLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
    playerLayer?.frame = view.layer.frame

    player?.actionAtItemEnd = AVPlayerActionAtItemEnd.none

    player?.play()

    view.layer.insertSublayer(playerLayer!, at: 0)

    NotificationCenter.default.addObserver(self, selector: #selector(playerItemReachedEnd), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem)
}

@objc fileprivate func playerItemReachedEnd(){
     // this works like a rewind button. It starts the player over from the beginning
     player?.seek(to: kCMTimeZero)
}

 // background event
@objc fileprivate func setPlayerLayerToNil(){
    // first pause the player before setting the playerLayer to nil. The pause works similar to a stop button
    player?.pause()
    playerLayer = nil
}

 // foreground event
@objc fileprivate func reinitializePlayerLayer(){

    if let player = player{

        playerLayer = AVPlayerLayer(player: player)

        if #available(iOS 10.0, *) {
            if player.timeControlStatus == .paused{
                player.play()
            }
        } else {
            // if app is running on iOS 9 or lower
            if player.isPlaying == false{
                player.play()
            }
        }
    }
}

isPlaying 不要忘记将扩展添加到AVPlayer

票数 21
EN

Stack Overflow用户

发布于 2018-08-20 06:53:38

接受的答案对我没有用。我的“欢迎”视频在某些场合随机暂停。

以下是我们所做的:

后台:由于播放器和playerLayer对象不会在应用程序"resignsActive“中被销毁,或者进入”背景“(在调用它们各自的通知时可以通过观察它们的状态来验证),所以我推测将这些对象中的任何一个设置为零,然后在进入背景或前景时重新初始化它们是有点不必要的。

当玩家对象进入前台时,我才再次播放它。

代码语言:javascript
运行
复制
var player: AVPlayer?
var playerLayer: AVPlayerLayer?

在ViewDidLoad中,我配置我的player对象。

代码语言:javascript
运行
复制
override func viewDidLoad() {
  configurePlayer()
}

configurePlayer()函数定义如下

代码语言:javascript
运行
复制
private func configurePlayer() {
  guard let URL = Bundle.main.url(forResource: "welcome", withExtension: ".mp4") else { return }

  player = AVPlayer.init(url: URL)
  playerLayer = AVPlayerLayer(player: player)
  playerLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
  playerLayer?.frame = view.layer.frame

  player?.actionAtItemEnd = AVPlayerActionAtItemEnd.none
  playItem()

  setupPlayNotificationItems()
}

下面是帮助函数的实现

代码语言:javascript
运行
复制
private func setupPlayNotificationItems() {
  NotificationCenter.default.addObserver(self,
                                        selector: #selector(restartPlayerItem),
                                        name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
                                        object: player?.currentItem)
  NotificationCenter.default.addObserver(self,
                                        selector: #selector(playItem),
                                        name: .UIApplicationWillEnterForeground,
                                        object: nil)
}

@objc private func playItem() {
  // If you please, you can also restart the video here
  restartPlayerItem()

  player?.play()

  if let playerlayer = playerLayer {
    view.layer.insertSublayer(playerlayer, at: 0)
  }
}

@objc func restartPlayerItem() {
  player?.seek(to: kCMTimeZero)
}
票数 5
EN

Stack Overflow用户

发布于 2018-01-27 07:29:51

添加观察者

代码语言:javascript
运行
复制
func addPlayerNotifications() {
    NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidPlayToEnd), name: .AVPlayerItemDidPlayToEndTime, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(applicationWillEnterForeground), name: .UIApplicationWillEnterForeground, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(applicationDidEnterBackground), name: .UIApplicationDidEnterBackground, object: nil)
}

移除观察者

代码语言:javascript
运行
复制
func removePlayerNotifations() {
    NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: nil)
    NotificationCenter.default.removeObserver(self, name: .UIApplicationWillEnterForeground, object: nil)
    NotificationCenter.default.removeObserver(self, name: .UIApplicationDidEnterBackground, object: nil)
}

方法

代码语言:javascript
运行
复制
// Player end.
@objc  func playerItemDidPlayToEnd(_ notification: Notification) {
    // Your Code.
    player.seek(to: kCMTimeZero)
}

//App enter in forground.
@objc func applicationWillEnterForeground(_ notification: Notification) {
      player.play()
}

//App enter in forground.
@objc func applicationDidEnterBackground(_ notification: Notification) {
      player.pause()
}

试试这段代码

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48473144

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档