如何在SceneKit中创建播放循环视频的素材?
答案 0 :(得分:24)
使用SpriteKit场景作为几何体material,可以在SceneKit中实现此目的。
以下示例将创建一个SpriteKit场景,使用视频播放器向其添加视频节点,制作视频播放器循环,创建SceneKit场景,添加SceneKit平面,最后将SpriteKit场景添加为平面的漫反射材质
import UIKit
import SceneKit
import SpriteKit
import AVFoundation
class ViewController: UIViewController, SCNSceneRendererDelegate {
@IBOutlet weak var sceneView: SCNView!
override func viewDidLoad() {
super.viewDidLoad()
// A SpriteKit scene to contain the SpriteKit video node
let spriteKitScene = SKScene(size: CGSize(width: sceneView.frame.width, height: sceneView.frame.height))
spriteKitScene.scaleMode = .aspectFit
// Create a video player, which will be responsible for the playback of the video material
let videoUrl = Bundle.main.url(forResource: "videos/video", withExtension: "mp4")!
let videoPlayer = AVPlayer(url: videoUrl)
// To make the video loop
videoPlayer.actionAtItemEnd = .none
NotificationCenter.default.addObserver(
self,
selector: #selector(ViewController.playerItemDidReachEnd),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
object: videoPlayer.currentItem)
// Create the SpriteKit video node, containing the video player
let videoSpriteKitNode = SKVideoNode(avPlayer: videoPlayer)
videoSpriteKitNode.position = CGPoint(x: spriteKitScene.size.width / 2.0, y: spriteKitScene.size.height / 2.0)
videoSpriteKitNode.size = spriteKitScene.size
videoSpriteKitNode.yScale = -1.0
videoSpriteKitNode.play()
spriteKitScene.addChild(videoSpriteKitNode)
// Create the SceneKit scene
let scene = SCNScene()
sceneView.scene = scene
sceneView.delegate = self
sceneView.isPlaying = true
// Create a SceneKit plane and add the SpriteKit scene as its material
let background = SCNPlane(width: CGFloat(100), height: CGFloat(100))
background.firstMaterial?.diffuse.contents = spriteKitScene
let backgroundNode = SCNNode(geometry: background)
scene.rootNode.addChildNode(backgroundNode)
...
}
// This callback will restart the video when it has reach its end
func playerItemDidReachEnd(notification: NSNotification) {
if let playerItem: AVPlayerItem = notification.object as? AVPlayerItem {
playerItem.seek(to: kCMTimeZero)
}
}
...
}
答案 1 :(得分:1)
可以使用AVPlayer作为场景背景的内容。 但是,直到我将.play(nil)发送到sceneView之前,它对我来说都是无效的。
override func viewDidLoad() {
super.viewDidLoad()
// Set the view's delegate
sceneView.delegate = self
// Show statistics such as fps and timing information
sceneView.showsStatistics = true
// Create a new scene
let scene = SCNScene(named: "art.scnassets/ship.scn")!
// create and add a camera to the scene
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode)
// Set the scene to the view
sceneView.scene = scene
let movieFileURL = Bundle.main.url(forResource: "example", withExtension: "mov")!
let player = AVPlayer(url:movieFileURL)
scene.background.contents = player
sceneView.play(nil) //without this line the movie was not playing
player.play()
}
答案 2 :(得分:1)
2019年解决方案:
from alembic import op
import sqlalchemy as sa
import textwrap
from sqlalchemy.dialects import postgresql
kernelstatus_new_values = [
'PENDING', # added
'PREPARING',
...
]
kernelstatus_new = postgresql.ENUM(*kernelstatus_new_values, name='kernelstatus')
kernelstatus_old_values = [
'PREPARING',
...
]
kernelstatus_old = postgresql.ENUM(*kernelstatus_old_values, name='kernelstatus')
def upgrade():
conn = op.get_bind()
sessionresult.create(conn)
sessiontypes.create(conn)
conn.execute('ALTER TYPE kernelstatus RENAME TO kernelstatus_old;')
kernelstatus_new.create(conn)
conn.execute(textwrap.dedent('''\
CREATE FUNCTION new_old_compare(
new_enum_val kernelstatus, old_enum_val kernelstatus_old
)
RETURNS boolean AS $$
SELECT new_enum_val::text <> old_enum_val::text;
$$ LANGUAGE SQL IMMUTABLE;
CREATE OPERATOR <> (
leftarg = kernelstatus,
rightarg = kernelstatus_old,
procedure = new_old_compare
);
'''))
op.alter_column(
table_name='kernels',
column_name='status',
type_=kernelstatus_new,
postgresql_using='status::text::kernelstatus',
)
conn.execute(textwrap.dedent('''\
DROP FUNCTION new_old_compare(
new_enum_val kernelstatus, old_enum_val kernelstatus_old
) CASCADE;
DROP TYPE kernelstatus_old;
'''))
... # the rest of migration
选择器中方法的代码:
let mat = SCNMaterial()
let videoUrl = Bundle.main.url(forResource: "YourVideo", withExtension: "mp4")!
let player = AVPlayer(url: videoUrl)
mat.diffuse.contents = player
player.actionAtItemEnd = .none
NotificationCenter.default.addObserver(self,
selector: #selector(playerItemDidReachEnd(notification:)),
name: .AVPlayerItemDidPlayToEndTime,
object: player.currentItem)
player.play()
当对象被释放时,别忘了删除您的通知观察器!像这样:
@objc private func playerItemDidReachEnd(notification: Notification) {
if let playerItem = notification.object as? AVPlayerItem {
playerItem.seek(to: .zero, completionHandler: nil)
}
}