Objective c 跳到AVQueuePlayer上的上一个AVPlayerItem/播放队列中的选定项目

Objective c 跳到AVQueuePlayer上的上一个AVPlayerItem/播放队列中的选定项目,objective-c,avplayer,skip,avqueueplayer,movieplayer,Objective C,Avplayer,Skip,Avqueueplayer,Movieplayer,我正在播放一个电视节目,该节目已被切割成不同的章节,我的项目使用的是AVQueuePlayer。 我还想提供跳到上一章/下一章的可能性,或者在AVQueuePlayer已经开始播放时动态选择不同的章节 AVQueuePlayer提供的advanceToNextItem可以跳到下一个项目,但是从队列中跳回或播放某个项目没有任何相似之处 因此,我不太确定这里最好的方法是什么: 使用AVPlayer而不是AVQueuePlayer,在actionAtItemEnd处调用replaceCurrentIt

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

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

因此,我不太确定这里最好的方法是什么:

  • 使用AVPlayer而不是AVQueuePlayer,在
    actionAtItemEnd
    处调用
    replaceCurrentItemWithPlayerItem:
    来播放下一个片段,只需使用“replaceCurrentItemWithPlayerItem”让用户选择某个章节
  • 使用“insertItem:afterItem:”和“removeAllItems”重新组织队列或当前播放机
  • 其他信息: 我按照不同视频在NSArray中出现的顺序存储它们的路径 用户应该通过按下代表章节的按钮跳转到特定章节。按钮具有标签,这些标签也是阵列中相应视频的索引

    希望我能说清楚? 有没有人有过这种经历?
    如果有人知道在哪里可以买到提供该功能的IOS VideoPlayerFramework,我也会很感激这个链接

    如果您希望您的程序可以播放上一个项目并播放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];
            }
        }
    }
    
    -(void)playatinex:(NSInteger)索引
    {
    [player removeAllItems];
    
    对于(int i=index;i第一个答案从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];
    
    
        }
    }
    
    -(iAction)发送方:(id)发送方
    {
    如果(globalSongCount>1){//继续运行已播放项目的计数
    [self resetAVQueue];//从AVQueuePlayer中删除所有项目
    对于(int i=1;i
    以下代码允许您跳转到游戏中的任何项目。无playerhead前进。简单明了。playerItemList是包含AVPlayerItem对象的NSArray

    - (void)playAtIndex:(NSInteger)index
    {
        [audioPlayer removeAllItems];
        AVPlayerItem* obj = [playerItemList objectAtIndex:index];
        [obj seekToTime:kCMTimeZero];
        [audioPlayer insertItem:obj afterItem:nil];
        [audioPlayer play];
    }
    
    创建了一个子类
    AVQueuePlayer
    ,该子类正好提供此功能

    你可以在电视上找到它


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

    如果您想使用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];
    }
    
    NSMutableArray*musicListarray(添加要在队列中播放的歌曲);
    AVQueuePlayer*音频播放器;
    AVPlayerItem*项目;
    -(作废)下一页
    {
    nowPlayingIndex=nowPlayingIndex+1;
    如果(正在播放索引>MusicListaray.count)
    {
    }
    其他的
    {
    [自行播放曲目];
    }
    }
    -(void)回放
    {
    如果(正在播放索引<0)
    {
    }
    其他的
    {
    nowPlayingIndex=nowPlayingIndex-1;
    [自行播放曲目];
    }
    }
    -(无效)播放曲目
    {
    @试一试
    {
    如果(musicArray.count>0)
    {
    item=[[AVPlayerItem alloc]initWithURL:[NSURL URLWithString:MusicListaray
    [nowPlayingIndex]];
    [audioPlayer用PlayerItem替换当前项:项];
    [音频播放器播放];
    }
    }
    @捕获(NSException*异常)
    {
    }
    }
    -(无效)播放歌曲索引
    {
    //过去的代码。。。
    nowPlayingIndex=i(从队列中声明索引)[audioPlayer播放];
    [自行播放曲目];
    }
    

    这里播放歌曲当你想播放歌曲时,请拨打电话。

    @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)
            }
        }
    }
    
    func播放(索引为Int){
    queue.removeAllItems()
    
    对于索引中的i..这应该是AVQueuePlayer对象的责任,而不是视图控制器本身,因此您应该使其可重用,并通过扩展公开其他answers实现,并以类似advanceToNextItem()的方式使用它:

    维护索引的一种方法是观察每个视频项目的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
        }
    }
    

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

    Swift 5.2

    var playerItems=[AVPlayerItem]()
    
    func播放(在itemIndex:Int处){
    player.removeAllItems()
    对于itemIndex…playerItems.count中的索引{
    如果let item=playerItems[安全:索引]{
    if player.canInsert(项目,之后:无){
    item.seek(收件人:.zero,completionHandler:nil)
    玩家。插入(项目,之后:无)
    }
    }
    }
    }
    
    谢谢。我也在做类似的事情。但最后我只使用了一个视频文件和一个数组,其中包含指向章节开始时间的指针和seekTo函数。如果你倾向于无间隙播放,你的解决方案似乎是唯一的方法。是否有任何方法可以在不清除队列的情况下执行此操作?这是有意义的。但是每次播放索引歌曲时,您都会从列表中删除所有内容。在这种情况下,为什么不直接使用AVPlayer?我给了您+1票,但AVQueuePlayer没有在索引中搜索项目的方法,这让我很着迷。我可以问一下,您是否有自定义控件来检测用户按了什么,或者您是否以某种方式管理了
    self.queuePlayer.advanceToPreviousItem(for: self.currentIndex, with: self.playerItems)
    
    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
        }
    }