How to play a video in a macOS application with swift? - swift

I'm trying to get a video I have in my project to play in my macOS application using swift. Here is my code:
import Cocoa
import AVKit
import AVFoundation
class ViewController: NSViewController {
var player : AVPlayer!
var avPlayerLayer : AVPlayerLayer!
#IBOutlet weak var videoPlayer: AVPlayerView!
override func viewDidLoad() {
super.viewDidLoad()
guard let path = Bundle.main.path(forResource: "Background", ofType:"mp4") else {
debugPrint("Not found")
return
}
let playerURL = AVPlayer(url: URL(fileURLWithPath: path))
let playerView = AVPlayerView()
playerView.player = playerURL
playerView.player?.play()
}
}
I also have an AVPlayerView in my storyboard that I have connected to my viewcontroller.
It builds and runs fine but when I run it nothing shows up. I just get a black screen.
Any help is appreciated. Thanks!

The problem is this line:
let playerView = AVPlayerView()
Instead of configuring the player view that is in your interface, you make a whole new separate player view — and then you throw it away.

Related

RealityKit for VR

Trying to use my RealityKit project as the foundation for an on screen app (VR) instead of projecting onto the real-world (AR) out the back camera.
Anyone know how to load a RealityKit project asynchronously with the .nonAR camera option, so it project in an app instead of leveraging the rear facing camera?
Do I create position information in the Swift code or the Reality Composer project?
Here's how you can asynchronously load .usdz VR-model with a help of RealityKit's .loadModelAsync() instance method and Combine's AnyCancellable type.
import UIKit
import RealityKit
import Combine
class VRViewController: UIViewController {
#IBOutlet var arView: ARView!
var anyCancellable: AnyCancellable? = nil
let anchorEntity = AnchorEntity(world: [0, 0,-2])
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
arView.backgroundColor = .black
arView.cameraMode = .nonAR
anyCancellable = ModelEntity.loadModelAsync(named: "horse").sink(
receiveCompletion: { _ in
self.anyCancellable?.cancel()
},
receiveValue: { [self] (object: Entity) in
if let model = object as? ModelEntity {
self.anchorEntity.addChild(model)
self.arView.scene.anchors.append(self.anchorEntity)
} else {
print("Can't load a model due to some issues")
}
}
)
}
}
However, if you wanna move inside 3D environment, instead of using .nonAR camera mode use:
arView.environment.background = .color(.black)

Loading correct image to detail view

I have a TableView app that plays music. The tracks are named: 0.mp3, 1.mp3 etc. They play in a detail view where I also want an image of the cover. I named the cover images: 0.png, 1.png etc.
I tried to load the correct image with the code:
self.bookImage.image = UIImage(named: ("/\(trackID!).png"))
but it doesn't work.
Here's some more of my code:
import UIKit
import AVFoundation
class AudioPlayerVC: UIViewController {
var trackID: Int!
var audioPlayer:AVAudioPlayer!
#IBOutlet var bookImage: UIImageView!
#IBAction func play(_ sender: AnyObject) {
if !audioPlayer.isPlaying{
audioPlayer.play()
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.bookImage.image = UIImage(named: ("/\(trackID!).png"))
trackLbl.text = "Track \(trackID!)"
let path: String! = Bundle.main.resourcePath?.appending("/\(trackID!).mp3")
let mp3URL = NSURL(fileURLWithPath: path)
do
{
audioPlayer = try AVAudioPlayer(contentsOf: mp3URL as URL)
audioPlayer.play()
}
}
}
You need
self.bookImage.image = UIImage(named:"\(trackID).png")
Or
self.bookImage.image = UIImage(named:"\(trackID)")
First, you don't need / character after opening quotation mark.
self.bookImage.image = UIImage(named:"\(trackID).png")
Should work just fine.
1) Please add your images to Assets.xcassets
2) Name images like 0,1,2,3 in Assets.xcassets
3) Write a code:
let trackId = String(trackId)
self.bookImage.image = UIImage(named: trackId)

Unexpectedly found nil while unwrapping an Optional value, while trying to play video

I am using AVFoundation kit to play a local video
I tried this code:
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player: AVPlayer?
#IBOutlet weak var videoViewContainer: UIView!
override func viewDidLoad() {
super.viewDidLoad()
initializeVideoPlayerWithVideo()
}
func initializeVideoPlayerWithVideo() {
// get the path string for the video from assets
let videoString:String? = Bundle.main.path(forResource: "Air Bike", ofType: "mov")
guard let unwrappedVideoPath = videoString else {return}
// convert the path string to a url
let videoUrl = URL(fileURLWithPath: unwrappedVideoPath)
// initialize the video player with the url
self.player = AVPlayer(url: videoUrl)
// create a video layer for the player
let layer: AVPlayerLayer = AVPlayerLayer(player: player)
// make the layer the same size as the container view
layer.frame = videoViewContainer.bounds
// make the video fill the layer as much as possible while keeping its aspect size
layer.videoGravity = AVLayerVideoGravity.resizeAspectFill
// add the layer to the container view
videoViewContainer.layer.addSublayer(layer)
}
#IBAction func playVideoButtonTapped(_ sender: UIButton) {
// play the video if the player is initialized
player?.play()
}
}
I've trying a few different approaches and I'm still getting the same error message can someone How can I resolve this issue?
func initializeVideoPlayerWithVideo() {
guard let path = Bundle.main.path(forResource: "jagdeep", ofType:".MOV") else {
debugPrint("video.m4v not found")
return
}
self.player = AVPlayer(url: URL(fileURLWithPath: path))
// create a video layer for the player
let layer: AVPlayerLayer = AVPlayerLayer(player: player)
// make the layer the same size as the container view
layer.frame = videoViewContainer.bounds
// make the video fill the layer as much as possible while keeping its aspect size
layer.videoGravity = AVLayerVideoGravity.resizeAspectFill
// add the layer to the container view
videoViewContainer.layer.addSublayer(layer)
player?.play()
}
HERE IS MY VIDEO NAME jagdeep and type .MOV AND its working on my side. It's problem in your code where you using type mov. I just drag a video name jagdeep.MOV to my project and use Your code. its ok
**use**
layer.frame = videoViewContainer.layer.bounds
instead of
// make the layer the same size as the container view
layer.frame = videoViewContainer.bounds
Thank you for your response seems like the file cannot be found here is a screenshot from my project what could be the reason the video is located in my main folder! Screenshot

How to loop background music without lag/delay when song repeats (seamless)

I am trying to make a game with background music looping in it. I made the song file in Adobe Audition (which is similar to audacity) and when I play it in a loop in Adobe Audition it loops how I want it.
When I play it in Xcode however, it has a lag in between the loops. I am using AVFoundations for the sound playing.
I have searched everywhere but I can't find the solution for the problem.
Is there any way you can loop audio files without there being any lags in between ? (I believe its called "seamless looping" )
here is the code:
class GameScene: SKScene {
...// Other Code
var ButtonAudio = URL(fileURLWithPath: Bundle.main.path(forResource: "Gamescene(new)", ofType: "mp3")!)
var ButtonAudioPlayer = AVAudioPlayer()
... //Other Code
}
And When I Call it:
override func didMove(to view: SKView) {
...//Code
ButtonAudioPlayer = try! AVAudioPlayer(contentsOf: ButtonAudio, fileTypeHint: nil)
ButtonAudioPlayer.numberOfLoops = -1
ButtonAudioPlayer.prepareToPlay()
ButtonAudioPlayer.play()
...//More Code
}
Can someone help me with this issue ?
Thank you in advance!
You can use AVPlayerLooper and AVQueuePlayer to do this.
import UIKit
import AVFoundation
class ViewController: UIViewController {
var queuePlayer = AVQueuePlayer()
var playerLooper: AVPlayerLooper?
override func viewDidLoad() {
super.viewDidLoad()
guard let url = Bundle.main.url(forResource: "Gamescene(new)", withExtension: "mp3") else { return }
let playerItem = AVPlayerItem(asset: AVAsset(url: url))
playerLooper = AVPlayerLooper(player: queuePlayer, templateItem: playerItem)
queuePlayer.play()
}
}
The solution proposed by #dave234 only works in iOS > 10. Since I needed to make seamless playback in iOS > 9, I did something different:
Instead of AVPlayer, I created AVQueuePlayer and immediately added two identical melodies to the queue.
Next, I made a listener to the penultimate melody.
When the listener was triggered, I added another similar record to the queue after the last one.
In fact, in order to avoid delay, I always play the penultimate record.
My code:
var player: AVQueuePlayer?
override func viewDidLoad() {
super.viewDidLoad()
if let path = Bundle.main.path(forResource: "music_file", ofType: "mp3") {
player = createPlayer(url: URL(fileURLWithPath: path))
}
}
func createPlayer(url: URL) -> AVQueuePlayer {
let player = AVQueuePlayer(items: [AVPlayerItem(url: url), AVPlayerItem(url: url)])
loopPlayer(playerItem: player.items()[player.items().count - 2])
return player
}
func loopPlayer(playerItem: AVPlayerItem) {
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: playerItem, queue: .main) { _ in
if let player = self.player, let url = (playerItem.asset as? AVURLAsset)?.url {
player.insert(AVPlayerItem(url: url), after: player.items()[player.items().count - 1])
self.loopPlayer(playerItem: player.items()[player.items().count - 2])
}
}
}

How to embed and stream a video from the web within an Xcode macOS Cocoa Application?

I am trying to get my macOS Cocoa Application Xcode project to play some video when I feed it a specified URL.
To simplify the question lets just use an empty Xcode project.
I want to achieve the same level of control as I am able to get using AVPlayerViewController() in my iOS Single View Application. Within that app I am currently using the solution offered here.
However, only the second example works in my macOS Cocoa Application when adapted like so:
import Cocoa
import AVFoundation
import AVKit
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let videoURL = URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
let player = AVPlayer(url: videoURL!)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.view.bounds
self.view.layer?.addSublayer(playerLayer)
player.play()
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
This option however has 2 downsides:
It creates a repetitive error message in the console:
2017-06-27 18:22:06.833441+0200 testVideoOnmacOS[24456:1216773] initWithSessionInfo: XPC connection interrupted
2017-06-27 18:22:06.834830+0200 testVideoOnmacOS[24456:1216773] startConfigurationWithCompletionHandler: Failed to get remote object proxy: Error Domain=NSCocoaErrorDomain Code=4097 "connection to service named com.apple.rtcreportingd" UserInfo={NSDebugDescription=connection to service named com.apple.rtcreportingd}
This AVPlayer solution does not offer the same control as the AVPlayerViewController() solution.
So I attempted to get the following AVPlayerViewController() solution to work. But it fails:
import Cocoa
import AVFoundation
import AVKit
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let videoURL = URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
let player = AVPlayer(url: videoURL!)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated: true) {
playerViewController.player!.play()
}
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
The error I am getting is:
Use of unresolved identifier 'AVPlayerViewController' - Did you mean
'AVPlayerViewControlsStyle'?
I have tried to work around this in many different ways. It will be confusing if I spell them all out. My prominent problem seems to be that the vast majority of online examples are iOS based.
Thus, the main question:
How would I be able to successfully use AVPlayerViewController within my Xcode macOS Cocoa Application?
Yes, got it working!
Thnx to ninjaproger's comment.
This seems to work in order to get a video from the web playing / streaming on a macOS Cocoa Application in Xcode.
First, create a AVKitPlayerView on your storyboard and link it as an IBOutlet to your NSViewController.
Then, simply apply this code:
import Cocoa
import AVFoundation
import AVKit
class ViewController: NSViewController {
#IBOutlet var videoView: AVPlayerView!
override func viewDidLoad() {
super.viewDidLoad()
let videoURL = URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
let player = AVPlayer(url: videoURL!)
let playerViewController = videoView
playerViewController?.player = player
playerViewController?.player!.play()
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
Update august 2021
Ok, it's been a while since this answer has been up and I am no longer invested in it. But it seems others still end up here and the solution needs a bit more work to work properly at times.
With thanks to #SouthernYankee65:
What is needed sometimes is to go to the project file and on the Signing & Capabilities tab check Outgoing Connection (Client). Then in the info.plist add App Transport Security Settings dictionary property, then add Allows Arbitrary Loads boolean property and set it to YES.
The video now plays. However, some errors in the console might occur.