You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用SceneKit与AVPlayer实现视频播放时,视频方向异常如何修正?

解决SceneKit中AVPlayer视频方向不正确的问题

我用SceneKit结合AVPlayer和SCNNode开发视频播放器,逻辑是通过视频URL创建AVPlayer,将播放器设为SCNPlane的漫反射内容,以此平面为几何体创建SCNNode后添加到SCNView的rootNode中,代码如下:

private func setupVideoNode() {
    videoPlayer = AVPlayer(url: videoURL)
    let videoTrack = videoPlayer!.currentItem!.asset.tracks(withMediaType: .video).first!
    let naturalSize = videoTrack.naturalSize
    let aspectRatio: CGFloat = CGFloat(naturalSize.width / naturalSize.height) * 10
    
    let videoPlane = SCNPlane(width: aspectRatio, height: 10)
    videoPlane.firstMaterial?.isDoubleSided = true
    videoPlane.firstMaterial?.diffuse.contents = videoPlayer
    videoNode = SCNNode(geometry: videoPlane)
    
    sceneView.scene?.rootNode.addChildNode(videoNode!)
}

但竖屏视频显示方向不正确(视频被旋转为横屏,画面横向拉伸)。尝试用preferredTransform计算视频宽高后,效果仍不符合预期(比例调整了但方向还是错误)。

正确的调整方案

问题出在只调整了平面的宽高比,没处理preferredTransform中包含的视频旋转信息。需要同时调整SCNNode的旋转角度和平面的正确宽高:

  1. 从preferredTransform中提取视频的旋转角度和实际显示尺寸
  2. 根据旋转角度设置SCNNode的rotation属性
  3. 基于实际显示尺寸计算SCNPlane的宽高比

修改后的完整代码:

private func setupVideoNode() {
    videoPlayer = AVPlayer(url: videoURL)
    guard let videoTrack = videoPlayer?.currentItem?.asset.tracks(withMediaType: .video).first else { return }
    
    let naturalSize = videoTrack.naturalSize
    let transform = videoTrack.preferredTransform
    
    // 提取旋转角度(处理90/270度旋转的情况)
    let rotationInRadians = atan2(transform.b, transform.a)
    // 计算实际显示的尺寸(应用变换后的尺寸)
    let adjustedSize = naturalSize.applying(transform)
    let fixedWidth = abs(adjustedSize.width)
    let fixedHeight = abs(adjustedSize.height)
    
    // 计算平面的宽高比,保持高度为10的话,宽度按比例调整
    let aspectRatio = fixedWidth / fixedHeight * 10
    let videoPlane = SCNPlane(width: aspectRatio, height: 10)
    
    videoPlane.firstMaterial?.isDoubleSided = true
    videoPlane.firstMaterial?.diffuse.contents = videoPlayer
    
    videoNode = SCNNode(geometry: videoPlane)
    // 设置节点的旋转(绕y轴旋转,适配AVPlayer的旋转方向)
    videoNode?.rotation = SCNVector4(0, 1, 0, -rotationInRadians)
    
    sceneView.scene?.rootNode.addChildNode(videoNode!)
    
    // 可选:启动播放
    videoPlayer?.play()
}

关键说明

  • 旋转角度提取:atan2(transform.b, transform.a)可以从preferredTransform矩阵中算出视频的旋转角度,这个矩阵包含了拍摄时的方向信息(比如竖屏拍摄的90度旋转)。
  • 尺寸修正:naturalSize.applying(transform)得到的是视频实际应该显示的宽高,取绝对值避免负数。
  • 节点旋转:SceneKit中SCNNode的rotation是绕指定轴的弧度值,这里绕y轴旋转并取负,是为了适配AVPlayer的旋转方向,确保画面正确 upright。

内容的提问来源于stack exchange,提问作者cleanrun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 20:05:21