
-
Captura videos cinematográficos en tu app
Descubre cómo con la API Cinematic Video tu app puede capturar videos de estilo cinematográfico. Mencionaremos cómo configurar una sesión de captura cinematográfica y los fundamentos de la creación de una IU de captura de video. Y exploraremos funcionalidades cinematográficas avanzadas, como los efectos de profundidad de campo para lograr el seguimiento y el enfoque en rack.
Capítulos
- 0:00 - Introducción
- 0:33 - Video cinematográfico
- 3:44 - Crea una gran experiencia de captura cinematográfica
Recursos
Videos relacionados
WWDC25
WWDC24
WWDC23
-
Buscar este video…
Hola, soy Roy. Soy ingeniero en el equipo de Camera Software. Me entusiasma hablar sobre cómo tus apps pueden capturar fácilmente videos profesionales de estilo cinematográfico con la API Cinematic Video.
Con el iPhone 13 y 13 Pro, presentamos el modo Cine.
Con su interfaz de usuario intuitiva y sus potentes algoritmos, transformó el iPhone en una potencia cinematográfica.
En esta charla, veremos qué hace que el modo Cine sea mágico y revisaremos algunos códigos juntos para ver cómo crear una excelente experiencia.
Entonces, ¿qué es el modo Cine?
Las herramientas clásicas de narración, como el enfoque recto y el de seguimiento son su centro.
Con una profundidad de campo superficial, el director dirige a los espectadores hacia los sujetos clave de la escena, mejorando el impacto narrativo. Cuando los sujetos se mueven, como ocurre en las películas, el seguimiento del enfoque los mantiene nítidamente a la vista.
Aunque son potentes, en el mundo real, estas técnicas de enfoque requieren de una gran experiencia, por lo que en un set de filmación hay monitores de enfoque cuya responsabilidad principal es realizar estas desafiantes tomas.
El modo Cine simplifica drásticamente esto al impulsar decisiones de enfoque de manera inteligente.
Por ejemplo, cuando un sujeto entra en el cuadro, el algoritmo lo enfoca en automático y comienza a seguirlo. Cuando un sujeto mira hacia otro lado, el foco cambia en automático a otro punto, volviendo al sujeto cuando es apropiado.
Este año, con las increíbles capacidades de la API Cinematic Video, puedes capturar fácilmente estos increíbles videos cinematográficos con tus apps. Exploremos cómo podemos crear una excelente experiencia de captura del modo Cine con la nueva API. Comencemos con una sesión de grabación típica en una app.
Primero, seleccionamos el dispositivo desde el que queremos capturar video.
Luego, lo agregamos a una entrada del dispositivo.
Dependiendo de los casos de uso, se pueden agregar múltiples salidas a la sesión.
Se crearán objetos de conexión cuando estas salidas se agreguen a la sesión de video.
No es una configuración trivial, pero habilitar la captura del modo Cine es fácil.
En iOS 26, agregamos una nueva propiedad, isCinematicVideoCaptureEnabled en la clase AVCaptureDeviceInput. Al configurarlo como verdadero, se configura toda la sesión para generar en modo Cine. Cada una de las salidas recibirá ahora el tratamiento del modo Cine.
El archivo de película producido será en modo Cine.
Contiene datos de disparidad, metadatos y el video original que permite la edición no destructiva. Para reproducirlo con el bokeh renderizado o editar el efecto bokeh, puedes usar la estructura Cinematic que presentamos en 2023.
Para obtener más información sobre esta estructura, consulta los videos de la sesión WWDC23 "Support Cinematic mode videos in your app". La salida de datos de video producirá cuadros con un efecto de profundidad de campo superficial incorporado. Esto es útil cuando necesitas acceso directo a los marcos, como al enviarlos a un dispositivo remoto.
De manera similar, la capa de vista previa tendrá el efecto bokeh renderizado en tiempo real. Es una forma sencilla de construir un visor.
Con esta arquitectura de alto nivel en mente, repasemos algunos códigos en las siguientes áreas.
Configuraremos una AVCaptureSession con todos sus componentes necesarios para la captura en modo Cine.
Luego, construiremos una interfaz para la captura de video usando SwiftUI.
Veremos cómo obtener metadatos tales como las detecciones faciales y cómo dibujarlos en la pantalla.
Con diferentes formas manuales de controlar el enfoque, aprovechamos todo el poder del modo Cine.
Y terminaremos con algunas funciones avanzadas para hacer nuestra app más pulida.
Comencemos con la sesión de captura de video.
Primero, busquemos el dispositivo de video desde el que queremos capturar la película. Para encontrar el dispositivo, creamos un objeto AVCaptureDevice.DiscoverySession.
El modo Cine es compatible tanto con la cámara Dual Wide trasera como con la cámara TrueDepth delantera.
En este caso, especificamos .builtInDualWideCamera en la matriz de tipos de dispositivos. Dado que estamos filmando una película, utilizamos .video como mediaType.
Y solicitamos la cámara trasera del dispositivo.
Como solo solicitamos un único tipo de dispositivo, podemos obtener el primer elemento de la sesión de descubrimiento de la matriz.
Para habilitar la captura en modo Cine, se debe utilizar un formato que admita esta función.
Para encontrar dichos formatos, podemos iterar a través de todos los formatos del dispositivo y usar el que devuelva verdadero en isCinematicVideoCaptureSupported.
Aquí están todos los formatos admitidos.
Tanto la cámara TrueDepth como la Dual Wide admiten 1080p y 4K a 30 cuadros por segundo.
Si te interesa grabar contenido SDR o EDR, puedes utilizar el rango de video 420 o el rango completo.
Si preferimos contenido de video HDR de 10 bits, utiliza x420.
Como esta no es una película muda, también queremos sonido. Usaremos la misma API DiscoverySession para encontrar el micrófono.
Con nuestros dispositivos en mano, creamos las entradas para cada uno de ellos. Luego agregamos estas entradas a la sesión de captura.
En este punto, podemos activar la captura en modo Cine en la entrada de video.
Para mejorar la experiencia del modo Cine podemos capturar audio espacial configurando ambisonics de primer orden como multichannelAudioMode.
Para obtener más información sobre audio espacial, consulta la sesión de este año: "Enhance your app’s audio content creation capabilities". En cuanto a las salidas, creamos un objeto AVCaptureMovieFileOutput y lo añadimos a la sesión.
Nuestras manos nunca son tan firmes como un tripié, por eso recomendamos activar la estabilización de video.
Para ello, primero buscamos la conexión de video de movieFileOutput y configuramos su preferredVideoStabilizationMode. En este caso, utilizamos cinematicExtendedEnhanced.
Por último, necesitamos asociar nuestra capa de vista previa con la sesión de captura.
Terminamos la sesión de captura por ahora. Pasemos a la interfaz de usuario.
Como AVCaptureVideoPreviewLayer es una subclase de CALayer, que no es parte de SwiftUI, para que puedan interoperar, debemos envolver la capa de vista previa en una estructura ajustada al protocolo UIViewRepresentable.
Dentro de esta estructura, creamos una subclase UIView CameraPreviewUIView.
Anulamos su propiedad layerClass para que previewLayer sea la capa de respaldo para la vista.
Y creamos una propiedad previewLayer para que sea fácilmente accesible como un tipo AVCaptureVideoPreviewLayer.
Podemos colocar nuestra vista previa en un ZStack, donde puede combinarse con otros elementos de la interfaz de usuario, como los controles de la cámara.
Como mencionamos, la profundidad de campo superficial es importante para contar historias. Al cambiar la propiedad simulatedAperture en la entrada del dispositivo, podemos ajustar la intensidad global del efecto bokeh.
Como vemos a la derecha, al controlar esta propiedad con un control deslizante, cambiamos la intensidad global del desenfoque.
Este valor se expresa en el número f estándar de la industria, que es la relación entre la distancia focal y el diámetro de apertura.
Moviéndolos, la apertura es la distancia focal dividida por el número f.
Por lo tanto, cuanto menor sea el número f, mayor será la apertura y más fuerte será el bokeh.
Podemos encontrar la apertura simulada mínima, máxima y predeterminada en el formato.
Los usamos para completar los elementos de interfaz de usuario adecuados, como un control deslizante.
Ahora, construyamos algunas posibilidades que permitan al usuario interactuar manualmente con el modo Cine.
Para que puedan controlar el enfoque manualmente, necesitamos mostrar posibles indicadores visuales a enfocar, como rostros. Y para hacerlo, necesitamos algunos metadatos de detección.
Utilizaremos un AVCaptureMetadataOutput para obtener estas detecciones, poder dibujar sus límites en la pantalla y que los usuarios interactúen con ellos. El algoritmo del modo Cine requiere ciertos metadataObjectTypes para funcionar de manera óptima.
Y se comunican con la nueva propiedad requiredMetadataObjectTypesForCinematicVideoCapture. Se lanza una excepción si los metadataObjectTypes proporcionados difieren de esta lista cuando el modo Cine está habilitado.
Por último, necesitamos proporcionar un delegado para recibir los metadatos y una cola en la que se llama al delegado.
Recibimos objetos de metadatos en la devolución de llamada del delegado de salida de metadatos.
Para comunicar fácilmente estos metadatos a nuestra capa de vista en SwiftUI, usamos una clase observable.
Cuando actualicemos su propiedad, la vista de observación se actualizará automáticamente.
En nuestra capa de vista, cada vez que se actualiza nuestro objeto observable, la vista se vuelve a dibujar automáticamente.
Y dibujamos un rectángulo para cada metadataObject.
Al crear estos rectángulos, es importante que transformemos los límites de los metadatos en el espacio de coordenadas de la capa de vista previa.
Usamos el método layerRectConverted fromMetadataOutputRect.
Ten en cuenta que X y Y en el método de posición se refieren al centro de la vista y no a la esquina superior izquierda utilizada por AVFoundation. Debemos realizar el ajuste correspondiente utilizando midX y midY del rectángulo.
Con rectángulos de metadatos dibujados en la pantalla, podemos usarlos para controlar el enfoque manualmente.
La API Cinematic Video ofrece tres formas de enfocar manualmente. Vamos a analizarlas una por una.
El método detectObjectID focusMode setCinematicVideoTrackingFocus se puede utilizar para enfocar un sujeto en particular identificado por el detectObjectID, que está disponible en los objetos AVMetadata que obtienes de la salida de metadatos.
focusMode configura el comportamiento de seguimiento del modo Cine.
La enumeración CinematicVideoFocusMode tiene tres casos: ninguno, fuerte y débil.
Strong le dice al modo Cine que siga rastreando un sujeto aún cuando hay candidatos que de otra manera habrían sido seleccionados en automático.
En este caso, aunque el gato se hizo más prominente en el marco, el enfoque fuerte indicado en el rectángulo amarillo sólido permaneció en el sujeto en el fondo.
Por otro lado, el enfoque débil permite que el algoritmo mantenga el control del enfoque. Cambia el foco automáticamente cuando lo considera oportuno.
En este caso, cuando el gato se dio la vuelta, se lo consideró más importante y el foco débil se desplazó automáticamente hacia él, como lo indica el rectángulo punteado.
El caso ninguno solo es útil para determinar si un objeto de metadatos tiene actualmente el foco, por lo que no se debe utilizar al establecer el foco.
El segundo método de enfoque toma un primer parámetro diferente. En lugar de un ID de objeto detectado, toma un punto en una vista.
Le dice al modo Cine que busque cualquier objeto interesante en el punto especificado. Cuando encuentre uno, creará un nuevo objeto de metadatos con el tipo de objeto saliente. Así que podemos dibujar el rectángulo alrededor en la pantalla.
El tercer método de enfoque es setCinematicVideoFixedFocus, que toma un punto y el modo de enfoque. Establece el foco a una distancia fija que se calcula internamente utilizando señales como la profundidad. Combinado con un modo de enfoque fuerte, este método bloquea efectivamente el enfoque en un plano particular de la escena, ignorando otras actividades incluso en primer plano.
Las apps pueden implementar la lógica de enfoque que tenga sentido para sus respectivos usos. En nuestra app, hacemos lo siguiente:
Al tocar un rectángulo de detección que no está enfocado, enfocamos hacia él con un enfoque débil. Con esto, podemos cambiar el enfoque de un sujeto a otro, tanto dentro como fuera de foco.
Al tocar un objeto de metadatos que ya tiene enfocado débil, se transforma en un foco fuerte, indicado por el rectángulo amarillo sólido.
Al tocar en un punto en el que no hay detecciones, queremos que el modo Cine encuentre algún objeto destacado y se centre débilmente en él.
Con una pulsación larga fijamos un foco fijo fuerte. Así cómo podemos implementar esta lógica en el código. En primer lugar, debemos hacer dos gestos.
El gesto de toque habitual se puede realizar con un SpatialTapGesture, el cual proporciona la ubicación del toque que necesitamos para establecer el foco.
Cuando hacemos clic, llamamos al método focusTap en nuestro objeto de modelo de cámara, donde tenemos acceso al AVCaptureDevice subyacente.
Por otro lado, la pulsación prolongada es más complicada, ya que el longPressGesture incorporado no proporciona la ubicación del toque que necesitamos para simular una pulsación prolongada con un DragGesture.
Al presionarlo, comenzamos con un temporizador de 0,3 segundos.
Cuando se activa, llamamos al método focusLongPress en el modelo de la cámara.
Luego creamos una vista rectangular para recibir los gestos. Se inserta al final del ZStack, esto lo coloca encima de todos los rectángulos de detección para que la entrada de gestos del usuario no se bloquee.
Como ya vimos en los videos anteriores, es importante diferenciar visualmente los rectángulos enfocados entre foco débil, foco fuerte y sin foco para ayudar al usuario a realizar la acción correcta.
Hacemos esto al implementar un método que toma un AVMetadataObject y devuelve una vista rectangular enfocada.
No olvidemos que debemos transformar los límites de los metadatos desde el espacio de coordenadas de salida de metadatos a la capa de vista previa.
Al configurar diferentes estilos de trazo y colores, creamos fácilmente rectángulos distintos para cada modo de enfoque.
Con el punto que pasa desde la capa de vista, podemos determinar qué método de enfoque utilizar.
Primero debemos averiguar si el usuario ha tocado un rectángulo de metadatos. Y hacemos esto en el método auxiliar, findTappedMetadataObject.
Aquí, iteramos a través de todos los metadatos que almacenamos en caché para cada cuadro y verificamos si el punto especificado cae dentro de sus límites.
De nuevo, nos aseguramos de que el punto y el rectángulo estén en el mismo espacio de coordenadas.
De vuelta al método focusTap, si se encuentra un objeto de metadatos y ya está en un foco débil, entonces lo convertimos en un foco fuerte.
Si no está ya en foco, lo centramos débilmente.
Si el usuario no tocó un rectángulo de metadatos, le indicamos a la estructura que intente encontrar un objeto destacado en este punto.
Con una pulsación larga, establecemos un enfoque fijo fuerte en el punto especificado.
En este punto, tenemos una app completamente funcional que puede capturar en modo Cine. Vamos a pulirla con algunos detalles más.
Actualmente, nuestro gráfico de captura de video se ve así. Tenemos tres salidas para capturar la película, recibir metadatos y la vista previa.
Si queremos admitir la captura de imágenes fijas durante la grabación, podemos hacerlo simplemente agregando un AVCapturePhotoOutput a la sesión.
Como nuestro gráfico ya está configurado en modo Cine, la salida de foto recibirá un tratamiento de modo Cine automáticamente.
La imagen devuelta por la salida de fotografía tendrá el efecto bokeh grabado.
Por último, el algoritmo del modo Cine requiere una cantidad suficiente de luz para funcionar correctamente. Querremos informar a los usuarios de este problema en la interfaz de usuario en una habitación demasiado oscura o si la cámara esta cubierta.
Para recibir una notificación cuando se produce esta condición, puedes observar la clave-valor de la nueva propiedad cinematicVideoCaptureSceneMonitoringStatuses en la clase AVCaptureDevice.
Actualmente, el único estado admitido para el modo Cine es falta de luz.
En el controlador KVO, podemos actualizar la interfaz de usuario correctamente cuando vemos que no hay suficiente luz.
Un conjunto vacío significa que todo ha vuelto a la normalidad.
En esta charla, vimos cómo el modo CIne permite a nuestros usuarios capturar hermosas películas de nivel profesional, incluso para momentos cotidianos como pasar tiempo con sus mascotas.
Y seguimos una guía detallada sobre cómo crear una excelente experiencia de captura en modo Cine con la API Cinematic Video.
Estamos ansiosos por ver cómo tus apps pueden aprovechar estas capacidades para ofrecer contenido más rico y cinematográfico.
Gracias por tu atención.
-
-
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 - Introducción
Usa la API Cinematic Video para capturar videos de estilo cinematográfico de nivel profesional en tus apps. El iPhone 13 y 13 Pro presentaron el modo Cine, que transformó al iPhone en una potencia cinematográfica.
- 0:33 - Video cinematográfico
El Video cinematográfico utiliza poca profundidad de campo y seguimiento del enfoque para guiar la atención del espectador, imitando técnicas cinematográficas. La API Cinematic Video en iOS 26 simplifica este proceso, permitiendo que las apps enfoquen y rastreen automáticamente. Para crear una experiencia de captura cinematográfica, configura una sesión de captura, selecciona un dispositivo y, a continuación, habilita la captura de video cinematográfica configurando “isCinematicVideoCaptureEnabled” como true en la clase “AVCaptureDeviceInput”. Esto configura la sesión para emitir video cinemático con datos de disparidad, metadatos y el video original, lo que permite una edición no destructiva. Puedes reproducir o editar el renderizado de bokeh con Cinematic Framework.
- 3:44 - Crea una gran experiencia de captura cinematográfica
El ejemplo comienza configurando una “AVCaptureSession” para habilitar la captura de video cinematográfico en dispositivos compatibles, como la cámara amplia doble en la parte posterior y la cámara TrueDepth en la parte frontal. El ejemplo selecciona un formato de video apropiado, donde “isCinematicVideoCaptureSupported” retorna como true, y, a continuación, agrega entrada de audio del micrófono a la sesión. La captura de audio espacial está habilitada para mejorar la experiencia cinematográfica. Para obtener más información sobre audio espacial, consulta “Mejora las capacidades de creación de contenido de audio de tu app”. A continuación, se habilita la estabilización de video para mejorar la experiencia del usuario y se obtiene una vista previa de la sesión de captura mediante una vista SwiftUI. Luego, el ejemplo crea una estructura representable personalizada para envolver “AVCaptureVideoPreviewLayer”, lo que permite integrarlo perfectamente en la interfaz SwiftUI. El ejemplo luego profundiza en el control del efecto de video cinematográfico, específicamente la profundidad de campo reducida. Al ajustar “simulatedAperture”, se puede fortalecer o debilitar el efecto bokeh, que brinda un control más creativo sobre el video. Para habilitar el control del enfoque manual, el ejemplo implementa la detección de metadatos para identificar candidatos al enfoque, como rostros. Luego, dibuja rectángulos en la pantalla para representar a estos candidatos, lo que permite a los usuarios tocar y centrarse en elementos específicos. La API Cinematic Video proporciona varios métodos para controlar el enfoque durante la grabación de video. La API genera objetos de metadatos que incluyen información sobre los sujetos detectados en el marco. El parámetro de configuración “focusMode” determina el comportamiento de seguimiento del video cinematográfico. Hay tres casos para esta enumeración: “ninguno”, “fuerte” y “débil”. El enfoque fuerte se centra en un sujeto, ignorando otros posibles candidatos para el enfoque. El enfoque débil permite que el algoritmo ajuste automáticamente el enfoque en función de la escena. El caso ninguno se utiliza principalmente para determinar el estado del enfoque en lugar de establecerlo. La API ofrece tres métodos de enfoque: el método “setCinematicVideoTrackingFocus” toma un ID de objeto detectado como entrada y establece el foco en ese objeto. El método “setCinematicVideoTrackingFocus” toma un punto en la vista como entrada. A continuación, el video cinematográfico busca un objeto interesante en ese punto y crea un nuevo objeto de metadatos del tipo “objeto destacado”, en el que luego se puede centrarse. “setCinematicVideoFixedFocus” establece un foco fijo en un punto específico de la escena, calculando la distancia internamente mediante señales de profundidad. Cuando se combina con un modo de enfoque fuerte, bloquea el enfoque en un plano particular, ignorando otras actividades en la escena. Puedes implementar lógica de enfoque personalizada en tus apps. Por ejemplo, al tocar un rectángulo de detección se puede cambiar el enfoque entre elementos, y un toque prolongado puede establecer un enfoque fijo fuerte. La app diferencia visualmente entre débil, fuerte y sin foco para guiar al usuario. Además, la API permite la captura de imágenes fijas durante la grabación, que recibirán automáticamente el tratamiento cinematográfico con el efecto bokeh. La app también puede usar la observación de valor clave para observar la propiedad “cinematicVideoCaptureSceneMonitoringStatuses” e informar al usuario cuando no hay suficiente luz para una captura de video cinematográfica adecuada.