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 CMTime
s, 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
In Swift, How to Declare a Variable of a Specific Type That Conforms to One or More Protocols
Making Text Bold Using Attributed String in Swift
Enterprise App Update Distribution on iOS 8
How to Allow Didset to Be Called During Initialization in Swift
Cgcontextdrawimage Draws Image Upside Down When Passed Uiimage.Cgimage
How to Determine Whether Code Is Running in Debug/Release Build
Unique Values of Array in Swift
iOS 7: Mpmusicplayercontroller Volume Deprecated. How to Change Device Volume Now
Swiftui App Life Cycle Ios14 Where to Put Appdelegate Code
How to Create Launch Images For Iphone 6/6 Plus Landscape Only Apps
Are There APIs For Custom Vibrations in Ios
How to Make Uilabel Display Outlined Text