【问题标题】:Replay items in AVQueuePlayer after last最后重播 AVQueuePlayer 中的项目
【发布时间】:2012-06-24 17:45:21
【问题描述】:

我需要在我的AVQueuePlayer 中创建类似无限循环的东西。特别是,一旦最后一个组件播放完毕,我想重播AVPlayerItems 的整个NSArray

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

【问题讨论】:

  • 您是卡在这一点上还是只需要从起点创建它?
  • 我现在实际上如何创建它并播放所有 AVQueuePlayers,我现在正在寻找在最后一个 QVPlayerItem 完成后重新启动播放器。
  • -(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];
  • 所以您使用的是包含视频的数组?这个数组包含视频的路径对吗?
  • 没错,你只需要传递路径,当你得到最后一个时,你可以重置回第一个索引。

标签: iphone ios avfoundation avplayer avqueueplayer


【解决方案1】:

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

  1. 创建一个队列,它是 AVPlayerItems 的 NSArray。
  2. 随着每个项目被添加到队列中,设置一个 NSNotificationCenter 观察者以在视频到达结尾时唤醒。
  3. 在观察者的选择器中,告诉 AVPlayerItem 您要循环回到开头,然后告诉播放器开始播放。

(注意:AVPlayerDemoPlaybackView 来自 Apple “AVPlayerDemo”示例。只是带有 setter 的 UIView 的子类)

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

就是这样!让我知道需要进一步解释的事情。

【讨论】:

  • 如果我在播放器中添加了添加的 3video,我应该怎么做。并且在完成所有 3video 后,最后一个视频将无限循环播放
  • 这里的逻辑没有得到 OP 想要的行为。它循环最后一个项目而不是整个播放器项目数组。
【解决方案2】:

我想出了一个解决方案来循环遍历我的视频队列中的所有视频,而不仅仅是一个。首先我初始化了我的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】:

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

    【讨论】:

    • 在将 notif.userInfo[@"object"] 更改为 notif.object (iOS 9) 后,这对我有用。
    【解决方案4】:

    在 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(),那是行不通的。

    【讨论】:

      【解决方案5】:

      截至 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")
      }
      

      }

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多