0
votes

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.

1

1 Answers

0
votes

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 :

NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: player.currentItem, queue: nil) { _ in
    player.seek(to: .zero)
    player.play()
}