How start AVPictureInPicture when video is paused

I have AVPlayer with AVPictureInPictureController. Play video in app and picture In Picture works except one situation. Issue is: I pause video in application and during switch to background is not PiP activate. What do I wrong?

import UIKit
import AVKit
import AVFoundation

class ViewControllerSec: UIViewController,AVPictureInPictureControllerDelegate {
    var pipPlayer: AVPlayer!
    var avCanvas : UIView!
    var pipCanvas: AVPlayerLayer?
    var pipController: AVPictureInPictureController!
    var mainViewControler : UIViewController!
    var playerItem : AVPlayerItem!
    var videoAvasset : AVAsset!

    public func link(to parentViewController : UIViewController) {
        mainViewControler = parentViewController
        setup()
    }

    @objc func appWillResignActiveNotification(application: UIApplication) {
        guard let pipController = pipController else {
            print("PiP not supported")
            return
        }

        print("PIP isSuspend: \(pipController.isPictureInPictureSuspended)")
        print("PIP isPossible: \(pipController.isPictureInPicturePossible)"

        if playerItem.status == .readyToPlay {
            if pipPlayer.rate == 0 {
                pipPlayer.play()
            }

            pipController.startPictureInPicture(). ---> Errorin log: Failed to start picture in picture.
        } else {
            print("Player not ready for PiP.")
        }
    }

    private func setupAudio() {
        do {
            let session = AVAudioSession.sharedInstance()
            try session.setCategory(.playback, mode: .moviePlayback)
            try session.setActive(true)
        } catch {
            print("Audio session setup failed: \(error.localizedDescription)")
        }
    }

    @objc func playerItemDidFailToPlayToEnd(_ notification: Notification) {
        if let error = notification.userInfo?[AVPlayerItemFailedToPlayToEndTimeErrorKey] as? Error {
            print("Failed to play to end: \(error.localizedDescription)")
        }
    }

    func setup() {
        setupAudio()
        guard let videoURL = URL(string: "https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.mp4/.m3u8") else { return }
        videoAvasset = AVAsset(url: videoURL)
        playerItem = AVPlayerItem(asset: videoAvasset)

        addPlayerObservers()
        pipPlayer = AVPlayer(playerItem: playerItem)

        avCanvas = UIView(frame: view.bounds)
        pipCanvas = AVPlayerLayer(player: pipPlayer)
        guard let pipCanvas else { return }

        pipCanvas.frame = avCanvas.bounds
        //pipCanvas.videoGravity = .resizeAspectFill

        mainViewControler.view.addSubview(avCanvas)
        avCanvas.layer.addSublayer(pipCanvas)
        if AVPictureInPictureController.isPictureInPictureSupported() {
            pipController = AVPictureInPictureController(playerLayer: pipCanvas)
            pipController?.delegate = self
            pipController?.canStartPictureInPictureAutomaticallyFromInline = true
        }

        let playButton = UIButton(frame: CGRect(x: 20, y: 50, width: 100, height: 50))
        playButton.setTitle("Play", for: .normal)
        playButton.backgroundColor = .blue
        playButton.addTarget(self, action: #selector(playTapped), for: .touchUpInside)

        mainViewControler.view.addSubview(playButton)
        let pauseButton = UIButton(frame: CGRect(x: 140, y: 50, width: 100, height: 50))

        pauseButton.setTitle("Pause", for: .normal)
        pauseButton.backgroundColor = .red
        pauseButton.addTarget(self, action: #selector(pauseTapped), for: .touchUpInside)
        mainViewControler.view.addSubview(pauseButton)
        let pipButton = UIButton(frame: CGRect(x: 260, y: 50, width: 150, height: 50))
        pipButton.setTitle("Start PiP", for: .normal)
        pipButton.backgroundColor = .green
        pipButton.addTarget(self, action: #selector(startPictureInPicture), for: .touchUpInside)
        mainViewControler.view.addSubview(pipButton)
        print("Error:\(String(describing: pipPlayer.error?.localizedDescription))")

        NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: nil) { [weak self] _ in
            guard let self = self else { return }
            if self.pipPlayer.rate == 0 {
                self.pipPlayer.play()
                pipController?.startPictureInPicture()
            }
        }

    func addPlayerObservers() {
         playerItem?.addObserver(self, forKeyPath: "status", options: [.old, .new], context: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying(_:)), name: .AVPlayerItemDidPlayToEndTime, object: playerItem)
     }

     override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
         if keyPath == "status" {
             if let statusNumber = change?[.newKey] as? NSNumber {
                 let status = AVPlayer.Status(rawValue: statusNumber.intValue)!
                 switch status {
                 case .readyToPlay:
                     print("Player is ready to play")
                 case .failed:
                     print("Player failed: \(String(describing: playerItem?.error))")
                 case .unknown:
                     print("Player status is unknown")
                 @unknown default:
                     fatalError()
                 }
             }
         }
     }

     @objc func playerDidFinishPlaying(_ notification: Notification) {
         print("Video finished playing.")
     }

    deinit {
         playerItem?.removeObserver(self, forKeyPath: "status")
         NotificationCenter.default.removeObserver(self)
     }

    @objc func playTapped() {
        pipPlayer.play()
    }

    @objc func pauseTapped() {
        pipPlayer.pause()
    }

    @objc func startPictureInPicture() {
        if let pipController = pipController, !pipController.isPictureInPictureActive {
            pipController.startPictureInPicture()
        }
    }

    @objc func stopPictureInPicture() {
        if let pipController = pipController, pipController.isPictureInPictureActive {
            pipController.stopPictureInPicture()
        }
    }

    func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) {
        print("Failed to start PiP: \(error.localizedDescription)")
        if let underlyingError = (error as NSError).userInfo[NSUnderlyingErrorKey] {
             print("Underlying error: \(underlyingError)")
         }
    }
}
Answered by krenem00 in 821549022

I found solution. Problem was in UIScene when appliaction did to background. AVPictureInPicture need during activation also active UIScene.

I changed Observer to uiSceneWillDeactivate where I activate PiP

        NotificationCenter.default.addObserver(
            self,
            selector: #selector(uiSceneWillDeactivate),
            name: UIScene.willDeactivateNotification,
            object: nil
        )
Accepted Answer

I found solution. Problem was in UIScene when appliaction did to background. AVPictureInPicture need during activation also active UIScene.

I changed Observer to uiSceneWillDeactivate where I activate PiP

        NotificationCenter.default.addObserver(
            self,
            selector: #selector(uiSceneWillDeactivate),
            name: UIScene.willDeactivateNotification,
            object: nil
        )
How start AVPictureInPicture when video is paused
 
 
Q