I’ve AVPlayer with AVPictureInPictureController. Play video in app and film In Image works besides one state of affairs.
Concern is: I pause video in software and through swap to background isn’t PiP activate. What do I fallacious?
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 hyperlink(to parentViewController : UIViewController) {
mainViewControler = parentViewController
setup()
}
@objc func appWillResignActiveNotification(software: UIApplication) {
pipController.startPictureInPicture()
}
non-public func setupAudio() {
do {
let session = AVAudioSession.sharedInstance()
strive session.setCategory(.playback, mode: .moviePlayback)
strive 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("Didn't play to finish: (error.localizedDescription)")
}
}
func setup() {
setupAudio()
// 1. Arrange AVPlayer
guard let videoURL = URL(string: "https://demo.unified-streaming.com/k8s/options/steady/video/tears-of-steel/tears-of-steel.mp4/.m3u8") else { return }
videoAvasset = AVAsset(url: videoURL)
playerItem = AVPlayerItem(asset: videoAvasset)
addPlayerObservers()
pipPlayer = AVPlayer(playerItem: playerItem)
// 2. Arrange AVPlayerLayer
avCanvas = UIView(body: view.bounds)
pipCanvas = AVPlayerLayer(participant: pipPlayer)
guard let pipCanvas else { return }
pipCanvas.body = avCanvas.bounds
//pipCanvas.videoGravity = .resizeAspectFill
mainViewControler.view.addSubview(avCanvas)
avCanvas.layer.addSublayer(pipCanvas)
// 3. Arrange AVPictureInPictureController
if AVPictureInPictureController.isPictureInPictureSupported() {
pipController = AVPictureInPictureController(playerLayer: pipCanvas)
pipController?.delegate = self
pipController?.canStartPictureInPictureAutomaticallyFromInline = true
}
let playButton = UIButton(body: CGRect(x: 20, y: 50, width: 100, top: 50))
playButton.setTitle("Play", for: .regular)
playButton.backgroundColor = .blue
playButton.addTarget(self, motion: #selector(playTapped), for: .touchUpInside)
mainViewControler.view.addSubview(playButton)
let pauseButton = UIButton(body: CGRect(x: 140, y: 50, width: 100, top: 50))
pauseButton.setTitle("Pause", for: .regular)
pauseButton.backgroundColor = .pink
pauseButton.addTarget(self, motion: #selector(pauseTapped), for: .touchUpInside)
mainViewControler.view.addSubview(pauseButton)
let pipButton = UIButton(body: CGRect(x: 260, y: 50, width: 150, top: 50))
pipButton.setTitle("Begin PiP", for: .regular)
pipButton.backgroundColor = .inexperienced
pipButton.addTarget(self, motion: #selector(startPictureInPicture), for: .touchUpInside)
mainViewControler.view.addSubview(pipButton)
print("Error:(String(describing: pipPlayer.error?.localizedDescription))")
// Observe software state modifications
NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: nil) { [weak self] _ in
guard let self = self else { return }
if self.pipPlayer.fee == 0 {
self.pipPlayer.play()
pipController?.startPictureInPicture()
}
}
func addPlayerObservers() {
// Observe participant merchandise's standing
playerItem?.addObserver(self, forKeyPath: "standing", choices: [.old, .new], context: nil)
// Observe playback completion
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying(_:)), identify: .AVPlayerItemDidPlayToEndTime, object: playerItem)
}
// Observe participant standing modifications
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "standing" {
if let statusNumber = change?[.newKey] as? NSNumber {
let standing = AVPlayer.Standing(rawValue: statusNumber.intValue)!
swap standing {
case .readyToPlay:
print("Participant is able to play")
case .failed:
print("Participant failed: (String(describing: playerItem?.error))")
case .unknown:
print("Participant standing is unknown")
@unknown default:
fatalError()
}
}
}
}
@objc func playerDidFinishPlaying(_ notification: Notification) {
print("Video completed taking part in.")
}
deinit {
// Clear up observers
playerItem?.removeObserver(self, forKeyPath: "standing")
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()
}
}
// MARK: - AVPictureInPictureControllerDelegate Strategies
func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
print("PiP will begin")
}
func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
print("PiP did begin")
}
func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) {
print("Failed to start out PiP: (error.localizedDescription)")
if let underlyingError = (error as NSError).userInfo[NSUnderlyingErrorKey] {
print("Underlying error: (underlyingError)")
}
}
func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
print("PiP will cease")
}
func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
print("PiP did cease")
}
}
I strive use observers with appWillResignActiveNotification to activate PiP. It’s name earlier than observer appDidEnterBackground however this additionally don’t clear up my drawback, additionally tryied to start out play video in observers after which activate PiP with the identical outcome.
I get error in occasion: Failed to start out PiP: Failed to start out image in image.