Background video loop not working in swift - ios

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

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()
}

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
}

How to play video in fullscreen size in portrait mode?

I want to play some video in splash screen in portrait mode - full screen.
Is that possible? please guide me through a proper solution.
I'm looking for swift solution
create a subclass of UIView, with a AVPlayer and a function createBackground:
import AVFoundation
class BackgroundVideo: UIView {
var player: AVPlayer?
func createBackground(name: String, type: String) { }
}
then your createBackground might be something like:
func createBackground(name: String, type: String) {
guard let path = Bundle.main.path(forResource: name, ofType: type) else { return }
player = AVPlayer(url: URL(fileURLWithPath: path))
player?.actionAtItemEnd = AVPlayerActionAtItemEnd.none;
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.frame
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
self.layer.insertSublayer(playerLayer, at: 0)
player?.seek(to: kCMTimeZero)
player?.play()
}
then you may add more stuff, such as observing notifications:
AVPlayerItemDidPlayToEndTime for implementing a video loop.
UIApplicationWillEnterForeground for controlling the video loop, after resuming from background.
finally you might attach this BackgroundView, wherever you need: fake splash screen, home screen, and so on...
SWIFT 3.0
You can not do that in default splash screen but you can do some workaround to acheive this.
First of all you should take first frame of your video which will be image.
You can do that using photoshop or by any other graphics tool.
Now you can set it to you default splash screen in UIImageView.
Now make a UIViewController and launch that as initial view controller.
And below is the code to play video
private func playFullScreenVideo() {
// drag your video file in project
// check if video file is available,if not return
guard let path = Bundle.main.path(forResource: "video", ofType:"mp4") else {
debugPrint("video.mp4 missing")
return
}
// create instance of videoPlayer with video path
let videoPlayer = AVPlayer(url: URL(fileURLWithPath: path))
// create instance of playerlayer with videoPlayer
let playerLayer = AVPlayerLayer(player: videoPlayer)
// set its videoGravity to AVLayerVideoGravityResizeAspectFill to make it full size
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
// add it to your view
playerLayer.frame = self.view.frame
playerView.layer.addSublayer(playerLayer)
// start playing video
videoPlayer?.play()
}
Let me know if having any trouble in this
Inside didFinishLaunch method of app delegate
let splashScreenVC = viewController(withIdentifier: "identifier", inStoryboard: "storyboard:) as? SplashViewController
window?.rootViewController = splashScreenVC
Inside the view controller SplashViewController, you can play video in viewDidLoad method.
func playVideo() {
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()
}

Cant replay video after finish playing

I use following code to reveal two different video-sources as background. The "selectVideo" (SegmentedControl) is used to select video. The problem is down below.
#IBAction func selectVideo(sender: AnyObject) {
if self.Controller.selectedIndex == 1 {
self.videoBackgroundCustomer()
}
if self.Controller.selectedIndex == 0 {
self.videoBackgroundDriver()
}
}
func videoBackgroundDriver() {
//Load video background.
let videoURL: NSURL = NSBundle.mainBundle().URLForResource("background_video_2", withExtension: "mp4")!
player = AVPlayer(URL: videoURL)
videoBackground()
}
//Video background customer
func videoBackgroundCustomer() {
//Load video background.
let videoURL: NSURL = NSBundle.mainBundle().URLForResource("background_video_1", withExtension: "mp4")!
player = AVPlayer(URL: videoURL)
videoBackground()
}
//Vieobackground-code part 2, provides with less code.
func videoBackground() {
player?.actionAtItemEnd = .None
player?.muted = true
let playerLayer = AVPlayerLayer(player: player)
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
playerLayer.zPosition = -1
playerLayer.frame = view.frame
view.layer.addSublayer(playerLayer)
player?.play()
//call loop video
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(LoginViewController.loopVideo), name: AVPlayerItemDidPlayToEndTimeNotification, object: player!.currentItem)
}
//Loop video
func loopVideo() {
player?.seekToTime(kCMTimeZero)
player?.play()
}
Problem: The video restarts when the last video should have ended. And not when the most recent video ends.
How do I make it repeat playing after the last playing video finished? Thanks
After investigation of the problem firstly take a look at documentation and return values of the method.
- (void)seekToTime:(CMTime)time;
Method that you are using return void and could not be compared to the CMTime
For solving of you're problem try this solution:
First you need to subscribe you're class to the notification that will indicate that video is ended.
NSNotificationCenter.defaultCenter().addObserver(self,selector: "itemDidReachEnd:",
name: AVPlayerItemDidPlayToEndTimeNotification,
object: player.currentItem)
And than define method to handle this notification.
func itemDidReachEnd(notification: NSNotification) {
player.seekToTime(kCMTimeZero)
player.play()
}
In this case you are tracking when video ends and you start it again.

Resources