Stretch AVPlayerLayer to fit into iPhone X format - ios

I have an app where I play a video in the background in a certain view.
This video is always supposed to play fullscreen. I recently asked a question about stretching this video to fill up an iPhone plus model (make AVPlayer fill the entire screen) However, I just tested on an iPhone X, and the following happens:
I made the background of the view containing the AVPlayerLayer purple, just to clarify. As you can see, the video plays in the top of the screen (appx. 80pt), then there's the complete video, and then there's nothingness. How is this possible if my view does actually cover the entire screen?
This is how the video is made:
#objc fileprivate func playVideo() {
log.debug("playVideo")
let file = self.videoName.components(separatedBy: ".")
guard let path = Bundle.main.path(forResource: file[0], ofType:file[1]) else {
debugPrint( "\(file.joined(separator: ".")) not found")
return
}
self.player = AVPlayer(url: URL(fileURLWithPath: path))
let playerLayer = AVPlayerLayer(player: self.player)
playerLayer.frame = self.bounds
self.layer.addSublayer(playerLayer)
self.play()
if (self.loop) {
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player.currentItem, queue: .main) { _ in
self.restart()
}
}
self.backgroundColor = .purple
NotificationCenter.default.addObserver(
self,
selector: #selector(self.restart),
name: Notification.Name("playVideos"),
object: nil)
}
This method is called after layoutSubviews().
How do I make the video stretch fully (with some overflow on the sides to make it visible of course)? Thanks :-).

Related

Dismissing AVPlayerViewController doesn't 'kill' the object - it's persisting

I am playing videos that are in my app bundle.
They are playing correctly.
However, when I call to dismiss the AVPlayerViewController, it visibly is removed from the view hierarchy but, if I turn off the iOS device and turn it back on again, on the lock screen there is a media control showing that video and a 'play' button.
If you touch play you only get the audio and no video.
My problem is I don't understand why the 'dismiss' is not completely 'killing' the player when I'm done with it.
Here is the presentation code:
let path = Bundle.main.path(forResource: filename, ofType: type)
let url = NSURL(fileURLWithPath: path!)
let player = AVPlayer(url: url as URL)
NotificationCenter.default.addObserver(self,
selector: #selector(VideoLibraryViewController.didFinishPlaying(notification:)),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
object: player.currentItem)
self.playerController = AVPlayerViewController()
self.playerController?.player = player
self.playerController?.allowsPictureInPicturePlayback = true
self.playerController?.showsPlaybackControls = YES
self.playerController?.delegate = self
self.playerController?.player?.play()
self.present(self.playerController!, animated: true, completion : nil)
Here is the dismissal code:
// Delegate can implement this method to be notified when Picture in Picture will start.
func playerViewController(_ playerViewController: AVPlayerViewController, willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator)
{
self.playerController?.dismiss(animated: NO, completion: nil )
}
And here's what's remaining in the systemwide media player that is shown on the lock screen / control centre:
iOS 13 SDK ONLY: Here's the solution, but the answer is that despite dismissing the AVPlayerViewController, the AVPlayer object that it's knows about is persistent and that needs to be set to nil.
private func killVideoPlayer()
{
self.playerController?.player?.pause()
self.playerController?.player = nil
self.playerController?.dismiss(animated: YES, completion: { self.playerController = nil })
}
Previous SDK's, this still isn't working.
Neither is setting the AVAudioSession.active to false... ?!?! Still need a pre iOS 13 SDK solution.

Set opacity of AVPlayerLayer

I have seen quite a few posts of people wanting to play videos with transparent backgrounds. That is not what I am trying to accomplish here.
I am trying to set the opacity of the whole layer. Below is how I am setting the opacity of an image layer. I have tried the same approach to set opacity for the video layer but nothing happened when I attempted to fade
#IBAction func imageFadeAction(_ sender: Any) {
let rounded = Int(round(imageFader.value))
imageLabel.text = "\(rounded.description)%"
let fade = imageFader.value / 100
SecondScreen.main?.backgroundImage.alpha = CGFloat(fade)
SecondScreen.main?.viewDidLoad()
}
This is how I am playing the video and generating the layer
func playVideo() {
let videoURL = SecondScreen.videoPath
self.player = AVPlayer(url: videoURL!)
self.avpController = AVPlayerLayer(player: player)
avpController.frame = self.view.bounds
self.view.layer.addSublayer(avpController)
player.play()
}
Can anyone tell me if its possible or point me in the right direction
To answer my own question, Use a container view. play your video in a separate ViewController and embed it in the container view. THEN you can set the opacity of the container view.
I don't know if this is the correct way to do it, but it works for me and what I needed. If someone has a better answer please feel free to share
You can set its opacity to 0. something like this:
guard let path = Bundle.main.path(forResource: "SomeVideo", ofType: ".Someformat") else {
return nil
}
let player = AVPlayer(url: URL(fileURLWithPath: path))
let playerLayer = AVPlayerLayer(player: player)
playerLayer.opacity = 0
.
.
.

Video gets freeze at the end when playing in iPhone 4s

I have one countdown video to be played before quiz which is kept in bundle. The Video gets played properly in iPhone 6 [iOS 11.2] and quiz starts, but when I run the app in iPhone 4s [iOS 9.3] the video starts with a slight delay and at the end of video, the screen gets freeze for sometime and then the Quiz starts.
func playVideo()
{
guard let path = Bundle.main.path(forResource: "countDown", ofType:"mp4") else {
debugPrint("Video not found")
return
}
let item = AVPlayerItem(url: URL(fileURLWithPath: path))
player = AVPlayer.init(playerItem: item)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.viewVideo.bounds
player?.volume = 0
self.viewVideo.isHidden = false
player?.play()
self.viewVideo.layer.addSublayer(playerLayer)
let resetPlayer = {
self.player?.pause()
self.viewVideo.isHidden = true
// Moves to quiz
self.performSegue(withIdentifier: "toFinalQuiz", sender: self)
}
playerObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player?.currentItem, queue: nil) {
notification in
resetPlayer()
}
}
Try adding this line after the comment,
// Moves to quiz
DispatchQueue.main.async {
self.performSegue(withIdentifier: "toFinalQuiz", sender: self)
}
Let me know if this doesn't work...
For the delay at the start, I'd recommend to keep the video loaded and ready to play beforehand. So that you can being playing the video using just player?.play() when you need it.

AVPlayer full screen issue only for iPhone x

I created a splash video using Av player view controller. its is coming properly for all the devices except I Phone X. I tried changing video gravity frame and everything but it won't work. any Idea about this? here is the sample code :
guard let videoPath = Bundle.main.path(forResource: "Redtaxi-splash", ofType:"mov") else {
return
}
let videoURL = URL(fileURLWithPath: videoPath)
let player = AVPlayer(url: videoURL)
playerViewController = AVPlayerViewController()
playerViewController?.player = player
playerViewController?.showsPlaybackControls = false
playerViewController?.view.frame = view.frame
playerViewController?.view.backgroundColor = .white
playerViewController?.view.contentMode = .scaleAspectFill
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying(note:)),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem)
view.addSubview((playerViewController?.view)!)
playerViewController?.player?.play()
I fixed this by giving the video gravity for the player view controller :
playerViewController.videoGravity = .resizeAspectFill
this will fix the issue by using the video gravity as aspect fill for the avplayer view controller. here is the documentation for video gravity:
The video gravity determines how the video content is scaled or stretched within the player layer’s bounds.
Swift 4
playerViewController.videoGravity = AVLayerVideoGravity.resizeAspectFill.rawValue

Black frame when changing rate of AVPlayer item

I am reading a 30s MP4 video with AVPlayer. When it comes to the end, I would like to keep play it backward, then normally, then backward, and so on...
It is working fine with the following code, but I am having a black frame every time I am playing the video again in the other way.
Here is my code is viewDidLoad:
let playerVideoURL = Bundle.main.url(forResource: "my_video", withExtension: "mp4")!
self.backgroundPlayer = AVPlayer(url: playerVideoURL)
self.backgroundPlayerLayer = AVPlayerLayer(player: self.backgroundPlayer)
self.backgroundPlayerLayer.videoGravity = AVLayerVideoGravityResize
self.backgroundPlayer.volume = 0
self.backgroundPlayer.actionAtItemEnd = .none
self.backgroundPlayerLayer.frame = view.layer.bounds
self.view.layer.insertSublayer(self.backgroundPlayerLayer, at: 0)
NotificationCenter.default.addObserver(self,
selector: #selector(playerItemDidReachEnd(notification:)),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
object: self.backgroundPlayer.currentItem)
And here is the method called each time the video finishes playing:
func playerItemDidReachEnd(notification: Notification) {
self.backgroundPlayer.rate *= -1
}
Thank you if you have any idea. I think it might be because the video is not buffered instantly when I'm changing the rate, but I didn't find what I am looking for in the AVPlayer documentation...

Resources