View in English

  • 打开菜单 关闭菜单
  • Apple Developer
搜索
关闭搜索
  • Apple Developer
  • 新闻
  • 探索
  • 设计
  • 开发
  • 分发
  • 支持
  • 账户
在“”范围内搜索。

快捷链接

5 快捷链接

视频

打开菜单 关闭菜单
  • 专题
  • 相关主题
  • 所有视频
  • 关于

更多视频

  • 简介
  • 概要
  • 转写文稿
  • 代码
  • 在你的 App 中录制电影效果视频

    了解如何使用 Cinematic Video API,让你的 App 轻松拍摄大片感满屏的精彩视频。我们将介绍如何配置电影效果拍摄会话,并讲解构建视频拍摄 UI 的基本知识。我们还将探索高级电影效果功能,例如应用景深效果来实现动态跟踪拍摄和巧妙的焦点切换。

    章节

    • 0:00 - 简介
    • 0:33 - 电影效果视频
    • 3:44 - 构建出色的电影效果视频拍摄体验

    资源

    • AVCam: Building a camera app
    • AVFoundation
    • Capturing cinematic video
    • Cinematic
      • 高清视频
      • 标清视频

    相关视频

    WWDC25

    • 利用拍摄控件提升你的相机体验

    WWDC24

    • 打造出色的锁定屏幕相机拍摄体验

    WWDC23

    • 在 iPadOS App 中支持外部摄像头
    • 打造更流畅的相机体验
    • 探索 tvOS 连续互通相机
  • 搜索此视频…

    大家好 我叫 Roy 是摄像头软件团队的一名工程师 今天 我很高兴为大家介绍 如何使用 Cinematic Video API 让你的 App 轻松拍摄出 影院级的专业视频

    我们在 iPhone 13 和 13 Pro 中 推出了电影效果模式

    它凭借直观的用户界面 和强大的算法 让 iPhone 变身为 专业级“摄影棚”

    在这次讲座中 我们将了解 电影效果视频的神奇之处

    并一起观看一些代码演练 学习如何打造出色的 电影效果视频拍摄体验

    那么 具有电影效果的视频 是什么样的?

    电影效果的核心在于 经典的叙事手法 比如转换焦点和跟踪对焦

    借助浅景深 导演将观众的注意力引导到 场景中的关键拍摄主体上 增强叙事冲击力 当拍摄主体移动时 跟踪对焦可以让主体总是 清晰地呈现在镜头中 就像电影中经常出现的那样

    虽然这些手法很厉害 但在现实世界中 这些调焦技巧需要 深厚的专业素养

    因此电影片场会有专门的调焦员 他们主要负责 拍摄这些效果出色 却极具挑战性的镜头

    电影效果视频 却能大大简化这一工作 因为它可以智能地调焦

    例如 当拍摄主体进入取景框时 算法会自动将焦点 对准他们并开始跟踪 当拍摄主体将视线移开时 焦点会自动切换到相应的其他点 并在必要时回到主体身上

    今年 我们就会推出这些 令人惊叹的功能 通过 Cinematic Video API 让你的 App 可以轻松拍摄出 具有电影效果的精彩视频 让我们来看看如何使用新的 API 打造出色的电影效果视频拍摄体验 我们先来看看 视频 App 的常见拍摄会话

    首先要选择用来拍摄电影的设备

    然后将它添加到设备输入中

    根据具体用例 可以将多个输出添加到会话中

    将这些输出添加到拍摄会话时 就会创建连接对象

    这种设置操作并不简单 但启用电影效果视频拍摄 却非常简单

    在 iOS 26 中 我们将为 AVCaptureDeviceInput 类 添加一个新属性 isCinematicVideoCaptureEnabled 这个属性设为 true 后 就会将整个拍摄会话 配置为输出电影效果视频 每个输出都会经过电影效果处理

    影片文件输出生成的影片文件 将具有电影效果

    它会包含视差数据、元数据 和原始视频 支持无损编辑 要以焦外成像效果播放 或编辑焦外成像效果 可以使用 2023 年推出的 Cinematic 框架

    要进一步了解这个框架 请观看 WWDC23 讲座“在你的 App 中 支持电影效果模式视频” 视频数据输出将生成 带有浅景深效果的帧 这在需要直接访问 这些帧时非常有用

    例如当发送它们到远程设备时

    同样 预览层将实时渲染 焦外成像效果 这样就可以很简单地构建取景器

    明白这种大致架构后 我们来看一些代码 涉及以下这些方面

    我们将配置 AVCaptureSession 以及拍摄电影效果 所需的所有组件

    然后 我们要用 SwiftUI 构建视频拍摄界面

    我们将了解如何获取 面部识别等元数据 以及如何在屏幕上呈现出来

    我们将借助多种手动调焦的方法 充分发挥电影效果视频的强大功能

    最后 我们将介绍 如何用一些高级功能 让 App 看起来更加精致

    我们先从拍摄会话开始

    首先 我们要找到用来 拍摄电影的视频设备 为找到设备 我们要创建一个 AVCaptureDevice.DiscoverySession 对象

    后置的双广角摄像头和 前置的原深感摄像头 均支持拍摄电影效果视频

    在这个例子中 我们在设备类型数组中指定 .builtInDualWideCamera 由于我们要拍摄电影 因此 使用 .video 作为 mediaType

    并请求设备的后置摄像头

    由于我们只请求一种设备类型 因此只需获取发现会话 设备数组中的第一个元素

    要启用电影效果视频拍摄 就必须使用支持这个功能的格式

    为找到这样的格式我们要遍历 所有设备的格式 并使用 isCinematicVideoCaptureSupported 属性返回 true 的格式

    这里列出了所有支持的格式

    双广角摄像头和原深感摄像头 都可在每秒 30 帧下 支持 1080p 和 4K

    如果你想录制 SDR 或 EDR 内容 可以使用 420 视频范围或全范围

    如果更喜欢 10 位 HDR 视频内容请改用 x420

    我们要拍的可不是无声电影 所以当然会有声音 我们仍用 DiscoverySession API 来找到麦克风

    我们要为使用的每个设备创建输入 然后将这些输入 添加到拍摄会话中

    到了这一步 我们可以在视频输入上打开 电影效果视频拍摄

    为了增强电影效果体验 我们可以录制空间音频 方法很简单 只需将 multichannelAudioMode 设为一阶 Ambisonics

    要进一步了解空间音频 请观看今年的讲座 “提升 App 的音频内容 制作功能” 我们接着看输出 创建 AVCaptureMovieFileOutput 对象并添加到会话中

    我们的手可不像三脚架那样稳定 所以最好启用视频防抖功能

    为此 首先找到 movieFileOutput 的视频连接 并设置它的 preferredVideoStabilizationMode 在这个示例中 我们设为 cinematicExtendedEnhanced

    最后 我们要将预览层 与拍摄会话相关联

    这样 拍摄会话就告一段落了 接下来是用户界面

    由于 AVCaptureVideoPreviewLayer 是 CALayer 的子类 而 CALayer 不属于 SwiftUI 为使它们能够互操作 我们需要将预览层 包装到一个结构体中 这个结构体应符合 UIViewRepresentable 协议

    我们要在这个结构体中 创建一个 UIView 子类 CameraPreviewUIView

    重写它的 layerClass 属性 使预览层成为视图的支撑层

    然后创建一个 previewLayer 属性 使它作为 AVCaptureVideoPreviewLayer 类型 可轻松访问

    然后 将预览视图放入 ZStack 中

    在那里它可以很容易地 与其他 UI 元素组合 例如摄像头控件

    正如介绍中提到的 浅景深 是一种很重要的电影叙事手法 通过更改设备输入的 simulatedAperture 属性 我们可以调整 焦外成像效果的全局强度

    如右侧所示 可以使用滑块 调整焦外成像模糊的全局强度

    这个值以行业标准的光圈系数表示

    也就是 焦距和光圈直径的比率

    换言之 光圈就是 焦距除以光圈系数

    因此 光圈系数越小 光圈越大 焦外成像效果也就越强

    我们可以在格式中 找到最小、最大和 默认的模拟光圈

    我们使用它们来创建 合适的 UI 元素 例如滑块

    现在 我们要构建一些 直观的功能提示 以便用户与电影效果视频 进行手动交互

    为了方便用户手动调焦 需要在人脸等可能成为焦点的部位 显示视觉指示符 为此就需要识别元数据

    我们将用 AVCaptureMetadataOutput 来获取识别结果 这样就能在屏幕上绘制人脸框 方便用户交互 电影效果视频算法 需要某些 metadataObjectTypes 才能良好运作

    这些类型通过新属性 requiredMetadataObjectTypesForCinematicVideoCapture 进行传递 如果在启用电影效果视频时提供的 metadataObjectTypes 与这个列表不同 就会触发一个异常

    最后我们需要提供一个委托 来接收元数据 以及一个调用这个委托的队列

    我们在元数据输出委托回调中 接收元数据对象

    为了将这些元数据轻松传递给 SwiftUI 中的视图层 要用一个可观察类

    当我们更新它的属性时 观察视图将自动刷新

    在视图层中 每当 可观察对象更新时 视图都会自动重新绘制

    我们为每个元数据对象 绘制一个方框

    绘制这些方框时 必须要将元数据的边界 转换到预览层的坐标空间中

    使用 layerRectConverted fromMetadataOutputRect 方法

    请注意 position 方法 中的 X 和 Y 是指视图的中心 而不是 AV Foundation 使用的左上角 因此我们需要使用方框的 midX 和 midY 进行相应的调整

    在屏幕上绘制元数据方框后 就可以用它们来手动调焦

    Cinematic Video API 提供了 三种手动调焦方式 现在让我们一一介绍一下

    setCinematicVideoTrackingFocus detectedObjectID focusMode 方法 可用于转换焦点到 检测到的特定主体上 这个主体由 detectedObjectID 标识 detectedObjectID 可在 AVMetadata 对象中找到 而 AVMetadata 对象 从元数据输出中获取

    focusMode 可配置 电影效果视频的跟踪行为

    CinematicVideoFocusMode 枚举有三项: none (无)、strong (强) 和 weak (弱)

    强聚焦会让电影效果视频 持续跟踪拍摄主体 即使有在其他情况下 可能自动聚焦的对象 强聚焦情况下 也不会改变聚焦对象

    在这个视频中 虽然猫 在画面中更加突出 但是由黄色实线方框表示的强聚焦 也会一直锁定后方的主体

    而弱聚焦则相反 会让算法来控制聚焦 算法会在它认为合适时自动转移焦点

    在这种情况下 当猫转过来后 算法认为猫更重要 于是弱聚焦就将焦点 自动转移到了猫身上 如虚线方框所示

    无聚焦仅在一种情况下有用 即要确定一个元数据对象 当前是否在焦点中 因此它不用于设置焦点

    第二种调焦方法 的第一个参数不同 它不是采用检测到的对象 ID 而是采用视图中的一个点

    它会让电影效果视频在指定的点 搜寻可能引起兴趣的对象 找到后 它会创建一个 新的元数据对象 类型为“显著对象” 这样就可以在屏幕上 围绕它绘制一个方框

    第三种调焦方法是 setCinematicVideoFixedFocus 它需要一个点和一个聚焦模式 它会聚焦在一个固定的距离上 这个距离是使用深度等信号 在内部计算得出的 搭配强聚焦模式 这种方法可有效地将焦点 锁定在场景中的某个平面上 忽略所有其他活动 即使前景活动也一样

    App 可以根据自身的用例 采用合适的聚焦逻辑 我们的 App 采用以下方法:

    如果轻点的检测方框不在焦点中 就会以弱聚焦的方式将焦点转移过去 用这种手法 我们可以 来回切换焦点 对焦不同的主体

    如果一个元数据对象已经在弱焦点中 轻点它就会将弱聚焦改为强聚焦

    用黄色实线方框表示

    如果在没有检测到 任何对象的地方轻点 电影效果视频就会尝试在这个地方 搜寻看起来显眼的物体 并对它弱聚焦

    而长按则可以设定 一个强聚焦的固定焦点 接下来我们看看如何 在代码中实现这些逻辑 首先需要创建两个手势

    常规的轻点手势可通过 SpatialTapGesture 轻松完成 它会提供要设置焦点的轻点位置

    轻点时 在摄像头模型对象上 调用 focusTap 方法 这样就可以访问底层的 AVCaptureDevice

    另一方面 长按要复杂一些 因为内置的 longPressGesture 不提供所需的轻点位置 所以要用 DragGesture 来模拟长按

    当按下时 我们开始一个 0.3 秒的计时器

    当它触发时 我们在摄像头模型上 调用 focusLongPress 方法

    然后创建一个方框视图 来接收手势 并将它插入到 ZStack 的末尾 这会将它置于所有检测方框之上 以防阻挡用户的手势输入

    正如我们在前面的视频中看到的 一定要在视觉上区分 弱聚焦、强聚焦和无聚焦 之间的聚焦方框 以便用户采取正确的操作

    为此 要实现一个方法 来接受 AVMetadataObject 并返回一个聚焦方框视图

    请记住 我们需要将元数据的边界 从元数据输出的坐标空间 转换到预览层的坐标空间

    通过设置不同的线样式和颜色 我们可以轻松地为每种聚焦模式 创建看起来截然不同的方框

    根据从视图层传递来的点 我们可以确定使用哪种聚焦方法

    首先 我们需要弄清楚 用户是否轻点了元数据方框 为此要用辅助方法 findTappedMetadataObject

    遍历为每一帧缓存的所有元数据 检查指定的点是否在 其中某个元数据的边界内

    此时同样要确保点和方框 位于同一坐标空间中

    回到 focusTap 方法 如果找到元数据对象 并且它已经处于弱焦点中 那就将它改为强焦点

    如果它不在焦点中 就对它弱聚焦

    如果用户轻点的位置 不在元数据方框内 就让框架在这个位置上 尝试找到一个显眼的对象

    长按时 只需在指定点设置 一个强聚焦的固定焦点

    至此我们就有了一个 功能完备的 App 可以拍摄电影效果视频 让我们再来打磨完善一下

    目前的视频拍摄示意图如下 有三个输出 用于拍摄影片、 接收元数据和预览

    如果希望在录制过程中支持 静态图像的拍摄 只需在会话中添加 AVCapturePhotoOutput 即可

    由于已经将画面配置为电影效果 输出的照片将自动接受 电影效果处理

    照片输出返回的图像 将具有焦外成像效果

    最后 电影效果视频算法 需要充足的光线 才能正常运作 因此 在房间过于昏暗 或摄像头被遮挡时 应在 UI 中告知用户这类问题

    为了在这种情况下通知用户 你可以对 AVCaptureDevice 类的 新属性 cinematicVideoCaptureSceneMonitoringStatuses 进行键值观察

    目前 电影效果视频唯一 支持的状态就是光线不足

    在 KVO 处理程序中 我们可以适当更新 UI 反映光线不足的问题

    空集意味着情况已经恢复正常

    在今天的讲座中 我们回顾了 电影效果视频 如何让用户拍摄 专业水平的精彩电影 比如与宠物的快乐日常 给平凡生活增添亮色

    我们进行了详细的演练 学习如何用 Cinematic Video API 打造出色的电影效果拍摄体验

    我们迫不及待地想看到你的 App 如何利用这些功能 让用户创作出更丰富、 更具电影感的内容

    感谢大家的观看

    • 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 - 简介
    • 使用 Cinematic Video API 在 App 中拍摄具有电影效果的专业级视频。iPhone 13 和 13 Pro 引入了电影效果模式,将 iPhone 打造成了强大的电影摄影工具。

    • 0:33 - 电影效果视频
    • 电影效果视频模仿电影拍摄技术,利用浅景深和追踪焦点来引导观众的注意力。iOS 26 中的 Cinematic Video API 简化了这一过程,使 App 能够自动转换和追踪焦点。 为了打造电影级拍摄体验,请设置拍摄会话,选择设备,然后在“AVCaptureDeviceInput”类上将“isCinematicVideoCaptureEnabled”设置为 true,从而启用电影效果视频拍摄。这样就能将会话配置为输出包含视差数据、元数据和原始视频的电影效果视频,从而允许进行无损剪辑。你可以使用 Cinematic 框架播放或剪辑焦外成像渲染。

    • 3:44 - 构建出色的电影效果视频拍摄体验
    • 这个示例将首先设置“AVCaptureSession”,以便在兼容设备 (例如背面的双广角摄像头和前置的原深感摄像头) 上拍摄电影效果视频。示例会选择合适的视频格式,并将“isCinematicVideoCaptureSupported”返回 true,然后将麦克风的音频输入添加到会话中。为了增强电影效果体验,启用了空间音频采集功能。 如需进一步了解空间音频,请参阅“提升 App 的音频录制功能”。 接下来,启用视频防抖功能以提升用户体验,并使用 SwiftUI 视图来预览拍摄会话。然后,示例会创建一个自定的可表示结构体来封装“AVCaptureVideoPreviewLayer”,使它能够无缝集成到 SwiftUI 界面中。 这个示例随后深入探讨了如何控制“电影效果视频”效果,特别是浅景深。通过调整“simulatedAperture”,可以增强或减弱焦外成像效果,从而对视频进行更有创意的控制。 为了启用手动对焦控制,这个示例实现了元数据检测来识别焦点候选对象 (例如人脸)。然后,它会在屏幕上绘制矩形来表示这些候选对象,以便用户轻点和专注于特定主体。 Cinematic Video API 提供了多种方法来控制视频录制过程中的对焦。这个 API 会输出元数据对象,其中包含在帧中检测到的主体的信息。 “focusMode”配置参数将决定电影效果视频的追踪行为。这个枚举有三种情况:“none”、“strong”和“weak”。强对焦会锁定一个主体,而忽略其他潜在的焦点候选对象。 弱对焦允许算法根据场景自动转换焦点。 “none”情况主要用于确定对焦状态,而不是设置对焦状态。 这个 API 提供了三种对焦方法: “setCinematicVideoTrackingFocus”方法将检测到的对象 ID 作为输入,并将焦点设置为这个对象。 “setCinematicVideoTrackingFocus”方法将视图中的一个点作为输入,电影效果视频随后会在该点搜索感兴趣的对象,并创建一个类型为“salient object”的新元数据对象,以便接下来将它作为焦点。 “setCinematicVideoFixedFocus”在场景中的特定点设置固定焦点,并使用深度信号在内部计算距离。与强对焦模式搭配使用时,这会将焦点锁定在特定平面上,而忽略场景中的其他活动。 你可以在 App 中实现自定对焦逻辑。例如,轻点一个检测矩形从而在不同主体之间切换焦点,以及通过长按来设置强固定焦点。App 会在视觉上区分弱对焦、强对焦和无对焦,以引导用户。 此外,这个 API 允许在录制过程中拍摄静态图像,这将自动获得具有焦外成像效果的电影效果处理。App 还可以使用键值观察来观察“cinematicVideoCaptureSceneMonitoringStatuses”属性,并在光线不足以正常拍摄电影效果视频时通知用户。

Developer Footer

  • 视频
  • WWDC25
  • 在你的 App 中录制电影效果视频
  • 打开菜单 关闭菜单
    • iOS
    • iPadOS
    • macOS
    • Apple tvOS
    • visionOS
    • watchOS
    打开菜单 关闭菜单
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    打开菜单 关闭菜单
    • 辅助功能
    • 配件
    • App 扩展
    • App Store
    • 音频与视频 (英文)
    • 增强现实
    • 设计
    • 分发
    • 教育
    • 字体 (英文)
    • 游戏
    • 健康与健身
    • App 内购买项目
    • 本地化
    • 地图与位置
    • 机器学习与 AI
    • 开源资源 (英文)
    • 安全性
    • Safari 浏览器与网页 (英文)
    打开菜单 关闭菜单
    • 完整文档 (英文)
    • 部分主题文档 (简体中文)
    • 教程
    • 下载 (英文)
    • 论坛 (英文)
    • 视频
    打开菜单 关闭菜单
    • 支持文档
    • 联系我们
    • 错误报告
    • 系统状态 (英文)
    打开菜单 关闭菜单
    • Apple 开发者
    • App Store Connect
    • 证书、标识符和描述文件 (英文)
    • 反馈助理
    打开菜单 关闭菜单
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program (英文)
    • News Partner Program (英文)
    • Video Partner Program (英文)
    • 安全赏金计划 (英文)
    • Security Research Device Program (英文)
    打开菜单 关闭菜单
    • 与 Apple 会面交流
    • Apple Developer Center
    • App Store 大奖 (英文)
    • Apple 设计大奖
    • Apple Developer Academies (英文)
    • WWDC
    获取 Apple Developer App。
    版权所有 © 2025 Apple Inc. 保留所有权利。
    使用条款 隐私政策 协议和准则