I'm playing around with ARKit and have created an app which overlays video content onto recognized images. Code is as follows:
import UIKit
import SceneKit
import ARKit
import SpriteKit
class ViewController: UIViewController, ARSCNViewDelegate {
@IBOutlet var sceneView: ARSCNView!
override func viewDidLoad() {
super.viewDidLoad()
sceneView.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let configuration = ARImageTrackingConfiguration()
if let imageToTrack = ARReferenceImage.referenceImages(inGroupNamed: "ALLimages", bundle: Bundle.main) {
configuration.trackingImages = imageToTrack
configuration.maximumNumberOfTrackedImages = 3
print("Images successfully added")
}
sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
sceneView.session.pause()
}
// MARK: - ARSCNViewDelegate
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
let node = SCNNode()
var videoNode = SKVideoNode()
var videoScene = SKScene()
if let imageAnchor = anchor as? ARImageAnchor {
videoNode = SKVideoNode(fileNamed: "video1.mp4")
videoNode.play()
videoScene = SKScene(size: CGSize(width: 640, height: 360))
videoNode.position = CGPoint(x: videoScene.size.width / 2, y: videoScene.size.height / 2)
videoNode.yScale = -1.0
videoScene.addChild(videoNode)
let plane = SCNPlane(width: imageAnchor.referenceImage.physicalSize.width, height: imageAnchor.referenceImage.physicalSize.height)
plane.firstMaterial?.diffuse.contents = videoScene
let planeNode = SCNNode(geometry: plane)
planeNode.eulerAngles.x = -.pi/2
node.addChildNode(planeNode)
}
return node
}
}
This works perfectly... once! But the problem is that once the video has finished playing, because of the limited controls available via SKVideoNode I cannot figure out how to restart the video automatically. Ideally these should play on a loop.
I've done some research and it seems that the best way is to initialize my video node using an AVPlayer object.
So, I attempted to do this but cannot get it working.
I added var player = AVPlayer() in my class and then tried to initialize my videoNode as follows:
var videoNode: SKVideoNode? = {
guard let urlString = Bundle.main.path(forResource: "video1", ofType: "mov") else {
return nil
}
let url = URL(fileURLWithPath: urlString)
let item = AVPlayerItem(url: url)
let player = AVPlayer(playerItem: item)
return SKVideoNode(avPlayer: player)
}()
I then attempted to user player.play() but the video never plays. Instead my plane just appears as a blank rectangle over my images.
Once I successfully initialize this I think I'm able to add an observer to check when the video has finished player and restart it, but I'm struggling to get to that point.
First, you don't need SKVideoNode in a SKScene in a SCNNode. You can directly use AVPlayer as diffuse content of you SCNNode :
plane.firstMaterial?.diffuse.contents = player
For looping you have to subscribe to a notification on the player and reset time to zero at end :