152

Is there a relatively easy way of looping a video in AVFoundation?

I've created my AVPlayer and AVPlayerLayer like so:

avPlayer = [[AVPlayer playerWithURL:videoUrl] retain];
avPlayerLayer = [[AVPlayerLayer playerLayerWithPlayer:avPlayer] retain];

avPlayerLayer.frame = contentView.layer.bounds;
[contentView.layer addSublayer: avPlayerLayer];

and then I play my video with:

[avPlayer play];

The video plays fine but stops at the end. With the MPMoviePlayerController all you have to do is set its repeatMode property to the right value. There doesn't appear to be a similar property on AVPlayer. There also doesn't seem to be a callback that will tell me when the movie has finished so I can seek to the beginning and play it again.

I'm not using MPMoviePlayerController because it has some serious limitations. I want to be able to play back multiple video streams at once.

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
orj
  • 13,234
  • 14
  • 63
  • 73
  • 1
    See this answer for a link to actual working code: http://stackoverflow.com/questions/7822808/smooth-video-looping-in-ios#answer-17183962 – MoDJ Aug 19 '13 at 17:39

22 Answers22

287

You can get a Notification when the player ends. Check AVPlayerItemDidPlayToEndTimeNotification

When setting up the player:

ObjC

  avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone; 

  [[NSNotificationCenter defaultCenter] addObserver:self
                                           selector:@selector(playerItemDidReachEnd:)
                                               name:AVPlayerItemDidPlayToEndTimeNotification
                                             object:[avPlayer currentItem]];

this will prevent the player to pause at the end.

in the notification:

- (void)playerItemDidReachEnd:(NSNotification *)notification {
    AVPlayerItem *p = [notification object];
    [p seekToTime:kCMTimeZero];
}

this will rewind the movie.

Don't forget un unregister the notification when releasing the player.

Swift

avPlayer?.actionAtItemEnd = .none

NotificationCenter.default.addObserver(self,
                                       selector: #selector(playerItemDidReachEnd(notification:)),
                                       name: .AVPlayerItemDidPlayToEndTime,
                                       object: avPlayer?.currentItem)

@objc func playerItemDidReachEnd(notification: Notification) {
    if let playerItem = notification.object as? AVPlayerItem {
        playerItem.seek(to: kCMTimeZero)
    }
}

Swift 4+

@objc func playerItemDidReachEnd(notification: Notification) {
    if let playerItem = notification.object as? AVPlayerItem {
        playerItem.seek(to: CMTime.zero, completionHandler: nil)
    }
}
excitedmicrobe
  • 2,338
  • 1
  • 14
  • 30
Bastian
  • 10,403
  • 1
  • 31
  • 40
  • 6
    ...and if you want to play it right after [p seekToTime:kCMTimeZero] (a "rewind" of sorts), simply do [p play] again. – thomax Dec 28 '11 at 11:31
  • 26
    this should not be necessary... if you do `avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;` it will not stop, so no need to set it to play again – Bastian Dec 31 '11 at 14:44
  • To make the sound play again you have to call `[player play];` after rewinding. – Kenny Winker Nov 15 '12 at 02:34
  • 13
    This solution works, but it is not completely seamless. I have a very small pause. Am I doing something wrong? – Joris van Liempd iDeveloper Dec 21 '12 at 11:20
  • http://stackoverflow.com/questions/3410003/iphone-smooth-transition-from-one-video-to-another/17138580#17138580 – MoDJ Jun 18 '13 at 01:09
  • Has anyone had issues with this solution over AirPlay? I've noticed that this works fine when I'm on the phone, but the AirPlay connection seems to die before my AVPlayerItemDidPlayToEndTimeNotification notification. Perhaps this is just iOS 8, I don't recall this being an issue before... – tyler Oct 12 '14 at 05:55
  • This answer seems awesome, but I found my `currentItem` will be released after playing. Thus, my `AVPlayer` can not play the item again even I do `[p seekToTime:kCMTimeZero];` . How do I retain my `player.currentItem` ? – Brian Mar 31 '15 at 17:48
  • you could store it in a strong property in your view? – Bastian Apr 02 '15 at 10:01
  • @Bastian Does your example code unregister the notification or is there something else we should be doing? – Praxiteles Jun 23 '15 at 04:26
  • 3
    @Praxiteles you need to unregister it when the view ist destroyed, or when you remove the videoplayer or whatever you do ,) You can use `[[NSNotificationCenter defaultCenter] removeObserver:self];` for example, when `self` is listening to the notifications. – Bastian Jun 24 '15 at 16:03
  • @Bastian That helps. What's the best approach to verify that the notification has indeed been unregistered? Can we count the number of outstanding NSNotification items, for example? – Praxiteles Jul 14 '15 at 10:24
  • you just have to unregister the notification when you are going to remove your object, because the app will crash if a notification is sent to an destroyed object. you can do it when the view is removed, or when you stop the movie for example. you don't need to check if it was really has been unregistered. http://stackoverflow.com/questions/6469209/objective-c-where-to-remove-observer-for-nsnotification – Bastian Jul 14 '15 at 15:57
  • Definitely use `avPlayer.actionAtItemEnd = .none` against replaying the video everytime. It's less CPU consuming. – yuji Jun 10 '19 at 09:40
  • How come in 2021 there is no simple property loop in AVPlayer? Apple, please write these observers yourself and expose a simple property to the developers. – user-123 Nov 13 '21 at 11:51
  • @JorisvanLiempdiDeveloper hey did you find a way to fix the small pause issue that you are facing? I am facing similar issue, any help with regards to this is really appreciated. – Ananth Kamath Feb 22 '22 at 14:26
83

In iOS / tvOS 10, there's a new AVPlayerLooper() that you can use to create seamless looping of video (Swift):

player = AVQueuePlayer()
playerLayer = AVPlayerLayer(player: player)
playerItem = AVPlayerItem(url: videoURL)
playerLooper = AVPlayerLooper(player: player, templateItem: playerItem)
player.play()    

This was presented at WWDC 2016 in "Advances in AVFoundation Playback": https://developer.apple.com/videos/play/wwdc2016/503/

Even using this code, I had a hiccup until I filed a bug report with Apple and got this response:

The movie file having movie duration longer than audio/video tracks is the problem. FigPlayer_File is disabling gapless transition because audio track edit is shorter than the movie duration (15.682 vs 15.787).

You need to either fix the movie files to have the movie duration and track durations to be same length or you can use the time range parameter of AVPlayerLooper (set time range from 0 to duration of audio track)

It turns out that Premiere had been exporting files with an audio track of a slightly different length than the video. In my case it was fine to remove the audio entirely, and that fixed the problem.

Nabha Cosley
  • 1,118
  • 10
  • 18
  • 4
    Nothing else worked for me. I am using an AVPlayerLooper and had this bug and fixing the discrepancy between video/audio lengths solved the issue. – Kevin Heap Oct 06 '17 at 15:56
  • 1
    Thank you for that information about Premiere. I added a timeRange to the looper and that fixed my "flashing video" issue. – Alexander Flenniken Aug 29 '18 at 08:31
  • 1
    @Nabha is it possible to use this for a certain time frame within the video? For example the video is 60 secs but I want to loop the first 10 secs only – Lance Samaria Jun 17 '20 at 19:10
  • @LanceSamaria Looks like it! The init accepts a range of time to loop through, see: https://developer.apple.com/documentation/avfoundation/avplayerlooper/1643626-init – Nabha Cosley Aug 12 '20 at 21:53
  • @Nabha thanks for the help, much appreciated. Cheers! – Lance Samaria Aug 12 '20 at 21:57
28

In Swift:

You can get a Notification when the player ends... check AVPlayerItemDidPlayToEndTimeNotification

when setting up the player:

avPlayer.actionAtItemEnd = AVPlayerActionAtItemEnd.None

NSNotificationCenter.defaultCenter().addObserver(self, 
                                                 selector: "playerItemDidReachEnd:", 
                                                 name: AVPlayerItemDidPlayToEndTimeNotification, 
                                                 object: avPlayer.currentItem)

this will prevent the player to pause at the end.

in the notification:

func playerItemDidReachEnd(notification: NSNotification) {
    if let playerItem: AVPlayerItem = notification.object as? AVPlayerItem {
        playerItem.seekToTime(kCMTimeZero)
    }
}

Swift3

NotificationCenter.default.addObserver(self,
    selector: #selector(PlaylistViewController.playerItemDidReachEnd),
     name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
     object: avPlayer?.currentItem)

this will rewind the movie.

Do not forget to unregister the notification when releasing the player.

King-Wizard
  • 15,628
  • 6
  • 82
  • 76
  • 5
    I'm seeing a small hiccup between loops with this method. I opened my video in Adobe Premier and verified there are no duplicate frames in the video, so the brief hiccup is definitely at playback. Has anybody found a way to make a video loop seamlessly in swift? – SpaceManGalaxy Oct 11 '15 at 22:45
  • @SpaceManGalaxy I’ve also noticed the hiccup. Have you found a way to fix this glitch? – Lance Samaria Jan 31 '18 at 06:49
21

Here's what I ended up doing to prevent the pause-hiccup issue:

Swift:

NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime,
                                       object: nil,
                                       queue: nil) { [weak self] note in
                                        self?.avPlayer.seek(to: kCMTimeZero)
                                        self?.avPlayer.play()
}

Objective C:

__weak typeof(self) weakSelf = self; // prevent memory cycle
NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter];
[noteCenter addObserverForName:AVPlayerItemDidPlayToEndTimeNotification
                        object:nil
                         queue:nil
                    usingBlock:^(NSNotification *note) {
                        [weakSelf.avPlayer seekToTime:kCMTimeZero];
                        [weakSelf.avPlayer play];
                    }];

NOTE: I didn't use avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone as it's not needed.

Islam
  • 3,654
  • 3
  • 30
  • 40
  • 2
    @KostiaDombrovsky did you try on an actual device or different videos? – Islam Apr 08 '15 at 07:20
  • @IslamQ. I record an MP4 file and then try to play it in a loop kinda like snapchat does. – Kostia Dombrovsky Apr 08 '15 at 07:22
  • @KostiaDombrovsky did you compare your playback with snapchat side-by-side? I think because the beginning and the ending frames don't match it seems as if it was paused, but it never pauses. – Islam Apr 08 '15 at 09:25
  • Didn't work for me either. I have a 6 second video with ceaseless audio and I keep hearing a split second of silence with this method – Cbas Jan 24 '16 at 03:05
  • I am seeing a memory leak when using this approach. It has to do with the `[weakSelf.avPlayer seekToTime:kCMTimeZero]; [weakSelf.avPlayer play];` lines - when I comment out these lines there is not longer a memory leak. I've profiled this in instruments. – Solsma Dev Nov 16 '16 at 16:39
10

Swift 5:

I've made some slight adjustments from previous answers like adding the playerItem to the queue before add it to the playerLayer.

let playerItem = AVPlayerItem(url: url)
let player = AVQueuePlayer(playerItem: playerItem)
let playerLayer = AVPlayerLayer(player: player)

playerLooper = AVPlayerLooper(player: player, templateItem: playerItem)

playerLayer.frame = cell.eventImage.bounds
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill

// Add the playerLayer to a UIView.layer

player.play()

And make playerLooper a property of your UIViewController, otherwise the video may only play once.

NSCoder
  • 1,594
  • 5
  • 25
  • 47
4

SWIFT 5:

private var player: AVPlayer?

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self,
                                           selector: #selector(restartVideo),
                                           name: .AVPlayerItemDidPlayToEndTime,
                                           object: self.player?.currentItem)
}

@objc func restartVideo() {
    player?.pause()
    player?.currentItem?.seek(to: CMTime.zero, completionHandler: { _ in
        self.player?.play()
    })
}
Nicolai Harbo
  • 1,064
  • 12
  • 25
3

I recommend using AVQueuePlayer to loop your videos seamlessly. Add the notification observer

AVPlayerItemDidPlayToEndTimeNotification

and in its selector, loop your video

AVPlayerItem *video = [[AVPlayerItem alloc] initWithURL:videoURL];
[self.player insertItem:video afterItem:nil];
[self.player play];
kevinnguy
  • 454
  • 4
  • 13
  • I tried this and it does not show any improvement over the method @Bastian suggested. Did you manage to totally remove the hiccup with this ? – amadour Sep 29 '14 at 19:32
  • 2
    @amadour what you can do is add 2 of the same videos in the AVQueuePlayer player when initialized and when the player posts the AVPlayerItemDidPlayToEndTimeNotification, add the same video to the player's queue. – kevinnguy Sep 30 '14 at 04:43
3

To avoid the gap when the video is rewound, using multiple copies of the same asset in a composition worked well for me. I found it here: www.developers-life.com/avplayer-looping-video-without-hiccupdelays.html (link now dead).

AVURLAsset *tAsset = [AVURLAsset assetWithURL:tURL];
CMTimeRange tEditRange = CMTimeRangeMake(CMTimeMake(0, 1), CMTimeMake(tAsset.duration.value, tAsset.duration.timescale));
AVMutableComposition *tComposition = [[[AVMutableComposition alloc] init] autorelease];
for (int i = 0; i < 100; i++) { // Insert some copies.
    [tComposition insertTimeRange:tEditRange ofAsset:tAsset atTime:tComposition.duration error:nil];
}
AVPlayerItem *tAVPlayerItem = [[AVPlayerItem alloc] initWithAsset:tComposition];
AVPlayer *tAVPlayer = [[AVPlayer alloc] initWithPlayerItem:tAVPlayerItem];
Matt
  • 74,352
  • 26
  • 153
  • 180
user2581875
  • 129
  • 1
  • 5
  • I guess you mean this link http://devbrief.blogspot.se/2011/12/avplayer-looping-video-without.html?m=1 – flame3 Aug 31 '16 at 09:35
1

this worked for me without hiccup issues, point is in pausing the player before calling seekToTime method:

  1. init AVPlayer

    let url = NSBundle.mainBundle().URLForResource("loop", withExtension: "mp4")
    let playerItem = AVPlayerItem(URL: url!)
    
    self.backgroundPlayer = AVPlayer(playerItem: playerItem)
    let playerLayer = AVPlayerLayer(player: self.backgroundPlayer)
    
    playerLayer.frame = CGRectMake(0, 0, UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height)
    self.layer.addSublayer(playerLayer)
    self.backgroundPlayer!.actionAtItemEnd = .None
    self.backgroundPlayer!.play()
    
  2. registering notification

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "videoLoop", name: AVPlayerItemDidPlayToEndTimeNotification, object: self.backgroundPlayer!.currentItem)
    
  3. videoLoop function

    func videoLoop() {
      self.backgroundPlayer?.pause()
      self.backgroundPlayer?.currentItem?.seekToTime(kCMTimeZero)
      self.backgroundPlayer?.play()
    }
    
Vojta
  • 810
  • 10
  • 16
1

my solution in objective-c wth AVQueuePlayer - it seems you have to duplicate the AVPlayerItem and upon finishing playback of first element instantly add another copy. "Kind of" makes sense and works for me without any hiccup

NSURL *videoLoopUrl; 
// as [[NSBundle mainBundle] URLForResource:@"assets/yourVideo" withExtension:@"mp4"]];
AVQueuePlayer *_loopVideoPlayer;

+(void) nextVideoInstance:(NSNotification*)notif
{
 AVPlayerItem *currItem = [AVPlayerItem playerItemWithURL: videoLoopUrl];

[[NSNotificationCenter defaultCenter] addObserver:self
                                      selector:@selector(nextVideoInstance:)
                                      name:AVPlayerItemDidPlayToEndTimeNotification
                                      object: currItem];

 [_loopVideoPlayer insertItem:currItem afterItem:nil];
 [_loopVideoPlayer advanceToNextItem];

}

+(void) initVideoPlayer {
 videoCopy1 = [AVPlayerItem playerItemWithURL: videoLoopUrl];
 videoCopy2 = [AVPlayerItem playerItemWithURL: videoLoopUrl];
 NSArray <AVPlayerItem *> *dummyArray = [NSArray arrayWithObjects: videoCopy1, videoCopy2, nil];
 _loopVideoPlayer = [AVQueuePlayer queuePlayerWithItems: dummyArray];

 [[NSNotificationCenter defaultCenter] addObserver: self
                                      selector: @selector(nextVideoInstance:)
                                      name: AVPlayerItemDidPlayToEndTimeNotification
                                      object: videoCopy1];

 [[NSNotificationCenter defaultCenter] addObserver: self
                                      selector: @selector(nextVideoInstance:)
                                      name: AVPlayerItemDidPlayToEndTimeNotification
                                      object: videoCopy2];
}

https://gist.github.com/neonm3/06c3b5c911fdd3ca7c7800dccf7202ad

neon M3
  • 11
  • 3
1

For Swift 3 & 4

NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.avPlayer?.currentItem, queue: .main) { _ in
     self.avPlayer?.seek(to: kCMTimeZero)
     self.avPlayer?.play()
}
vp2698
  • 1,783
  • 26
  • 28
1

I couldn't find my solution among answers. It can be helpful to observe boundary time specified to asset duration. When observer is triggered, seek to the start and replay.

player?.addBoundaryTimeObserver(forTimes: [NSValue(time: asset.duration)], queue: .main) { [weak self] in
    self?.player?.seek(to: .zero, completionHandler: { [weak self] _ in
        self?.player?.play()
    })
}
Rashid
  • 1,515
  • 16
  • 16
0

After loading the video into the AVPlayer (via its AVPlayerItem, of course):

 [self addDidPlayToEndTimeNotificationForPlayerItem:item];

The addDidPlayToEndTimeNotificationForPlayerItem method:

- (void)addDidPlayToEndTimeNotificationForPlayerItem:(AVPlayerItem *)item
{
    if (_notificationToken)
        _notificationToken = nil;

    /*
     Setting actionAtItemEnd to None prevents the movie from getting paused at item end. A very simplistic, and not gapless, looped playback.
     */
    _player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
    _notificationToken = [[NSNotificationCenter defaultCenter] addObserverForName:AVPlayerItemDidPlayToEndTimeNotification object:item queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
        // Simple item playback rewind.
        [[_player currentItem] seekToTime:kCMTimeZero];
    }];
}

In your viewWillDisappear method:

if (_notificationToken) {
        [[NSNotificationCenter defaultCenter] removeObserver:_notificationToken name:AVPlayerItemDidPlayToEndTimeNotification object:_player.currentItem];
        _notificationToken = nil;
    }

In your view controller's interface declaration within the implementation file:

id _notificationToken;

Need to see this up-and-running before you try? Download and run this sample app:

https://developer.apple.com/library/prerelease/ios/samplecode/AVBasicVideoOutput/Listings/AVBasicVideoOutput_APLViewController_m.html#//apple_ref/doc/uid/DTS40013109-AVBasicVideoOutput_APLViewController_m-DontLinkElementID_8

In my app, which uses this very code, there is no pause whatsoever between the end of the video and the beginning. In fact, depending on the video, there's no way for me to tell the video is at the beginning again, save the timecode display.

James Bush
  • 1,485
  • 14
  • 19
0

you can add a AVPlayerItemDidPlayToEndTimeNotification observer and replay video from start in selector, code like below

 //add observer
[[NSNotificationCenter defaultCenter] addObserver:self                                                 selector:@selector(playbackFinished:)                                                     name:AVPlayerItemDidPlayToEndTimeNotification
object:_aniPlayer.currentItem];

-(void)playbackFinished:(NSNotification *)notification{
    [_aniPlayer seekToTime:CMTimeMake(0, 1)];//replay from start
    [_aniPlayer play];
}
shujucn
  • 145
  • 1
  • 5
0

What I did is to make it loop playing, like my code below:

[player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0)
queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
    float current = CMTimeGetSeconds(time);
    float total = CMTimeGetSeconds([playerItem duration]);
    if (current >= total) {
        [[self.player currentItem] seekToTime:kCMTimeZero];
        [self.player play];
    }
}];
Sid Singh
  • 53
  • 7
Johnny
  • 1,112
  • 1
  • 13
  • 21
0

The following is working for me in WKWebView in swift 4.1 The main part of the WKWebView in WKwebviewConfiguration

wkwebView.navigationDelegate = self
wkwebView.allowsBackForwardNavigationGestures = true
self.wkwebView =  WKWebView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height))
let config = WKWebViewConfiguration()
config.allowsInlineMediaPlayback = true
wkwebView = WKWebView(frame: wkwebView.frame, configuration: config)
self.view.addSubview(wkwebView)
self.wkwebView.load(NSURLRequest(url: URL(string: self.getUrl())!) as URLRequest)
Nrv
  • 280
  • 1
  • 6
0

Swift 4.2 in Xcode 10.1.

Yes, there is a relatively easy way of looping a video in AVKit/AVFoundation using AVQueuePlayer(), Key-Value Observation (KVO) technique and a token for it.

This definitely works for a bunch of H.264/HEVC videos with a minimal burden for CPU.

Here's a code:

import UIKit
import AVFoundation
import AVKit

class ViewController: UIViewController {

    private let player = AVQueuePlayer()
    let clips = ["01", "02", "03", "04", "05", "06", "07"]
    private var token: NSKeyValueObservation?
    var avPlayerView = AVPlayerViewController()

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(true)

        self.addAllVideosToPlayer()
        present(avPlayerView, animated: true, completion: { self.player.play() })
    }

    func addAllVideosToPlayer() {
        avPlayerView.player = player

        for clip in clips {
            let urlPath = Bundle.main.path(forResource: clip, ofType: "m4v")!
            let url = URL(fileURLWithPath: urlPath)
            let playerItem = AVPlayerItem(url: url)
            player.insert(playerItem, after: player.items().last)

            token = player.observe(\.currentItem) { [weak self] player, _ in
                if self!.player.items().count == 1 { self?.addAllVideosToPlayer() }
            }
        }
    }
}
Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
0

Swift 5

import UIKit
import AVKit
import AVFoundation

class VideoViewControler: UIViewController {
    
    // init video background and its path
    var player: AVPlayer?
    let videoURL: NSURL = Bundle.main.url(forResource: "farmer_watering", withExtension: "mp4")! as NSURL
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        
        // begin implementing the avplayer
        player = AVPlayer(url: videoURL as URL)
        player?.actionAtItemEnd = .none
        player?.isMuted = true
        
        let playerLayer = AVPlayerLayer(player: player)
        playerLayer.videoGravity = AVLayerVideoGravity.resizeAspect
        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(loopVideo),
            name: .AVPlayerItemDidPlayToEndTime,
            object: self.player?.currentItem
        )
    }
    
    // if video ends, will restart
    func playerItemDidReachEnd() {
        player?.seek(to: CMTime.zero)
    }
    
    // add this loop at the end, after viewDidLoad
    @objc func loopVideo() {
        playerItemDidReachEnd()
        player?.play()
    }
}
Hamid Zandi
  • 2,714
  • 24
  • 32
0

SwiftUI example:

//  VideoLooper.swift

import SwiftUI
import AVKit

struct VideoLooper: UIViewRepresentable {
    private let player: AVQueuePlayer
    private let videoURL: URL
    
    init(resourceInBundle name: String, ofType type: String = "mp4") {
        self.init(url: URL(fileURLWithPath: Bundle.main.path(forResource: name, ofType: type)!))
    }
    
    init(url: URL) {
        self.videoURL = url
        self.player = AVQueuePlayer()
    }

    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<VideoLooper>) {
    }

    func makeUIView(context: Context) -> UIView {
       
        return PlayerUIView(player: player, videoURL: videoURL)
    }

}

class PlayerUIView: UIView {
    private let playerLayer: AVPlayerLayer
    private let playerItem: AVPlayerItem
    private let playerLooper: AVPlayerLooper

    init(player: AVQueuePlayer, videoURL: URL) {
        playerLayer = AVPlayerLayer(player: player)
        playerItem = AVPlayerItem(url: videoURL)
        playerLooper = AVPlayerLooper(player: player, templateItem: playerItem)
        super.init(frame: .zero)
      
        playerLayer.player = player
        layer.addSublayer(playerLayer)
        player.play()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        playerLayer.frame = bounds
    }
}

Usage:

// Video-Resource in Bundle
VideoLooper(resourceInBundle: "moviename", ofType: "mp4")

// Video-Resource from URL
VideoLooper(url: URL(string: "https://my-video-server.local/christmas.mp4")!)
Peter Kreinz
  • 7,979
  • 1
  • 64
  • 49
0

For 2023.

If specifically using an AVPlayerViewController.

Change this line player = AVPlayer(url: u) to these two lines:

// player = AVPlayer(url: u)
player = AVQueuePlayer()
loopy = AVPlayerLooper(player: player as! AVQueuePlayer,
                       templateItem: AVPlayerItem(url: u))

Add

var loopy: AVPlayerLooper?

to the vc. That's it, you're done.

Fattie
  • 27,874
  • 70
  • 431
  • 719
-1

use AVPlayerViewController below code, its working for me

        let type : String! = "mp4"
        let targetURL : String? = NSBundle.mainBundle().pathForResource("Official Apple MacBook Air Video   YouTube", ofType: "mp4")

        let videoURL = NSURL(fileURLWithPath:targetURL!)


        let player = AVPlayer(URL: videoURL)
        let playerController = AVPlayerViewController()

        playerController.player = player
        self.addChildViewController(playerController)
        self.playView.addSubview(playerController.view)
        playerController.view.frame = playView.bounds

        player.play()

All controls to be showed, hope its helpful

Iyyappan Ravi
  • 3,205
  • 2
  • 16
  • 30
-2
/* "numberOfLoops" is the number of times that the sound will return to the beginning upon reaching the end. 
A value of zero means to play the sound just once.
A value of one will result in playing the sound twice, and so on..
Any negative number will loop indefinitely until stopped.
*/
@property NSInteger numberOfLoops;

This property is already defined inside AVAudioPlayer. Hope this can help you. I'm using Xcode 6.3.

Ajith Renjala
  • 4,934
  • 5
  • 34
  • 42
Julz
  • 1
  • 2