如何在RealityKit的VideoMaterial中异步加载网络视频到AVPlayer
解决RealityKit视频加载卡顿及占位显示问题
核心思路
- 检测到参考图像后,先在主线程创建带占位材质(SimpleMaterial)的平面,立即添加到场景,避免用户看到卡顿或空白
- 异步加载网络视频资源,将视频加载逻辑放到后台队列,不阻塞主线程
- 视频加载完成后,切回主线程替换材质为VideoMaterial,再启动播放
修改后的完整代码
class Coordinator: NSObject, ARSessionDelegate { var parent: ARViewContainer var videoPlayer: AVPlayer? init(parent: ARViewContainer) { self.parent = parent } func session(_ session: ARSession, didAdd anchors: [ARAnchor]) { guard let imageAnchor = anchors.first as? ARImageAnchor, imageAnchor.name == "referenceimg1" else { return } // 1. 主线程创建占位平面(带SimpleMaterial),立即显示 let planeSize = imageAnchor.referenceImage.physicalSize let placeholderMaterial = SimpleMaterial( color: .gray, isMetallic: false ) let videoPlane = ModelEntity( mesh: .generatePlane( width: Float(planeSize.width), depth: Float(planeSize.height), cornerRadius: 10 ), materials: [placeholderMaterial] ) let anchorEntity = AnchorEntity(anchor: imageAnchor) anchorEntity.addChild(videoPlane) parent.arView.scene.addAnchor(anchorEntity) // 2. 后台队列异步加载视频,避免阻塞主线程 DispatchQueue.global(qos: .userInitiated).async { [weak self] in guard let self = self, let videoURL = URL(string: "https://swiftanytime-content.s3.ap-south-1.amazonaws.com/SwiftUI-Beginner/Video-Player/iMacAdvertisement.mp4") else { return } let playerItem = AVPlayerItem(url: videoURL) // 监听视频加载状态 let statusObservation = playerItem.observe(\.status, options: [.new]) { [weak self, weak videoPlane] item, change in guard let self = self, let videoPlane = videoPlane else { return } DispatchQueue.main.async { switch item.status { case .readyToPlay: // 3. 主线程替换为VideoMaterial并播放 self.videoPlayer = AVPlayer(playerItem: item) let videoMaterial = VideoMaterial(avPlayer: self.videoPlayer!) videoPlane.model?.materials = [videoMaterial] self.videoPlayer?.play() case .failed: // 加载失败可添加错误处理,比如显示错误材质 let errorMaterial = SimpleMaterial(color: .red, isMetallic: false) videoPlane.model?.materials = [errorMaterial] case .unknown: break @unknown default: break } } } // 保留观察对象,避免被提前释放 statusObservation.retain() } } }
关键细节说明
- 异步加载:用
DispatchQueue.global(qos: .userInitiated)将视频加载逻辑放到后台,主线程只负责UI相关操作,彻底解决卡顿 - 占位材质:先显示灰色SimpleMaterial,你可以根据需求修改颜色、添加纹理(比如加载动画图片)
- 状态监听:通过KVO监听
AVPlayerItem的status属性,确保视频真正准备好后再替换材质,避免播放异常 - 弱引用处理:使用
[weak self]和[weak videoPlane]避免循环引用,防止内存泄漏
内容的提问来源于stack exchange,提问作者user1207524
相关产品推荐
相关产品推荐

