Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AVPictureInPictureController doesn't automatically start picture-in-picture when backgrounding the app

Tags:

ios

avkit

ios14

When creating a custom video player using the AVPlayer + AVPlayerLayer + AVPictureInPictureController for a iPhone running iOS 14 (beta 7) the video does not automatically enter picture-in-picture-mode when the app enters the background after player.start() is called from a UIButton action.

The issue does not reproduce using the AVPlayerViewController which seems to indicate a problem with the AVPictureInPictureController on iOS 14 in general, but I was wondering if anyone else had run into this problem and know of any workarounds. I've also filed this problem with Apple under rdar://8620271

Sample code.

import UIKit
import AVFoundation
import AVKit

class ViewController: UIViewController {
    private let player = AVPlayer(url: URL(string: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4")!)
    private var pictureInPictureController: AVPictureInPictureController!
    private var playerView: PlayerView!
    private var playButton: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        playerView = PlayerView(frame: CGRect(x: 0, y: 44, width: view.bounds.width, height: 200))
        playerView.backgroundColor = .black
        playerView.playerLayer.player = player
        view.addSubview(playerView)

        playButton = UIButton(frame: CGRect(x: view.bounds.midX - 50, y: playerView.frame.maxY + 20, width: 100, height: 22))
        playButton.setTitleColor(.blue, for: .normal)
        playButton.setTitle("Play", for: .normal)
        playButton.addTarget(self, action: #selector(play), for: .touchUpInside)
        view.addSubview(playButton)

        pictureInPictureController = AVPictureInPictureController(playerLayer: playerView.playerLayer)

        do {
            let audioSession = AVAudioSession.sharedInstance()
            try audioSession.setCategory(.playback)
            try audioSession.setMode(.moviePlayback)
            try audioSession.setActive(true)
        } catch let e {
            print(e.localizedDescription)
        }
    }

    @objc func play() {
        player.play()
    }
}

class PlayerView: UIView {
    override class var layerClass: AnyClass {
        return AVPlayerLayer.self
    }

    var playerLayer: AVPlayerLayer! {
        return layer as? AVPlayerLayer
    }
}
like image 282
Claus Jørgensen Avatar asked Sep 04 '20 11:09

Claus Jørgensen


People also ask

Is Picture in Picture available for iOS?

The Picture-in-Picture (PiP) option was first made available to Premium users on iPhones and iPad in the US in June. Now, YouTube has announced that this functionality is now available globally on all iOS and iPadOS 15.0 and higher-running devices.

What is Avplayerlayer?

An object that presents the visual contents of a player object.

How do you implement an image in Swift?

While PiP is active, dismiss playback controls in your main player, and present artwork in the PiP window to indicate that PiP mode is active. To implement this functionality, use the pictureInPictureControllerWillStartPictureInPicture(_:) and pictureInPictureControllerDidStopPictureInPicture(_:)


Video Answer


2 Answers

The root cause of the problem ended up being twofold:

  1. AVAudioSession.sharedInstance().setActive(true) must be called before the AVPictureInPictureController is initialised.

  2. The frame size for the AVPlayerLayer must have a aspect ratio no greater than 16/9 (filed as a separate bug, rdar://8689203)

  3. For iPads, the video must be the same width as the device (in any given orientation). No separate rdar, as Apple have acknowledged the other bug already.

(The 2nd issues is not present in the example above)

Apple have acknowledged these bugs, and reported back to me that they have been / will be fixed (a rare case of a radar actually resulting in a reply!)

like image 146
Claus Jørgensen Avatar answered Nov 15 '22 01:11

Claus Jørgensen


Starting iOS 14.2, Apple has exposed an api to start PIP when app goes into background:

if #available(iOS 14.2, *) {
   pictureInPictureController.canStartPictureInPictureAutomaticallyFromInline = true
}

Additionally, it is worth noting that Apple has forbidden to start picture-in-picture without user manually tapping the button. It will result in app rejection. Best bet is to use Apple's API mentioned above to avoid rejection.

like image 27
atulkhatri Avatar answered Nov 15 '22 00:11

atulkhatri