ios 使用 AVFoundation AVPlayer 循环播放视频?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5361145/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Looping a video with AVFoundation AVPlayer?
提问by orj
Is there a relatively easy way of looping a video in AVFoundation?
在 AVFoundation 中循环播放视频是否有相对简单的方法?
I've created my AVPlayer and AVPlayerLayer like so:
我已经像这样创建了我的 AVPlayer 和 AVPlayerLayer:
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.
视频播放正常,但在最后停止。使用 MPMoviePlayerController,您所要做的就是将其repeatMode
属性设置为正确的值。AVPlayer 上似乎没有类似的属性。似乎也没有回调会告诉我电影何时结束,这样我就可以找到开头并再次播放。
I'm not using MPMoviePlayerController because it has some serious limitations. I want to be able to play back multiple video streams at once.
我没有使用 MPMoviePlayerController 因为它有一些严重的限制。我希望能够一次播放多个视频流。
回答by Bastian
You can get a Notification when the player ends. Check AVPlayerItemDidPlayToEndTimeNotification
当玩家结束时,您可以获得通知。查看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+
斯威夫特 4+
@objc func playerItemDidReachEnd(notification: Notification) {
if let playerItem = notification.object as? AVPlayerItem {
playerItem.seek(to: CMTime.zero, completionHandler: nil)
}
}
回答by Nabha
If it helps, in iOS / tvOS 10, there's a new AVPlayerLooper() that you can use to create seamless looping of video (Swift):
如果有帮助,在 iOS / tvOS 10 中,有一个新的 AVPlayerLooper() 可用于创建视频的无缝循环 (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/
这是在 WWDC 2016 上的“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:
即使使用此代码,我也遇到了问题,直到我向 Apple 提交了错误报告并得到了以下回复:
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)
电影持续时间长于音频/视频轨道的电影文件是问题所在。FigPlayer_File 正在禁用无间隙过渡,因为音轨编辑比电影持续时间短(15.682 对 15.787)。
您需要修复电影文件以使电影持续时间和轨道持续时间相同,或者您可以使用 AVPlayerLooper 的时间范围参数(设置时间范围从 0 到音轨的持续时间)
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.
事实证明,Premiere 导出的文件的音轨长度与视频略有不同。就我而言,完全删除音频没问题,这解决了问题。
回答by King-Wizard
In Swift:
在斯威夫特:
You can get a Notification when the player ends... check AVPlayerItemDidPlayToEndTimeNotification
您可以在播放器结束时收到通知...检查 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
斯威夫特3
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.
释放播放器时不要忘记取消注册通知。
回答by Islam Q.
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:
目标 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.
注意:我没有使用,avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone
因为它不需要。
回答by user2581875
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).
为了避免视频倒带时的间隙,在合成中使用相同资产的多个副本对我来说效果很好。我在这里找到它:www.developers-life.com/avplayer-looping-video-without-hiccupdelays.html(链接现已失效)。
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];
回答by kevinnguy
I recommend using AVQueuePlayer to loop your videos seamlessly. Add the notification observer
我建议使用 AVQueuePlayer 无缝循环播放您的视频。添加通知观察者
AVPlayerItemDidPlayToEndTimeNotification
and in its selector, loop your video
并在其选择器中循环播放您的视频
AVPlayerItem *video = [[AVPlayerItem alloc] initWithURL:videoURL];
[self.player insertItem:video afterItem:nil];
[self.player play];
回答by Vojta
this worked for me without hiccup issues, point is in pausingthe player before calling seekToTime method:
这对我有用,没有打嗝问题,重点是在调用 seekToTime 方法之前暂停播放器:
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()
registering notification
NSNotificationCenter.defaultCenter().addObserver(self, selector: "videoLoop", name: AVPlayerItemDidPlayToEndTimeNotification, object: self.backgroundPlayer!.currentItem)
videoLoop function
func videoLoop() { self.backgroundPlayer?.pause() self.backgroundPlayer?.currentItem?.seekToTime(kCMTimeZero) self.backgroundPlayer?.play() }
初始化 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()
登记通知
NSNotificationCenter.defaultCenter().addObserver(self, selector: "videoLoop", name: AVPlayerItemDidPlayToEndTimeNotification, object: self.backgroundPlayer!.currentItem)
视频循环功能
func videoLoop() { self.backgroundPlayer?.pause() self.backgroundPlayer?.currentItem?.seekToTime(kCMTimeZero) self.backgroundPlayer?.play() }
回答by vp2698
For Swift 3 & 4
对于 Swift 3 & 4
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.avPlayer?.currentItem, queue: .main) { _ in
self.avPlayer?.seek(to: kCMTimeZero)
self.avPlayer?.play()
}
回答by neon M3
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
我在带有 AVQueuePlayer 的 Objective-c 中的解决方案-似乎您必须复制 AVPlayerItem 并在完成第一个元素的播放后立即添加另一个副本。“有点”是有道理的,对我来说没有任何问题
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
https://gist.github.com/neonm3/06c3b5c911fdd3ca7c7800dccf7202ad
回答by shujucn
you can add a AVPlayerItemDidPlayToEndTimeNotification observer and replay video from start in selector, code like below
您可以在选择器中添加一个 AVPlayerItemDidPlayToEndTimeNotification 观察者并从头开始重播视频,代码如下
//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];
}