Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2025 Audio & VideoSpatial Computing

WWDC25 · 26 min · Audio & Video / Spatial Computing

Support immersive video playback in visionOS apps

Discover how to play immersive videos in visionOS apps. We’ll cover various immersive rendering modes, review the frameworks that support them, and walk through how to render immersive video in your app. To get the most out of this video, we recommend first watching “Explore video experiences for visionOS” from WWDC25.

Watch at developer.apple.com ↗

Transcript all transcripts

Chapters

  • 0:00 — Introduction
  • 1:24 — Video profiles supported in visionOS 26
  • 3:09 — Immersive video playback in 
Quick Look
  • 4:25 — Immersive video playback with AVKit
  • 9:11 — Comfort mitigation detection
  • 9:51 — Custom playback 
in RealityKit
  • 11:48 — Progressive immersion mode in RealityKit
  • 16:32 — Spatial video rendering with RealityKit
  • 21:16 — Comfort mitigation detection in RealityKit
  • 22:57 — RealityKit content integration with SwiftUI

Code shown on screen · 14 snippets

AVExperienceController - AutomaticTransitionToImmersive swift · at 5:03 ↗
struct ExpandedConfiguration {
    enum AutomaticTransitionToImmersive {
  		case `default`
  		case  none
    }
}
Disable Automatic Transitions to immersive swift · at 5:50 ↗
import AVKit

let controller = AVPlayerViewController()

let experienceController = controller.experienceController
experienceController.allowedExperiences = .recommended(including: [.expanded, .immersive])

experienceController.configuration.expanded.automaticTransitionToImmersive = .none

await experienceController.transition(to: .expanded)
AVExperienceController - Immersive swift · at 6:26 ↗
enum Experience {
    case immersive
}

struct Configuration {
		struct Placement {
			static var unspecified: Placement
			static func over(scene: UIScene) -> Placement
		}
}
Transition to immersive swift · at 6:53 ↗
import AVKit

let controller = AVPlayerViewController()

let experienceController = controller.experienceController
experienceController.allowedExperiences = .recommended(including: [.immersive])

let myScene = getMyPreferredWindowUIScene()
experienceController.configuration.placement = .over(scene: myScene)

await experienceController.transition(to: .immersive)
AVExperienceController.Delegate swift · at 8:13 ↗
func experienceController(_ controller: AVExperienceController, didChangeAvailableExperiences availableExperiences: AVExperienceController.Experiences)

func experienceController(_ controller: AVExperienceController, prepareForTransitionUsing context: AVExperienceController.TransitionContext) async

func experienceController(_ controller: AVExperienceController, didChangeTransitionContext context: AVExperienceController.TransitionContext)
PortalVideoView swift · at 12:52 ↗
@main
struct ImmersiveVideoApp: App {
    var body: some Scene {
        WindowGroup {
            PortalVideoView()
        }
    }
}
Portal Rendering swift · at 13:03 ↗
import AVFoundation
import RealityKit
import SwiftUI

struct PortalVideoView: View {
    var body: some View {
        RealityView { content in
            guard let url = URL(string: "https://cdn.example.com/My180.m3u8") else { return }
            let player = AVPlayer(playerItem: AVPlayerItem(url: url))
            let videoEntity = Entity()
            var videoPlayerComponent = VideoPlayerComponent(avPlayer: player)
            videoPlayerComponent.desiredImmersiveViewingMode = .portal
            videoEntity.components.set(videoPlayerComponent)
            videoEntity.scale *= 0.4
            content.add(videoEntity)
        }
    }
}
Progressive Immersion Rendering swift · at 13:57 ↗
import AVFoundation
import RealityKit
import SwiftUI

struct ProgressiveVideoView: View {
    var body: some View {
        RealityView { content in
            guard let url = URL(string: "https://cdn.example.com/My180.m3u8") else { return }
            let player = AVPlayer(playerItem: AVPlayerItem(url: url))
            let videoEntity = Entity()
            var videoPlayerComponent = VideoPlayerComponent(avPlayer: player)
            videoPlayerComponent.desiredImmersiveViewingMode = .progressive
            videoEntity.components.set(videoPlayerComponent)
            content.add(videoEntity)
        }
    }
}
ProgressiveVideoView swift · at 14:20 ↗
import AVFoundation
import RealityKit
import SwiftUI

@main
struct ImmersiveVideoApp: App {
    var body: some Scene {
        ImmersiveSpace {
            ProgressiveVideoView()
        }
				.immersionStyle(selection: .constant(.progressive(0.1...1, initialAmount: 1.0)), in: .progressive)    
    }
}
SpatialVideoMode swift · at 17:22 ↗
if let vpc = components.get[VideoPlayerComponent.self] {
	vpc.desiredSpatialVideoMode = .spatial
}
Spatial Video Portal Rendering swift · at 18:32 ↗
import AVFoundation
import RealityKit
import SwiftUI

struct PortalSpatialVideoView: View {
    var body: some View {
        RealityView { content in
            let url = Bundle.main.url(forResource: "MySpatialVideo", withExtension: "mov")!
            let player = AVPlayer(url: url)
            let videoEntity = Entity()
            var videoPlayerComponent = VideoPlayerComponent(avPlayer: player)
            videoPlayerComponent.desiredViewingMode = .stereo
            videoPlayerComponent.desiredSpatialVideoMode = .spatial
            videoPlayerComponent.desiredImmersiveViewingMode = .portal
            videoEntity.components.set(videoPlayerComponent)
            videoEntity.scale *= 0.4
            content.add(videoEntity)
        }
    }
}
Spatial Video Immersive Rendering swift · at 19:02 ↗
import AVFoundation
import RealityKit
import SwiftUI

struct PortalSpatialVideoView: View {
    var body: some View {
        RealityView { content in
            let url = Bundle.main.url(forResource: "MySpatialVideo", withExtension: "mov")!
            let player = AVPlayer(url: url)
            let videoEntity = Entity()
            var videoPlayerComponent = VideoPlayerComponent(avPlayer: player)
            videoPlayerComponent.desiredViewingMode = .stereo
            videoPlayerComponent.desiredSpatialVideoMode = .spatial
            videoPlayerComponent.desiredImmersiveViewingMode = .full
            videoEntity.position = [0, 1.5, -1]
            videoEntity.components.set(videoPlayerComponent)
            content.add(videoEntity)
        }
    }
}
ImmersiveSpatialVideoView swift · at 19:46 ↗
import AVFoundation
import RealityKit
import SwiftUI

@main
struct SpatialVideoApp: App {
    var body: some Scene {
        ImmersiveSpace {
            ContentSimpleView()
        }
        .immersionStyle(selection: .constant(.mixed), in: .mixed)
        .immersiveEnvironmentBehavior(.coexist)
    }
}
Comfort Mitigation Event swift · at 21:40 ↗
switch event.comfortMitigation {
case .reduceImmersion:
    // Default behavior
    break
case .play:
    // No action
    break
case .pause:
    // Show custom pause dialog
    break
}

Resources