Основная проблема:
Я добавляю этот раздел ПОСЛЕ, чтобы прояснить проблему. - Я могу ПРИОСТАНОВИТЬ свое видео (я не хочу, чтобы оно воспроизводилось в цикле). Когда мой узел появляется в поле зрения, он воспроизводит мое видео, даже если оно приостановлено. Если воспроизведение моего видео закончилось и оно появится в поле зрения, оно перезапустится. Я хочу УДАЛИТЬ это поведение.
В моем приложении у меня есть 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()
}
Это останавливает все, поэтому камера даже зависает, так как ничего не отслеживается. Здесь это совершенно бесполезно.
createVideoNode
плз. Обычно мы добавляем видео узел как дочерний узел вdidAdd node
. - person Alok Subedi   schedule 24.12.2018SKVideoNode
- вы можете назначитьAVPlayer
непосредственноcontents
материала SceneKit в iOS 11.0 и более поздних версиях. - person rickster   schedule 05.01.2019let config = ARWorldTrackingConfiguration() config.planeDetection = [] sceneView.session.run(config)
- person Mihai Erős   schedule 07.01.2019