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

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

Related

Autoplaying an MP4 video in a UIView that is on loop like a GIF?

Once users go to this specific page, I want the video to immediately start playing inside of a UIView with no controls. It will just loop like a gif. This is code I found off of github and it doesn't seem to work. It just shows the white UIView when I pull up the page. My file name is correct as well.
import Foundation
import UIKit
import AVKit
import AVFoundation
class RampWallPush: UIViewController {
#IBOutlet weak var videoView: UIView!
var player: AVPlayer?
override func viewDidLoad() {
super.viewDidLoad()
// Load video resource
if let videoUrl = Bundle.main.url(forResource: "small", withExtension: "mp4") {
// Init video
self.player = AVPlayer(url: videoUrl)
self.player?.isMuted = true
self.player?.actionAtItemEnd = .none
// Add player layer
let playerLayer = AVPlayerLayer(player: player)
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
playerLayer.frame = view.frame
// Add video layer
self.videoView.layer.addSublayer(playerLayer)
// Play video
self.player?.play()
// Observe end
NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidReachEnd), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem)
}
}
// MARK: - Loop video when ended.
#objc func playerItemDidReachEnd(notification: NSNotification) {
self.player?.seek(to: CMTime.zero)
self.player?.play()
}
}
Your code is works you need to verify the file exists in your project and .......
// Load video resource
if let videoUrl = Bundle.main.url(forResource: "sample", withExtension: "mp4") {
// exists
}
else {
print("NoFile")
}
So select the file and
You need to change the frame
playerLayer.frame = CGRect(x:0,y:0,width:200,height:300)
self.videoView.layer.addSublayer(playerLayer)
playerLayer.center = videoView.center
Created a demo Here

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

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

play video in background swift 3

hello im trying to play video in background but its give an error and the error is " unexpectedly found nil while unwrapping an Optional value " and im already set the video in Bundle and select copy if need
this the my code
import UIKit
import AVFoundation
class ViewController: UIViewController {
var avPlayer: AVPlayer!
var avPlayerLayer: AVPlayerLayer!
var paused: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
let url = Bundle.main.url(forResource: "Grad_Cap_Toss", withExtension: "mp4")
avPlayer = AVPlayer(url: url!)
avPlayerLayer = AVPlayerLayer(player: avPlayer)
avPlayerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
avPlayer.volume = 0
avPlayer.actionAtItemEnd = AVPlayerActionAtItemEnd.none
avPlayerLayer.frame = view.layer.bounds
view.backgroundColor = UIColor.clear;
view.layer.insertSublayer(avPlayerLayer, at: 0)
NotificationCenter.default.addObserver(self,selector: Selector(("playerItemDidReachEnd:")),name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,object: avPlayer.currentItem)
}
please any body have ideas for that , thank you
First, drag and drop video file in your project.
Then import AVFundation.
Add example: var player: AVPlayer?
#IBOutlet weak var videoView: UIView! - Connect view from storyboard to your VC class.
After that, you paste this code into your ViewController.
call "playBackgoundVideo()" in your viewDidLoad()
private func playBackgoundVideo() {
if let filePath = Bundle.main.path(forResource: "your_video_here", ofType:"mp4") {
let filePathUrl = NSURL.fileURL(withPath: filePath)
player = AVPlayer(url: filePathUrl)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.videoView.bounds
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem, queue: nil) { (_) in
self.player?.seek(to: kCMTimeZero)
self.player?.play()
}
self.videoView.layer.addSublayer(playerLayer)
player?.play()
}
}
This is swift 4.0
it works ^__^
I had a similar problem and followed the instructions from Rick's answer to resolve it.
My problem was that I didn't have the video file in the bundle resources.
Check if your video file is listed in Copy Bundle Resources under Build Phases in the project. If not , use the + button to add the file in there.
Also you might be missing the avPlayer.play() statement in your code.
I was also trying to play a video in the background of my login page's view controller. I think the main issue is that the syntax you're using is deprecated.
Your code might look like the following:
import UIKit
import AVKit
import AVFoundation
class ViewController: UIViewController {
#IBOutlet var blackOverlay: UIView!
// init video background and its path
var player: AVPlayer?
let videoURL: NSURL = Bundle.main.url(forResource: "videoName", withExtension: "mp4")! as NSURL
override func viewDidLoad() {
super.viewDidLoad()
// Adds a black overlay to the looped video in question
blackOverlay.alpha = 0.75;
blackOverlay.layer.zPosition = 0;
// begin implementing the avplayer
player = AVPlayer(url: videoURL as URL)
player?.actionAtItemEnd = .none
player?.isMuted = true
let playerLayer = AVPlayerLayer(player: player)
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
playerLayer.zPosition = -1
playerLayer.frame = view.frame
view.layer.addSublayer(playerLayer)
player?.play()
// add observer to watch for video end in order to loop video
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.loopVideo), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: self.player)
// if video ends, will restart
func playerItemDidReachEnd() {
player!.seek(to: kCMTimeZero)
}
}
// maybe add this loop at the end, after viewDidLoad
// func loopVideo() { player?.seek(to: kCMTimeZero) player?.play()}}
Please let me know if this helps!
Drag and drop video file in your project.
Then import AVFundation
List item
#IBOutlet weak var videoView: UIView!, #IBOutlet weak var label: UILabel!,#IBOutlet weak var image: UIImageView!,#IBOutlet weak var loginBtn: UIButton!
Connect view from storyboard to your VC class.
And then paste the given code in your ViewController and call playVideo() function in viewDidLoad().
func playVideo(){
guard let path = Bundle.main.path(forResource: "intro", ofType: "mp4") else {
return
}
let player = AVPlayer(url: URL(fileURLWithPath: path))
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.view.bounds
playerLayer.videoGravity = .resizeAspectFill
self.videoLayer.layer.addSublayer(playerLayer)
player.play()
videoLayer.bringSubviewToFront(image)
videoLayer.bringSubviewToFront(label)
videoLayer.bringSubviewToFront(loginBtn)
videoLayer.bringSubviewToFront(signupBtn)
}
If url is nil and the video is being successfully copied into the bundle, the likely problem is case sensitivity. Make sure the filename is using the correct case. The iOS device filesystem is case sensitive, where as the iPhone simulator is not.
I had a same problem when I tried to play video background. I think it is the problem of video resource.
I resolved the problem like this way.
When you drag your video, you should check both of 'Copy items if needed' and 'Add to targets'.
Or you can just right click on the project navigator, click 'Add' to add resource.

How do i make a looping video in swift

How would i go about making a local .mp4 file with no sound play on a loop, so it would only take up part of the screen and have no user controls. Just a looping video, sort of like a gif. I am using xcode, swift2.
class ViewController: UIViewController {
var playerViewController = AVPlayerViewController()
var playerView = AVPlayer()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewDidAppear(animated: Bool) {
var fileURL = NSURL(fileURLWithPath: "/Users/Mantas/Desktop/123/123/video-1453562323.mp4.mp4")
playerView = AVPlayer(URL: fileURL)
playerViewController.player = playerView
self.presentViewController(playerViewController, animated: true){
self.playerViewController.player?.play()
}
}
}
I have made this, it plays the video, but in full screen, i dont know how to make it only take up part of the screen and how to make it loop
Alternate version in Swift 3.0:
Add these properties:
fileprivate var player: AVPlayer? {
didSet { player?.play() }
}
fileprivate var playerObserver: Any?
Add this to your deinit:
deinit {
guard let observer = playerObserver else { return }
NotificationCenter.default.removeObserver(observer)
}
Add this function:
func videoPlayerLayer() -> AVPlayerLayer {
let fileURL = URL(fileURLWithPath: mediaPath)
let player = AVPlayer(url: fileURL)
let resetPlayer = {
player.seek(to: kCMTimeZero)
player.play()
}
playerObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem, queue: nil) { notification in
resetPlayer()
}
self.player = player
return AVPlayerLayer(player: player)
}
Then add to your layer wherever you feel like (viewDidLoad, viewDidAppear, viewDidFinishLayingOutSubviews):
let playerLayer = videoPlayerLayer()
playerLayer.frame = view.bounds
view.layer.insertSublayer(playerLayer, at: 0)
Adding observer when video going to finish you can make replay the video
override func viewDidAppear(animated: Bool) {
super.viewDidAppear()
var fileURL = NSURL(fileURLWithPath: "/Users/Mantas/Desktop/123/123/video-1453562323.mp4.mp4")
playerView = AVPlayer(URL: fileURL)
NSNotificationCenter.defaultCenter().addObserver(self,
selector: "playerItemDidReachEnd:",
name: AVPlayerItemDidPlayToEndTimeNotification,
object: self.playerView.currentItem) // Add observer
playerViewController.player = playerView
//amend the frame of the view
self.playerViewController.player.frame = CGRectMake(0, 0, 200, 200)
//reset the layer's frame, and re-add it to the view
var playerLayer: AVPlayerLayer = AVPlayerLayer.playerLayerWithPlayer(self.playerView)
playerLayer.frame = videoHolderView.bounds
videoHolderView.layer.addSublayer(playerLayer)
/* Full Screen
self.presentViewController(playerViewController, animated: true){
self.playerViewController.player?.play()
} */
}
func playerItemDidReachEnd(notification: NSNotification) {
self.playerView.seekToTime(kCMTimeZero)
self.playerView.play()
}
For a seemless repeating video without a black flash. Use the AVPlayerLooper like so:
private var player: AVQueuePlayer!
private var playerLayer: AVPlayerLayer!
private var playerItem: AVPlayerItem!
private var playerLooper: AVPlayerLooper!
override func viewDidLoad(){
super.viewDidLoad()
let path = Bundle.main.path(forResource: "background_cloudy", ofType: "mov")
let pathURL = URL(fileURLWithPath: path!)
let duration = Int64( ( (Float64(CMTimeGetSeconds(AVAsset(url: pathURL).duration)) * 10.0) - 1) / 10.0 )
player = AVQueuePlayer()
playerLayer = AVPlayerLayer(player: player)
playerItem = AVPlayerItem(url: pathURL)
playerLooper = AVPlayerLooper(player: player, templateItem: playerItem,
timeRange: CMTimeRange(start: kCMTimeZero, end: CMTimeMake(duration, 1)) )
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
playerLayer.frame = view.layer.bounds
view.layer.insertSublayer(playerLayer, at: 1)
}
This is tested with Swift 5, I found in https://gist.github.com/lanserxt/33fd8c479185cba181497315299e0e31
import UIKit
import AVFoundation
class LoopedVideoPlayerView: UIView {
fileprivate var videoURL: URL?
fileprivate var queuePlayer: AVQueuePlayer?
fileprivate var playerLayer: AVPlayerLayer?
fileprivate var playbackLooper: AVPlayerLooper?
func prepareVideo(_ videoURL: URL) {
let playerItem = AVPlayerItem(url: videoURL)
self.queuePlayer = AVQueuePlayer(playerItem: playerItem)
self.playerLayer = AVPlayerLayer(player: self.queuePlayer)
guard let playerLayer = self.playerLayer else {return}
guard let queuePlayer = self.queuePlayer else {return}
self.playbackLooper = AVPlayerLooper.init(player: queuePlayer, templateItem: playerItem)
playerLayer.videoGravity = .resizeAspectFill
playerLayer.frame = self.frame
self.layer.addSublayer(playerLayer)
}
func play() {
self.queuePlayer?.play()
}
func pause() {
self.queuePlayer?.pause()
}
func stop() {
self.queuePlayer?.pause()
self.queuePlayer?.seek(to: CMTime.init(seconds: 0, preferredTimescale: 1))
}
func unload() {
self.playerLayer?.removeFromSuperlayer()
self.playerLayer = nil
self.queuePlayer = nil
self.playbackLooper = nil
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
self.playerLayer?.frame = self.bounds
}
}

Resources