ARKit SKVideoNode воспроизводится на рендере

Основная проблема:

Я добавляю этот раздел ПОСЛЕ, чтобы прояснить проблему. - Я могу ПРИОСТАНОВИТЬ свое видео (я не хочу, чтобы оно воспроизводилось в цикле). Когда мой узел появляется в поле зрения, он воспроизводит мое видео, даже если оно приостановлено. Если воспроизведение моего видео закончилось и оно появится в поле зрения, оно перезапустится. Я хочу УДАЛИТЬ это поведение.

В моем приложении у меня есть SKVideoNode, созданный из AVPlayer(:URL) внутри 3D-пространства с использованием SCNNode объектов и SCNGeometry объектов. Я использую ARKit .ImageTracking, чтобы определить, когда найдено конкретное изображение, и воспроизвести оттуда видео. Все хорошо, за исключением того, что игрок решает играть в свое время, каждый раз, когда AVPlayer появляется в поле зрения; однако это может произойти всякий раз, когда ARImageAnchor, к которому прикреплен SCNNode, оказывается в поле зрения. В любом случае AVPlayer воспроизводится каждый раз, когда узел попадает в поле зрения объектива камеры. я использую

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if(keyPath == "rate") {
        print((object as! AVPlayer).rate)
    }
}

чтобы распечатать скорость, и она равна 1, однако она была 0.

Я распечатал какую-то функцию печати print("Play") для всех моих функций, использующих player.pause () или player.play (), и ни одна из них не вызывается при изменении скорости выше. Как я могу найти источник того, что меняет рейтинг моего плеера?

Я проверил исходный корневой узел self.sceneview.scene.rootNode.childNodes, чтобы убедиться, что я не создаю лишних VideoNodes / SCNNodes / AVPlayers и т. Д., И кажется, что их всего 1.

Любые идеи о том, почему SKVideoNode / AVPlayer воспроизводится, когда SCNNode попадает в поле зрения камеры с помощью ARKit? Заранее спасибо!

Редактировать1:

Сделано обходное решение, чтобы определить ТОЛЬКО, когда пользователь щелкнул этот узел

let tap = UITapGestureRecognizer(target: self, action: #selector(self!.tapGesture))
tap.delegate = self!
tap.name = "MyTap"
self!.sceneView.addGestureRecognizer(tap)

а затем внутри следующей функции я помещаю

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

где videoPlayer3D - это SCNNode, содержащий SKVideoNode.

Однако я получаю сообщение об ошибке com.apple.scenekit.scnview-renderer (17): EXC_BAD_ACCESS (code=2, address=0x16d8f7ad0) в разделе, помеченном «ЗДЕСЬ» выше. Кажется, что средство визуализации представления сцены пытается изменить мой видеоузел в функции визуализации, хотя я даже не использую функцию renderer(updateAtTime:), я использую только

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {

    guard let imageAnchor = anchor as? ARImageAnchor else { return }

    createVideoNode(imageAnchor)

}

чтобы определить, когда я вижу изображение, то есть imageTracking, и я создаю узел. Какие-нибудь советы?

Мысль 1

Представлена ​​ошибка, указывающая, что из объекта SCNView для средства визуализации метода вызывается некоторый метод (это то, что я понимаю из ошибки), но у меня нет специально вызываемого узла. Я думаю, что, возможно, вызывается действие по умолчанию, когда узел приближается к просмотру, однако я не уверен на 100%, как получить к нему доступ или определить, какой метод. Объекты, которые я использую, не являются объектами SCNView, и я не верю, что они наследуются от объектов SCNView (см. 1-й абзац, чтобы увидеть используемые переменные). Просто хочу удалить "действие" узла, играющего каждый раз, когда он находится в поле зрения.

ДОПОЛНЕНИЕ:

Ради того, чтобы следить за созданием моего видеоплеера, если интересно, вот он. Дайте мне знать, если есть что-нибудь еще, что вы хотели бы увидеть (не знаю, что еще вы могли бы увидеть), и спасибо за вашу помощь.

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
}

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

Усилия1:

Я пытался остановить отслеживание через:

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])
    }
}

Выше я пытался сбросить конфигурацию. Это только заставляет его сбрасывать плоскости, как кажется, поскольку видео все еще воспроизводится при рендеринге. Независимо от того, приостановлено оно или завершено, оно будет сброшено и начнется заново или продолжит воспроизведение с того места, где оно было остановлено.

Усилия2:

я пытался

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()
}

Это останавливает все, поэтому камера даже зависает, так как ничего не отслеживается. Здесь это совершенно бесполезно.


person impression7vx    schedule 12.11.2018    source источник
comment
мы можем увидеть createVideoNode плз. Обычно мы добавляем видео узел как дочерний узел в didAdd node.   -  person Alok Subedi    schedule 24.12.2018
comment
Как создание того места, где я добавляю свой узел, влияет на то, что мое видео будет воспроизводиться каждый раз, когда оно доходит до просмотра? Даже если он на паузе.   -  person impression7vx    schedule 24.12.2018
comment
Не уверен, просто я предполагаю, что должна быть какая-то логическая проблема, если он воспроизводится каждый раз, когда обнаруживается imageAnchor.   -  person Alok Subedi    schedule 25.12.2018
comment
Я добавил это для шуток и хихиканья   -  person impression7vx    schedule 01.01.2019
comment
Где здесь используется код вашей ARWorldTrackingConfiguration? Можно ли было бы остановить «обнаружение» после начала воспроизведения видео?   -  person Mihai Erős    schedule 04.01.2019
comment
Интересно!!!!! Дай мне подумать об этом @Mihai и определить, нужно ли мне это делать. Это феноменально. Я вернусь сюда в течение 12 часов.   -  person impression7vx    schedule 04.01.2019
comment
@ MihaiErős как мне остановить отслеживание, не выключая камеру?   -  person impression7vx    schedule 05.01.2019
comment
Это не обязательно является частью основной проблемы, но вам не нужно возиться с настройкой сцены SpriteKit и SKVideoNode - вы можете назначить AVPlayer непосредственно contents материала SceneKit в iOS 11.0 и более поздних версиях.   -  person rickster    schedule 05.01.2019
comment
Я займусь этим. Может быть, это тоже решит проблему. Сделаю это чуть позже. Спасибо за это @rickster   -  person impression7vx    schedule 05.01.2019
comment
@ impression7vx вы можете остановить отслеживание, не останавливая камеру, используя: let config = ARWorldTrackingConfiguration() config.planeDetection = [] sceneView.session.run(config)   -  person Mihai Erős    schedule 07.01.2019
comment
Я думал, что если вы перестанете отслеживать, вы не будете добавлять новые узлы, это должно помочь ..   -  person Mihai Erős    schedule 07.01.2019


Ответы (1)


Ok. Итак, вот исправление. см. 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()
    }
}

Используйте эту идею в своем коде.

person Alok Subedi    schedule 07.01.2019
comment
Что делать, если видео приостановлено? Это моя проблема. Я НЕ хочу, чтобы видео зацикливалось. Я хочу, чтобы у меня была возможность приостановить это. БЕЗ системы, ПРОИГРЫВАЮЩИЕ мое видео каждый раз, когда оно появляется в поле зрения - person impression7vx; 07.01.2019
comment
Если бы моя проблема заключалась в том, чтобы воспроизводить видео, это было бы легко. Как он уже это делает. Я хочу приостановить свое видео и воспроизвести его ТОЛЬКО, когда я нажимаю на узел, реализующий действие типа жеста. - person impression7vx; 07.01.2019
comment
Попробую и дам знать! - person impression7vx; 07.01.2019
comment
Ты хитрый чувак. Я использовал renderer(_:updateAtTime) для РАЗЛИЧНЫХ других действий в моем приложении, но никогда не рассматривал возможность этого. Это сработало, добавив if! play { player.pause() }. Однако отличный обходной путь: необходимо изменить естественное поведение Apple. Спасибо за это, серьезно. У меня была эта проблема какое-то время - person impression7vx; 08.01.2019