Streaming is available in most browsers,
and in the Developer app.
-
Build compelling spatial photo and video experiences
Learn how to adopt spatial photos and videos in your apps. Explore the different types of stereoscopic media and find out how to capture spatial videos in your iOS app on iPhone 15 Pro. Discover the various ways to detect and present spatial media, including the new QuickLook Preview Application API in visionOS. And take a deep dive into the metadata and stereo concepts that make a photo or video spatial.
Chapters
- 0:00 - Introduction
- 1:07 - Types of stereoscopic experiences
- 4:13 - Tour of the new APIs
- 13:14 - Deep dive into spatial media formats
Resources
- AVCam
- Converting side-by-side 3D video to multiview HEVC and spatial video
- Creating spatial photos and videos with spatial metadata
- Forum: Spatial Computing
- Writing spatial photos
Related Videos
WWDC24
- Bring your iOS or iPadOS game to visionOS
- Enhance the immersion of media viewing in custom environments
- Optimize for the spatial web
- What’s new in Quick Look for visionOS
WWDC23
-
DownloadArray
-
-
6:19 - Spatial video capture on iPhone 15 Pro
class CaptureManager { var session: AVCaptureSession! var input: AVCaptureDeviceInput! var output: AVCaptureMovieFileOutput! func setupSession() throws -> Bool { session = AVCaptureSession() session.beginConfiguration() guard let videoDevice = AVCaptureDevice.default( .builtInDualWideCamera, for: .video, position: .back ) else { return false } var foundSpatialFormat = false for format in videoDevice.formats { if format.isSpatialVideoCaptureSupported { try videoDevice.lockForConfiguration() videoDevice.activeFormat = format videoDevice.unlockForConfiguration() foundSpatialFormat = true break } } guard foundSpatialFormat else { return false } let videoDeviceInput = try AVCaptureDeviceInput(device: videoDevice) guard session.canAddInput(videoDeviceInput) else { return false } session.addInput(videoDeviceInput) input = videoDeviceInput let movieFileOutput = AVCaptureMovieFileOutput() guard session.canAddOutput(movieFileOutput) else { return false } session.addOutput(movieFileOutput) output = movieFileOutput guard let connection = output.connection(with: .video) else { return false } guard connection.isVideoStabilizationSupported else { return false } connection.preferredVideoStabilizationMode = .cinematicExtendedEnhanced guard movieFileOutput.isSpatialVideoCaptureSupported else { return false } movieFileOutput.isSpatialVideoCaptureEnabled = true session.commitConfiguration() session.startRunning() return true } }
-
9:13 - Observing spatial capture discomfort reasons
let observation = videoDevice.observe(\.spatialCaptureDiscomfortReasons) { (device, change) in guard let newValue = change.newValue else { return } if newValue.contains(.subjectTooClose) { // Guide user to move back } if newValue.contains(.notEnoughLight) { // Guide user to find a brighter environment } }
-
9:58 - PhotosPicker
import SwiftUI import PhotosUI struct PickerView: View { @State var selectedItem: PhotosPickerItem? var body: some View { PhotosPicker(selection: $selectedItem, matching: .spatialMedia) { Text("Choose a spatial photo or video") } } }
-
10:14 - PhotoKit - all spatial assets
import Photos func fetchSpatialAssets() { let fetchOptions = PHFetchOptions() fetchOptions.predicate = NSPredicate( format: "(mediaSubtypes & %d) != 0", argumentArray: [PHAssetMediaSubtype.spatialMedia.rawValue] ) fetchResult = PHAsset.fetchAssets(with: fetchOptions) }
-
10:36 - AVAssetPlaybackAssistant
import AVFoundation extension AVURLAsset { func isSpatialVideo() async -> Bool { let assistant = AVAssetPlaybackAssistant(asset: self) let options = await assistant.playbackConfigurationOptions return options.contains(.spatialVideo) } }
-
-
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.