Looping a Video With Avfoundation Avplayer

Looping a video with AVFoundation AVPlayer?

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

How to loop a video with AVPlayer (macOS)?

I found a solution just now. This might not be the best approach to this question. If anyone have better ideas, please post your answers and let's discuss together.

So, in this case, we can use NotificationCenter to add an observer to our AVPlayer object.

Here's the code.

struct AVPlayerViewRepresented: NSViewRepresentable {
var player: AVPlayer

func makeNSView(context: Context) -> some NSView {
let controller = AVPlayerView()
controller.player = player
controller.controlsStyle = .none
loopVideo(player: player)

return controller
}

func updateNSView(_ nsView: NSViewType, context: Context) {}

func loopVideo(player plr: AVPlayer) {
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: plr.currentItem, queue: nil) { notification in
plr.seek(to: .zero)
plr.play()
}
}
}

Looping a video with AVFoundation AVPlayer?

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

Loop a segment of a video with AVPlayer

What you're looking for is called a boundary time observer. You give your AVPlayer a list of CMTimes, and it will notify you when the player's currentTime is approximately any of those times.

It works like this:

//Use an unretained reference in the block to break the retain cycle of the player retaining the block retaining the player retaining…
__unsafe_unretained AVPlayer *weakPlayer = _myPlayer;

_myObserver = [_myPlayer addBoundaryTimeObserverForTimes:@[ [NSValue valueWithCMTime:markOutTime] ]
queue:dispatch_get_main_queue()
usingBlock:^{
[weakPlayer seekToTime:markInTime
/*optional:
toleranceBefore:kCMTimeZero
toleranceAfter:kCMTimeZero
*/
];
}
];

Later, of course, you must use removeTimeObserver: to tell the AVPlayer to stop this observation. You give it the object you got from addBoundaryTimeObserver…:::.

Notes/Caveats/Warnings

  • Despite the name, they do not have to be “boundary” times. You can have exactly one time, and even when you don't, AVPlayer makes no inferences about whether any of the times is a start time, end time, midpoint, or whatever.
  • The same block may be called multiple times for the same “boundary”. Make sure you handle this appropriately (in my case, I had to make sure not to show the same subtitle twice).
  • Boundary time observers are not called when seeking (again, the times are not really interpreted as “boundaries” in the sense of start and end). If you seek straight to a boundary time (more or less—see next point), you should get notified for that, but seeking to a point in between two boundaries, or among many boundaries, will not cause an observation.
  • I said approximately, and I mean it. The main case in which I've seen AVPlayer notify multiple times is when AVPlayer notifies a little early, and then notifies again at (or at least closer to) the exact time. Don't assume that currentTime will be exactly equal to any time you supplied.


Related Topics



Leave a reply



Submit