snukumas
snukumas

Reputation: 75

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

Upvotes: 5

Views: 12382

Answers (4)

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

Upvotes: 0

mikejcooper
mikejcooper

Reputation: 41

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

Upvotes: 4

Reid
Reid

Reputation: 734

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)

Upvotes: 11

Mayank Patel
Mayank Patel

Reputation: 3908

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

Upvotes: 8

Related Questions