在AVQueuePlayer中重复播放最后一个项目

7

我需要在我的AVQueuePlayer中创建类似无限循环的东西。特别地,我想要在最后一个组件播放完成后重新播放整个AVPlayerItemNSArray

我必须承认我并不知道如何实现这一点,希望您能给我一些线索。


你是卡在这个点上了还是需要从起点开始创建? - Dhruv
-(void) playVideoAtIndex:(NSInteger)index{[self performSelector:@selector(setObservationInfo)]; currentIndex = index; AVPlayerItem *videoItem = [AVPlayerItem playerItemWithURL:[NSURL fileURLWithPath:[arrVideoList objectAtIndex:index]]];}需要检查:if (currentIndex < [arrVideoList count]-1) { currentIndex++; } else { currentIndex = 0; } [self playVideoAtIndex:currentIndex]; - Dhruv
所以你正在使用一个视频数组?这个数组包含了视频的路径,对吗? - Edelweiss
是的,确切地说,你只需要传递路径,当你到达最后一个时,就可以重置回第一个索引。 - Dhruv
我会进一步学习这个,你真的很好。谢谢。 - Edelweiss
显示剩余3条评论
5个回答

4

在AVQueuePlayer中循环播放视频序列的最佳方法。

对AVQueuePlayer中的每个playeritem进行观察。

queuePlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
for(AVPlayerItem *item in items) {
    [[NSNotificationCenter defaultCenter] addObserver:self 
            selector:@selector(nextVideo:) 
            name:AVPlayerItemDidPlayToEndTimeNotification 
            object:item ];
}

在每个nextVideo插入当前项目以再次排队进行播放。确保为每个项目搜索到零。在advanceToNextItem之后,AVQueuePlayer将从队列中删除当前项目。
-(void) nextVideo:(NSNotification*)notif {
    AVPlayerItem *currItem = notif.userInfo[@"object"];
    [currItem seekToTime:kCMTimeZero];
    [queuePlayer advanceToNextItem];
    [queuePlayer insertItem:currItem afterItem:nil];
}

1
在我修改 notif.userInfo[@"object"]notif.object 后,这个方法对我有效(iOS 9)。 - Tomas Andrle

2
这基本上是从头开始的。组件包括:
  1. 创建一个AVPlayerItems的NSArray队列。
  2. 每当项目添加到队列中,设置一个NSNotificationCenter观察者,在视频到达结尾时唤醒。
  3. 在观察者的选择器中,告诉AVPlayerItem要循环回到开头,然后告诉播放器播放。
(注意:AVPlayerDemoPlaybackView来自苹果的“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;
    }
}

这就是全部了!如果需要进一步解释,请告诉我。

如果我在播放器中添加了3个视频,完成所有3个视频后,最后一个视频将无限循环播放,该怎么办? - ios developer
1
这里的逻辑并不能获得原帖作者所期望的行为。它只会循环最后一个项目,而不是整个玩家项目数组。 - Jordan H

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]];
        }
    }
}

0

截至2021年3月,已适用于iOS 14

作为一名新手编码者,我想发布我的解决方案,这花费了我一些时间才弄清楚。我的解决方案在UIView内循环使用avqueuplayer,而不使用AVLooper。这个想法来自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")
}

}


0
在Swift 4中,您可以使用类似以下的东西:
        NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil, queue: nil) {

            notification in

            print("A PlayerItem just finished playing !")

            currentVideo += 1

            if(currentVideo >= videoPaths.count) {

               currentVideo = 0
            }

            let url = URL(fileURLWithPath:videoPaths[currentVideo])
            let playerItem = AVPlayerItem.init(url: url)

            player.insert(playerItem, after: nil)
        }

据我所知,AVQueuePlayer会删除上次播放的最后一项,因此您需要将刚刚播放的视频添加到队列中,否则它实际上会播放完所有视频!您不能只是使用.seek跳转到0并再次.play(),这样是行不通的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接