swift avplayer arkit scnnode skvideonode

swift - ARKit SKVideoNode Reproduciendo en Render



avplayer scnnode (1)

De acuerdo. Así que aquí hay una solución. ver renderer(_:updateAtTime:) .

var player: AVPlayer! var play = true @objc func tap(_ recognizer: UITapGestureRecognizer){ if play{ play = false player.pause() }else{ play = true player.play() } } func setVideo() -> SKScene{ let size = CGSize(width: 500, height: 500) let skScene = SKScene(size: size) let videoURL = Bundle.main.url(forResource: "video.mp4", withExtension: nil)! player = AVPlayer(url: videoURL) skScene.scaleMode = .aspectFit videoSpriteNode = SKVideoNode(avPlayer: player) videoSpriteNode.position = CGPoint(x: size.width/2, y: size.height/2) videoSpriteNode.size = size videoSpriteNode.yScale = -1 skScene.addChild(videoSpriteNode) player.play() return skScene } func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { if let image = anchor as? ARImageAnchor{ print("found") let planeGeometry = SCNPlane(width: image.referenceImage.physicalSize.width, height: image.referenceImage.physicalSize.height) let plane = SCNNode(geometry: planeGeometry) planeGeometry.materials.first?.diffuse.contents = setVideo() plane.transform = SCNMatrix4MakeRotation(-.pi/2, 1, 0, 0) node.addChildNode(plane) } } func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) { if !play{ player.pause() } }

Usa esta idea en tu código.

Problema principal:

Estoy agregando esta sección DESPUÉS para aclarar el problema. - Puedo PAUSAR mi video (no quiero que se reproduzca en un bucle). Cuando mi nodo aparece, mi nodo reproduce mi video, incluso si está en pausa. Si mi video ha terminado de reproducirse y aparece a la vista, se reiniciará. Quiero eliminar este comportamiento.

En mi aplicación, tengo un SKVideoNode creado a partir de un AVPlayer(:URL) dentro de 3D Space usando objetos SCNGeometry y objetos SCNGeometry . Uso ARKit .ImageTracking para determinar cuándo se encuentra una imagen específica y reproduzco un video desde allí. Todo es bueno y elegante, excepto que el jugador decide jugar en su propio tiempo, cada vez que el AVPlayer aparece a la vista; sin embargo, podría ser cuando ARImageAnchor el SCNNode esté conectado a la vista. De cualquier manera, el AVPlayer está jugando cada vez que el nodo ve la lente de la cámara. yo suelo

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, fue 0.

Imprimí algún tipo de impresión de función de print("Play") para todas mis funciones utilizando player.pause () o player.play () y no se llama a ninguna de ellas cuando la tasa se cambia arriba. ¿Cómo puedo encontrar la fuente de lo que está cambiando la velocidad de mi jugador?

Revisé el rootnode original, self.sceneview.scene.rootNode.childNodes para asegurarme de que no estoy creando VideoNodes / SCNNodes / AVPlayers adicionales, etc., y parece que solo hay 1.

¿Alguna idea sobre por qué SKVideoNode / AVPlayer se está reproduciendo cuando SCNNode ve la cámara con ARKit? ¡Gracias por adelantado!

Edit1:

Hizo una solución para determinar SÓLO cuando 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 siguiente función, puse

@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 } } }

donde videoPlayer3D es el SCNNode que contiene el SKVideoNode.

Sin embargo, recibo el error com.apple.scenekit.scnview-renderer (17): EXC_BAD_ACCESS (code=2, address=0x16d8f7ad0) en la sección "AQUÍ" arriba. Parece que el renderizador de la vista en pantalla está intentando alterar mi nodo de video en la función de renderizado, aunque, ni siquiera uso el renderer(updateAtTime:) , 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

Se presenta el error que indica que se está llamando a algún método desde un objeto SCNView para el renderizador del método (eso es lo que entiendo del error), pero no tengo el nodo llamado específicamente. Creo que tal vez se está llamando una acción predeterminada, ya que el nodo se está viendo, sin embargo, no estoy 100% seguro de cómo acceder a él o determinar qué método. Los objetos que estoy usando no son objetos SCNView, y no creo que hereden de los objetos SCNView (mire el primer párrafo para ver las variables utilizadas). Solo busco eliminar la "acción" del nodo que se está reproduciendo cada vez que está a la vista.

ADICIÓN:

Por el bien de seguir la creación de mi reproductor de video si está interesado, aquí está. Déjame saber si hay algo más que te gustaría ver (no estoy seguro de qué más quieres ver) y gracias por tu 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 reinicie los planos que parece, ya que el video aún se está reproduciendo en render. Ya sea que esté en pausa o finalizado, se reiniciará y volverá a comenzar o continuará jugando donde lo dejó.

Esfuerzos2:

Yo he tratado

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 que la cámara incluso se congela ya que no se está rastreando. Es completamente inútil aquí.