我需要在AVQueuePlayer
中创建类似无限循环的内容。特别是,我希望在最后一个组件完成播放后重播NSArray
AVPlayerItem
个。{/ p>
我必须承认,我实际上并不知道如何实现这一点,并希望你能给我一些线索。
答案 0 :(得分:3)
在AVQueuePlayer中循环播放一系列视频的最佳方式。
观察AVQueuePlayer中的每个播放项目。
queuePlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
for(AVPlayerItem *item in items) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(nextVideo:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:item ];
}
每个nextVideo上的再次插入currentItem以将其排队以进行回放。确保为每个项目寻求零。在advanceToNextItem之后,AVQueuePlayer将从队列中删除currentItem。
-(void) nextVideo:(NSNotification*)notif {
AVPlayerItem *currItem = notif.userInfo[@"object"];
[currItem seekToTime:kCMTimeZero];
[queuePlayer advanceToNextItem];
[queuePlayer insertItem:currItem afterItem:nil];
}
答案 1 :(得分:2)
这是从头开始的。组件是:
(注意:AVPlayerDemoPlaybackView来自Apple“AVPlayerDemo”示例。只是UIView的子类,带有setter)
BOOL videoShouldLoop = YES;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSMutableArray *videoQueue = [[NSMutableArray alloc] init];
AVQueuePlayer *mPlayer;
AVPlayerDemoPlaybackView *mPlaybackView;
// You'll need to get an array of the files you want to queue as NSARrray *fileList:
for (NSString *videoPath in fileList) {
// Add all files to the queue as AVPlayerItems
if ([fileManager fileExistsAtPath: videoPath]) {
NSURL *videoURL = [NSURL fileURLWithPath: videoPath];
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL: videoURL];
// Setup the observer
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(playerItemDidReachEnd:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: playerItem];
// Add the playerItem to the queue
[videoQueue addObject: playerItem];
}
}
// Add the queue array to the AVQueuePlayer
mPlayer = [AVQueuePlayer queuePlayerWithItems: videoQueue];
// Add the player to the view
[mPlaybackView setPlayer: mPlayer];
// If you should only have one video, this allows it to stop at the end instead of blanking the display
if ([[mPlayer items] count] == 1) {
mPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
}
// Start playing
[mPlayer play];
- (void) playerItemDidReachEnd: (NSNotification *)notification
{
// Loop the video
if (videoShouldLoop) {
// Get the current item
AVPlayerItem *playerItem = [mPlayer currentItem];
// Set it back to the beginning
[playerItem seekToTime: kCMTimeZero];
// Tell the player to do nothing when it reaches the end of the video
// -- It will come back to this method when it's done
mPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
// Play it again, Sam
[mPlayer play];
} else {
mPlayer.actionAtItemEnd = AVPlayerActionAtItemEndAdvance;
}
}
就是这样!让我知道需要进一步解释的事情。
答案 2 :(得分:0)
我想出了一个解决方案来循环播放视频队列中的所有视频,而不只是一个。首先,我初始化了AVQueuePlayer
:
- (void)viewDidLoad
{
NSMutableArray *vidItems = [[NSMutableArray alloc] init];
for (int i = 0; i < 5; i++)
{
// create file name and make path
NSString *fileName = [NSString stringWithFormat:@"intro%i", i];
NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:@"mov"];
NSURL *movieUrl = [NSURL fileURLWithPath:path];
// load url as player item
AVPlayerItem *item = [AVPlayerItem playerItemWithURL:movieUrl];
// observe when this item ends
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:item];
// add to array
[vidItems addObject:item];
}
// initialize avqueueplayer
_moviePlayer = [AVQueuePlayer queuePlayerWithItems:vidItems];
_moviePlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
// create layer for viewing
AVPlayerLayer *layer = [AVPlayerLayer playerLayerWithPlayer:_moviePlayer];
layer.frame = self.view.bounds;
layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
// add layer to uiview container
[_movieViewContainer.layer addSublayer:layer];
}
发布通知时
- (void)playerItemDidReachEnd:(NSNotification *)notification {
AVPlayerItem *p = [notification object];
// keep playing the queue
[_moviePlayer advanceToNextItem];
// if this is the last item in the queue, add the videos back in
if (_moviePlayer.items.count == 1)
{
// it'd be more efficient to make this a method being we're using it a second time
for (int i = 0; i < 5; i++)
{
NSString *fileName = [NSString stringWithFormat:@"intro%i", i];
NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:@"mov"];
NSURL *movieUrl = [NSURL fileURLWithPath:path];
AVPlayerItem *item = [AVPlayerItem playerItemWithURL:movieUrl];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:item];
// the difference from last time, we're adding the new item after the last item in our player to maintain the order
[_moviePlayer insertItem:item afterItem:[[_moviePlayer items] lastObject]];
}
}
}
答案 3 :(得分:0)
在Swift 4中,您可以使用类似以下的内容:
NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil, queue: nil) {
notification in
print("A PlayerItem just finished playing !")
var newVideo = currentVideo - 1
if newVideo < 0 {
newVideo = videoPaths.count - 1
}
currentVideo += 1
if(currentVideo >= videoPaths.count) {
currentVideo = 0
}
let url = URL(fileURLWithPath:videoPaths[newVideo])
let playerItem = AVPlayerItem.init(url: url)
player.insert(playerItem, after: nil)
}
根据我的理解,AVQueuePlayer
将删除上一个播放的项目,因此您需要继续将刚播放的视频添加到队列中,否则实际上会用光所有视频!您不能只将.seek
设为0,然后再次.play()
,这是行不通的。
答案 4 :(得分:0)
自 2021 年 3 月起在 iOS 14 上工作
我只是想将我的解决方案作为一个新的编码员发布,这花了我一段时间才弄明白。 我的解决方案在不使用 AVLooper 的情况下在 UIView 内循环了一个 avqueuplayer。这个想法来自raywenderlich。您可以忽略 UIView 包装器等。这个想法是使用 NSKeyValeObservation 而不是像其他人建议的那样使用通知中心。
class QueuePlayerUIView: UIView {
private var playerLayer = AVPlayerLayer()
private var playerLooper: AVPlayerLooper?
@objc let queuePlayer = AVQueuePlayer()
var token: NSKeyValueObservation?
var clips = [URL]()
func addAllVideosToPlayer() {
for video in clips {
let asset = AVURLAsset(url: video)
let item = AVPlayerItem(asset: asset)
queuePlayer.insert(item, after: queuePlayer.items().last)
}
}
init(videosArr: [URL]) {
super.init(frame: .zero)
self.clips = videosArr
addAllVideosToPlayer()
playerLayer.player = queuePlayer
playerLayer.videoGravity = .resizeAspectFill
layer.addSublayer(playerLayer)
queuePlayer.play()
token = queuePlayer.observe(\.currentItem) { [weak self] player, _ in
if player.items().count == 1 {
self?.addAllVideosToPlayer()
}
}
}
override func layoutSubviews() {
super.layoutSubviews()
playerLayer.frame = bounds
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}