如何关闭以前的 AVPlayer 和 AVPlayerItem
How to close previous AVPlayer and AVPlayerItem
我正在 Swift 中制作一个 iOS 应用程序,它在屏幕右上角的一个小层中循环播放视频,显示特定颜色项目的视频。然后用户点击屏幕上相应的彩色项目。当他们这样做时,videoName 变量会随机更改为下一种颜色并触发相应的视频。如所附代码所示,使用 AVPlayer、AVPlayerItem 提升、播放和循环播放视频没有问题。
我被卡住的地方是,每当显示下一个视频时,之前的视频都会在它后面保持打开状态。另外,播放完 16 个视频后,播放器在我的 iPad 上完全消失了。我已经尝试了这个网站和其他网站上提出的许多建议,但要么 swift 发现这些建议有问题,要么根本不起作用。
所以问题:在我的代码中,我该如何告诉它 "hey the next video has started to play, remove the previous video and it's layer and free up the memory so i can play as many videos as needed"?
//set variables for video play
var playerItem:AVPlayerItem?
var player:AVPlayer?
//variables that contain video file path, name and extension
var videoPath = NSBundle.mainBundle().resourcePath!
var videoName = "blue"
let videoExtension = ".mp4"
//DISPLAY VIDEO
func showVideo(){
//Assign url path
let url = NSURL(fileURLWithPath: videoPath+"/Base.lproj/"+videoName+videoExtension)
playerItem = AVPlayerItem(URL: url)
player=AVPlayer(playerItem: playerItem!)
let playerLayer=AVPlayerLayer(player: player!)
//setplayser location in uiview and show video
playerLayer.frame=CGRectMake(700, 5, 350, 350)
self.view.layer.addSublayer(playerLayer)
player!.play()
// Add notification to know when the video ends, then replay it again. THIS IS A CONTINUAL LOOP
NSNotificationCenter.defaultCenter().addObserverForName(AVPlayerItemDidPlayToEndTimeNotification, object: player!.currentItem, queue: nil)
{ notification in
let t1 = CMTimeMake(5, 100);
self.player!.seekToTime(t1)
self.player!.play()
}
}
`
为什么不直接在播放器上使用 replaceCurrentItemWithPlayerItem
?您将为所有视频保留同一个播放器。我认为这是一个更好的方法。
编辑: replaceCurrentItemWithPlayerItem
必须在主线程上调用
在继续下一首曲目之前,首先检查播放器是否有任何视频或音乐,要停止它,请执行以下检查:
Swift代码-
if player!.rate > 0 && player!.error == nil
{
player!.pause()
player = nil
}
Objective-C代码-
if (player.rate > 0 && !player.error)
{
[player setRate:0.0];
}
我采纳了@Anupam Mishra 的Swift 代码建议。一开始它没有用,但最后我想我必须把 playerLayer 带到函数之外,并在我将播放器设置为 nil 后关闭 playerLayer。还。我没有使用毫无疑问仍然有效的 'if(player!.rate>0 ...)',而是创建了一个变量开关来指示何时说 "kill the player AND the layer",如下所示。它可能不漂亮,但它确实有效! 以下内容适用于像我这样的绝对新手 - 我从这次经历中学到的东西:在我看来,ios 设备只允许将 16 层添加到 viewController(或超级图层)。所以每一层都需要在用它的播放器打开下一层之前被删除,除非你真的想要一次全部 16 层 运行。 下面这段代码实际上为您做了什么:这段代码在现有 viewController 上创建了一个可调整大小的图层,并播放 您的捆绑包中的视频 无限循环。当即将调用下一个视频时,将当前视频和层完全移除,释放内存,然后播放包含新视频的新层。使用 playerLayer.frame=CGRectMake(left, top, width, height) 参数可以完全自定义视频层的大小和位置。 如何让它全部工作:假设您已经将视频添加到您的捆绑包中,请为您的按钮点击创建另一个功能。在该函数中,首先调用 'stopPlayer()' 函数,将 'videoName' 变量更改为您想要的新视频名称,然后调用 'showVideo()' 函数。 (如果您需要更改视频扩展名,请将 'videoExtension' 从 let 更改为 var。
`
//set variables for video play
var playerItem:AVPlayerItem?
var player:AVPlayer?
var playerLayer = AVPlayerLayer() //NEW playerLayer var location
//variables that contain video file path, name and extension
var videoPath = NSBundle.mainBundle().resourcePath!
var videoName = "blue"
let videoExtension = ".mp4"
var createLayerSwitch = true /*NEW switch to say whether on not to create the layer when referenced by the closePlayer and showVideo functions*/
//DISPLAY VIDEO
func showVideo(){
//Assign url path
let url = NSURL(fileURLWithPath: videoPath+"/Base.lproj/"+videoName+videoExtension)
playerItem = AVPlayerItem(URL: url)
player=AVPlayer(playerItem: playerItem!)
playerLayer=AVPlayerLayer(player: player!) //NEW: remove 'let' from playeLayer here.
//setplayser location in uiview and show video
playerLayer.frame=CGRectMake(700, 5, 350, 350)
self.view.layer.addSublayer(playerLayer)
player!.play()
createLayerSwitch = false //NEW switch to tell if a layer is already created or not. I set the switch to false so that when the next tapped item/button references the closePlayer() function, the condition is triggered to close the player AND the layer
// Add notification to know when the video ends, then replay it again without a pause between replays. THIS IS A CONTINUAL LOOP
NSNotificationCenter.defaultCenter().addObserverForName(AVPlayerItemDidPlayToEndTimeNotification, object: player!.currentItem, queue: nil)
{ notification in
let t1 = CMTimeMake(5, 100);
self.player!.seekToTime(t1)
self.player!.play()
}
}
//NEW function to kill the current player and layer before playing the next video
func closePlayer(){
if (createLayerSwitch == false) {
player!.pause()
player = nil
playerLayer.removefromsuperlayer()
}
}
`
我正在 Swift 中制作一个 iOS 应用程序,它在屏幕右上角的一个小层中循环播放视频,显示特定颜色项目的视频。然后用户点击屏幕上相应的彩色项目。当他们这样做时,videoName 变量会随机更改为下一种颜色并触发相应的视频。如所附代码所示,使用 AVPlayer、AVPlayerItem 提升、播放和循环播放视频没有问题。 我被卡住的地方是,每当显示下一个视频时,之前的视频都会在它后面保持打开状态。另外,播放完 16 个视频后,播放器在我的 iPad 上完全消失了。我已经尝试了这个网站和其他网站上提出的许多建议,但要么 swift 发现这些建议有问题,要么根本不起作用。 所以问题:在我的代码中,我该如何告诉它 "hey the next video has started to play, remove the previous video and it's layer and free up the memory so i can play as many videos as needed"?
//set variables for video play
var playerItem:AVPlayerItem?
var player:AVPlayer?
//variables that contain video file path, name and extension
var videoPath = NSBundle.mainBundle().resourcePath!
var videoName = "blue"
let videoExtension = ".mp4"
//DISPLAY VIDEO
func showVideo(){
//Assign url path
let url = NSURL(fileURLWithPath: videoPath+"/Base.lproj/"+videoName+videoExtension)
playerItem = AVPlayerItem(URL: url)
player=AVPlayer(playerItem: playerItem!)
let playerLayer=AVPlayerLayer(player: player!)
//setplayser location in uiview and show video
playerLayer.frame=CGRectMake(700, 5, 350, 350)
self.view.layer.addSublayer(playerLayer)
player!.play()
// Add notification to know when the video ends, then replay it again. THIS IS A CONTINUAL LOOP
NSNotificationCenter.defaultCenter().addObserverForName(AVPlayerItemDidPlayToEndTimeNotification, object: player!.currentItem, queue: nil)
{ notification in
let t1 = CMTimeMake(5, 100);
self.player!.seekToTime(t1)
self.player!.play()
}
}
`
为什么不直接在播放器上使用 replaceCurrentItemWithPlayerItem
?您将为所有视频保留同一个播放器。我认为这是一个更好的方法。
编辑: replaceCurrentItemWithPlayerItem
必须在主线程上调用
在继续下一首曲目之前,首先检查播放器是否有任何视频或音乐,要停止它,请执行以下检查:
Swift代码-
if player!.rate > 0 && player!.error == nil
{
player!.pause()
player = nil
}
Objective-C代码-
if (player.rate > 0 && !player.error)
{
[player setRate:0.0];
}
我采纳了@Anupam Mishra 的Swift 代码建议。一开始它没有用,但最后我想我必须把 playerLayer 带到函数之外,并在我将播放器设置为 nil 后关闭 playerLayer。还。我没有使用毫无疑问仍然有效的 'if(player!.rate>0 ...)',而是创建了一个变量开关来指示何时说 "kill the player AND the layer",如下所示。它可能不漂亮,但它确实有效! 以下内容适用于像我这样的绝对新手 - 我从这次经历中学到的东西:在我看来,ios 设备只允许将 16 层添加到 viewController(或超级图层)。所以每一层都需要在用它的播放器打开下一层之前被删除,除非你真的想要一次全部 16 层 运行。 下面这段代码实际上为您做了什么:这段代码在现有 viewController 上创建了一个可调整大小的图层,并播放 您的捆绑包中的视频 无限循环。当即将调用下一个视频时,将当前视频和层完全移除,释放内存,然后播放包含新视频的新层。使用 playerLayer.frame=CGRectMake(left, top, width, height) 参数可以完全自定义视频层的大小和位置。 如何让它全部工作:假设您已经将视频添加到您的捆绑包中,请为您的按钮点击创建另一个功能。在该函数中,首先调用 'stopPlayer()' 函数,将 'videoName' 变量更改为您想要的新视频名称,然后调用 'showVideo()' 函数。 (如果您需要更改视频扩展名,请将 'videoExtension' 从 let 更改为 var。
`
//set variables for video play
var playerItem:AVPlayerItem?
var player:AVPlayer?
var playerLayer = AVPlayerLayer() //NEW playerLayer var location
//variables that contain video file path, name and extension
var videoPath = NSBundle.mainBundle().resourcePath!
var videoName = "blue"
let videoExtension = ".mp4"
var createLayerSwitch = true /*NEW switch to say whether on not to create the layer when referenced by the closePlayer and showVideo functions*/
//DISPLAY VIDEO
func showVideo(){
//Assign url path
let url = NSURL(fileURLWithPath: videoPath+"/Base.lproj/"+videoName+videoExtension)
playerItem = AVPlayerItem(URL: url)
player=AVPlayer(playerItem: playerItem!)
playerLayer=AVPlayerLayer(player: player!) //NEW: remove 'let' from playeLayer here.
//setplayser location in uiview and show video
playerLayer.frame=CGRectMake(700, 5, 350, 350)
self.view.layer.addSublayer(playerLayer)
player!.play()
createLayerSwitch = false //NEW switch to tell if a layer is already created or not. I set the switch to false so that when the next tapped item/button references the closePlayer() function, the condition is triggered to close the player AND the layer
// Add notification to know when the video ends, then replay it again without a pause between replays. THIS IS A CONTINUAL LOOP
NSNotificationCenter.defaultCenter().addObserverForName(AVPlayerItemDidPlayToEndTimeNotification, object: player!.currentItem, queue: nil)
{ notification in
let t1 = CMTimeMake(5, 100);
self.player!.seekToTime(t1)
self.player!.play()
}
}
//NEW function to kill the current player and layer before playing the next video
func closePlayer(){
if (createLayerSwitch == false) {
player!.pause()
player = nil
playerLayer.removefromsuperlayer()
}
}
`