以某种方式生成 AVPlayerItem 的副本而不是访问引用
Somehow generating copy of AVPlayerItem instead accessing reference
我有一个使用 AVQueuePlayer 对象的简单音频播放列表。通过迭代包含自定义 UITableViewCell 对象 PlayableAudioCell 的 UITableView 来填充队列。这些对象有一个 AVPlayerItem 属性、audioTrack,我将其插入到 AVQueueplayer 的队列中。
我想在播放和完成项目时向这些轨道添加和删除 AVPlayerItemDidPlayToEndTime 观察器,以便我可以更新我创建的音乐播放器的标签。
出于某种原因,如果队列超过 2 个轨道,则队列中的第三个轨道不会将观察者添加到其中,因为应用它的函数传递了轨道的副本,而不是保存到 PlayableAudioCell 对象的曲目。
我不明白这是怎么回事,但我一定是在某处创建了音轨的副本,而不是引用单元格的音轨。从音轨内存位置的打印语句可以看出我是。
只是对我的应用程序布局的一些解释...它包含一个 tableViewController 视图,允许用户 select 一个单元格并播放曲目.我还有一个 SplitViewController,它允许 FooterView 弹出并显示播放曲目信息(标签、持续时间、曲目滑块)。
操作顺序:
用户在 TableView 的单元格上按下播放键
AudioQueuePlayer 的队列由按下的曲目及其下方的每个曲目填充
TableViewController的委托(FooterViewController)调用它的委托播放方法
AVPlayerItemDidPlayToEndTime 观察器添加到播放的第一个项目
当跟踪结束时,selector 被观察者调用。在此 selector 中,删除了当前轨道的观察器,并添加了将 AVPlayerItemDidPlayToEndTime 观察器添加到下一轨道的代码。
当我访问单元格副本或音轨副本而不是引用 currentlySelectedAudioCell 的音轨 属性 时,是否会弹出此代码?
我将在表中省略我的代码ViewController...我可以看出它在 table 中被正确填充并且问题必须出在页脚的代码中ViewController:
func play(cell: PlayableAudioCell){
if let previousCell = currentlySelectedAudioCell, let prevFilename = currentlySelectedAudioCell?.chapter?.value(forKey: "Filename") as? String, let filename = cell.chapter?.value(forKey: "Filename") as? String{
if filename != prevFilename{
runningTime = 0
}
}
updateWithObserver(cell: cell)
//show footerview if it is hidden.
if(footerView.superview?.isHidden)!{
hideShowFooterView()
}
if(self.isPlaying)!{
//print("Should pause cell")
audioQueuePlayer?.pause()
switchState(state: "Pause")
playPauseRunTime(state: "Pause")
}else{
//print("Should play cell")
audioQueuePlayer?.play()
switchState(state: "Play")
playPauseRunTime(state: "Play")
}
updateFooterView()
}
func updateWithObserver(cell: PlayableAudioCell){
self.currentlySelectedAudioCell = cell
delegate?.currentlySelectedAudioCell = cell
let track = cell.audioTrack!
NotificationCenter.default.addObserver(self, selector: #selector(FooterViewController.playerItemDidReachEnd(notification:)), name: Notification.Name.AVPlayerItemDidPlayToEndTime, object: track)
}
func playerItemDidReachEnd(notification: Notification){
NotificationCenter.default.removeObserver(self, name: notification.name, object: audioQueuePlayer?.currentItem)
if didAudioPlayEntirely(){
recordAudioHistoryToFirebase(didFinish: true)
if let currentlySelectedAudioCell = delegate?.currentlySelectedAudioCell{
revealCheckMark(cell: currentlySelectedAudioCell)
}
}
runningTime = 0
//If there is just one more item in the queue, regardless of whether playAll() has been toggled...
if (audioQueuePlayer?.items().count)! <= 1 {
playPauseRunTime(state: "Pause")
audioQueuePlayer?.removeAllItems()
resetFooterViewUI()
switchState(state: "Pause")
hideShowFooterView()
audioQueuePlayer?.rate = 0.0
delegate?.currentlySelectedAudioCell = nil
currentlySelectedAudioCell = nil
delegate?.indexPathOfSelectedCell = nil
indexPathOfSelectedCell = nil
}else{
if let indexPathOfSelectedCell = indexPathOfSelectedCell{
playPauseRunTime(state: "Pause")
playPauseRunTime(state: "Play")
let row = indexPathOfSelectedCell.row + 1
let newIndexPath = IndexPath(row: row, section: 0)
self.indexPathOfSelectedCell = newIndexPath
delegate?.indexPathOfSelectedCell = newIndexPath
let newCell = self.tableView!.cellForRow(at: newIndexPath) as? PlayableAudioCell
updateWithObserver(cell: newCell!)
updateFooterView()
self.tableView!.reloadData()
}
}
}
显然,AVQueuePlayer 每次播放完其队列中的项目时都会删除一个 AVPlayerItem。我的逻辑是尝试将队列的索引与 tableview 单元格的数组相匹配,经过几次迭代后,索引将关闭并且观察者不会被添加到正确的播放器项目中。通过用 AVPlayer 和 AVPlayerItems 数组替换 AVQueuePlayer 来修复。
我有一个使用 AVQueuePlayer 对象的简单音频播放列表。通过迭代包含自定义 UITableViewCell 对象 PlayableAudioCell 的 UITableView 来填充队列。这些对象有一个 AVPlayerItem 属性、audioTrack,我将其插入到 AVQueueplayer 的队列中。
我想在播放和完成项目时向这些轨道添加和删除 AVPlayerItemDidPlayToEndTime 观察器,以便我可以更新我创建的音乐播放器的标签。
出于某种原因,如果队列超过 2 个轨道,则队列中的第三个轨道不会将观察者添加到其中,因为应用它的函数传递了轨道的副本,而不是保存到 PlayableAudioCell 对象的曲目。
我不明白这是怎么回事,但我一定是在某处创建了音轨的副本,而不是引用单元格的音轨。从音轨内存位置的打印语句可以看出我是。
只是对我的应用程序布局的一些解释...它包含一个 tableViewController 视图,允许用户 select 一个单元格并播放曲目.我还有一个 SplitViewController,它允许 FooterView 弹出并显示播放曲目信息(标签、持续时间、曲目滑块)。
操作顺序:
用户在 TableView 的单元格上按下播放键
AudioQueuePlayer 的队列由按下的曲目及其下方的每个曲目填充
TableViewController的委托(FooterViewController)调用它的委托播放方法
AVPlayerItemDidPlayToEndTime 观察器添加到播放的第一个项目
当跟踪结束时,selector 被观察者调用。在此 selector 中,删除了当前轨道的观察器,并添加了将 AVPlayerItemDidPlayToEndTime 观察器添加到下一轨道的代码。
当我访问单元格副本或音轨副本而不是引用 currentlySelectedAudioCell 的音轨 属性 时,是否会弹出此代码?
我将在表中省略我的代码ViewController...我可以看出它在 table 中被正确填充并且问题必须出在页脚的代码中ViewController:
func play(cell: PlayableAudioCell){
if let previousCell = currentlySelectedAudioCell, let prevFilename = currentlySelectedAudioCell?.chapter?.value(forKey: "Filename") as? String, let filename = cell.chapter?.value(forKey: "Filename") as? String{
if filename != prevFilename{
runningTime = 0
}
}
updateWithObserver(cell: cell)
//show footerview if it is hidden.
if(footerView.superview?.isHidden)!{
hideShowFooterView()
}
if(self.isPlaying)!{
//print("Should pause cell")
audioQueuePlayer?.pause()
switchState(state: "Pause")
playPauseRunTime(state: "Pause")
}else{
//print("Should play cell")
audioQueuePlayer?.play()
switchState(state: "Play")
playPauseRunTime(state: "Play")
}
updateFooterView()
}
func updateWithObserver(cell: PlayableAudioCell){
self.currentlySelectedAudioCell = cell
delegate?.currentlySelectedAudioCell = cell
let track = cell.audioTrack!
NotificationCenter.default.addObserver(self, selector: #selector(FooterViewController.playerItemDidReachEnd(notification:)), name: Notification.Name.AVPlayerItemDidPlayToEndTime, object: track)
}
func playerItemDidReachEnd(notification: Notification){
NotificationCenter.default.removeObserver(self, name: notification.name, object: audioQueuePlayer?.currentItem)
if didAudioPlayEntirely(){
recordAudioHistoryToFirebase(didFinish: true)
if let currentlySelectedAudioCell = delegate?.currentlySelectedAudioCell{
revealCheckMark(cell: currentlySelectedAudioCell)
}
}
runningTime = 0
//If there is just one more item in the queue, regardless of whether playAll() has been toggled...
if (audioQueuePlayer?.items().count)! <= 1 {
playPauseRunTime(state: "Pause")
audioQueuePlayer?.removeAllItems()
resetFooterViewUI()
switchState(state: "Pause")
hideShowFooterView()
audioQueuePlayer?.rate = 0.0
delegate?.currentlySelectedAudioCell = nil
currentlySelectedAudioCell = nil
delegate?.indexPathOfSelectedCell = nil
indexPathOfSelectedCell = nil
}else{
if let indexPathOfSelectedCell = indexPathOfSelectedCell{
playPauseRunTime(state: "Pause")
playPauseRunTime(state: "Play")
let row = indexPathOfSelectedCell.row + 1
let newIndexPath = IndexPath(row: row, section: 0)
self.indexPathOfSelectedCell = newIndexPath
delegate?.indexPathOfSelectedCell = newIndexPath
let newCell = self.tableView!.cellForRow(at: newIndexPath) as? PlayableAudioCell
updateWithObserver(cell: newCell!)
updateFooterView()
self.tableView!.reloadData()
}
}
}
显然,AVQueuePlayer 每次播放完其队列中的项目时都会删除一个 AVPlayerItem。我的逻辑是尝试将队列的索引与 tableview 单元格的数组相匹配,经过几次迭代后,索引将关闭并且观察者不会被添加到正确的播放器项目中。通过用 AVPlayer 和 AVPlayerItems 数组替换 AVQueuePlayer 来修复。