16

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

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

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

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

或者

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

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

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

4

8 回答 8

23

如果您希望您的程序可以播放上一个项目并从您的 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)。

于 2012-08-31T05:49:16.917 回答
3

第一个答案从 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];


    }
}
于 2012-10-11T08:59:07.753 回答
2

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

- (void)playAtIndex:(NSInteger)index
{
    [audioPlayer removeAllItems];
    AVPlayerItem* obj = [playerItemList objectAtIndex:index];
    [obj seekToTime:kCMTimeZero];
    [audioPlayer insertItem:obj afterItem:nil];
    [audioPlayer play];
}
于 2013-06-10T13:20:35.280 回答
2

djiovann创建了一个AVQueuePlayer提供此功能的子类。

你可以在github上找到它。

我还没有测试过它,但是通过浏览代码似乎可以完成工作。代码也有很好的文档记录,因此它至少应该作为功能自定义实现的一个很好的参考(我建议使用类别而不是子类化)。

于 2014-03-11T11:25:53.447 回答
1

这应该是 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
    }
}

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

于 2018-09-13T13:13:50.960 回答
1

斯威夫特 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)
            }
        }
    }
}
于 2020-04-25T18:13:20.747 回答
0

如果您想使用 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。

于 2016-12-17T05:33:39.963 回答
0

@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)
        }
    }
}
于 2018-05-26T09:47:06.407 回答