使用AVQueuePlayer实现向前跳转的更好方式是什么?

21

我在应用程序中使用了一个 AVQueuePlayer。我有两个划动手势,可以跳转到下一个和上一个 avplayeritem。现在,为了跳到下一个,我只是调用了 avqueueplayer 上的 advanceToNextItem,这个方法运行得很好。

然而,对于上一个跳过操作,我正在删除所有项目,并将它们再次添加到队列中,以前一个视频在最前面。这在多次跳过上一个视频时非常慢。如何让这个过程变得更快,就像调用advanceToNextItem一样?

我的代码如下:

func skipToPrevious() {
    queuePlayer.removeAllItems()
    // move the previous playerItem to the front of the list then add them all back in
    for playerItem in playerItems:
        queuePlayer.insertItem(playerItem, afterItem: nil)

}

我也遇到了这个问题一段时间了。我已经采取重新创建队列播放器的方法,当用户点击前一个项目时。然后我使用延迟函数和屏幕上的进度条,以便队列播放器至少有1.0秒的时间来重新创建队列并缓冲一些该项目。当然,这并不能解决您想要更快速度的问题。我会再深入研究一下。 - MikeG
1
目前我发现最好的方法是在AVPlayerItem播放完成后将其保留在内存中,这样它就已经被缓冲了,然后只需像我在问题中所做的那样重新插入项目,而不是完全重新创建队列。 - Tyler
一个想法 -> 当一个项目正在播放时,将当前正在播放的项目存储在变量中,并加载到AVPlayer中。当用户点击倒带时,您可以立即播放AVPlayer,然后可以简单地在幕后暂停和倒带AVQueuePlayer,以便当AVPlayer完成时,队列播放器已准备好运行... 有点hacky。 - MikeG
https://github.com/dgiovann/AVQueuePlayerPrevious 这个可能有点老,但是可能会对你有所帮助。 - MikeG
6个回答

30

看起来当调用advanceToNextItem时,AVQueuePlayer会从播放队列中移除当前项目。理论上,没有办法在重建队列之前恢复此项目。

你可以使用标准的AVPlayer,拥有一个AVPlayerItems数组和一个整数索引,它保存当前曲目的索引。

Swift 3:

let player = AVPlayer()
let playerItems = [AVPlayerItem]() // your array of items
var currentTrack = 0

func previousTrack() {
    if currentTrack - 1 < 0 {
        currentTrack = (playerItems.count - 1) < 0 ? 0 : (playerItems.count - 1)
    } else {
        currentTrack -= 1
    }

    playTrack()
}

func nextTrack() {
    if currentTrack + 1 > playerItems.count {
        currentTrack = 0
    } else {
        currentTrack += 1;
    }

    playTrack()
}

func playTrack() {

    if playerItems.count > 0 {
        player.replaceCurrentItem(with: playerItems[currentTrack])
        player.play()
    }
}

Swift 2.x:

func previousTrack() {
    if currentTrack-- < 0 {
        currentTrack = (playerItems.count - 1) < 0 ? 0 : (playerItems.count - 1)
    } else {
        currentTrack--
    }

    playTrack()
}

func nextTrack() {
    if currentTrack++ > playerItems.count {
        currentTrack = 0
    } else {
        currentTrack++;
    }

    playTrack()
}

func playTrack() {

    if playerItems.count > 0 {
        player.replaceCurrentItemWithPlayerItem(playerItems[currentTrack])
        player.play()
    }
}

这正是我所思考的,但我一直试图避免。有avqueueplayer源代码的示例吗?我想在此基础上构建。 - Tyler
@Tyler AVQueuePlayer 的源代码? 我认为没有人拥有它,因为它是苹果内部的。我可以为您提供 AVQueuePlayerAVQueuePlayerInternal 的私有头文件。 - JAL
1
这是最好的解决方案100%。关键是将AVPlayerItem保留在内存中,而不是在跳转到上一个时重新加载它们。只需要小心,不要保留太多,有些最终会被丢弃。谢谢! - Tyler
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Sulthan
请问您能否更新您的答案并解释一下如何监听nextTrack等事件吗? - XcodeNOOB
显示剩余7条评论

2

队列是否可以平稳处理多个跳过操作?若可以,您可以在索引n+2处不断重新插入前一个视频。当用户想要播放前一个曲目时,您需要向前跳过两次。

如果没有任何跳过操作,从 A 到 F 进行播放,该模式将如下所示:

A B C D E F
B C D E F

// re-insert A after next track
B C A D E F
C A D E F

// remove A then re-insert B
C D E F
C D B E F
D B E F

// remove B then re-insert C
D E F
D E C F
E C F

// remove C then re-insert D
E F
E F D
F D

// remove D then re-insert E
F
FE

使用这种模式,您只能平稳地向后跳过一次,但可以修改它以允许更多次。

绝对不是一个理想的解决方案,但可能有效!


2
“replaceCurrentItemWithPlayerItem”有限制,应尽可能避免使用。在苹果文档中,它指出:“新项目必须具有与其替换的项目相同的合成器,或者没有合成器。”
相反,可以使用循环逐个插入playerItems,只需创建一个AVQueuePlayer即可更快地完成。
func skipToPrevious() {
    queuePlayer = AVQueuePlayer.queuePlayerWithItems(playerItem)
}

2
为什么这会有任何速度提升呢?它变慢的原因是因为它没有预缓存前一个AVPlayerItem。 - Tyler

2

我在考虑一种非常不同的方法,实际上是关于advanceToNextItem方法的。你说advanceToNextItem很好用。所以我想知道是否可以使用advanceToNextItem实现跳到先前的操作,但是通过将队列指向当前播放项的两个项目之前。

例如,如果您的队列如下,而粗体字是当前项目

A B C D E F G H

然后将当前项设置为C,然后使用advanceToNextItem,这样它就会播放D。

不确定您的advanceToNextItem是如何实现的。因此它取决于那个东西。


1
您可以将当前项目替换为上一个项目并添加被替换的项目:
let previousItem = AVPlayerItem(url: url )
let currentItem = AVPlayerItem(url: currentUrl)

 player.replaceCurrentItem(with: previousItem)
    if player.canInsert(currentItem, after: previousItem) {
         player.insert(currentItem, after: previousItem)
    }

// 或者像这样

 let previousItem = AVPlayerItem(url: url )
 if let currentItem = player.currentItem {
   player.replaceCurrentItem(with: previousItem)
     if player.canInsert(currentItem, after: previousItem) {
         player.insert(currentItem, after: previousItem)
     }
  }

-1
  1. 在第一项后插入第一项:
    player.insert(player.items()[0], after: player.items()[0])
  2. 在第一项后插入重新创建的上一个项目:
    player.insert(prevItem, after: player.items()[0])
  3. 调用player.advanceToNextItem()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接