我正在使用AVQueuePlayer
播放远程音频文件列表。我想默认实现全部重复。
我的方法是,我正在观察AVPlayerItemDidPlayToEndTime
通知,并在完成播放后将playerItem添加到队列的后面。
nextAudio(notification: Notification)
根本没有运行。在这种或更好的方法上需要帮助,以实现无限游戏。
func playAudio(_ items: [AVPlayerItem]) {
let avPlayerVC = AVPlayerViewController()
let player = AVQueuePlayer(items: items)
player.actionAtItemEnd = .pause
avPlayerVC.player = player
for item in items {
NotificationCenter.default.addObserver(self,
selector: #selector(FilesViewController.nextAudio(notification:)),
name: .AVPlayerItemDidPlayToEndTime, object: item)
}
present(avPlayerVC, animated: true) {
self.player.play()
}
}
@objc func nextAudio(notification: Notification) {
debugPrint("nextAudio was called")
guard player != nil else { return }
debugPrint("AVPlayerItemDidPlayToEndTime notif info \(notification.userInfo)")
if let currentItem = notification.userInfo!["object"] as? AVPlayerItem {
currentItem.seek(to: kCMTimeZero)
self.player.advanceToNextItem()
self.player.insert(currentItem, after: nil)
}
}
答案 0 :(得分:0)
我确定您已经弄明白了,但是我遇到了自己并决定回答:
当您指定通知对象时,它似乎未交付(顺便说一句,应该是正确的方式)。可能是iOS中的错误...
您需要通过nil
:
NotificationCenter.default.addObserver(self, selector: #selector(videoDidPlayToEnd), name: .AVPlayerItemDidPlayToEndTime, object: nil)
然后,您应该在方法中检查通知对象的对象是您的播放器项目。文档实际上是不一致的,因为Apple声明通知的对象是AVplayer
但它是AVPlayerItem
:
@objc
private func videoDidPlayToEnd(_ notification: Notification) {
guard let playerItem = notification.object as? AVPlayerItem, let urlAsset = playerItem.asset as? AVURLAsset else { return }
gpLog("Sender urlAsset: \(urlAsset.url.absoluteString)")
// Compare an asset URL.
}
答案 1 :(得分:0)
感谢@Lukasz的出色回答!我在多个视频在错误的时间触发通知时遇到了问题。您的回答帮助我解决了问题。
如果有人在寻找如何与 SwiftUI 结合使用的示例,这是我的以下代码:
首先创建一个播放器:
import SwiftUI
import AVKit
struct VideoPlayer : UIViewControllerRepresentable {
func makeCoordinator() -> VideoPlayer.Coordinator {
return VideoPlayer.Coordinator(parent1: self)
}
@Binding var didFinishVideo : Bool
@Binding var player : AVPlayer
var play: Bool
var loop: Bool
var videoName: String
var controller = AVPlayerViewController()
func makeUIViewController(context: UIViewControllerRepresentableContext<VideoPlayer>) -> AVPlayerViewController {
controller.player = player
controller.showsPlaybackControls = false
controller.videoGravity = .resize
NotificationCenter.default.addObserver(context.coordinator, selector: #selector(context.coordinator.playerDidFinishPlaying(_:)), name: .AVPlayerItemDidPlayToEndTime, object: nil)
return controller
}
func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<VideoPlayer>) {
if play {
player.play()
}
}
class Coordinator : NSObject{
var parent : VideoPlayer
init(parent1 : VideoPlayer) {
parent = parent1
}
@objc func playerDidFinishPlaying(_ notification: Notification) {
guard let playerItem = notification.object as? AVPlayerItem, let urlAsset = playerItem.asset as? AVURLAsset else { return }
print("Sender urlAsset: \(urlAsset.url.absoluteString)")
if urlAsset.url.absoluteString.contains(parent.videoName) {
if parent.loop {
parent.player.seek(to: CMTime.zero)
parent.player.play()
} else {
parent.didFinishVideo = true
}
}
}
}
}
您可以使用它来创建多个视频,如下所示:
import SwiftUI
import AVKit
struct ExampleVideo: View {
@Binding var didFinishVideo : Bool
var play: Bool
@State private var player = AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "exampleVideoFileName", ofType: "mp4")!))
var body: some View {
VideoPlayer(didFinishVideo: $didFinishVideo, player: $player, play: play, loop: false, videoName: "exampleVideoFileName")
}
}
struct ExampleVideo_Previews: PreviewProvider {
static var previews: some View {
CeveraIntroVideo(didFinishVideo: .constant(true), play: true)
}
}
下面是在加载某些内容后在视图中使用它的示例:
struct IntroScreens: View {
@State var loadingComplete = false
@State var didFinishVideo = false
var body: some View {
ZStack{
ExampleVideo(didFinishVideo: $didFinishVideo, play: loadingComplete)
.zIndex(loadingComplete ? 3 : 0)
.animation(Animation.easeInOut(duration: 1))
}
}
}