How to play AVQueuePlayer in loop - ios

I have a couple of videos that need to be played in a loop.
let player = AVQueuePlayer(items: items)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = view.bounds
playerLayer.videoGravity = .resizeAspectFill
view.layer.addSublayer(playerLayer)
player.play()
I am trying to achieve by notification but I am getting the proper update.
NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem, queue: .main) {[weak self] (notification) in
print("Notification: \(notification.object)")
}
Also tried to use PlayerLooper but it's playing the single item in loop.
queuePlayer = AVQueuePlayer(items: items)
playerLooper = AVPlayerLooper(player: queuePlayer!, templateItem: items[0])
let playerLayer = AVPlayerLayer(player: queuePlayer!)
playerLayer.frame = view.bounds
playerLayer.videoGravity = .resizeAspectFill
view.layer.addSublayer(playerLayer)
queuePlayer?.play()

Related

Is there a way to make a video repeat in swift?

I added a video to my viewController() as a background and I want to loop it or in other words make it repeat itself forever.
Here is all the code:
import UIKit
import AVKit
import AVFoundation
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
let player = AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "background", ofType: "mp4")!))
let layer = AVPlayerLayer(player: player)
layer.frame = view.bounds
layer.videoGravity = .resizeAspectFill
layer.repeatCount = .greatestFiniteMagnitude
layer.repeatDuration = .greatestFiniteMagnitude
view.layer.addSublayer(layer)
player.play()
}
}
When I run the app It only plays once and never repeats itself.
Any suggestions? Thanks.
One solution is to observe AVPlayerItemDidPlayToEndTime and then simply restart the video.
NotificationCenter.default.addObserver(
forName: .AVPlayerItemDidPlayToEndTime,
object: player.currentItem,
queue: .main
) { [weak player] _ in
player?.seek(to: .zero)
player?.play()
}
Don't forget to detach this observer whenever it's no longer necessary.
Use an AVPlayerLooper. Its name tells you that this is exactly what it's for.
https://developer.apple.com/documentation/avfoundation/avplayerlooper
A couple comments.
According to AVPlayerLayer doc: Set one or the other. Not both.
If both repeatDuration and repeatCount are specified the behavior is
undefined.
In general, I've struggled with getting AVPlayerLayer to repeat mp4's using the repeatCount or repeatDuration way. Instead I use AVQueuePlayer with a AVPlayerLooper. Credit to: https://developer.apple.com/forums/thread/658909
import AVFoundation
var playerQueue: AVQueuePlayer!
var playerLayer: AVPlayerLayer!
var playerLooper: AVPlayerLooper!
private func configLooper(file: String, ext: String) {
guard let videoURL = Bundle.main.url(forResource: file, withExtension: ext) else { return }
playerQueue = AVQueuePlayer()
playerLayer = AVPlayerLayer(player: playerQueue)
let playerItem = AVPlayerItem(url: videoURL)
playerLooper = AVPlayerLooper(player: playerQueue, templateItem: playerItem)
playerLayer.frame = view.bounds
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
view.layer.insertSublayer(playerLayer, below: someOtherView.layer)
playerQueue.play()
}

Video not play in AVPlayer or safari but play in chrome

I have open AVPlayer to play the video by url link, but video not play in player and also not play in safari but video play on chrome browser.
My code is >>>>>>>>>
if url != nil {
let player = AVPlayer(url: url!)
player.allowsExternalPlayback = true
let playerLayer = AVPlayerLayer(player: player)
playerLayer.videoGravity = .resizeAspectFill
player.play()
}
Try this >>>
let player = AVPlayer(url: url)
player.rate = 1
playerViewController.player = player
playerViewController.view.frame = self.view.frame
playerViewController.showsPlaybackControls = true
addChild(playerViewController)
self.view.addSubview(playerViewController.view)
playerViewController.didMove(toParent: self)
It is work fine .

How to properly show multiple videos in the same avPlayerLayer

What I'm trying to achieve?
I'm showing users a subview with integrated page control. I want to show three different videos on the same subview.
How I implemented avPlayer?
First step:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
avPlayerLayer.frame = (WelcomeView?.video.layer.bounds)!
}
First video aligned perfectly in the middle of my view with this snippet of code:
WelcomeView?.frame.size.width = deviceScreen.frame.size.width
WelcomeView?.frame.size.height = deviceScreen.frame.size.height
WelcomeView?.video.layer.cornerRadius = 15
WelcomeView?.video.clipsToBounds = true
WelcomeView?.gradient.layer.cornerRadius = 25
WelcomeView?.gradient.clipsToBounds = true
WelcomeView?.label.addShadow()
let theURL = Bundle.main.url(forResource:"welcome1", withExtension: "MP4")
avPlayer = AVPlayer(url: theURL!)
avPlayerLayer = AVPlayerLayer(player: avPlayer)
avPlayerLayer.videoGravity = AVLayerVideoGravity.resizeAspect
avPlayer.volume = 0
avPlayer.actionAtItemEnd = .none
NotificationCenter.default.addObserver(self,
selector: #selector(playerItemDidReachEnd(notification:)),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
object: avPlayer.currentItem)
WelcomeView?.video.frame = (WelcomeView?.video.layer.bounds)!
WelcomeView?.video.layer.insertSublayer(self.avPlayerLayer, at: 0)
WelcomeView?.tapNext.addTarget(self, action: #selector(GalleryViewController.nextOutboarding(sender:)))
self.view.addSubview(WelcomeView!)
avPlayer.play()
Second step (when user clicks on the button next and when I'm trying to show different video on the same subview):
#objc func nextOutboarding(sender: UITapGestureRecognizer) {
WelcomeView?.pageControl.currentPage += 1
if WelcomeView?.pageControl.currentPage == 1 {
avPlayer.pause()
avPlayerLayer.removeFromSuperlayer()
let theURL = Bundle.main.url(forResource:"welcome2", withExtension: "MP4")
avPlayer = AVPlayer(url: theURL!)
avPlayerLayer = AVPlayerLayer(player: avPlayer)
avPlayerLayer.videoGravity = AVLayerVideoGravity.resizeAspect
avPlayer.volume = 0
avPlayer.actionAtItemEnd = .none
NotificationCenter.default.addObserver(self,
selector: #selector(playerItemDidReachEnd(notification:)),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
object: avPlayer.currentItem)
WelcomeView?.video.frame = (WelcomeView?.video.layer.bounds)!
WelcomeView?.video.layer.insertSublayer(self.avPlayerLayer, at: 0)
self.view.addSubview(WelcomeView!)
avPlayer.play()
}
}
My problem:
When I'm trying to show second video on the same avPlayerLayer - my video has different alignment (basically not in the middle anymore).
Any tips? Thank you.
First of all, create new AvPlayerItem.
var playerItem:AVPlayerItem?
Then use replaceCurrentItem function.
playerItem = AVPlayerItem(url: theURL!)
avPlayer.replaceCurrentItem(with: playerItem)
That's it. Now you can reuse existing avPlayer.
Instead of adding new subview with AVPlayerLayer every time user clicks next, you should reuse the AVPlayerLayer that you've already added to play the next video. In your code, you're trying to add the WelcomeView again as the subview but it's already is a subview and the previous layer has all the properties that you've set to it and you don't need to repeat that.
#objc func nextOutboarding(sender: UITapGestureRecognizer) {
WelcomeView?.pageControl.currentPage += 1
if WelcomeView?.pageControl.currentPage == 1 {
avPlayer.pause()
let theURL = Bundle.main.url(forResource:"welcome2", withExtension: "MP4")
avPlayer = AVPlayer(url: theURL!)
avPlayerLayer = AVPlayerLayer(player: avPlayer)
avPlayer.play()
}
}

Resize background video - SWIFT

I've created a background video in swift. But by any reason the video is way to small. It's not covering the whole screen like it supposed to.
This is the result at the moment. What I'm trying to do is to cover the whole screen.
private func setupView()
{
let path = URL(fileURLWithPath: Bundle.main.path(forResource: "mydogwalkvideo", ofType: ".mp4")!)
let player = AVPlayer(url: path)
let newLayer = AVPlayerLayer(player: player)
newLayer.frame = self.videoView.frame
self.videoView.layer.addSublayer(newLayer)
newLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill //It's set to resize aspect to fill
player.play()
player.actionAtItemEnd = AVPlayer.ActionAtItemEnd.none
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.videoDidPlayToEnd(notification:)), name: NSNotification.Name(rawValue: "AVPlayerItemDidPlayToEndTimeNotification"), object: player.currentItem)
}
#objc func videoDidPlayToEnd(notification: Notification)
{
let player: AVPlayerItem = notification.object as! AVPlayerItem
player.seek(to: CMTime.zero)
}
Additional information:
The video size is normally 1920x1080
You need to update frame inside the method viewDidLayoutSubviews
override func viewDidLayoutSubviews() {
self.newLayer.frame = self.view.bounds
self.videoView.frame = self.view.bounds
}

Background video loop not working in swift

I have been created page control, and in that the background videos will be get loaded, the videos is getting played but the loop is not working, i had refered in stackoverflow but i couldn't get the right answer
here is mine code:
override func viewDidLoad() {
super.viewDidLoad()
self.pageController.currentPage = 0
loadVideo(currentPage: 0)
}
func loadVideo(currentPage: Int) {
DispatchQueue.main.async {
self.videoPath = Bundle.main.url(forResource: "BoardVideoArray[currentPage]", withExtension: "mp4")
self.player = AVPlayer(url: self.videoPath!)
self.player?.actionAtItemEnd = .none
self.player?.isMuted = true
self.playerLayer = AVPlayerLayer(player: self.player)
self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
self.playerLayer.zPosition = -1
}
playerLayer.frame = videoVIew.frame
videoVIew.layer.addSublayer(playerLayer)
videoVIew.bringSubview(toFront: pageController)
player?.play()
//loop video
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem, queue: nil, using: { (_) in
DispatchQueue.main.async {
self.player?.seek(to: kCMTimeZero)
self.player = AVPlayer(url: self.videoPath!)
self.player.play()
}
})
}
I have declared the player and avplayerLayer out of scope
var player: AVPlayer!
var playerLayer = AVPlayerLayer()
I spend more than 4 hours, but i couldn't found the mistake which i have made.
The issue is video is not geting looping
Try this way.
guard let path = Bundle.main.path(forResource: "video", ofType:"m4v") else {
debugPrint("video.m4v not found")
return
}
//VideoPlayer is a subClass of AVPlayer
let player = VideoPlayer(url: URL(fileURLWithPath: path))
playerViewController = AVPlayerViewController()
playerViewController.videoGravity = AVLayerVideoGravityResizeAspectFill
playerViewController.player = player
//self.videoPlayerView is a UIView in which i want to show video.
playerViewController.view.frame = self.videoPlayerView.bounds
self.videoPlayerView.addSubview(playerViewController.view)
playerViewController.showsPlaybackControls = false
playerViewController.player?.play()
player.shouldMute(mute: true)
NotificationCenter.default.addObserver(self, selector: #selector(HomeViewController.videoDidStopPlaying(notification:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
func videoDidStopPlaying(notification:Notification) {
self.playerViewController.player?.seek(to: kCMTimeZero)
self.playerViewController.player?.play()
}
I wrote a Swift UIView subclass called SDLoopingVideoView that plays and loops any compatible AVPlayer video file and automatically scales it to fill the view. It can be entirely setup in Interface Builder This work great as a video background and you won't have to worry about the video looping since it's managed automatically. You can find it on Github here: SDLoopingVideoView

Resources