最后重播AVQueuePlayer中的项目

时间:2012-06-22 12:16:27

标签: iphone ios avfoundation avplayer avqueueplayer

我需要在AVQueuePlayer中创建类似无限循环的内容。特别是,我希望在最后一个组件完成播放后重播NSArray AVPlayerItem个。{/ p>

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

5 个答案:

答案 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)

这是从头开始的。组件是:

  1. 创建一个队列,该队列是AVPlayerItems的NSArray。
  2. 当每个项目都添加到队列中时,设置一个NSNotificationCenter观察器,以便在视频到达结束时唤醒。
  3. 在观察者的选择器中,告诉AVPlayerItem你要循环回到开头,然后告诉玩家玩。
  4. (注意: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")
}

}