ハイライト
Apple は iOS 上で EDR API をオープンしました。開発者は AVFoundation と Metal を使用して、単純な AVPlayer の再生からフレームごとの抽出、コア イメージ フィルター処理、および Metal シェーダー レンダリングに至るまで、完全な HDR ビデオ再生およびリアルタイム処理パイプラインを構築できます。
主要内容
適切なビデオ フレーム レベルを選択します
HDR ビデオを再生する場合、開発者は選択を迫られます。高レベルのフレームワークを使用して迅速に実装するか、低レベルのフレームワークを使用してより詳細な制御を行うかです。 Apple のビデオ フレームワークは、高レベルから低レベルまで、AVKit、AVFoundation、Core Video、Video Toolbox、Core Media です。高レベルのフレームワークには独自の最適化が備わっているため、原則として、低レベルのフレームワークではなく高レベルのフレームワークを使用します。
AVKit は、プログレス バー、チャプター ナビゲーション、ピクチャ イン ピクチャ、字幕などの完全な再生 UI を提供します。 HDR ビデオを再生する必要があるだけの場合、AVPlayerViewController は 1 行のコードで EDR レンダリングを処理できます。
ただし、多くのアプリケーションでは、カラー グレーディング、キーイング、特殊効果のスーパーインポーズなど、ビデオ フレームのリアルタイム処理が必要です。現時点では、AVFoundation を使用してデコードされたフレームを抽出し、それを Core Image または Metal に渡して処理する必要があります。
シンプルな再生: AVPlayer の 2 つの方法
HDR 再生には 2 つの基本的なオプションがあります。
1 つ目は AVPlayerViewController を使用します。これは、完全な再生コントロールが必要なシナリオに適しています。 (06:58)
let player = AVPlayer(url: videoURL)
var playerViewController = AVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated: true) {
playerViewController.player!.play()
}
キーポイント:
- AVPlayerViewController はすべての再生 UI と EDR レンダリングを自動的に処理します
- カスタム処理を必要としない純粋な再生シナリオに適しています
2 番目のタイプは、ビデオをカスタム ビューに埋め込むのに適した AVPlayerLayer を使用します。 (07:38)
let player = AVPlayer(url: videoURL)
var playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.view.bounds
self.view.layer.addSublayer(playerLayer)
player.play()
キーポイント:
- AVPlayerLayer はビデオをカスタム CALayer にレンダリングします
- EDR も自動的にサポートされるため、追加の構成は必要ありません
- ビデオをビューの一部として含める必要があるシナリオに適しています
リアルタイム処理: デコードされたフレームからメタル レンダリングまで
再生中に特殊効果を追加する必要がある場合、プロセスは次のようになります。AVPlayer デコード -> CADisplayLink フレーム抽出 -> Core Image または Metal 処理 -> CAMetalLayer レンダリング。
このプロセスの鍵は、CAMetalLayer を正しく設定することです。 EDR を有効にし、半浮動小数点ピクセル形式を使用し、拡張色域を設定する必要があります。 (09:28)
次に、出力形式と色空間を指定して AVPlayerItemVideoOutput を作成します。 AVFoundation は、異なるクリップ間のカラー変換を自動的に処理します。 (11:33)
CADisplayLink がフレームごとにコールバックされると、最新の CVPixelBuffer が VideoOutput からコピーされます。画面のリフレッシュ レートがビデオ フレーム レートよりも高い場合があるため、copyPixelBuffer が毎回成功するとは限らないことに注意してください。失敗した場合は、前のフレームを保持してください。 (13:02)
詳細
EDR レンダリング層を構成する
let layer: CAMetalLayer
// 启用 EDR
layer.wantsExtendedDynamicRangeContent = true
// 使用半浮点像素格式
layer.pixelFormat = MTLPixelFormatRGBA16Float
// 使用扩展线性 Display P3 色域
layer.colorspace = kCGColorSpaceExtendedLinearDisplayP3
キーポイント:
wantsExtendedDynamicRangeContenttrue に設定する必要があります。そうでない場合、HDR コンテンツは SDR に圧縮されます。MTLPixelFormatRGBA16FloatEDRで必要な拡張輝度値を格納できる半浮動小数点形式です。kCGColorSpaceExtendedLinearDisplayP3Apple デバイスで最も一般的に使用される広色域であり、線形伝達関数により拡張範囲の値が維持されます。
ビデオ出力と表示リンクを作成する
let videoColorProperties = [
AVVideoColorPrimariesKey: AVVideoColorPrimaries_P3_D65,
AVVideoTransferFunctionKey: AVVideoTransferFunction_Linear,
AVVideoYCbCrMatrixKey: AVVideoYCbCrMatrix_ITU_R_2020
]
let outputVideoSettings = [
AVVideoAllowWideColorKey: true,
AVVideoColorPropertiesKey: videoColorProperties,
kCVPixelBufferPixelFormatTypeKey as String: NSNumber(value: kCVPixelFormatType_64RGBAHalf)
] as [String : Any]
let videoPlayerItemOutput = AVPlayerItemVideoOutput(outputSettings: outputVideoSettings)
キーポイント:
AVVideoColorPrimaries_P3_D65ディスプレイ P3 の色域を指定するAVVideoTransferFunction_LinearEDRの鍵となる線形伝達関数を使用して、輝度値の圧縮を回避しますkCVPixelFormatType_64RGBAHalf64 ビット RGBA 半浮動小数点形式ですAVPlayerItemVideoOutputビデオに色域の異なる複数のクリップが含まれている場合でも、色変換は自動的に処理されます。
lazy var displayLink: CADisplayLink = CADisplayLink(
target: self,
selector: #selector(displayLinkCopyPixelBuffers(link:))
)
var statusObserver: NSKeyValueObservation?
statusObserver = videoPlayerItem.observe(\.status, options: [.new, .old]) { playerItem, change in
if playerItem.status == .readyToPlay {
playerItem.add(videoPlayerItemOutput)
displayLink.add(to: .main, forMode: .common)
videoPlayer?.play()
}
}
キーポイント:
- 観察する
statusプロパティ。プレーヤーの準備ができるまで待ってから出力を追加し、リンクの表示を開始します。 - CADisplayLink はメイン実行ループの共通モードに関連付けられており、スクロール中にコールバックも実行できるようにします。
- macOS では CADisplayLink の代わりに CVDisplayLink を使用する必要があります
フレームごとの処理: Core Image フィルター
@objc func displayLinkCopyPixelBuffers(link: CADisplayLink) {
let currentTime = videoPlayerItemOutput.itemTime(forHostTime: CACurrentMediaTime())
if videoPlayerItemOutput.hasNewPixelBuffer(forItemTime: currentTime) {
if let buffer = videoPlayerItemOutput.copyPixelBuffer(
forItemTime: currentTime,
itemTimeForDisplay: nil
) {
let image = CIImage(cvPixelBuffer: buffer)
let filter = CIFilter.sepiaTone()
filter.inputImage = image
let output = filter.outputImage ?? CIImage.empty()
// 使用 context 渲染到 CIRenderDestination
}
}
}
キーポイント:
itemTime(forHostTime:)ホスト時間をビデオ タイムラインにマッピングするhasNewPixelBuffer不必要なコピーを避けるために新しいフレームを確認してくださいcopyPixelBuffer失敗する可能性があります (ビデオ フレーム レートが画面のリフレッシュ レートより低い)、失敗した場合はレンダリングをスキップします- 使用
CIImage(cvPixelBuffer:)ピクセルバッファから直接画像を作成 - 注: すべての CIFilter が EDR をサポートしているわけではありません。EDR を使用する必要があります。
CICategoryHighDynamicRangeフィルター対応フィルター
フレームごとの処理: 金属テクスチャ
let mtlDevice = MTLCreateSystemDefaultDevice()
var mtlTextureCache: CVMetalTextureCache? = nil
CVMetalTextureCacheCreate(
allocator: kCFAllocatorDefault,
cacheAttributes: nil,
metalDevice: mtlDevice,
textureAttributes: nil,
cacheOut: &mtlTextureCache
)
let width = CVPixelBufferGetWidth(pixelBuffer)
let height = CVPixelBufferGetHeight(pixelBuffer)
var cvTexture: CVMetalTexture? = nil
CVMetalTextureCacheCreateTextureFromImage(
allocator: kCFAllocatorDefault,
textureCache: mtlTextureCache,
sourceImage: pixelBuffer,
textureAttributes: nil,
pixelFormat: MTLPixelFormatRGBA16Float,
width: width,
height: height,
planeIndex: 0,
textureOut: &cvTexture
)
let texture = CVMetalTextureGetTexture(cvTexture)
キーポイント:
CVMetalTextureCacheIOSurfaceの手動管理を回避するためのCore VideoとMetalを接続するブリッジです。- キャッシュはパフォーマンスを向上させるために MTLTexture から IOSurface へのマッピングを維持します。
- メタル テクスチャはこの形式をネイティブにサポートしているため、半浮動小数点ピクセル形式を使用します。
- CVMetalTextureRef は、Objective-C の Metal コマンド バッファ完了コールバックで解放される必要があります。
重要ポイント
-
リアルタイム ビデオ色補正ツール: AVPlayerItemVideoOutput + Core Image フィルター チェーンを使用して、HDR をサポートするリアルタイム色補正パネルを作成します。ユーザーはスライダーをスライドして色温度と彩度を調整でき、フィルターはビデオ フレームに直接作用します。エントリーAPIは
AVPlayerItemVideoOutputそしてCIFilter。 -
HDR ビデオ キーイング合成: メタル シェーダーを使用してクロマ キー キーイングを実装し、グリーン スクリーン ビデオと背景レイヤーをリアルタイムで合成します。 EDR は、合成された HDR コンテンツが輝度情報を失わないことを保証します。エントリーAPIは
CVMetalTextureCacheカスタム メタル フラグメント シェーダ。 -
プロのビデオ プレーヤー向けにカスタマイズされたレンダリング エンジン: AVPlayerViewController を放棄し、CADisplayLink + Metal を使用して独自の再生レンダリング パイプラインを構築し、フレームごとの正確な制御と特殊効果のオーバーレイをサポートします。編集ソフトウェアやプロのプレーヤーに適しています。エントリーAPIは
CADisplayLinkそしてCAMetalLayer。 -
HDR スクリーンショットとフレーム エクスポート: CADisplayLink コールバックで、処理された CVPixelBuffer を HEIF/PNG として保存し、HDR メタデータを保持します。ビデオのフレームごとのエクスポート ツールにすることができます。エントリーAPIは
CVPixelBufferそしてCIContext.writeHEIFRepresentation。
関連セッション
- iOS で EDR を探索する — EDR API が初めて iOS プラットフォームをサポート
- Core Image、Metal、SwiftUI で EDR コンテンツを表示 — SwiftUI で EDR コンテンツを表示
- AVFoundation を使用した HDR ビデオの編集と再生 — AVVideoComposition を使用して HDR ビデオを処理する
- ビデオ アプリ向けに Core Image パイプラインを最適化する — Core Image ビデオ処理パイプラインの最適化
コメント
GitHub Issues · utterances