4

I am implemeting Picture-in-picture on iOS. I added the background mode capability. When I call isPictureInPicturePossible, it returns false with error:

pictureInPictureController failedToStartPictureInPictureWithError Error Domain=AVKitErrorDomain Code=-1001 "Failed to start picture in picture." UserInfo={NSLocalizedDescription=Failed to start picture in picture., NSLocalizedFailureReason=The UIScene for the content source has an activation state other than UISceneActivationStateForegroundActive, which is not allowed.}

But I when I log the activationState, I can see that it is actually foregroundActive. Any idea what could be the reason?

(before that isPictureInPictureActive returns true, isPictureInPictureSuspended returns false, isPictureInPictureActive returns false.)

Taha Selim Bebek
  • 45
  • 1
  • 8
  • 16

2 Answers2

1

I don't know if you still need this. But I ran into the same problem and I managed to solve it working a bit with the AVPlayerLayer.

This is my code (inside the AVPlayerViewController, but works also in a normal UIViewController):

extension PlayerContainerViewController: AVPictureInPictureControllerDelegate {
        
    func setupPictureInPicture() {
        if AVPictureInPictureController.isPictureInPictureSupported() {
            
            
            let playerLayer: AVPlayerLayer = AVPlayerLayer()
            playerLayer.player = player              // player is your AVPlayer
            self.view.layer.addSublayer(playerLayer) // <-- Very important 
            playerLayer.videoGravity = .resizeAspect
            playerLayer.frame = self.view.bounds

            pipController = AVPictureInPictureController(playerLayer: playerLayer)
            pipController?.delegate = self
        }
    }
    
    func pictureInPictureController(
        _ pictureInPictureController: AVPictureInPictureController,
        restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void
    ) {
            completionHandler(true)
    }
    
    func pictureInPictureControllerWillStartPictureInPicture(
        _ pictureInPictureController: AVPictureInPictureController) {
            // Pip starting
    }
    
    func pictureInPictureControllerDidStopPictureInPicture(
        _ pictureInPictureController: AVPictureInPictureController) {
            // Pip stopped
    }
    
    func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) {
            print("[PIP] \(error)")
    }
        
    func startPip() {
        pipController?.startPictureInPicture()
    }
    
    func stopPip() {
        pipController?.stopPictureInPicture()
    }
    
}

Hope this helps.

0

It is a little difficult to give an opinion without access to the code and without knowing the versions.

My suggestions:

Check if the app is really in the foreground. You can verify this by looking at the activationState of the UIScene for the content source. If it's not foregroundActive, you can't start the image in the image. If you are using a UISceneDelegate, you can check the activation state of the scene in the sceneWillEnterForeground: method. If using an AppDelegate, you can check the activation state of the scene in the applicationDidBecomeActive: method.

If you use using a UISceneDelegate instead of a AppDelegate. Change it to AppDelegate.

Set the sceneActivationState to foregroundActive in the AppDelegate.swift file:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    if #available(iOS 13.0, *) {

        let scene = UIApplication.shared.connectedScenes.first

        if let sceneDelegate = scene?.delegate as? SceneDelegate {

            sceneDelegate.window?.windowScene?.activationState = .foregroundActive

        }

    }

    return true

}
 

If no option to validate. We will give you more information about the problem.

Tibic4
  • 3,709
  • 1
  • 13