Swift Mac OS - 如何播放另一个视频/点击按钮后用AVPlayer更改视频的URL?

问题描述:

我是swift的新手,我试图制作一个Mac OS应用程序,该应用程序启动后,使用AVPlayer作为窗口背景,从应用程序的资源中循环播放视频。当用户选择一个菜单项/点击一个按钮时,背景视频将立即变成与应用程序资源不同的视频,并开始将该视频作为窗口背景循环播放。Swift Mac OS - 如何播放另一个视频/点击按钮后用AVPlayer更改视频的URL?

我能够发挥第一视频曾跟随本教程中的应用程序启动:(https://youtu.be/QgeQc587w70),我也成功地进行了视频环本身无缝以下这篇文章:(Looping AVPlayer seamlessly)。

我现在面临的问题是,一旦选择菜单项/单击按钮,将视频更改为另一个。我想要的方法是更改​​网址,并使用新的URL创建一个新的AVPlayer,并将其影响到playerView.player后面的这个帖子:(Swift How to update url of video in AVPlayer when clicking on a button?)。但每次选择菜单项时,应用程序都会崩溃,并显示错误“thread 1 exc_bad_instruction(code = exc_i386_invop subcode = 0x0)”。这显然是由playerView的值为零造成的。我真的不明白这个原因,因为playerView是一个AVPlayerView对象,我使用xib文件创建并通过控制拖动链接到swift文件,我似乎无法找到另一个适当的方法来执行我想要的操作去做。如果你知道这个原因和解决方法,请给我一些帮助,或者如果你知道一个更好的方法做我以上提到的,请告诉我。任何帮助将非常感激!

我的代码如下,即崩溃的应用程序行是在底部:

import Cocoa 
import AppKit 
import AVKit 
import AVFoundation 

struct videoVariables { 
    static var videoName = "Test_Video" //declaring the video name as a global variable 
} 

    var videoIsPlaying = true 
    var theURL = Bundle.main.url(forResource:videoVariables.videoName, withExtension: "mp4") //creating the video url 
    var player = AVPlayer.init(url: theURL!) 

class BackgroundWindow: NSWindowController {  
    @IBOutlet weak var playerView: AVPlayerView! // AVPlayerView Linked using control-drag from xib file 
    @IBOutlet var mainWindow: NSWindow! 
    @IBOutlet weak var TempBG: NSImageView! 
    override var windowNibName : String! { 
     return "BackgroundWindow" 
    } 


//function used for resizing the temporary background image and the playerView to the window’s size 
    func resizeBG() { 
     var scrn: NSScreen = NSScreen.main()! 
     var rect: NSRect = scrn.frame 
     var height = rect.size.height 
     var width = rect.size.width 
     TempBG.setFrameSize(NSSize(width: Int(width), height: Int(height))) 
     TempBG.frame.origin = CGPoint(x: 0, y: 0) 
     playerView!.setFrameSize(NSSize(width: Int(width), height: Int(height))) 
     playerView!.frame.origin = CGPoint(x: 0, y: 0) 
    } 

    override func windowDidLoad() { 
     super.windowDidLoad() 
     self.window?.titleVisibility = NSWindowTitleVisibility.hidden //hide window’s title 
     self.window?.styleMask = NSBorderlessWindowMask //hide window’s border 
     self.window?.hasShadow = false //hide window’s shadow 
     self.window?.level = Int(CGWindowLevelForKey(CGWindowLevelKey.desktopWindow)) //set window’s layer as desktopWindow layer 
     self.window?.center() 
     self.window?.makeKeyAndOrderFront(nil) 
     NSApp.activate(ignoringOtherApps: true) 
     if let screen = NSScreen.main() { 
      self.window?.setFrame(screen.visibleFrame, display: true, animate: false) //resizing the window to cover the whole screen 
     } 
     resizeBG() //resizing the temporary background image and the playerView to the window’s size 
     startVideo() //start playing and loop the first video as the window’s background 
    } 

//function used for starting the video again once it has been played fully 
    func playerItemDidReachEnd(notification: NSNotification) { 
     playerView.player?.seek(to: kCMTimeZero) 
     playerView.player?.play() 
    } 

//function used for starting and looping the video  
    func startVideo() { 
     //set the seeking time to be 2ms ahead to prevent a black screen every time the video loops 
     let playAhead = CMTimeMake(2, 100); 
      //loops the video 
      NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: 
       playerView.player?.currentItem, queue: nil, using: { (_) in 
        DispatchQueue.main.async { 

         self.playerView.player?.seek(to: playAhead) 
         self.playerView.player?.play() 
        } 
       }) 

     var playerLayer: AVPlayerLayer? 
     playerLayer = AVPlayerLayer(player: player) 
     playerView?.player = player 
     print(playerView?.player) 
     playerLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill 
     player.play() 


    } 


//changing the url to the new url and create a new AVPlayer then affect it to the playerView.player once the menu item is being selected 
    @IBAction func renderBG(_ sender: NSMenuItem) { 
     videoVariables.videoName = "Test_Video_2" 
     var theNewURL = Bundle.main.url(forResource:videoVariables.videoName, withExtension: "mp4") 
     player = AVPlayer.init(url: theNewURL!) 

     //!!this line crashes the app with the error "thread 1 exc_bad_instruction (code=exc_i386_invop subcode=0x0)" every time the menu item is being selected!! 
     playerView.player = player  
    } 

} 

此外,背景视频不应该是互动的(例如,用户不能暂停/快进视频),所以任何可能由用户交互引起的问题都可以忽略。该应用的目的是发挥创建运行命令完全相同的效果在用户的桌面上的视频:

“/System/Library/Frameworks/ScreenSaver.framework/Resources/ ScreenSaverEngine.app/Contents/MacOS/ScreenSaverEngine -background“。

任何帮助将不胜感激!

您不需要从url创建AVPlayer。有AVPlayerItem类来操纵播放器播放队列。

let firstAsset = AVURLAsset(url: firstVideoUrl) 
let firstPlayerItem = AVPlayerItem(asset: firstAsset) 

let player = AVPlayer(playerItem: firstPlayerItem) 

let secondAsset = AVURLAsset(url: secondVideoUrl)  
let secondPlayerItem = AVPlayerItem(asset: secondAsset) 

player.replaceCurrentItem(with: secondPlayerItem) 

文档约AVPlayerItem