我正在使用 AVPlayer 在 Swift 中播放本地视频文件(mp4)。 有谁知道如何检测视频何时播放结束? 谢谢
要获得
AVPlayerItemDidPlayToEndTimeNotification
,您的对象需要是 AVPlayerItem
。
为此,只需使用 .currentItem
AVPlayer
属性即可
现在,视频结束后您将收到通知!
看我的例子:
let videoPlayer = AVPlayer(URL: url)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "playerDidFinishPlaying:",
name: AVPlayerItemDidPlayToEndTimeNotification, object: videoPlayer.currentItem)
func playerDidFinishPlaying(note: NSNotification) {
print("Video Finished")
}
斯威夫特3
let videoPlayer = AVPlayer(URL: url)
NotificationCenter.default.addObserver(self, selector: Selector(("playerDidFinishPlaying:")),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: videoPlayer.currentItem)
func playerDidFinishPlaying(note: NSNotification) {
print("Video Finished")
}
不要忘记删除您的
deinit
中的观察者
斯威夫特 4、5
NotificationCenter.default
.addObserver(self,
selector: #selector(playerDidFinishPlaying),
name: .AVPlayerItemDidPlayToEndTime,
object: videoPlayer.currentItem
)
斯威夫特3.0
let videoPlayer = AVPlayer(URL: url)
NotificationCenter.default.addObserver(self, selector:#selector(self.playerDidFinishPlaying(note:)),name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem)
@objc func playerDidFinishPlaying(note: NSNotification){
print("Video Finished")
}
Swift 4.2 版本:
var player: AVPlayer!
//
//
// Configure Player
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let filepath: String? = Bundle.main.path(forResource: "selectedFileName", ofType: "mp4")
if let filepath = filepath {
let fileURL = URL.init(fileURLWithPath: filepath)
player = AVPlayer(url: fileURL)
let playerLayer = AVPlayerLayer(player: player)
// Register for notification
NotificationCenter.default.addObserver(self,
selector: #selector(playerItemDidReachEnd),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
object: nil) // Add observer
playerLayer.frame = self.view.bounds
self.view.layer.addSublayer(playerLayer)
player.play()
}
}
// Notification Handling
@objc func playerItemDidReachEnd(notification: NSNotification) {
player.seek(to: CMTime.zero)
player.play()
}
// Remove Observer
deinit {
NotificationCenter.default.removeObserver(self)
}
如果您喜欢使用组合:
private var cancelBag: Set<AnyCancellable> = []
NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime)
.sink { _ in
player.seek(to: CMTime.zero)
player.play()
}
.store(in: &cancelBag)
适用于 SWIFT 3.0 这工作正常
class PlayVideoViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(PlayVideoViewController.finishVideo), name: NSNotification.Name.AVPlayerItemDidPlayToEndTimeNotification, object: nil)
}
func finishVideo()
{
print("Video Finished")
}
}
斯威夫特4.0
这个对我有用。感谢@频道
private func playVideo(fileURL: String) {
// Create RUL object
let url = URL(string: fileURL)
// Create Player Item object
let playerItem: AVPlayerItem = AVPlayerItem(url: url!)
// Assign Item to Player
let player = AVPlayer(playerItem: playerItem)
// Prepare AVPlayerViewController
let videoPlayer = AVPlayerViewController()
// Assign Video to AVPlayerViewController
videoPlayer.player = player
NotificationCenter.default.addObserver(self, selector: #selector(myViewController.finishVideo), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
// Present the AVPlayerViewController
present(videoPlayer, animated: true, completion: {
// Play the Video
player.play()
})
}
@objc func finishVideo()
{
print("Video Finished")
}
SWIFT 5 更新
带有 @objc 函数的观察者方法不是原生的。最好在 swift 5 中使用事件发布器。非常简单。
在结构中声明以下内容:
var pub = NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime)
然后在任何视图上添加
.onReceive(pub) { (output) in
print("Video Finished")
}
其实就是这么简单
override func viewDidLoad() {
super.viewDidLoad()
// Note, somewhat bizarrely, it's perfectly OK to add
// the end time notification once for a view controller,
// and even before you create the player, etc. See notes.
NotificationCenter.default.addObserver(
self,
selector: #selector(fileComplete),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
object: nil
)
...
(
object
(调用的最后一个参数)为零就可以了。)
然后
@objc func fileComplete() {
print("one video has reached -0 time")
}
这就是为什么......
如果您多次运行上述代码,则每次视频结束时您都会收到多个通知。即“fileComplete”将被多次调用。
通常你的代码看起来像..
有点烦人的是,您必须安排调用有问题的代码,
.addObserver
,整个系统只能调用一次。
并且,是的,即使您还没有打开 AVPlayer,也完全可以
.addObserver
。
事实上,最好的地方就是 viewDidLoad - 确保它只运行一次。
多年来,在 iOS 中,当屏幕消失时,您不需要删除观察者。 Apple 对此有完整记录。
需要明确的是,如果您使用
addObserver
的块形式,那么完成后您仍然必须删除观察者。使用 addObserver 的选择器形式(如此处的代码示例所示),完成后不需要显式删除观察者。
斯威夫特3.0
let videoPlayer = AVPlayer(URL: url)
NotificationCenter.default.addObserver(self, selector:#selector(self.playerDidFinishPlaying(note:)),name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem)
func playerDidFinishPlaying(note: NSNotification){
//Called when player finished playing
}
适用于 SWIFT 3.0
这里“fullUrl”是视频的URL,并确保URL中没有空格,您应该将“Space”替换为“%20”,以便URL可以工作。
let videoURL = NSURL(string: fullUrl)
let player = AVPlayer(url: videoURL! as URL)
playerViewController.delegate = self
playerViewController.player = player
self.present(playerViewController, animated: false) {
self.playerViewController.player!.play()
NotificationCenter.default.addObserver(self, selector: #selector(yourViewControllerName.playerDidFinishPlaying), name: Notification.Name.AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem)
}
在视图控制器中添加下面给定的方法。
func playerDidFinishPlaying(){
print("Video Finished playing in style")
}
我知道这里有很多被接受的答案......
但是,另一种方法可能是向 AVPlayer 添加边界时间观察器。您必须拥有视频的持续时间,您可以从
player.currentItem
获取该持续时间,然后将其添加为您所需的时间范围。
fileprivate var videoEndObserver: Any?
func addVideoEndObserver() {
guard let player = YOUR_VIDEO_PLAYER else { return }
// This is just in case you are loading a video from a URL.
guard let duration = player.currentItem?.duration, duration.value != 0 else {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: { [weak self] in
self?.addVideoEndObserver()
})
return
}
let endTime = NSValue(time: duration - CMTimeMakeWithSeconds(0.1, duration.timescale))
videoEndObserver = player.addBoundaryTimeObserver(forTimes: [endTime], queue: .main, using: {
self.removeVideoEndObserver()
// DO YOUR STUFF HERE...
})
}
func removeVideoEndObserver() {
guard let observer = videoEndObserver else { return }
videoPlayer.player?.removeTimeObserver(observer)
videoEndObserver = nil
}
func shareEditedVedio() -> AVPlayer {
let editedVedioPlayer = AVPlayer(url: self.vedioData.vedioURLWithAddedSounds!)
NotificationCenter.default.addObserver(self, selector:#selector(self.playerDidFinishPlaying(note:)),name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: editedVedioPlayer.currentItem)
return editedVedioPlayer
}
@objc func playerDidFinishPlaying(note: NSNotification){
//Called when player finished playing
}
在 Swift 3 和 RxSwift 3.5 中,您所要做的就是:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.rx.notification(Notification.Name.AVPlayerItemDidPlayToEndTime)
.asObservable().subscribe(onNext: { [weak self] notification in
//Your action
}).addDisposableTo(disposeBag)
}
使用
Combine
,并确保通知来自您感兴趣的 AVPlayerItem 而不仅仅是任何通知。我同时播放多个项目,因此这也适用于这种情况。
private var subscriptions: Set<AnyCancellable> = []
NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime, object: player.currentItem)
.receive(on: RunLoop.main)
.sink { [weak self] notification in
guard let item = notification.object as? AVPlayerItem else { return }
if item == self?.player.currentItem {
//.... Here you know it was the item you are interested in that played to end and not just any
}
}
.store(in: &subscriptions)
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: nil, queue: .main) { noti in
guard let item = noti.object as? AVPlayerItem else{
return
}
//DidPlayToEndTime
}
我遇到了通知从未被调用的问题,在 AVPlayerViewController 的演示文稿中设置通知为我解决了这个问题:
func presentVideo(url:URL) {
let player = AVPlayer(url: url)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated: true) {
DispatchQueue.main.async {
playerViewController.player!.play()
//NOTE: The notification must be created here for it to work as expected
NotificationCenter.default.addObserver(self, selector: #selector(self.videoDidEnd), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
}
}
}
另一种解决方案:
player.observe(\AVPlayer.actionAtItemEnd) { player, _ in
print("video did end")
}