-
Admite reproducción de video envolvente en apps de visionOS
Descubre cómo reproducir videos envolventes en las apps de visionOS. Hablaremos de modos de renderizado envolvente, revisaremos las estructuras que los admiten y explicaremos cómo renderizar videos envolventes en tu app. Para aprovechar al máximo este video, recomendamos ver primero “Explora experiencias de video para visionOS” de WWDC25.
Capítulos
- 0:00 - Introducción
- 1:24 - Perfiles de video compatibles con visionOS 26
- 3:09 - Reproducción de video envolvente en Quick Look
- 4:25 - Reproducción de video envolvente con AVKit
- 9:11 - Detección de mitigación de confort
- 9:51 - Reproducción personalizada en RealityKit
- 11:48 - Modo de inmersión progresiva en RealityKit
- 16:32 - Renderizado de video espacial con RealityKit
- 21:16 - Detección de mitigación de confort en RealityKit
- 22:57 - Integración de contenido de RealityKit con SwiftUI
Recursos
- HTTP Live Streaming Examples
- Playing immersive media with RealityKit
- Playing immersive media with AVKit
- RealityKit
- AVFoundation
- AVKit
Videos relacionados
WWDC25
- Conoce las tecnologías de Apple Immersive Video
- Explora experiencias de video para visionOS
- Más información sobre Apple Projected Media Profile
- Novedades de la web espacial
WWDC24
WWDC23
-
Buscar este video…
-
-
5:03 - AVExperienceController - AutomaticTransitionToImmersive
struct ExpandedConfiguration { enum AutomaticTransitionToImmersive { case `default` case none } } -
5:50 - Disable Automatic Transitions to immersive
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) -
6:26 - AVExperienceController - Immersive
enum Experience { case immersive } struct Configuration { struct Placement { static var unspecified: Placement static func over(scene: UIScene) -> Placement } } -
6:53 - Transition to immersive
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) -
8:13 - AVExperienceController.Delegate
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) -
12:52 - PortalVideoView
@main struct ImmersiveVideoApp: App { var body: some Scene { WindowGroup { PortalVideoView() } } } -
13:03 - Portal Rendering
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) } } } -
13:57 - Progressive Immersion Rendering
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) } } } -
14:20 - ProgressiveVideoView
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) } } -
17:22 - SpatialVideoMode
if let vpc = components.get[VideoPlayerComponent.self] { vpc.desiredSpatialVideoMode = .spatial } -
18:32 - Spatial Video Portal Rendering
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) } } } -
19:02 - Spatial Video Immersive Rendering
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) } } } -
19:46 - ImmersiveSpatialVideoView
import AVFoundation import RealityKit import SwiftUI @main struct SpatialVideoApp: App { var body: some Scene { ImmersiveSpace { ContentSimpleView() } .immersionStyle(selection: .constant(.mixed), in: .mixed) .immersiveEnvironmentBehavior(.coexist) } } -
21:40 - Comfort Mitigation Event
switch event.comfortMitigation { case .reduceImmersion: // Default behavior break case .play: // No action break case .pause: // Show custom pause dialog break }
-
-
- 0:00 - Introducción
Este video cubre las nuevas API en AVKit y Quick Look para reproducción de video envolvente y detalla cómo personalizar experiencias de reproducción envolvente con RealityKit.
- 1:24 - Perfiles de video compatibles con visionOS 26
visionOS 26 presenta nuevos perfiles de video como APMP para videos de 180, 360 y Wide FOV y Apple Immersive Video para la mejor experiencia envolvente. Puedes usar Quick Look, AVKit o RealityKit para reproducir en la app.
- 3:09 - Reproducción de video envolvente en Quick Look
Quick Look en visionOS 26 ofrece dos API: PreviewApplication para presentaciones de medios fuera de proceso y QLPreviewController para vistas previas multimedia dentro de la app. Ambas API se actualizan para admitir videos de fotos espaciales, Apple Immersive Video y Apple Projected Media Profile. Las apps que ya usan las API de Quick Look admitirán automáticamente estos nuevos perfiles multimedia envolventes.
- 4:25 - Reproducción de video envolvente con AVKit
AVKit presenta AVExperienceController, que ofrece dos experiencias principales: ampliado y envolvente. Ahora, puedes configurar la experiencia ampliada para una reproducción envolvente, con una opción para deshabilitar las transiciones automáticas. Esto permite un “tratamiento de portal” del contenido envolvente cuando se detecta. Puedes realizar la transición explícita a la experiencia envolvente usando la nueva API de experiencia envolvente, que proporciona una API de configuración para definir la ubicación de la reproducción de video envolvente. AVExperienceController maneja animaciones y transiciones entre experiencias. El protocolo de delegado del controlador es esencial para monitorear los cambios de estado de transición y presentación, garantizando que tus apps se adapten adecuadamente a diferentes tipos de contenido e interacciones del usuario.
- 9:11 - Detección de mitigación de confort
visionOS 26 presenta la detección de mitigación de comodidad para videos envolventes en Quick Look y AVKit. El sistema reduce automáticamente los niveles de inmersión durante la reproducción de alto movimiento del contenido de Apple Projected Media Profile para evitar la incomodidad del espectador, con personalizaciones disponibles en la app Configuración.
- 9:51 - Reproducción personalizada en RealityKit
RealityKit es la estructura ideal para la reproducción de video envolvente personalizada en visionOS, especialmente para juegos y apps con IU únicas. En visionOS 26, VideoPlayerComponent de RealityKit admite la reproducción nativa de videos envolventes, incluidos los formatos 180, 360 y campo de visión amplio, así como también Apple Immersive Video y reproducción de video espacial al igual que la app Fotos. Este componente maneja automáticamente las actualizaciones de malla y las animaciones, lo que lo hace ideal para crear experiencias de video envolvente dinámicas.
- 11:48 - Modo de inmersión progresiva en RealityKit
En visionOS 26, VideoPlayerComponent admite tres modos envolventes para Apple Projected Media Profile y Apple Immersive Video: portal, progresivo y completo. El modo portal presenta el video en un portal. El modo progresivo, nuevo en visionOS 26, permite a los usuarios ajustar la inmersión usando la Digital Crown y es el preferido por su comodidad y flexibilidad. Es equivalente a la inmersión al 100%. Para configurar la reproducción, configura el desiredImmersiveViewingMode. Para el modo progresivo, usa un ImmersiveSpace con un ImmersionStyle progresivo y combina el modo con el estilo. Los eventos del modo de visualización envolvente indican cuándo alternar la visibilidad de la IU durante las transiciones.
- 16:32 - Renderizado de video espacial con RealityKit
RealityKit ahora admite de forma nativa videos espaciales, lo que permite una reproducción envolvente con estilo espacial completo. Puedes configurar el estilo espacial con la propiedad desiredSpatialVideoMode en VideoPlayerComponent. Si se establece esta propiedad en .spatial, se habilita el estilo espacial, lo que permite la reproducción en los modos de visualización envolvente .portal o .full. El modo .full siempre se usa para videos espaciales envolventes, que se reproducen en un tamaño fijo según el campo de visión del contenido. El modo predeterminado, .screen, reproduce videos espaciales en estéreo tradicional en una malla de pantalla. Puedes suscribirte al evento SpatialVideoModeDidChange para supervisar las actualizaciones de la propiedad spatialVideoMode. Para crear un portal de video espacial, configura desiredSpatialVideoMode en .spatial y desiredImmersiveViewingMode en .portal. Para una reproducción de video espacial envolvente, usa el modo .full con un estilo de inmersión mixto y la opción coexistir para immersiveEnvironmentBehavior.
- 21:16 - Detección de mitigación de confort en RealityKit
RealityKit detecta automáticamente el movimiento alto en los videos de Apple Projected Media Profile y aplica mitigación de comodidad. Tu app recibe notificaciones de estos ajustes a través del evento VideoComfortMitigationDidOccur. Se requiere la reproducción progresiva para mitigar la “inmersión reducida” y la reproducción del portal no lo necesita porque ya es cómodo para la mayoría del contenido. El evento ContentTypeDidChange te ayuda a adaptarte a diferentes tipos de video y su modo de visualización específico y requisitos de mitigación.
- 22:57 - Integración de contenido de RealityKit con SwiftUI
Al integrar VideoPlayerComponent de RealityKit con SwiftUI, asegúrate de que los ejes X e Y de la entidad de video tengan una escala uniforme para mantener la relación de aspecto, especialmente en el modo portal. Usa GeometryReader3D para modificar la escala del video según el tamaño de ventana disponible para evitar recortes. La IU personalizada que quede en el mismo plano que la malla de video tiene un comportamiento de ordenación indefinido. Para resolver problemas de clasificación con la IU personalizada en el mismo plano, agrega un ModelSortGroupComponent con una categoría planarUIPlacement.