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
}
}
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.
An object that presents the visual contents of a player object.
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(_:)
The root cause of the problem ended up being twofold:
AVAudioSession.sharedInstance().setActive(true)
must be called before the AVPictureInPictureController
is initialised.
The frame size for the AVPlayerLayer
must have a aspect ratio no greater than 16/9 (filed as a separate bug, rdar://8689203
)
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!)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With