I'm attempting to play a simple local clip in Xcode7 using Swift for tvOS. All I want is for the video to load fullscreen in the view and loop. Every tutorial I see has the old MPMoviePlayerController or loads from a URL. What is the best way to do this on tvOS?
Update2: This got the video to loop but there is a pause in between. looking into it now to see how to do it seamlessly.
import UIKit
import AVKit
import AVFoundation
class ViewController: UIViewController {
var videoPlayer: AVPlayer!
var playerLayer: AVPlayerLayer?
override func viewDidLoad() {
super.viewDidLoad()
let path = NSBundle.mainBundle().pathForResource("video", ofType:"mp4")
let url = NSURL(fileURLWithPath: path!)
let playerItem = AVPlayerItem(URL: url)
self.videoPlayer = AVPlayer(playerItem: playerItem)
self.playerLayer = AVPlayerLayer(player: self.videoPlayer)
self.playerLayer!.frame = self.view.frame
self.videoPlayer!.play()
self.view.layer.addSublayer(self.playerLayer!)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("playerDidReachEnd:"), name: AVPlayerItemDidPlayToEndTimeNotification, object:nil)
}
func playerDidReachEnd(notification: NSNotification) {
self.videoPlayer.seekToTime(kCMTimeZero)
self.videoPlayer.play()
}
}
Let's say your file is named video.m4v and is stored locally on the device (don't forget to add it to Copy Bundle Resources in Build Phases). Create a stored property so you can access the AVPlayer-instance:
var videoPlayer: AVPlayer?
Setup the video player and add the AVPlayerLayer to your view hierarchy.
if let path = NSBundle.mainBundle().pathForResource("video", ofType:"m4v") {
let url = NSURL(fileURLWithPath: path)
let playerItem = AVPlayerItem(URL: url)
self.videoPlayer = AVPlayer(playerItem: playerItem)
self.playerLayer = AVPlayerLayer(player: self.videoPlayer)
self.playerLayer!.frame = self.view.frame
self.view.layer.addSublayer(self.playerLayer!)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("playerDidReachEnd:"), name: AVPlayerItemDidPlayToEndTimeNotification, object:nil)
}
You have now subscribed a notification that i sent when this video ends. Implement handler and tell the player to replay video.
func playerDidReachEnd(notification: NSNotification) {
self.videoPlayer.seekToTime(kCMTimeZero)
self.videoPlayer.play()
}
Related
I am trying to determine if a user taps on the unmute button while watching a video. I am aware of player.isMuted but I am not sure how to check if there is a change if value. I am using AVPlayerVideoViewController and want to override the unmute button functionality.
I created a sample ViewController, you can observe isMuted changes easily:
import AVKit
class ViewController: AVPlayerViewController {
var muteObservation: NSKeyValueObservation?
override func viewDidLoad() {
super.viewDidLoad()
guard let videoPath = Bundle.main.path(forResource: "video", ofType: "mov") else {
return
}
let videoURL = URL(fileURLWithPath: videoPath)
player = AVPlayer(url: videoURL)
player?.play()
muteObservation = player?.observe(\.isMuted) { player, _ in
print("isMuted: \(player.isMuted)")
}
}
}
I am showing mp4 video file in the background with UIView component and AVFoundation framework.
But it gives an error after app is minimized by app user. Because player.pause() method causes a crash as you see. Here is the error: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
import UIKit
import AVFoundation
import AVKit
class ViewController: UIViewController {
private var player: AVPlayer!
#IBOutlet weak var videoUiViewOutlet: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.setupView()
}
private func setupView()
{
let path = URL(fileURLWithPath: Bundle.main.path(forResource: "clouds", ofType: "mp4")!)
let player = AVPlayer(url: path)
let newLayer = AVPlayerLayer(player: player)
newLayer.frame = self.videoUiViewOutlet.frame
self.videoUiViewOutlet.layer.addSublayer(newLayer)
newLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
player.play()
player.actionAtItemEnd = AVPlayer.ActionAtItemEnd.none
NotificationCenter.default.addObserver(self, selector: #selector(self.videoDidPlayToEnd(notification:)),
name: NSNotification.Name(rawValue: "AVPlayerItemDidPlayToEndTimeNotification"), object: player.currentItem)
NotificationCenter.default.addObserver(self, selector: #selector(enteredBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(enteredForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
}
#objc func videoDidPlayToEnd(notification: Notification)
{
let player: AVPlayerItem = notification.object as! AVPlayerItem
player.seek(to: .zero, completionHandler: nil)
}
#objc func enteredBackground() {
print("scope: enteredBackground")
player.pause()
}
#objc func enteredForeground() {
print("scope: enteredForeground")
player.play()
}
}
How can I solve this problem? Video should pause after app is minimized. And video should continue after app is maximized.
The issue is because you haven't assigned the value to player property of the class ViewController, i.e.
private var player: AVPlayer!
And since it is a forced unwrap optional, it'll have nil as its default value. And using it in enteredBackground() method will result in runtime exception.
Solution:
In setupView() method, replace
let player = AVPlayer(url: path)
with
self.player = AVPlayer(url: path)
I'm having an issue with an animation (mp4) on my app. Sometimes(not all the time) when the app is launched, it requests microphone access but I am not requesting for one anywhere in the app. I'm only using AVPlayer to play the mp4 content. The code below is the only one related to the player. Any idea why I'm being requested for mic access? Thanks
import UIKit
import Foundation
import MediaPlayer
import AVKit
class AnimationLaunchscreen: UIViewController {
var player: AVPlayer?
override func viewDidLoad() {
super.viewDidLoad()
let timer = Timer.scheduledTimer(timeInterval: 6.0, target: self, selector: #selector(timeToMoveOn), userInfo: nil, repeats: false)
self.loadVideo()
}
#objc func timeToMoveOn() {
self.performSegue(withIdentifier: "goToTableView", sender: self)
}
func loadVideo() {
let path = Bundle.main.path(forResource: "stopwatchAnimation", ofType:"mp4")
let filePathURL = NSURL.fileURL(withPath: path!)
let player = AVPlayer(url: filePathURL)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.view.frame
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
playerLayer.zPosition = -1
self.view.layer.addSublayer(playerLayer)
player.seek(to: CMTime.zero)
player.play()
}
override func viewWillAppear(_ animated: Bool) {
makeStatusBarBlack()
}
}
Adding the answer here so it is easier to find than reading the comments. This really appears to be a bug on simulator only (https://forums.developer.apple.com/thread/110423). Running on device works just fine.
I created a game that has a starting menu screen. In this menu screen, I want a video(I recorded me playing this game via iPhone 6 simulator) to play in the background of this menu screen. I got the video to play, but what happens is when the view loads, it will show the menu, then it will show the video replacing the menu itself.
import SpriteKit
import UIKit
import AVFoundation
class MenuScene: SKScene {
var player:AVPlayer!
var VideoSprite:SKVideoNode!
override func didMoveToView(view: SKView) {
let videoURL: NSURL = NSBundle.mainBundle().URLForResource("startTo32", withExtension: "mov")!
player = AVPlayer(URL: videoURL)
player?.actionAtItemEnd = .None
player?.muted = true
let playerLayer = AVPlayerLayer(player: player)
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
playerLayer.zPosition = -100
playerLayer.frame = view.frame
player?.play()
NSNotificationCenter.defaultCenter().addObserver(self,
selector: #selector(MenuScene.loopVideo),
name: AVPlayerItemDidPlayToEndTimeNotification,
object: nil)
view.layer.addSublayer(playerLayer)
}
func loopVideo() {
player?.seekToTime(kCMTimeZero)
player?.play()
}
--------UPDATE--------
Here's the link to see what I'm talking about. the screen with the words is where I'm trying to get the video to play, but instead it just makes the screen go white and then plays the video itself.
Any questions or need for me to add information, just comment.
Thanks, Jordan.
Try using your SKVideoNode instead of adding a layer to your view.
Something like this:
let videoURL: NSURL = NSBundle.mainBundle().URLForResource("startTo32", withExtension: "mov")!
VideoSprite = SKVideoNode(url: videoURL)
self.addChild(VideoSprite)
VideoSprite.play()
SKVideoNode documentation is available here.
Here is your updated video player for Swift 4 and Spritekit:
import SpriteKit
import UIKit
import AVFoundation
class MenuScene: SKScene {
var player:AVPlayer!
var VideoSprite:SKVideoNode!
override func didMoveToView(view: SKView) {
let videoURL: NSURL = Bundle.main.url(forResource: "yourvideonamehere", withExtension: "mp4")! as NSURL
VideoSprite = SKVideoNode(url: videoURL as URL)
self.addChild(VideoSprite)
VideoSprite.play()
player = AVPlayer(url: videoURL as URL)
player?.actionAtItemEnd = .none
player?.isMuted = true
let playerLayer = AVPlayerLayer(player: player)
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
playerLayer.zPosition = 100
playerLayer.frame = view.frame
player?.play()
}
func loopVideo() {
player?.seek(to: CMTime.zero)
player?.play()
}
}
I hope this helps.
Here is a rudimentary playMe function calling AVPlayer, playing a MP3, MP4 or Wav via Swift with AppleTV. How do I combine this with the AVPlayerViewController - i.e., how do I make the playMe("video", "mp4") play inside an AVPlayerViewController, what are the required steps to make a connection between the Main.storyboard and the AVPlayerViewController, in the GUI and in the Swift code?
func playMe(inputfile: String, inputtype: String) {
let path = NSBundle.mainBundle().pathForResource(inputfile, ofType:inputtype)!
let videoURL = NSURL(fileURLWithPath: path)
let player = AVPlayer(URL: videoURL)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.view.bounds
self.view.layer.addSublayer(playerLayer)
player.play()
}
One way you could do this is by subclassing AVPlayerViewController. AVPlayerViewController has an AVPlayer property named player. A subclass of AVPlayerViewController might look something like this:
import UIKit
import AVKit
class MyPlayerViewController: AVPlayerViewController {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let path = NSBundle.mainBundle().pathForResource("myVideo", ofType:"mov")!
let videoURL = NSURL(fileURLWithPath: path)
player = AVPlayer(URL: videoURL)
}
}
This implementation would show the default playback controls and would work out of the box with the Siri remote.
Here is the code to do this via a button press using prepareForSegue:
import UIKit
import AVFoundation
import AVKit
let playerViewControllerSegue = "play";
class MyViewController: UIViewController {
#IBAction func playMovie(sender: UIButton) {
self.performSegueWithIdentifier(playerViewControllerSegue, sender: self);
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == playerViewControllerSegue){
let path = NSBundle.mainBundle().pathForResource("7second", ofType:"mp4")!
let videoURL = NSURL(fileURLWithPath: path)
let player = AVPlayer(URL: videoURL)
let playerViewController = segue.destinationViewController as! AVPlayerViewController
playerViewController.player = player
playerViewController.player?.play()
}
}
}