WWDC Quick Look 💓 By SwiftGGTeam
Support Cinematic mode videos in your app

Support Cinematic mode videos in your app

元の動画を見る

ハイライト

iOS 17/macOS Sonoma は、Cinematic API (接頭辞 CN) を導入します。これにより、サードパーティ製アプリケーションが iPhone 13/14 で撮影されたムービーエフェクト モードのビデオを再生および編集できるようになり、絞りのリアルタイム調整、フォーカスの意思決定の非破壊的な変更、および編集ステータスの保存/読み込みがサポートされます。

主な内容

動画エフェクトモードの2層ファイル構造

iPhone でキャプチャされたシネエフェクト モードのビデオには 2 つのファイルが含まれています。1 つは、共有および直接再生できる被写界深度効果を備えたレンダリングされた通常の QuickTime ムービーです。もう 1 つは、編集に必要なすべての生データと情報を含む特殊な映画効果アセットです。

映画効果アセットには 3 つのトラックがあります。ビデオ トラックはオリジナル ショットであり、最大 4K@30fps をサポートします。視差トラックは、デュアル カメラのピクセル オフセットを記録します。近くのオブジェクトのオフセットは大きく、遠くのオブジェクトのオフセットは小さくなります。このデータは被写界深度のレンダリングを推進します。メタデータ トラックには、レンダリング プロパティ (フォーカス視差と絞り F 値) およびムービー スクリプト (自動的に検出された顔、頭、胴体、およびフォーカス決定の記録) が含まれています。

焦点の意思決定の階層的メカニズム

シネマティック エンジンはシーン内のオブジェクトを自動的に検出し、撮影時にフォーカスを決定します。これらの自動決定は基本決定と呼ばれます。ポストエディット中に、ユーザーは 2 種類のユーザー決定を挿入できます。弱い決定は、次のベースまたはユーザーの決定までのみ持続します。強い決定は、検出されたオブジェクトが画面から離れるか、ユーザーが手動でフォーカスを再度切り替えるまで、後続のすべての基本決定を上書きします。

この階層設計により、ユーザーはクリップ内のフォーカスの一部のみを変更しながら、残りの部分については自動的な意思決定を維持できるため、ナラティブを細かく制御できます。

リアルタイム レンダリング データ ストリーム

再生または編集時に、Cinematic エンジンは現在のフレームのレンダリング プロパティを読み取り、視差マップを組み合わせて浅い被写界深度効果を計算します。フォーカスが切り替わると、エンジンが事前にラック フォーカスを開始し、プロのフォーカス プラーのようにキー フレームを予測し、スムーズな移行効果を生み出します。

##詳細

ムービーエフェクトアセットを取得する

08:16

写真ライブラリからムービーエフェクトアセットを取得します。

// 使用 Photos picker 筛选电影效果视频
let picker = PHPickerViewController(configuration: config)

// 获取资产标识后请求原始版本
let options = PHVideoRequestOptions()
options.version = .original
options.isNetworkAccessAllowed = true // 资产可能在 iCloud

PHImageManager.default().requestAVAsset(
    forVideo: phAsset,
    options: options
) { avAsset, _, _ in
    // avAsset 包含电影效果资产的所有轨道
}

キーポイント:

  • version = .original設定する必要があります。設定しないと、レンダリングされた通常のビデオのみを取得できます。
  • isNetworkAccessAllowed = trueiCloud 上でアセットを扱う
  • AirDrop転送中に「すべての写真データ」を選択すると、完全なムービーエフェクトデータが保持されます

レンダリング セッションをセットアップする

10:08

// 1. 从资产获取渲染属性
let assetInfo = try await CNScript.loadFromAsset(asset)
let renderingAttributes = assetInfo.renderingAttributes

// 2. 创建渲染会话
let session = try CNRenderingSession(
    attributes: renderingAttributes,
    commandQueue: metalCommandQueue
)

// 3. 设置渲染质量
session.quality = .export // 或 .preview

キーポイント:

  • CNRenderingSessionMetal コマンド キューが必要です。レンダリングは GPU で実行されます
  • .previewリアルタイム再生に適しており、.export最終出力に適した
  • CNScript.loadFromAssetアセットのスクリプト データを非同期でロードする

カスタムビデオシンセサイザーを構築する

10:53

// 将资产轨道添加到 composition
let composition = AVMutableComposition()
let cinematicComposition = try await composition.loadCinematicCompositionInfo(
    from: assetInfo
)

// 获取电影脚本
let script = assetInfo.script

// 创建渲染指令
let instruction = CinematicVideoCompositionInstruction(
    renderingSession: session,
    composition: cinematicComposition,
    script: script,
    aperture: fNumber // 从 UI 获取的光圈值
)

// 构建视频合成
let videoComposition = AVMutableVideoComposition(
    propertiesOf: composition,
    instruction: instruction
)
videoComposition.customVideoCompositorClass = CinematicCompositor.self

// 播放
let playerItem = AVPlayerItem(asset: composition)
playerItem.videoComposition = videoComposition
let player = AVPlayer(playerItem: playerItem)

キーポイント:

  • loadCinematicCompositionInfo複数のトラックを 1 つのステップで追加する
  • CinematicVideoCompositionInstructionレンダリングパラメータをカプセル化する
  • カスタムコンポジターstartRequestCinematic レンダラーを呼び出す

コンポジターでのレンダリング プロセスをカスタマイズする

12:10

class CinematicCompositor: NSObject, AVVideoCompositing {
    func startRequest(_ request: AVAsynchronousVideoCompositionRequest) {
        // 获取源缓冲区
        let videoBuffer = request.sourceFrame(
            byTrackID: cinematicComposition.videoTrackID
        )
        let disparityBuffer = request.sourceFrame(
            byTrackID: cinematicComposition.disparityTrackID
        )
        let metadataBuffer = request.sourceFrame(
            byTrackID: cinematicComposition.metadataTrackID
        )
        
        // 创建输出缓冲区
        let outputBuffer = ...
        
        // 从元数据获取帧渲染属性
        var frameAttributes = session.frameAttributes(
            from: metadataBuffer
        )
        
        // 应用用户调整:修改光圈
        if let aperture = instruction.aperture {
            frameAttributes.aperture = aperture
        }
        
        // 应用用户调整:修改焦点(从更新后的脚本获取)
        let scriptFrame = script.frame(at: request.compositionTime)
        frameAttributes.focusDisparity = scriptFrame.focusDisparity
        
        // 编码渲染
        let commandBuffer = commandQueue.makeCommandBuffer()!
        session.encodeRender(
            frameAttributes: frameAttributes,
            frame: videoBuffer,
            disparity: disparityBuffer,
            output: outputBuffer,
            commandBuffer: commandBuffer
        )
        
        commandBuffer.addCompletedHandler { _ in
            request.finish(withComposedVideoFrame: outputBuffer)
        }
        commandBuffer.commit()
    }
}

キーポイント:

  • からmetadataBuffer抽出するCNRenderingFrameAttributes、レンダリングの中心となる入力です
  • 絞りとフォーカスの視差をレンダリング前に変更できるため、リアルタイムの調整が可能になります
  • 焦点視差が更新されましたCNScriptスムーズトランジション計算を含むフレーム取得
  • レンダリングは完全に GPU 上で行われ、Metal コマンド バッファーを介してスケジュールされます。

描画検出ボックス

18:09

// 获取当前时间的脚本帧
let scriptFrame = script.frame(at: compositionTime)

// 遍历所有检测对象绘制矩形
for detection in scriptFrame.detections {
    let rect = detection.rect
    // 用白色绘制检测框
    drawRect(rect, color: .white)
}

// 焦点检测用黄色高亮
if let focusDetection = scriptFrame.focusDetection {
    let rect = focusDetection.rect
    drawRect(rect, color: .yellow)
}

キーポイント:

  • scriptFrame.detections現在のフレーム内のすべての顔、頭、胴体の検出が含まれます
  • focusDetection現在フォーカスされている検出オブジェクトです
  • 顔のみを表示するなど、必要に応じて検出フレームをフィルタリングできます

フォーカス決定を変更する

19:40

// 用户点击画面时判断点击位置是否在检测对象内
for detection in scriptFrame.detections {
    if detection.rect.contains(tapPoint) {
        // 根据点击方式决定决策强度
        let strength: CNDecisionStrength = isDoubleTap ? .strong : .weak
        
        let decision = CNDecision(
            time: compositionTime,
            detectionID: detection.detectionID,
            strength: strength
        )
        
        // 添加到脚本
        script.addUserDecision(decision)
        break
    }
}

キーポイント:

  • CNDecisionStrength.weak次の自動決定またはユーザーによる決定までのみ持続します
  • CNDecisionStrength.strong検出されたオブジェクトが消えるまで、その後のすべての自動決定を無効にします
  • 後続のフレームの決定を追加しますfocusDisparity新しいフォーカスを自動的に追跡します
  • エンジンはラックフォーカスを早期に開始し、スムーズな移行を実現します。

編集内容の保存とロード

22:19

// 保存编辑
let scriptChanges = script.changes
let data = scriptChanges.dataRepresentation()
try data.write(to: editFileURL)

// 加载编辑
let loadedData = try Data(contentsOf: editFileURL)
let loadedChanges = try CNScript.Changes(dataRepresentation: loadedData)
let newScript = try CNScript(asset: asset, changes: loadedChanges)

キーポイント:

  • 編集内容は独立したデータ ファイルに保存され、元のアセットは変更されません。
  • dataRepresentation()コンパクトなバイナリ形式を生成する
  • 元のスクリプトと変更はロード時に同時に渡すことができ、それらは自動的にマージされます。
  • いつでも変更を破棄して元の状態に戻すことができます

重要ポイント

  1. ムービーエフェクトビデオエディタ
  • やるべきこと: iMovie の簡易バージョンを作成します。具体的には、ムービー エフェクト ビデオのフォーカスと絞りを編集します。
  • 実行する価値がある理由: Cinematic API は完全な非破壊編集機能を提供し、1 本指/2 本指でクリックしてフォーカスを切り替える操作は直感的かつ自然です。
  • 開始方法: を使用しますPHPickerViewControllerムービーエフェクトビデオをフィルタリングしてビルドするCNRenderingSession+カスタマイズAVVideoCompositing、クリックしてフォーカスを切り替えるインタラクションを実現します。
  1. 物語の自動生成ツール
  • やるべきこと: ビデオコンテンツに基づいて、複数の焦点を当てたナラティブバージョン (サスペンスバージョン、コメディバージョン、温かみのあるバージョン) を自動的に生成します。
  • 実行する価値がある理由: シネマティック スクリプトの検出データは、シーン内のすべての顔とオブジェクトを公開し、フォーカス切り替えのタイミングはルール エンジンに基づいて自動的に調整できます。
  • 開始方法: 分析CNScript顔の向き、距離、画面への出入りのタイミングに基づいてさまざまな決定シーケンスを生成するアルゴリズムを使用した検出タイムライン
  1. 被写界深度ビデオソーシャルプラットフォーム
  • 何をすべきか: ユーザーが映画効果のビデオをアップロードした後、視聴者は画面をクリックしてフォーカスを切り替え、再生中に隠れた詳細を発見できます。
  • 実行する価値がある理由: 強/弱の決定メカニズムにより、視聴者は作者の物語を破壊することなく一時的に焦点を切り替えることができ、双方向性が向上します。
  • 開始方法: バックエンドはオリジナルのムービーエフェクトアセットとユーザーの編集変更を保存し、フロントエンドはCNRenderingSessionリアルタイムレンダリング、クリック時に弱い判定を挿入
  1. ビデオカバーのインテリジェントな生成
  • 操作方法: 映画効果のビデオから、焦点が明確で被写界深度が最も美しいフレームを自動的に抽出し、カバーとして使用します
  • 実行する価値がある理由: 絞りを最大値 (f/1.4) に調整することで各フレームのボケ効果をプレビューし、最も視覚的なインパクトが強い写真を選択できます。
  • 開始方法:ビデオキーフレームをトラバースし、使用しますCNRenderingSessionさまざまな絞り値でレンダリングし、ボケ領域のコントラスト/色の豊かさを計算し、スコアリングによって最適なものを選択します。

関連セッション

コメント

GitHub Issues · utterances