View in English

  • Global Nav Open Menu Global Nav Close Menu
  • Apple Developer
Search
Cancel
  • Apple Developer
  • News
  • Discover
  • Design
  • Develop
  • Distribute
  • Support
  • Account
Only search within “”

Quick Links

5 Quick Links

Vídeos

Abrir menu Fechar menu
  • Coleções
  • Tópicos
  • Todos os vídeos
  • Sobre

Voltar para WWDC25

  • Sobre
  • Resumo
  • Transcrição
  • Código
  • Grave vídeos com qualidade de cinema em seu app

    Saiba como a API Cinematic Video permite que seu app grave vídeos com qualidade de cinema de forma simples. Abordaremos como configurar uma sessão de gravação cinematográfica e apresentaremos os fundamentos da criação de uma interface de gravação de vídeos. Também vamos explorar recursos cinematográficos avançados, como a aplicação de um efeito de profundidade de campo para alcançar foco com rastreamento e foco alternado (rack focus).

    Capítulos

    • 0:00 - Introdução
    • 0:33 - API Cinematic Video
    • 3:44 - Criar uma ótima experiência de captura cinematográfica

    Recursos

    • AVCam: Building a camera app
    • AVFoundation
    • Capturing cinematic video
    • Cinematic
      • Vídeo HD
      • Vídeo SD

    Vídeos relacionados

    WWDC25

    • Melhore a experiência com a câmera usando os controles de captura

    WWDC24

    • Build a great Lock Screen camera capture experience

    WWDC23

    • Create a more responsive camera experience
    • Discover Continuity Camera for tvOS
    • Support external cameras in your iPadOS app
  • Buscar neste vídeo...

    Olá! Meu nome é Roy. Sou engineer da equipe Camera Software. Hoje, vou falar sobre como seus apps podem capturar facilmente vídeos profissional no estilo cinema com uma API Cinematic Video.

    No iPhone 13 e o iPhone 13 Pro, lançamos o modo Cinema.

    Com sua interface intuitiva e algoritmos avançados, ele transformou o iPhone em uma potência cinematográfica.

    Hoje, vamos conhecer a magia do vídeo cinematográfico e analisar alguns códigos juntos, para ver como criar uma ótima experiência de captura cinematográfica. Então, o que é o vídeo cinematográfico?

    Em sua essência estão as ferramentas clássicas para contar histórias, como foco retangular e foco de rastreamento.

    Com uma profundidade de campo rasa, o diretor orienta a atenção do espectador para os principais elementos da cena, potencializando os impactos narrativos. Quando os elementos se movem, como geralmente acontece em filmes, o foco de rastreamento os mantém nitidamente à vista.

    Apesar de avançadas, no mundo real essas técnicas de foco exigem muita experiência, por isso que em um set de filmagem, existem assistentes cuja responsabilidade é executar essas cenas impactantes, mas desafiadoras.

    O vídeo cinematográfico simplifica isso, direcionando as decisões de foco de forma inteligente.

    Por exemplo, quando um elemento entra no quadro, o algoritmo automaticamente direciona o foco para ele e começa a rastrear. Quando um elemento desvia o olhar, o foco transita automaticamente para outro ponto, retornando ao elemento quando apropriado.

    Este ano, vamos disponibilizar recursos incríveis, como a API Cinematic Video, para que seus apps possam capturar facilmente esses vídeos incríveis no estilo cinema. Vamos explorar como criar uma ótima experiência de captura de vídeos cinematográficos usando a nova API. Vamos começar com uma sessão de captura típica para um app de vídeo.

    Primeiro, selecionamos o dispositivo onde queremos capturar filmes.

    Em seguida, adicionamos a uma entrada de dispositivo. Dependendo dos casos de uso, várias saídas podem ser adicionadas à sessão.

    Os objetos de conexão serão criados quando essas saídas forem adicionadas à sessão de captura.

    Essa não é uma configuração simples, mas ativar a captura de vídeo cinematográfico é fácil.

    No iOS 26, vamos adicionar uma nova propriedade, isCinematicVideoCaptureEnabled na classe AVCaptureDeviceInput. Ao definir como True, configuramos toda a sessão de captura para saída de vídeo cinematográfico. E cada uma das saídas agora receberá o tratamento Cinematográfico.

    O arquivo de filme produzido pela saída do arquivo de filme será Cinematográfico.

    Ele contém os dados de disparidade, metadados e o vídeo original que permite edição não destrutiva. Para reproduzir com bokeh renderizado ou editar o efeito bokeh, use o framework cinematográfico que lançamos em 2023.

    Para saber mais sobre esse framework, confira os vídeos da sessão "Suporte ao modo Cinema" da WWDC23 no seu app. A saída de dados do vídeo produzirá quadros com um efeito integrado de profundidade de campo raso. Isso é útil quando você precisa de acesso direto aos quadros, como ao enviar para um dispositivo remoto.

    Da mesma forma, a camada de prévia terá o bokeh renderizado nela em tempo real. É uma maneira fácil de criar um visor.

    Com essa arquitetura de alto nível em mente, vamos analisar alguns códigos nas seguintes áreas.

    Vamos configurar um AVCaptureSession com todos os componentes necessários para a captura cinematográfica.

    Depois, criaremos uma interface para captura de vídeo usando SwiftUI.

    Vamos explicar como obter metadados, como detecções de rosto, e como desenhá-los na tela.

    Com diferentes maneiras de direcionar o foco manualmente, aproveitamos toda a capacidade do vídeo cinematográfico.

    E finalizamos com alguns recursos avançados para deixar nosso app mais refinado.

    Vamos começar com a sessão de captura.

    Primeiro, vamos encontrar o dispositivo de vídeo onde queremos capturar o filme. Para localizar o dispositivo, criamos um objeto AVCaptureDevice.DiscoverySession.

    O vídeo cinematográfico é compatível com a câmera grande-angular dupla na parte traseira e na câmera TrueDepth na parte frontal.

    Nesse caso, especificamos .builtInDualWideCamera na matriz de tipos de dispositivo. Como estamos gravando um filme, usamos .video como mediaType.

    E solicitamos a câmera na parte de trás do dispositivo.

    Como estamos solicitando um único tipo de dispositivo, podemos apenas obter o primeiro elemento na matriz de dispositivos da sessão de descoberta.

    Para ativar a captura de vídeo cinematográfico, é preciso usar um formato que suporte esse recurso.

    Para encontrar esses formatos, podemos iterar por todos os formatos do dispositivo e usar aquele cuja propriedade isCinematicVideoCaptureSupported retorna true.

    Aqui estão todos os formatos suportados.

    Para câmeras grande-angular duplas e TrueDepth, tanto 1080p quanto 4K são compatíveis a 30 quadros por segundo.

    Se você quiser gravar conteúdo SDR ou EDR, pode usar a faixa de vídeo 420 ou gama completa.

    Se quisermos conteúdo de vídeo HDR de 10 bits, use x420.

    Como esse não é um filme mudo, queremos som também. Usaremos a mesma API DiscoverySession para localizar o microfone.

    Com os aparelhos em mãos, criamos as entradas para cada um deles. Em seguida, vamos adicionar essas entradas na sessão de captura. Aqui, podemos ativar a captura de vídeo cinematográfico na entrada de vídeo.

    Para melhorar a experiência cinematográfica, podemos capturar áudio espacial definindo os ambissônicos de primeira ordem como multichannelAudioMode.

    Para saber mais sobre áudio espacial, confira a sessão "Aprimorar os recursos de criação de conteúdo de áudio do seu app". Para as saídas, criamos um objeto AVCaptureMovieFileOutput e o adicionamos à sessão.

    Nossas mãos nunca são tão firmes quanto um tripé, então recomendamos ativar a estabilização de vídeo.

    Para fazer isso, primeiro encontramos a conexão de vídeo do movieFileOutput e definimos o preferredVideoStabilizationMode. Neste caso, usamos cinematicExtendedEnhanced.

    Por fim, precisamos associar a camada de prévia à sessão de captura.

    Por enquanto, terminamos com a sessão de captura. Vamos passar para a interface.

    Como AVCaptureVideoPreviewLayer é uma subclasse de CALayer, que não faz parte do SwiftUI, para fazê-los interoperar, precisamos encapsular a camada de prévia em uma estrutura em conformidade com o protocolo UIViewRepresentable.

    Dentro dessa estrutura, criamos uma subclasse UIView CameraPreviewUIView.

    Substituímos a propriedade layerClass para fazer do previewLayer a camada de suporte para visualização.

    E criamos uma propriedade previewLayer para facilitar o acesso, como um tipo AVCaptureVideoPreviewLayer.

    Podemos colocar nossa prévia em um ZStack, onde ela pode ser composta com outros elementos da interface, como controles de câmera.

    Como falei antes, a profundidade de campo rasa é uma ferramenta importante para contar histórias. Alterando a propriedade simulatedAperture na entrada do dispositivo, podemos ajustar a intensidade global do efeito bokeh. Exibido à direita, ajustando essa propriedade com um controle deslizante, alteramos a intensidade global do desfoque.

    Esse valor é expresso em f-stops, padrão do mercado, que é a proporção entre a distância focal e o diâmetro de abertura.

    Ao movê-los, a abertura é a distância focal dividida pelo número f.

    Portanto, quanto menor o número f, maior a abertura e mais intenso será o bokeh.

    Podemos encontrar a abertura simulada mínima, máxima e padrão no formato.

    Usamos isso para preencher os elementos de interface apropriados, como um controle deslizante.

    Agora, vamos criar alguns recursos que permitem que o usuário interaja manualmente com o vídeo cinematográfico.

    Para que os usuários direcionem manualmente o foco, precisamos mostrar indicadores visuais para candidatos de foco, como rostos. E, para fazer isso, precisamos de alguns metadados de detecção.

    Usaremos um AVCaptureMetadataOutput para obter essas detecções, para poder desenhar os limites na tela para os usuários interagirem. O algoritmo do vídeo cinematográfico requer que certos metadataObjectTypes funcionem perfeitamente. E eles são comunicados com a nova propriedade requiredMetadataObjectTypesForCinematicVideoCapture. Uma exceção será lançada se os metadataObjectTypes fornecidos forem diferentes dessa lista quando o vídeo cinematográfico estiver habilitado.

    Por fim, precisamos fornecer um delegado para receber os metadados e uma fila na qual o delegado é chamado.

    Recebemos objetos de metadados no retorno de chamada delegado da saída de metadados.

    Para comunicar esses metadados à nossa camada de visualização no SwiftUI, usamos uma classe observable.

    Quando atualizarmos sua propriedade, a visualização de observação será atualizada automaticamente.

    Na camada de visualização, sempre que nosso objeto observable é atualizado, ela é automaticamente redesenhada. E desenhamos um retângulo para cada metadataObject.

    Ao criar esses retângulos, é importante transformar os limites dos metadados no espaço de coordenadas da camada de prévia. Usando o método layerRectConverted fromMetadataOutputRect.

    Observe que X e Y no método de posição referem-se ao centro da visualização, em vez do canto superior esquerdo usado pelo AV Foundation. Então, precisamos ajustar de acordo usando o midX e midY do retângulo.

    Com retângulos de metadados desenhados na tela, podemos usá-los para direcionar o foco manualmente.

    A API Cinematic Video oferece três maneiras para focar manualmente. Vamos conhecer cada uma. O método setCinematicVideoTrackingFocus detectedObjectID focusMode pode ser usado para colocar o foco em rack em um elemento específico, identificado pelo detectedObjectID, que está disponível nos objetos AVMetadata obtidos da saída de metadados. O focusMode configura o comportamento de rastreamento do vídeo cinematográfico. O enum CinematicVideoFocusMode tem três casos: nenhum, forte e fraco. "Forte" indica ao vídeo cinematográfico para continuar acompanhando o elemento mesmo quando há candidatos de foco que seriam selecionados automaticamente.

    Neste caso, embora o gato tenha se tornado mais proeminente no quadro, o foco forte, como indicado pelo retângulo amarelo sólido, permaneceu no elemento atrás. O foco fraco, por outro lado, permite que o algoritmo mantenha o controle do foco. O foco altera automaticamente quando ele achar melhor. Neste caso, quando o gato virou, ele foi considerado mais importante, e o foco fraco mudou automaticamente para ele, como indicado pelo retângulo tracejado.

    O caso "nenhum" só é útil para determinar se um objeto de metadados tem o foco no momento, portanto, ele não deve ser usado ao definir o foco.

    O segundo método de foco usa um primeiro parâmetro diferente. Em vez de um ID de objeto detectado, ele usa um ponto de vista.

    Ele diz ao vídeo cinematográfico para procurar qualquer objeto interessante no ponto especificado. Quando encontrar um, ele criará um novo objeto de metadados com o tipo de objeto em destaque. Assim, podemos desenhar o retângulo em torno dele na tela.

    O terceiro método de foco é setCinematicVideoFixedFocus, que usa um ponto e o modo de foco. Ele define o foco a uma distância fixa que é calculada internamente usando sinais como a profundidade. Emparelhado com um modo de foco forte, esse método bloqueia efetivamente o foco em um plano específico na cena, ignorando outras atividades mesmo em primeiro plano.

    Qualquer app pode implementar a lógica de foco que faz sentido em seus respectivos casos de uso. Em nosso app, fazemos o seguinte: Ao tocar em um retângulo de detecção que não está em foco, direcionamos o foco para ele com um foco fraco. Com isso, podemos alternar o foco entre elementos dentro e fora do foco.

    Tocar em um objeto de metadados que já tem um foco fraco o transforma em um foco forte, indicado pelo retângulo amarelo sólido.

    Tocando em um ponto onde não há detecções existentes, queremos que o vídeo cinematográfico tente encontrar qualquer objeto em destaque e se concentre fracamente nisso. Com uma pressão longa, estabelecemos um foco forte fixo. Aqui está como podemos implementar essa lógica no código. Em primeiro lugar, precisamos fazer dois gestos.

    O gesto de toque normal pode ser feito facilmente com um SpatialTapGesture, que fornece a localização do toque que precisamos para definir o foco.

    Após tocar, chamamos o método focusTap no nosso objeto de modelo de câmera, onde temos acesso ao AVCaptureDevice subjacente.

    A pressão longa, por outro lado, é um pouco mais complicada, já que o longPressGesture integrado não fornece a localização do toque que precisamos para simular uma pressão longa com DragGesture.

    Ao pressionar, começamos com o cronômetro de 0,3 segundo.

    Quando ele dispara, chamamos o método focusLongPress no modelo da câmera.

    Em seguida, criamos uma visualização retangular para receber os gestos. Ela é inserida no final do ZStack, o que a coloca sobre todos os retângulos de detecção para que a entrada de gestos do usuário não seja bloqueada.

    Como vimos nos vídeos anteriores, é importante diferenciar visualmente os retângulos focados entre foco fraco, foco forte e nenhum foco para ajudar o usuário a tomar a ação certa.

    Fazemos isso implementando um método que usa um AVMetadataObject e retorna uma visualização de retângulo focada. Não vamos esquecer que precisamos transformar os limites dos metadados do espaço de coordenadas da saída de metadados para o da camada de prévia.

    Ao definir diferentes estilos de traçado e cores, podemos facilmente criar retângulos visualmente distintos para cada modo de foco.

    Com o ponto passado da camada de visualização, podemos determinar qual método de foco usar.

    Primeiro, precisamos descobrir se o usuário tocou em um retângulo de metadados. E fazemos isso no método auxiliar, findTappedMetadataObject.

    Aqui, iteramos todos os metadados que armazenamos em cache para cada quadro e verificamos se o ponto especificado cai em um de seus limites. Novamente, verificamos se o ponto e o retângulo estão no mesmo espaço de coordenadas.

    Voltando ao método focusTap, se um objeto de metadados for encontrado e já estiver em foco fraco, então o transformaremos em um foco forte.

    Se ainda não está em foco, ativamos o foco fraco.

    Se o usuário não tocou em um retângulo de metadados, então dizemos ao framework para tentar encontrar um objeto saliente neste ponto. Com uma pressão longa, nós definimos um foco forte fixo no ponto especificado. Agora, temos um app totalmente funcional que pode capturar vídeo cinematográfico. Vamos refinar com mais alguns detalhes.

    Atualmente, nosso gráfico de captura de vídeo se parece com isso. Temos três saídas para capturar o filme, receber metadados e a prévia. Se quisermos habilitar a captura de imagem estática durante a gravação, basta adicionar um AVCapturePhotoOutput à sessão.

    Como nosso gráfico já está configurado para ser Cinematográfico, a saída da foto receberá um tratamento Cinematográfico automaticamente.

    A imagem retornada pela saída da foto terá o efeito bokeh gravado.

    Por fim, o algoritmo de vídeo cinematográfico requer quantidade suficiente de luz para funcionar corretamente. Então, em uma sala muito escura ou quando a câmera está coberta, queremos informar os usuários desse problema na interface. Para receber a notificação nesses casos, você pode observar a chave-valor da nova propriedade cinematicVideoCaptureSceneMonitoringStatuses na classe AVCaptureDevice. Atualmente, o único status compatível com vídeo cinematográfico é luz insuficiente.

    No gerenciador KVO, podemos atualizar a interface corretamente quando vemos luz insuficiente.

    Um conjunto vazio significa que tudo voltou ao normal.

    Hoje, revisamos como o vídeo cinematográfico permite que usuários capturem belos filmes de nível profissional, mesmo em momentos comuns, como ao passear com seu animal de estimação. E tivemos um passo a passo detalhado sobre como criar uma ótima experiência de captura cinematográfica com a API Cinematic Video. Mal podemos esperar para ver como seus apps vão utilizar esses recursos para oferecer conteúdo mais rico e cinematográfico. Agradeço sua participação.

    • 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
      }
    • 6:33 - Add outputs to the session & configure video stabilization & associate the preview layer with the capture session

      // 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 - Introdução
    • Use a API Cinematic Video para gravar vídeos com qualidade profissional e estilo cinematográfico nos seus apps. O iPhone 13 e o iPhone 13 Pro introduziram o modo Cinema, que transformou o iPhone em uma ferramenta cinematográfica robusta.

    • 0:33 - API Cinematic Video
    • O vídeo cinematográfico usa profundidade de campo rasa e foco de rastreamento para guiar a atenção do espectador, imitando técnicas do cinema. A API Cinematic Video no iOS 26 simplifica esse processo, permitindo que os apps rastreiem e direcionem o foco. Para criar uma experiência de gravação cinematográfica, configure uma sessão de gravação, selecione um dispositivo e ative a gravação de vídeo cinematográfico definindo isCinematicVideoCaptureEnabled como true na classe AVCaptureDeviceInput. Isso configura a sessão para gerar vídeo cinematográfico com dados de disparidade, metadados e o vídeo original, permitindo edição não destrutiva. É possível reproduzir ou editar a renderização do bokeh com o framework Cinematic.

    • 3:44 - Criar uma ótima experiência de captura cinematográfica
    • O exemplo começa com a configuração de uma AVCaptureSession para ativar a gravação de vídeo cinematográfico em dispositivos compatíveis, como a câmera grande-angular dupla na parte traseira e a câmera TrueDepth na frente. Em seguida, seleciona-se um formato de vídeo apropriado, com isCinematicVideoCaptureSupported retornando true, e o microfone é adicionado à sessão como entrada de áudio. A gravação de áudio espacial é ativada para aprimorar a experiência cinematográfica. Para saber mais sobre o Áudio Espacial, confira “Aprimorar os recursos de criação de conteúdo de áudio do seu app”. Na sequência, a estabilização de vídeo é ativada para melhorar a experiência do usuário, e a sessão de gravação é exibida em uma view da SwiftUI. O exemplo então cria uma struct representável personalizada para encapsular o AVCaptureVideoPreviewLayer, permitindo sua integração perfeita à interface da SwiftUI. Depois, o exemplo aborda o controle do efeito cinematográfico, especificamente a profundidade de campo rasa. Ajustando a simulatedAperture, é possível intensificar ou suavizar o efeito bokeh, oferecendo mais controle criativo sobre o vídeo. Para permitir o controle manual de foco, o exemplo implementa a detecção de metadados para identificar candidatos a foco, como rostos. Em seguida, desenha retângulos na tela representando esses candidatos, permitindo que os usuários toquem em um deles para focar em um sujeito específico. A API Cinematic Video tem diversos métodos para controlar o foco durante a gravação de vídeo. Ela gera objetos de metadados que incluem informações sobre os sujeitos detectados no quadro. O parâmetro de configuração focusMode determina o comportamento de rastreamento do foco no vídeo cinematográfico. Esse enum tem três valores: none, strong e weak. O foco strong fixa-se em um sujeito, ignorando outros candidatos em potencial. O foco weak permite que o algoritmo alterne automaticamente o foco com base na cena. O caso none é usado principalmente para determinar o status do foco em vez de defini-lo. A API oferece três métodos de foco: O método setCinematicVideoTrackingFocus usa um ID de objeto detectado como entrada e define o foco para esse objeto. O método setCinematicVideoTrackingFocus também usa um ponto na visualização como entrada. O vídeo cinematográfico procura um objeto de interesse nesse ponto e cria um novo objeto de metadado do tipo salient object, que pode ser usado como ponto de foco. O método setCinematicVideoFixedFocus define um foco fixo em um ponto específico da cena, calculando internamente a distância com base em sinais de profundidade. Quando combinado com o modo de foco forte, o foco é fixado em um plano específico, ignorando outras atividades na cena. Você pode implementar uma lógica de foco personalizada nos seus apps. Por exemplo, um toque em um retângulo de detecção pode alternar o foco entre os sujeitos, e um toque longo pode definir um foco forte fixo. O app diferencia visualmente os modos de foco fraco, forte e nenhum para orientar o usuário. Além disso, a API permite capturar fotos durante a gravação, e essas imagens recebem automaticamente o tratamento cinematográfico com o efeito bokeh. O app também pode usar observação chave-valor para monitorar a propriedade cinematicVideoCaptureSceneMonitoringStatuses e informar o usuário quando houver pouca luz para gravar vídeo cinematográfico adequadamente.

Developer Footer

  • Vídeos
  • WWDC25
  • Grave vídeos com qualidade de cinema em seu app
  • Open Menu Close Menu
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • Icon Composer
    • SF Symbols
    Open Menu Close Menu
    • Accessibility
    • Accessories
    • App Store
    • Audio & Video
    • Augmented Reality
    • Business
    • Design
    • Distribution
    • Education
    • Fonts
    • Games
    • Health & Fitness
    • In-App Purchase
    • Localization
    • Maps & Location
    • Machine Learning & AI
    • Open Source
    • Security
    • Safari & Web
    Open Menu Close Menu
    • Documentation
    • Sample Code
    • Tutorials
    • Downloads
    • Forums
    • Videos
    Open Menu Close Menu
    • Support Articles
    • Contact Us
    • Bug Reporting
    • System Status
    Open Menu Close Menu
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles
    • Feedback Assistant
    Open Menu Close Menu
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program
    • News Partner Program
    • Video Partner Program
    • Security Bounty Program
    • Security Research Device Program
    Open Menu Close Menu
    • Meet with Apple
    • Apple Developer Centers
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Academies
    • WWDC
    Get the Apple Developer app.
    Copyright © 2025 Apple Inc. All rights reserved.
    Terms of Use Privacy Policy Agreements and Guidelines