【问题标题】:Skip to Previous AVPlayerItem on AVQueuePlayer / Play selected Item from queue跳到 AVQueuePlayer 上的上一个 AVPlayerItem / 播放队列中的选定项目
【发布时间】:2012-08-29 11:17:53
【问题描述】:

我正在播放一个电视节目,该节目已使用 AVQueuePlayer 在我的项目中分成不同的章节。 我还想提供在 AVQueuePlayer 已经在播放时跳到上一章/下一章或动态选择不同章节的可能性。

跳到下一个项目与 AVQueuePlayer 提供的advanceToNextItem 没有问题,但是跳回或播放队列中的某个项目没有任何相似之处。

所以我不太确定这里最好的方法是什么:

  1. 使用 AVPlayer 而不是 AVQueuePlayer,在 actionAtItemEnd 调用 replaceCurrentItemWithPlayerItem: 来播放下一个项目,然后使用“replaceCurrentItemWithPlayerItem”让用户选择某个章节

  1. 使用“insertItem:afterItem:”和“removeAllItems”重新组织队列或当前播放器

附加信息: 我按照它们应该出现在 NSArray 中的顺序存储不同视频的路径 用户应该通过按下代表章节的按钮跳转到某些章节。 Buttons 有标签,也是数组中对应视频的索引。

希望我能说清楚吗? 有没有人遇到过这种情况? 如果有人知道在哪里可以买到提供该功能的优质 IOS VideoPlayerFramework,我也将不胜感激该链接。

【问题讨论】:

  • 让我着迷的是,AVQueuePlayer 的 index 方法中没有寻找项目。
  • 请问,您是否有自定义控件来检测用户按下的内容,或者您​​是否设法通过 AVQeuePlayer 上的默认控件检测下一个按钮按下?

标签: objective-c avplayer skip avqueueplayer movieplayer


【解决方案1】:

如果您希望您的程序可以播放上一个项目并从您的 playeritems(NSArray) 中播放所选项目,您可以这样做。

- (void)playAtIndex:(NSInteger)index
{
    [player removeAllItems];
    for (int i = index; i <playerItems.count; i ++) {
        AVPlayerItem* obj = [playerItems objectAtIndex:i];
        if ([player canInsertItem:obj afterItem:nil]) {
        [obj seekToTime:kCMTimeZero];
        [player insertItem:obj afterItem:nil];
        }
    }
}

编辑: playerItems 是您存储 AVPlayerItems 的 NSMutableArray(NSArray)。

【讨论】:

  • 谢谢。我正在做类似的事情。但最后我只使用了一个视频文件和一个数组,其中包含指向章节开始时间的指针和 seekTo 函数。
  • 如果你倾向于无间隙地玩,你的解决方案似乎是唯一的办法。
  • 有没有在不清除队列的情况下这样做?
【解决方案2】:

第一个答案从 AVQueuePlayer 中删除所有项目,并从作为索引 arg 传递的迭代开始重新填充队列。这将使用前一个项目(假设您传递了正确的索引)以及从该点向前的现有 playerItems 数组中的其余项目启动新填充的队列,但它不允许多次反转,例如您在第 10 首曲目上,想返回并重播第 9 首曲目,然后重播第 5 首曲目,但上述操作无法完成。但是在这里你可以...

-(IBAction) prevSongTapped: (id) sender
{
    if (globalSongCount>1){ //keep running tally of items already played
    [self resetAVQueue]; //removes all items from AVQueuePlayer

        for (int i = 1; i < globalSongCount-1; i++){
            [audioQueuePlayer advanceToNextItem];
        }
   globalSongCount--;
   [audioQueuePlayer play];


    }
}

【讨论】:

    【解决方案3】:

    下面的代码允许你跳转到你的任何项目。没有玩家前进。干净利落。 playerItemList 是带有 AVPlayerItem 对象的 NSArray。

    - (void)playAtIndex:(NSInteger)index
    {
        [audioPlayer removeAllItems];
        AVPlayerItem* obj = [playerItemList objectAtIndex:index];
        [obj seekToTime:kCMTimeZero];
        [audioPlayer insertItem:obj afterItem:nil];
        [audioPlayer play];
    }
    

    【讨论】:

    • 这是有道理的。但是每次您想播放该索引歌曲时,您都会从列表中删除所有内容。为什么不在这种情况下使用 AVPlayer 呢?我给了你 +1 票
    【解决方案4】:

    djiovann 创建了一个 AVQueuePlayer 的子类,它提供了这个功能。

    你可以在github找到它。

    我尚未对其进行测试,但通过浏览代码似乎可以完成工作。此外,代码有据可查,因此它至少应该作为功能自定义实现的良好参考(我建议使用类别而不是子类)。

    【讨论】:

      【解决方案5】:

      这应该是 AVQueuePlayer 对象的责任,而不是您的视图控制器本身,因此您应该使其可重用并通过扩展公开其他答案实现,并以类似于 AdvanceToNextItem() 的方式使用它:

      extension AVQueuePlayer {
      func advanceToPreviousItem(for currentItem: Int, with initialItems: [AVPlayerItem]) {
          self.removeAllItems()
          for i in currentItem..<initialItems.count {
              let obj: AVPlayerItem? = initialItems[i]
              if self.canInsert(obj!, after: nil) {
                  obj?.seek(to: kCMTimeZero, completionHandler: nil)
                  self.insert(obj!, after: nil)
              }
          }
      }
      }
      

      用法(您只需存储索引和对初始队列播放器项目的引用):

      self.queuePlayer.advanceToPreviousItem(for: self.currentIndex, with: self.playerItems)
      

      维护索引的一种方法是观察每个视频项目的 AVPlayerItemDidPlayToEndTime 通知:

      func addDidFinishObserver() {
          queuePlayer.items().forEach { item in
              NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying), name: Notification.Name.AVPlayerItemDidPlayToEndTime, object: item)
          }
      }
      
      func removeDidFinishObserver() {
          queuePlayer.items().forEach { item in
              NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: item)
          }
      }
      
      @objc func playerDidFinishPlaying(note: NSNotification) {
          if queuePlayer.currentItem == queuePlayer.items().last {
              print("last item finished")
          } else {
              print("item \(currentIndex) finished")
              currentIndex += 1
          }
      }
      

      这种观察对于其他用例(进度条、当前视频计时器重置...)也非常有用。

      【讨论】:

        【解决方案6】:

        Swift 5.2

        var playerItems = [AVPlayerItem]()
        
        func play(at itemIndex: Int) {
        
            player.removeAllItems()
        
            for index in itemIndex...playerItems.count {
                if let item = playerItems[safe: index] {
                    if player.canInsert(item, after: nil) {
                        item.seek(to: .zero, completionHandler: nil)
                        player.insert(item, after: nil)
                    }
                }
            }
        }
        

        【讨论】:

          【解决方案7】:

          @saiday 的回答对我有用,这是他回答的快速版本

           func play(at index: Int) {
              queue.removeAllItems()
              for i in index..<items.count {
                  let obj: AVPlayerItem? = items[i]
                  if queue.canInsert(obj!, after: nil) {
                      obj?.seek(to: kCMTimeZero, completionHandler: nil)
                      queue.insert(obj!, after: nil)
                  }
              }
          }
          

          【讨论】:

            【解决方案8】:

            如果您想使用 AVQueuePlayer 从任何索引播放歌曲。那么下面的代码可以帮助您。

            NSMutableArray *musicListarray (add song that you want to play in queue);
            AVQueuePlayer *audioPlayer;
            AVPlayerItem *item;
            
            -(void) nextTapped
            {
              nowPlayingIndex = nowPlayingIndex + 1;
            
              if (nowPlayingIndex > musicListarray.count)
              {
              }
              else
              {
                [self playTrack];
              }
            }
            
            -(void) playback
            {
              if (nowPlayingIndex < 0)
              {
              }
              else
              {
                nowPlayingIndex = nowPlayingIndex - 1;
                [self playTrack];
              }
            }
            
            -(void) playTrack
            {
              @try
              {
                if (musicArray.count > 0)
                {
                  item =[[AVPlayerItem alloc] initWithURL: [NSURL URLWithString:musicListarray
                  [nowPlayingIndex]]];
                  [audioPlayer replaceCurrentItemWithPlayerItem:item];
                  [audioPlayer play];
                }
              }
              @catch (NSException *exception)
              {
              }
            }
            
            -(void) PlaySongAtIndex
            {
              //yore code...
              nowPlayingIndex = i (stating index from queue)[audioPlayer play];
              [self playTrack];
            }
            

            当您想播放歌曲时,请在此处调用 PlaySongAtIndex。

            【讨论】:

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