
-
Tournez des vidéos de qualité cinéma dans votre app
Découvrez comment l'API Cinematic Video permet à votre app de capturer sans effort des vidéos dignes du cinéma. Nous verrons comment configurer une séance de capture cinématographique et présenterons les bases de la création d'une interface utilisateur de capture vidéo. Nous explorerons également des fonctionnalités cinématographiques avancées, telles que l'application d'un effet de profondeur de champ pour obtenir à la fois un suivi de mise au point et un effet de rack focus.
Chapitres
- 0:00 - Introduction
- 0:33 - Vidéo cinématographique
- 3:44 - Créez une expérience de capture cinématographique exceptionnelle
Ressources
Vidéos connexes
WWDC25
WWDC24
WWDC23
-
Rechercher dans cette vidéo…
Bonjour, je suis Roy. Je suis ingénieur dans l’équipe Camera Software. Je vais vous montrer comment vos apps peuvent facilement capturer des vidéos de qualité cinématographique avec l’API Cinematic Video.
Avec l’iPhone 13 et 13 Pro, nous avons lancé le mode Cinématographique.
Grâce à son interface intuitive et ses algorithmes puissants, il a transformé l’iPhone en véritable outil de cinéma.
Dans cette présentation, nous verrons ce qui rend le mode Cinématique si magique et nous découvrirons du code pour créer une expérience de capture réussie.
Qu’est-ce que le mode Cinématique ?
Au cœur de cette technique, il y a des outils classiques comme la mise au point et le suivi.
Avec une faible profondeur de champ, le réalisateur guide le regard vers les sujets clés, renforçant ainsi l’impact narratif. Quand les sujets bougent, le suivi de la mise au point les maintient nets à l’écran.
Ces puissantes techniques de mise au point demandent une grande expertise. C’est pourquoi, sur un tournage, des opérateurs spécialisés sont chargés d’exécuter ces prises complexes et précises.
Le mode Cinématique automatise de manière intelligente la gestion de la mise au point.
Par exemple, lorsqu’un sujet entre dans le cadre, l’algorithme ajuste automatiquement la mise au point et lance le suivi. Quand un sujet détourne le regard, la mise au point passe automatiquement à un autre point, puis revient sur le sujet au bon moment.
Cette année, nous rendons ces fonctionnalités accessibles via l’API Cinematic Video, pour que vos apps capturent facilement des vidéos au style cinématographique. Voyons comment créer une expérience de capture réussie en mode Cinématique avec la nouvelle API. Commençons par une séance de capture classique dans une app vidéo.
D’abord, nous choisissons l’appareil avec lequel filmer.
Puis, nous l’ajoutons en tant qu’entrée.
Selon les besoins, plusieurs sorties peuvent être ajoutées à la séance.
Des objets de connexion sont créés lorsque ces sorties sont ajoutées à la séance de capture.
Ce n’est pas une configuration simple, mais le mode Cinématique est très facile à activer.
Dans iOS 26, une nouvelle propriété, isCinematicVideoCaptureEnabled, est ajoutée à la classe AVCaptureDeviceInput. En la définissant sur true, toute la séance de capture est configurée pour produire une vidéo en mode Cinématique. Chaque sortie bénéficiera désormais de l’effet cinématographique.
Le fichier vidéo produit sera au format cinématographique.
Il contient les données de disparité, les métadonnées et la vidéo originale, permettant un montage non destructif. Pour lire la vidéo avec le bokeh appliqué ou modifier le bokeh, vous pouvez utiliser le framework Cinematic présenté en 2023.
Pour en savoir plus sur ce framework, regardez la vidéo « Support Cinematic mode videos in your app » de la WWDC23. La sortie vidéo produit des images avec un effet de faible profondeur de champ intégré. Cela est utile lorsque vous avez besoin d’accéder directement aux images, par exemple pour les envoyer à un appareil distant.
De la même façon, l’arrière-plan flouté sera appliqué en temps réel dans la couche d’aperçu. C’est une façon simple de créer un viseur.
Avec cette architecture de haut niveau en tête, explorons le code dans les domaines suivants.
Nous allons configurer une AVCaptureSession avec tous les éléments pour la capture cinématographique.
Ensuite, nous allons créer une interface de capture vidéo avec SwiftUI.
Nous verrons comment récupérer les métadonnées, comme la détection des visages, et les afficher à l’écran.
En pilotant la mise au point manuellement, on exploite tout le potentiel du mode Cinématique.
Enfin, nous terminerons par des fonctionnalités avancées pour peaufiner notre app.
Commençons par la séance de capture.
D’abord, nous devons trouver l’appareil vidéo avec lequel capturer le film. Pour cela, nous créons un objet AVCaptureDevice.DiscoverySession.
Le mode Cinématique est pris en charge à la fois par la caméra arrière Dual Wide et par la caméra frontale TrueDepth.
Ici, nous spécifions .builtInDualWideCamera dans la liste des types d’appareils. Comme nous filmons, nous utilisons .video comme mediaType.
Et nous demandons la caméra arrière de l’appareil.
Puisqu’on demande un seul type d’appareil, on prend simplement le premier élément de la liste des appareils de la séance de découverte.
Pour activer la capture en mode Cinématique, il faut utiliser un format compatible avec cette fonctionnalité.
Pour le trouver, on parcourt tous les formats de l’appareil et on choisit celui dont la propriété isCinematicVideoCaptureSupported renvoie true.
Voici tous les formats pris en charge.
Pour les caméras Dual Wide et TrueDepth, le 1080p et le 4K sont pris en charge à 30 images par seconde.
Pour enregistrer du contenu SDR ou EDR, vous pouvez utiliser la plage vidéo 420 ou la plage complète.
Pour du contenu HDR 10 bits, préférez le format x420.
Comme ce n’est pas un film muet, nous voulons aussi du son. Nous utiliserons la même API DiscoverySession pour trouver le microphone.
Avec nos appareils en main, nous créons les entrées pour chacun d’entre eux. Ensuite, nous ajoutons ces entrées à la séance de capture.
À ce stade, nous pouvons activer la capture en mode Cinématique sur l’entrée vidéo.
Pour enrichir l’expérience en mode Cinématique, on peut capturer de l’audio spatial en définissant simplement le mode multichannelAudioMode sur firstOrderAmbisonics.
Pour en savoir plus sur l’audio spatial, regardez la vidéo « Enhance your app’s audio content creation capabilities ». Passons aux sorties : nous créons un objet AVCaptureMovieFileOutput que nous ajoutons à la séance.
Nos mains ne sont pas aussi stables qu’un trépied, alors nous recommandons d’activer la stabilisation vidéo.
Pour cela, on récupère d’abord la connexion vidéo de movieFileOutput et on règle son mode preferredVideoStabilizationMode. Dans ce cas, nous utilisons cinematicExtendedEnhanced.
Enfin, nous devons associer notre couche d’aperçu à la séance de capture.
C’est terminé pour la séance de capture pour l’instant. Passons à l’interface utilisateur.
Comme AVCaptureVideoPreviewLayer hérite de CALayer, qui ne fait pas partie de SwiftUI, pour les faire fonctionner ensemble, il faut envelopper la couche d’aperçu dans une structure conforme au protocole UIViewRepresentable.
Dans cette structure, nous créons une sous-classe UIView CameraPreviewUIView.
Nous redéfinissons la propriété layerClass pour que previewLayer soit la couche principale de la vue.
Nous ajoutons une propriété previewLayer pour y accéder facilement en tant que AVCaptureVideoPreviewLayer.
Nous pouvons placer notre vue d’aperçu dans un ZStack, pour la combiner facilement avec d’autres éléments d’UI, comme les commandes caméra.
Comme dit en introduction, la faible profondeur de champ est un outil clé du storytelling. En modifiant la propriété simulatedAperture sur l’entrée de l’appareil, on peut ajuster l’intensité globale de l’effet bokeh.
À droite, la gestion de cette propriété via un curseur permet de modifier l’intensité globale du flou.
Cette valeur est exprimée en f-stops, la norme utilisée dans l’industrie, qui correspond simplement au rapport entre la longueur focale et le diamètre de l’ouverture.
L’ouverture est calculée en divisant la longueur focale par le nombre f.
Par conséquent, plus le nombre f est petit, plus l’ouverture est grande et plus le bokeh est prononcé.
On peut obtenir l’ouverture simulée minimale, maximale et par défaut pour le format.
Nous les utilisons pour alimenter les éléments d’UI, comme un curseur.
Créons maintenant des commandes qui permettent à l’utilisateur d’interagir manuellement avec le mode Cinématique.
Pour permettre la mise au point manuelle, il faut afficher des indicateurs visuels sur les cibles, comme les visages. Pour cela, nous avons besoin de métadonnées de détection.
Nous utilisons AVCaptureMetadataOutput pour détecter et afficher les contours à l’écran pour permettre à l’utilisateur d’interagir. L’algorithme du mode Cinématique nécessite certains metadataObjectTypes pour fonctionner au mieux.
Ils sont spécifiés via la nouvelle propriété requiredMetadataObjectTypesForCinematicVideoCapture. Une exception est levée si les metadataObjectTypes fournis diffèrent de cette liste lorsque le mode Cinématique est activé.
Enfin, il faut fournir un délégué pour recevoir les métadonnées et une file d’attente sur laquelle ce délégué sera appelé.
Nous recevons les objets de métadonnées dans le rappel du délégué de sortie des métadonnées.
Pour transmettre facilement ces métadonnées à notre interface SwiftUI, nous utilisons une classe observable.
Lorsque nous mettons à jour sa propriété, la vue qui l’observe est automatiquement actualisée.
Dans notre couche d’affichage, chaque fois que notre objet observable est mis à jour, la vue est automatiquement redessinée.
Nous dessinons un rectangle pour chaque metadataObject.
Lors de la création de ces rectangles, il faut transformer les contours des métadonnées dans le système de coordonnées de la couche d’aperçu.
En utilisant la méthode layerRectConverted fromMetadataOutputRect.
Notez que X et Y dans la méthode position correspondent au centre de la vue, et non au coin supérieur gauche comme dans AVFoundation. Il faut donc ajuster en utilisant les valeurs midX et midY du rectangle.
Avec les rectangles de métadonnées affichés, on peut piloter la mise au point manuellement.
L’API Cinematic Video propose 3 méthodes pour effectuer la mise au point manuelle. Voyons-les maintenant une par une.
La méthode setCinematicVideoTrackingFocus detectedObjectID focusMode permet d’effectuer une transition de mise au point vers un sujet identifié par detectedObjectID, que l’on retrouve dans les objets AVMetadata obtenus via la sortie des métadonnées.
focusMode configure le comportement de suivi du mode Cinématique.
L’énumération CinematicVideoFocusMode comporte 3 cas : none, strong et weak.
Strong indique au mode Cinématique de continuer à suivre un sujet, même si d’autres cibles pourraient être automatiquement sélectionnées.
Ici, même si le chat est devenu plus visible, la mise au point forte, indiquée par le rectangle jaune plein, est restée verrouillée sur le sujet à l’arrière.
En revanche, weak laisse l’algorithme garder le contrôle de la mise au point. Il ajuste automatiquement la mise au point quand il le juge nécessaire.
Quand le chat s’est retourné, il est devenu prioritaire, et la mise au point faible s’est automatiquement déplacée vers lui, comme le montre le rectangle en pointillés.
None sert uniquement à vérifier si un objet métadonnée a la mise au point, ne doit donc pas être utilisé pour la définir.
La 2e méthode de mise au point utilise un 1er paramètre différent. Au lieu d’un ID d’objet détecté, elle prend un point dans une vue.
Elle indique au mode Cinématique de rechercher un objet intéressant au point spécifié. Lorsqu’elle en trouve un, elle crée un nouvel objet métadonnée de type salient object. Nous pouvons donc dessiner le rectangle qui l’entoure à l’écran.
La 3è méthode est setCinematicVideoFixedFocus, qui prend un point et un mode de mise au point. Elle fixe la mise au point à une distance calculée en interne grâce à des signaux comme la profondeur. Associée au mode strong, cette méthode verrouille la mise au point sur un plan précis, en ignorant toute activité, même au premier plan.
Chaque app peut implémenter la logique de mise au point adaptée à ses besoins. Dans notre app, voici ce que nous faisons :
Toucher un rectangle de détection qui n’est pas net déclenche une mise au point faible sur celui-ci. Cela permet de basculer la mise au point d’un sujet à l’autre, qu’il soit net ou non.
Toucher un objet de métadonnées déjà en mise au point faible le passe en mise au point forte, indiquée par un rectangle jaune plein.
Toucher un point sans détection indique au mode Cinématique de rechercher un objet saillant et d’y appliquer une mise au point faible.
Avec un toucher long, on définit une mise au point fixe forte. Voici comment implémenter cette logique dans le code. D’abord, il faut créer deux gestes.
Le toucher standard s’implémente facilement avec SpatialTapGesture, qui fournit la position nécessaire pour définir la mise au point.
Au toucher, nous appelons la méthode focusTap sur notre objet modèle de caméra, qui donne accès à l’AVCaptureDevice sous-jacent.
Le toucher long est plus complexe, car le geste longPressGesture intégré ne fournit pas la position nécessaire pour simuler un toucher long à l’aide d’un DragGesture.
Lors du toucher, nous lançons un timer de 0,3 seconde.
Lorsqu’il se déclenche, nous appelons la méthode focusLongPress sur le modèle de caméra.
Nous créons ensuite une vue rectangulaire pour recevoir les gestes. Elle est placée en dernier dans la ZStack, ce qui la met au-dessus de tous les rectangles de détection afin que les gestes de l’utilisateur ne soient pas bloqués.
Comme vu précédemment, il est important de différencier visuellement les rectangles selon qu’ils sont en mise au point faible, forte ou sans mise au point, afin d’aider l’utilisateur à agir correctement.
Pour cela, nous implémentons une méthode qui prend un AVMetadataObject en entrée et renvoie une vue de rectangle mise au point.
N’oublions pas qu’il faut transformer les limites des métadonnées depuis l’espace de coordonnées de la sortie des métadonnées vers celui de la couche d’aperçu.
En définissant des styles et couleurs de trait différents, on crée facilement des rectangles distincts pour chaque mode de mise au point.
Grâce au point fourni par la vue, nous déterminons la méthode de mise au point à utiliser.
Il faut d’abord déterminer si l’utilisateur a touché un rectangle de métadonnées. Et cela se fait dans la méthode utilitaire findTappedMetadataObject.
Nous parcourons toutes les métadonnées mises en cache pour chaque image, et vérifions si le point donné se trouve dans l’un de leurs contours.
Là encore, on vérifie que le point et le rectangle sont dans le même espace de coordonnées.
Pour en revenir à la méthode focusTap, si un objet de métadonnées est trouvé et déjà en mise au point faible, on le passe en mise au point forte.
Sinon, nous appliquons une mise au point faible dessus.
Si l’utilisateur n’a pas touché un rectangle de métadonnées, on demande au framework de chercher un objet saillant à cet endroit.
Avec un toucher long, on fixe une mise au point forte au point indiqué.
À ce stade, nous avons une app fonctionnelle capable de capturer des vidéos en mode Cinématique. Terminons en peaufinant quelques détails.
Notre graphique de capture vidéo ressemble à ceci. Nous avons 3 sorties pour la capture vidéo, la réception des métadonnées et l’aperçu.
Si l’on souhaite prendre des photos pendant l’enregistrement, il suffit d’ajouter un AVCapturePhotoOutput à la séance.
Comme notre configuration est cinématographique, la sortie photo bénéficiera automatiquement de cet effet.
L’image retournée par la sortie photo aura l’effet bokeh intégré.
Enfin, l’algorithme du mode Cinématique nécessite une quantité de lumière suffisante pour bien fonctionner. Dans une pièce trop sombre ou si la caméra est obstruée, nous souhaitons informer l’utilisateur de ce problème dans l’UI.
Pour être averti lorsque cette condition se produit, vous pouvez utiliser l’observation clé-valeur sur la nouvelle propriété cinematicVideoCaptureSceneMonitoringStatuses de la classe AVCaptureDevice.
Actuellement, le seul état pris en charge pour la vidéo Cinématique est « luminosité insuffisante ».
Dans le gestionnaire KVO, nous pouvons mettre à jour l’UI correctement lorsqu’une lumière insuffisante est détectée.
Un ensemble vide signifie que tout est revenu à la normale.
Aujourd’hui, nous avons rappelé comment le mode Cinématique permet de capture de superbes films, même dans des moments simples du quotidien, comme avec son animal de compagnie.
Et nous avons exploré en détail comment créer une excellente expérience de capture cinématographique avec l’API Cinematic Video.
Nous avons hâte de voir vos apps offrir un contenu encore plus riche et cinématographique grâce à ces fonctionnalités.
Merci de votre attention.
-
-
4:26 - Select a video device
// Select a video device let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInDualWideCamera], mediaType: .video, position: .back) guard let camera = deviceDiscoverySession.devices.first else { print("Failed to find the capture device") return }
-
5:07 - Select a format that supports Cinematic Video capture
// Select a format that supports Cinematic Video capture for format in camera.formats { if format.isCinematicVideoCaptureSupported { try! camera.lockForConfiguration() camera.activeFormat = format camera.unlockForConfiguration() break } }
-
5:51 - Select a microphone
// Select a microphone let audioDeviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes [.microphone], mediaType: .audio, position: .unspecified) guard let microphone = audioDeviceDiscoverySession.devices.first else { print("Failed to find a microphone") return }
-
6:00 - Add devices to input & add inputs to the capture session & enable Cinematic Video capture
// Add devices to inputs let videoInput = try! AVCaptureDeviceInput(device: camera) guard captureSession.canAddInput(videoInput) else { print("Can't add the video input to the session") return } let audioInput = try! AVCaptureDeviceInput(device: microphone) guard captureSession.canAddInput(audioInput) else { print("Can't add the audio input to the session") return } // Add inputs to the capture session captureSession.addInput(videoInput) captureSession.addInput(audioInput) // Enable Cinematic Video capture if (videoInput.isCinematicVideoCaptureSupported) { videoInput.isCinematicVideoCaptureEnabled = true }
-
6:17 - Capture spatial audio
// Configure spatial audio if audioInput.isMultichannelAudioModeSupported(.firstOrderAmbisonics) { audioInput.multichannelAudioMode = .firstOrderAmbisonics }
-
// Add outputs to the session let movieFileOutput = AVCaptureMovieFileOutput() guard captureSession.canAddOutput(movieFileOutput) else { print("Can't add the movie file output to the session") return } captureSession.addOutput(movieFileOutput) // Configure video stabilization if let connection = movieFileOutput.connection(with: .video), connection.isVideoStabilizationSupported { connection.preferredVideoStabilizationMode = .cinematicExtendedEnhanced } // Add a preview layer as the view finder let previewLayer = AVCaptureVideoPreviewLayer() previewLayer.session = captureSession
-
7:11 - Display the preview layer with SwiftUI
// Display the preview layer with SwiftUI struct CameraPreviewView: UIViewRepresentable { func makeUIView(context: Context) -> PreviewView { return PreviewView() } class CameraPreviewUIView: UIView { override class var layerClass: AnyClass { AVCaptureVideoPreviewLayer.self } var previewLayer: AVCaptureVideoPreviewLayer { layer as! AVCaptureVideoPreviewLayer } ... } ... }
-
7:54 - Display the preview layer with SwiftUI
// Display the preview layer with SwiftUI @MainActor struct CameraView: View { var body: some View { ZStack { CameraPreviewView() CameraControlsView() } } }
-
8:05 - Adjust bokeh strength with simulated aperture
// Adjust bokeh strength with simulated aperture open class AVCaptureDeviceInput : AVCaptureInput { open var simulatedAperture: Float ... }
-
8:40 - Find min, max, and default simulated aperture
// Adjust bokeh strength with simulated aperture extension AVCaptureDeviceFormat { open var minSimulatedAperture: Float { get } open var maxSimulatedAperture: Float { get } open var defaultSimulatedAperture: Float { get } ... }
-
9:12 - Add a metadata output
// Add a metadata output let metadataOutput = AVCaptureMetadataOutput() guard captureSession.canAddOutput(metadataOutput) else { print("Can't add the metadata output to the session") return } captureSession.addOutput(metadataOutput) metadataOutput.metadataObjectTypes = metadataOutput.requiredMetadataObjectTypesForCinematicVideoCapture metadataOutput.setMetadataObjectsDelegate(self, queue: sessionQueue)
-
9:50 - Update the observed manager object
// Update the observed manager object func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { self.metadataManager.metadataObjects = metadataObjects } // Pass metadata to SwiftUI @Observable class CinematicMetadataManager { var metadataObjects: [AVMetadataObject] = [] }
-
10:12 - Observe changes and update the view
// Observe changes and update the view struct FocusOverlayView : View { var body: some View { ForEach( metadataManager.metadataObjects, id:\.objectID) { metadataObject in rectangle(for: metadataObject) } } }
-
10:18 - Make a rectangle for a metadata
// Make a rectangle for a metadata private func rectangle(for metadata: AVMetadataObjects) -> some View { let transformedRect = previewLayer.layerRectConverted(fromMetadataOutputRect: metadata.bounds) return Rectangle() .frame(width:transformedRect.width, height:transformedRect.height) .position( x:transformedRect.midX, y:transformedRect.midY) }
-
10:53 - Focus methods
open func setCinematicVideoTrackingFocus(detectedObjectID: Int, focusMode: AVCaptureDevice.CinematicVideoFocusMode) open func setCinematicVideoTrackingFocus(at point: CGPoint, focusMode: AVCaptureDevice.CinematicVideoFocusMode) open func setCinematicVideoFixedFocus(at point: CGPoint, focusMode: AVCaptureDevice.CinematicVideoFocusMode)
-
10:59 - Focus method 1 & CinematicVideoFocusMode
// Focus methods open func setCinematicVideoTrackingFocus(detectedObjectID: Int, focusMode: AVCaptureDevice.CinematicVideoFocusMode) public enum CinematicVideoFocusMode : Int, @unchecked Sendable { case none = 0 case strong = 1 case weak = 2 } extension AVMetadataObject { open var cinematicVideoFocusMode: Int32 { get } }
-
12:19 - Focus method no.2
// Focus method no.2 open func setCinematicVideoTrackingFocus(at point: CGPoint, focusMode: AVCaptureDevice.CinematicVideoFocusMode)
-
12:41 - Focus method no.3
// Focus method no.3 open func setCinematicVideoFixedFocus(at point: CGPoint, focusMode: AVCaptureDevice.CinematicVideoFocusMode)
-
13:54 - Create the spatial tap gesture
var body: some View { let spatialTapGesture = SpatialTapGesture() .onEnded { event in Task { await camera.focusTap(at: event.location) } } ... }
-
14:15 - Simulate a long press gesture with a drag gesture
@State private var pressLocation: CGPoint = .zero @State private var isPressing = false private let longPressDuration: TimeInterval = 0.3 var body: some View { ... let longPressGesture = DragGesture(minimumDistance: 0).onChanged { value in if !isPressing { isPressing = true pressLocation = value.location startLoopPressTimer() } }.onEnded { _ in isPressing = false } ... } private func startLoopPressTimer() { DispatchQueue.main.asyncAfter(deadline: .now() + longPressDuration) { if isPressing { Task { await camera.focusLongPress(at: pressLocation) } } } }
-
14:36 - Create a rectangle view to receive gestures.
var body: some View { let spatialTapGesture = ... let longPressGesture = ... ZStack { ForEach( metadataManager.metadataObjects, id:\.objectID) { metadataObject in rectangle(for: metadataObject) } Rectangle() .fill(Color.clear) .contentShape(Rectangle()) .gesture(spatialTapGesture) .gesture(longPressGesture)} } }
-
15:03 - Create the rectangle view
private func rectangle(for metadata: AVMetadataObject) -> some View { let transformedRect = previewLayer.layerRectConverted(fromMetadataOutputRect: metadata.bounds) var color: Color var strokeStyle: StrokeStyle switch metadata.focusMode { case .weak: color = .yellow strokeStyle = StrokeStyle(lineWidth: 2, dash: [5,4]) case .strong: color = .yellow strokeStyle = StrokeStyle(lineWidth: 2) case .none: color = .white strokeStyle = StrokeStyle(lineWidth: 2) } return Rectangle() .stroke(color, style: strokeStyle) .contentShape(Rectangle()) .frame(width: transformedRect.width, height: transformedRect.height) .position(x: transformedRect.midX, y: transformedRect.midY) }
-
15:30 - Implement focusTap
func focusTap(at point:CGPoint) { try! camera.lockForConfiguration() if let metadataObject = findTappedMetadataObject(at: point) { if metadataObject.cinematicVideoFocusMode == .weak { camera.setCinematicVideoTrackingFocus(detectedObjectID: metadataObject.objectID, focusMode: .strong) } else { camera.setCinematicVideoTrackingFocus(detectedObjectID: metadataObject.objectID, focusMode: .weak) } } else { let transformedPoint = previewLayer.metadataOutputRectConverted(fromLayerRect: CGRect(origin:point, size:.zero)).origin camera.setCinematicVideoTrackingFocus(at: transformedPoint, focusMode: .weak) } camera.unlockForConfiguration() }
-
15:42 - Implement findTappedMetadataObject
private func findTappedMetadataObject(at point: CGPoint) -> AVMetadataObject? { var metadataObjectToReturn: AVMetadataObject? for metadataObject in metadataObjectsArray { let layerRect = previewLayer.layerRectConverted(fromMetadataOutputRect: metadataObject.bounds) if layerRect.contains(point) { metadataObjectToReturn = metadataObject break } } return metadataObjectToReturn }
-
16:01 - focusTap implementation continued
func focusTap(at point:CGPoint) { try! camera.lockForConfiguration() if let metadataObject = findTappedMetadataObject(at: point) { if metadataObject.cinematicVideoFocusMode == .weak { camera.setCinematicVideoTrackingFocus(detectedObjectID: metadataObject.objectID, focusMode: .strong) } else { camera.setCinematicVideoTrackingFocus(detectedObjectID: metadataObject.objectID, focusMode: .weak) } } else { let transformedPoint = previewLayer.metadataOutputRectConverted(fromLayerRect: CGRect(origin:point, size:.zero)).origin camera.setCinematicVideoTrackingFocus(at: transformedPoint, focusMode: .weak) } camera.unlockForConfiguration() }
-
16:23 - Implement focusLongPress
func focusLongPress(at point:CGPoint) { try! camera.lockForConfiguration() let transformedPoint = previewLayer.metadataOutputRectConverted(fromLayerRect:CGRect(origin: point, size: CGSizeZero)).origin camera.setCinematicVideoFixedFocus(at: pointInMetadataOutputSpace, focusMode: .strong) camera.unlockForConfiguration() }
-
17:10 - Introduce cinematicVideoCaptureSceneMonitoringStatuses
extension AVCaptureDevice { open var cinematicVideoCaptureSceneMonitoringStatuses: Set<AVCaptureSceneMonitoringStatus> { get } } extension AVCaptureSceneMonitoringStatus { public static let notEnoughLight: AVCaptureSceneMonitoringStatus }
-
17:42 - KVO handler for cinematicVideoCaptureSceneMonitoringStatuses
private var observation: NSKeyValueObservation? observation = camera.observe(\.cinematicVideoCaptureSceneMonitoringStatuses, options: [.new, .old]) { _, value in if let newStatuses = value.newValue { if newStatuses.contains(.notEnoughLight) { // Update UI (e.g., "Not enough light") } else if newStatuses.count == 0 { // Back to normal. } } }
-
-
- 0:00 - Introduction
Utilisez l’API Cinematic Video pour capturer des vidéos de qualité cinéma dans vos apps. L’iPhone 13 et l’iPhone 13 Pro ont introduit le mode Cinematic, qui transforme l’iPhone en un véritable outil de cinéma.
- 0:33 - Vidéo cinématographique
La vidéo Cinematic utilise une faible profondeur de champ et un suivi de la mise au point pour guider l’attention du spectateur. Elle imite ainsi les techniques utilisées au cinéma. L’API Cinematic Video dans iOS 26 simplifie ce processus en permettant aux apps de régler et de suivre automatiquement la mise au point. Pour créer une expérience de capture Cinematic, configurez une session de capture, sélectionnez un appareil, puis activez la capture vidéo Cinematic en définissant « isCinematicVideoCaptureEnabled » sur « true » dans la classe « AVCaptureDeviceInput ». Cette opération configure la session pour qu’elle produise une vidéo Cinematic avec des données de disparité, des métadonnées et la vidéo originale, ce qui permet un montage non destructif. Vous pouvez lire ou modifier le rendu bokeh avec le framework Cinematic.
- 3:44 - Créez une expérience de capture cinématographique exceptionnelle
L’exemple commence par configurer une « AVCaptureSession » afin d’activer la capture vidéo Cinematic sur les appareils compatibles, tels que la double caméra grand-angle à l’arrière et la caméra TrueDepth à l’avant. L’exemple sélectionne un format vidéo approprié, avec « isCinematicVideoCaptureSupported » renvoyant la valeur « true », puis ajoute l’entrée audio du microphone à la session. La capture de l'audio spatial est activée pour améliorer l’expérience Cinematic. Pour en savoir plus sur l’audio spatial, regardez la session « Améliorez les capacités de création de contenu audio de votre app ». Ensuite, la stabilisation vidéo est activée afin d’améliorer l’expérience utilisateur, et la session de capture est prévisualisée à l’aide d’une vue SwiftUI. L’exemple crée ensuite une structure représentable personnalisée pour encapsuler « AVCaptureVideoPreviewLayer », ce qui permet de l’intégrer facilement dans l’interface SwiftUI. L’exemple aborde ensuite le contrôle de l’effet vidéo Cinematic, en particulier la faible profondeur de champ. En ajustant le paramètre « simulatedAperture », vous pouvez renforcer ou atténuer l’effet bokeh, ce qui vous offre un contrôle plus créatif sur la vidéo. Pour activer le contrôle manuel de la mise au point, l’exemple utilise la détection des métadonnées afin d’identifier les candidats à la mise au point, tels que les visages. Il affiche ensuite des rectangles à l’écran pour identifier ces candidats, ce qui permet aux utilisateurs de toucher l’écran et de faire la mise au point sur des sujets spécifiques. L’API Cinematic Video fournit plusieurs méthodes pour contrôler la mise au point pendant l’enregistrement vidéo. Elle génère des objets de métadonnées qui incluent des informations sur les sujets détectés dans le cadre. Le paramètre de configuration « focusMode » détermine le comportement de suivi de la vidéo Cinematic. Il existe trois cas pour cette énumération : none, strong et weak. La mise au point forte ou strong se verrouille sur un sujet et ignore les autres candidats potentiels à la mise au point. La mise au point faible ou weak permet à l’algorithme de changer automatiquement la mise au point en fonction de la scène. La mise au point inexistante ou none est principalement utilisée pour déterminer l’état de mise au point plutôt que pour le définir. L’API propose trois méthodes de mise au point : La méthode « setCinematicVideoTrackingFocus » prend en entrée l’ID d’un objet détecté et définit la mise au point sur cet objet. La méthode « setCinematicVideoTrackingFocus » utilise un point de la vue comme entrée. La vidéo Cinematic recherche ensuite un objet intéressant à cet endroit et crée un nouvel objet de métadonnées de type « salient object », sur lequel il est alors possible de se concentrer. « setCinematicVideoFixedFocus » définit une mise au point fixe sur un point spécifique de la scène en calculant la distance en interne à l’aide de signaux de profondeur. Associée à un mode de mise au point forte, cette méthode verrouille la mise au point sur un plan particulier en ignorant les autres activités de la scène. Vous pouvez utiliser une logique de mise au point personnalisée dans vos apps. Par exemple, appuyer sur un rectangle de détection peut basculer la mise au point entre les sujets, et un appui long peut définir une mise au point fixe forte. L’app différencie visuellement entre une mise au point faible, forte et inexistante pour guider l’utilisateur. De plus, l’API permet de capturer des images fixes pendant l’enregistrement, qui bénéficieront automatiquement du traitement Cinematic avec l’effet bokeh. L’app peut également utiliser l’observation clé-valeur pour observer la propriété « cinematicVideoCaptureSceneMonitoringStatuses » et informer l’utilisateur lorsque la luminosité est insuffisante pour une bonne capture vidéo Cinematic.