ARKit SKVideoNode Jugando en Render
Problema principal
Añado esta sección DESPUÉS para aclarar el problema. - Puedo pausar mi video (no quiero que se reproduzca en bucle). Cuando mi nodo aparece, mi nodo reproduce mi video, incluso si está en pausa. Si mi video ha terminado de reproducirse y aparece, se reiniciará. Quiero ELIMINAR este comportamiento.
En mi aplicación, tengo unSKVideoNode
creado a partir de unaAVPlayer(:URL)
dentro del espacio 3D usandoSCNNode
objetos ySCNGeometry
objetos. Yo sueloARKit
.ImageTracking
para determinar cuándo se encuentra una imagen específica y reproducir un video desde allí. Todo es bueno y elegante, excepto que el jugador determina jugar en su propio tiempo, cada vez que aparece AVPlayer; sin embargo, podría ser cuando elARImageAnchor
elSCNNode
se adjunta a aparece a la vista. De cualquier manera, laAVPlayer
se reproduce cada vez que el nodo ve la lente de la cámara. Yo suel
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if(keyPath == "rate") {
print((object as! AVPlayer).rate)
}
}
para imprimir la tasa, y es 1, sin embargo, era 0.
Imprimí algún tipo de función de impresiónprint("Play")
para todas mis funciones que utilizan player.pause () o player.play () y ninguna de ellas se llama cada vez que se cambia la tasa anterior. ¿Cómo puedo encontrar la fuente de lo que está cambiando la velocidad de mi reproductor?
Revisé el rootnode original,self.sceneview.scene.rootNode.childNodes
para asegurarse de que no estoy creando VideoNodes / SCNNodes / AVPlayers adicionales, etc., y parece que solo hay 1.
¿Alguna idea de por qué el SKVideoNode / AVPlayer se está reproduciendo cuando el SCNNode aparece a la vista de la cámara con ARKit? ¡Gracias por adelantado
Edit1:
Hizo una solución alternativa para determinar SOLO cuándo un usuario hizo clic en este nodo
let tap = UITapGestureRecognizer(target: self, action: #selector(self!.tapGesture))
tap.delegate = self!
tap.name = "MyTap"
self!.sceneView.addGestureRecognizer(tap)
y luego dentro de esta próxima función, pongo
@objc func tapGesture(_ gesture:UITapGestureRecognizer) {
let tappedNodes = self.sceneView.hitTest(gesture.location(in: gesture.view), options: [SCNHitTestOption.searchMode: 1])
if !tappedNodes.isEmpty {
for nodes in tappedNodes {
if nodes.node == videoPlayer3D {
videoPlayer3D.tappedVideoPlayer = true
videoPlayer3D.playOrPause()
break
}
}
}
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if(keyPath == "rate") {
print((object as! AVPlayer).rate)
if(!self.tappedVideoPlayer) {
self.player.pause() //HERE
}
}
}
where videoPlayer3D es elSCNNode
que contiene el SKVideoNode.
Sin embargo, me sale el errorcom.apple.scenekit.scnview-renderer (17): EXC_BAD_ACCESS (code=2, address=0x16d8f7ad0)
en la sección etiquetada "AQUÍ" arriba. Parece que el renderizador de la vista de escena está intentando alterar mi nodo de video en la función de renderizado, aunque ni siquiera uso larenderer(updateAtTime:)
unción @, solo uso
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
guard let imageAnchor = anchor as? ARImageAnchor else { return }
createVideoNode(imageAnchor)
}
para determinar cuándo veo una imagen, es decir,imageTracking
y creo el nodo. ¿Algun consejo
Pensamiento 1
El error se presenta indicando que se está llamando a algún método desde un objeto SCNView para el procesador de métodos (eso es lo que entiendo por el error), pero no tengo el nodo llamado específicamente. Creo que tal vez se está llamando a una acción predeterminada, ya que el nodo se está viendo, sin embargo, no estoy 100% seguro de cómo acceder o determinar qué método. Los objetos que estoy usando no son objetos SCNView, y no creo que hereden de los objetos SCNView (mira el primer párrafo para ver las variables utilizadas). Solo busco eliminar la "acción" del nodo que se reproduce cada vez que está a la vista.
ADICIÓN
En aras de seguir la creación de mi reproductor de video si está interesado, aquí está. Avíseme si hay algo más que le gustaría ver (no estoy seguro de qué más le gustaría ver) y gracias por su ayuda.
func createVideoNode(_ anchor:ARImageAnchor, initialPOV:SCNNode) -> My3DPlayer? {
guard let currentFrame = self.sceneView.session.currentFrame else {
return nil
}
let delegate = UIApplication.shared.delegate as! AppDelegate
var videoPlayer:My3DPlayer!
videoPlayer = delegate.testing ? My3DPlayer(data: nil, currentFrame: currentFrame, anchor: anchor) : My3DPlayer(data: self.urlData, currentFrame: currentFrame, anchor: anchor)
//Create TapGesture
let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture))
tap.delegate = self
tap.name = "MyTap"
self.sceneView.addGestureRecognizer(tap)
return videoPlayer
}
Clase My3dPlayer:
class My3DPlayer: SCNNode {
init(geometry: SCNGeometry?) {
super.init()
self.geometry = geometry
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
convenience init(data:Data?, currentFrame:ARFrame, anchor:ARImageAnchor) {
self.init(geometry: nil)
self.createPlayer(currentFrame, data, anchor)
}
private func createPlayer(_ frame:ARFrame, _ data:Data?,_ anchor:ARImageAnchor) {
let physicalSize = anchor.referenceImage.physicalSize
print("Init Player W/ physicalSize: \(physicalSize)")
//Create video
if((UIApplication.shared.delegate! as! AppDelegate).testing) {
let path = Bundle.main.path(forResource: "Bear", ofType: "mov")
self.url = URL(fileURLWithPath: path!)
}
else {
let url = data!.getAVAssetURL(location: "MyLocation")
self.url = url
}
let asset = AVAsset(url: self.url)
let track = asset.tracks(withMediaType: AVMediaType.video).first!
let playerItem = AVPlayerItem(asset: asset)
let player = AVPlayer(playerItem: playerItem)
self.player = player
var videoSize = track.naturalSize.applying(track.preferredTransform)
videoSize = CGSize(width: abs(videoSize.width), height: abs(videoSize.height))
print("Init Video W/ size: \(videoSize)")
//Determine if landscape or portrait
self.landscape = videoSize.width > videoSize.height
print(self.landscape == true ? "Landscape" : "Portrait")
//Do something when video ended
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying(note:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
//Add observer to determine when Player is ready
player.addObserver(self, forKeyPath: "status", options: [], context: nil)
//Create video Node
let videoNode = SKVideoNode(avPlayer: player)
//Create 2d scene to put 2d player on - SKScene
videoNode.position = CGPoint(x: videoSize.width/2, y: videoSize.height/2)
videoNode.size = videoSize
//Portrait -- //Landscape doesn't need adjustments??
if(!self.landscape) {
let width = videoNode.size.width
videoNode.size.width = videoNode.size.height
videoNode.size.height = width
videoNode.position = CGPoint(x: videoNode.size.width/2, y: videoNode.size.height/2)
}
let scene = SKScene(size: videoNode.size)
//Add videoNode to scene
scene.addChild(videoNode)
//Create Button-look even though we don't use the button. Just creates the illusion to pressing play and pause
let image = UIImage(named: "PlayButton")!
let texture = SKTexture(image: image)
self.button = SKSpriteNode(texture: texture)
self.button.position = videoNode.position
//Makes the button look like a square
let minimumSize = [videoSize.width, videoSize.height].min()!
self.button.size = CGSize(width: minimumSize/4, height: minimumSize/4)
scene.addChild(button)
//Get ratio difference from physicalsize and video size
let widthRatio = Float(physicalSize.width)/Float(videoSize.width)
let heightRatio = Float(physicalSize.height)/Float(videoSize.height)
let finalRatio = [widthRatio, heightRatio].min()!
//Create a Plane (SCNPlane) to put the SKScene on
let plane = SCNPlane(width: scene.size.width, height: scene.size.height)
plane.firstMaterial?.diffuse.contents = scene
plane.firstMaterial?.isDoubleSided = true
//Set Self.geometry = plane
self.geometry = plane
//Size the node correctly
//Find the real scaling variable
let scale = CGFloat(finalRatio)
let appearanceAction = SCNAction.scale(to: scale, duration: 0.4)
appearanceAction.timingMode = .easeOut
//Set initial scale to 0 then use action to scale up
self.scale = SCNVector3Make(0, 0, 0)
self.runAction(appearanceAction)
}
@objc func playerDidFinishPlaying(note: Notification) {
self.player.seek(to: .zero, toleranceBefore: .zero, toleranceAfter: .zero)
self.player.seek(to: .zero, toleranceBefore: .zero, toleranceAfter: .zero)
self.setButtonAlpha(alpha: 1)
}
}
Esfuerzos1:
He intentado detener el seguimiento a través de:
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
guard let imageAnchor = anchor as? ARImageAnchor else { return }
createVideoNode(imageAnchor)
self.resetConfiguration(turnOnConfig: true, turnOnImageTracking: false)
}
func resetConfiguration(turnOnConfig: Bool = true, turnOnImageTracking:Bool = false) {
let configuration = ARWorldTrackingConfiguration()
if(turnOnImageTracking) {
guard let referenceImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: nil) else {
fatalError("Missing expected asset catalog resources.")
}
configuration.planeDetection = .horizontal
configuration.detectionImages = referenceImages
}
else {
configuration.planeDetection = []
}
if(turnOnConfig) {
sceneView.session.run(configuration, options: [.resetTracking])
}
}
Arriba, he intentado restablecer la configuración. Esto solo hace que restablezca los planos, ya que el video todavía se está reproduciendo en el render. Ya sea que esté en pausa o terminado, se reiniciará y comenzará de nuevo o continuará jugando donde se dejó.
Esfuerzos2:
Yo he tratad
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
guard let imageAnchor = anchor as? ARImageAnchor else { return }
createVideoNode(imageAnchor)
self.pauseTracking()
}
func pauseTracking() {
self.sceneView.session.pause()
}
Esto detiene todo, por lo tanto, la cámara incluso se congela ya que no se está rastreando nada. Es completamente inútil aquí.