ハイライト
AVFoundation は、WWDC22 で一連の非同期/待機 API を導入しました。これにより、ビデオ サムネイルの生成、オーディオとビデオの合成、リソース属性の読み取りなどの操作がメイン スレッドをブロックしなくなり、新しい
entireLengthAvailableOnDemandローカル カスタム データ ソースの再生パフォーマンスを最適化するためのフラグ。
主要内容
サムネイル生成: ブロッキングから非同期へ
アプリにはビデオプレーヤーがあります。ユーザーが進行状況バーをドラッグしたときに、対応する時点のプレビューを進行状況バーの上に表示したいとします。この要件は非常に一般的ですが、実装方法はユーザー エクスペリエンスに直接影響します。
以前に使用したAVAssetImageGeneratorのcopyCGImage(at:actualTime:)サムネイルを生成するメソッドです。このメソッドは、ビデオ フレーム データをメイン スレッドに同期的にロードします。ビデオ ファイルが大きく、リモート サーバーに保存されている場合、メイン スレッドが停止し、UI がフリーズし、ユーザーにはグルグル回転するだけが表示されます。
WWDC22が与えるものAVAssetImageGenerator追加image(at:)非同期メソッド。スレッドは呼び出されるとすぐに解放され、データのロードが完了した後に渡されます。await結果を返します。ユーザー インターフェイスは応答性を維持し、サムネイルの生成はバックグラウンドで行われます。
バッチサムネイル: 非同期シーケンスによりコードがより自然になります
タイムライン全体をプレビューする場合など、複数時点のサムネイルを一度に生成する必要があります。以前に使用したgenerateCGImagesAsynchronously(forTimes:completionHandler:)、置く必要があります[CMTime]にマッピングされる[NSValue]コールバックで結果を処理するため、コードが長くなります。
新しいAPIimages(for:)直接受け入れる[CMTime]、非同期シーケンスを返します。使用for awaitループは結果を 1 つずつ取得し、成功と失敗はループ本体で処理されます。コード構造は、配列を同期的に走査するのと同じくらい直感的です。
オーディオとビデオの合成: 隠された同期トラップ
AVMutableCompositionのinsertTimeRange(_:of:at:)複合トラックに参照を挿入しているだけのように見えますが、内部的にはソース リソースのトラック情報を同期的に読み取ります。トラックデータがまだロードされていない場合、メインスレッドはここでブロックされます。
WWDC22がスタートinsertTimeRange必要なプロパティをバックグラウンドで自動的にロードする非同期バージョン。AVVideoCompositionコンストラクターと検証メソッドには非同期バージョンもあるため、リソース プロパティを手動でプリロードする必要がなくなります。
リソースプロパティの読み取り: 文字列キーに代わるタイプセーフな代替手段
昨年導入されたload(_:)このメソッドは、従来の文字列のキーと値の読み込みをタイプセーフなプロパティ識別子に置き換えます。文字列キーのスペルが間違っている場合、エラーは報告されませんが、プロパティが同期的に読み込まれ、スレッドがブロックされます。load(_:)エラーはコンパイル時に検出され、指定された型の属性値が直接返されます。
今年、Apple は文字列ベースの非同期キーと値の読み込みを正式に放棄し、すべての新しいコードでそれを使用することを推奨しました。load(_:)。
カスタム データ ソース: ローカル メディアはキャッシュをスキップします
使用AVAssetResourceLoaderカスタム データ ソースをロードする場合、AVAsset はデフォルトでネットワーク通信の前提に基づいてキャッシュを管理します。データがローカルにある場合でも、キャッシュがいっぱいになるまで再生を開始するのを待ちます。
新しいプロパティentireLengthAvailableOnDemandキャッシュ メカニズムをスキップして、データをオンデマンドですぐにフェッチできることを AVAsset に伝えます。これにより、メモリの使用量が削減され、再生の起動時間が短縮されます。ただし、これは純粋にローカル データ ソースにのみ適用され、ネットワーク操作が関与すると再生の信頼性が低くなります。
詳細
単一のサムネイルを非同期的に生成する
(01:41)
func thumbnail() async throws -> UIImage {
let generator = AVAssetImageGenerator(asset: asset)
generator.requestedTimeToleranceBefore = .zero
generator.requestedTimeToleranceAfter = CMTime(seconds: 3, preferredTimescale: 600)
let thumbnail = try await generator.image(at: time).image
return UIImage(cgImage: thumbnail)
}
キーポイント:
AVAssetImageGenerator(asset:)ジェネレーターを作成し、ターゲットのビデオリソースにバインドします。requestedTimeToleranceBeforeそしてrequestedTimeToleranceAfter時間許容誤差を設定して、ジェネレーターにより多くのフレーム選択スペースを与えます。generator.image(at: time)を返す新しい非同期メソッドです。(image: CGImage, actualTime: CMTime)タプル.imageCGImage を直接取得し、タプルを解凍する手順を省略します。CMTime(seconds: 3, preferredTimescale: 600)3 秒の許容範囲を示します。タイムスケール 600 はビデオ編集の一般的な精度です。
許容度について: ビデオ圧縮フレームは、iFrame (独立してデコードできるキー フレーム) と依存フレーム (デコードするには隣接するフレームが必要) に分割されます。時間許容誤差をゼロに設定すると、ジェネレーターは正確な時点を検索するようになります。そのフレームは依存フレームである可能性が高く、ジェネレーターは周囲の複数のフレームをロードする必要があります。許容値を緩和すると、ジェネレーターは最も近いキーフレームを選択し、ロードするデータを減らし、より速く戻ることができます。
サムネイルをバッチで生成する
(02:56)
func timelineThumbnails(for times: [CMTime]) async {
for await result in generator.images(for: times) {
switch result {
case .success(requestedTime: let requestedTime, image: let image, actualTime: _):
updateThumbnail(for: requestedTime, with: image)
case .failed(requestedTime: let requestedTime, error: _):
updateThumbnail(for: requestedTime, with: placeholder)
}
}
}
キーポイント:
images(for:)受け入れる[CMTime]に変換する必要はありません。[NSValue]- を使用して非同期シーケンスを返します。
for await結果を 1 つずつ消費する - 各結果には以下が含まれます
requestedTime(要求された時点)、image(生成された画像)、actualTime(実際に使用されるフレーム時間) - 失敗したら行く
.failedブランチ、プレースホルダー マップにフォールバックできます
より簡潔な書き方:
(03:49)
func timelineThumbnails(for times: [CMTime]) async {
for await result in generator.images(for: times) {
updateThumbnail(for: result.requestedTime, with: (try? result.image) ?? placeholder)
}
}
ここresult.imageエラーがスローされる可能性があります。使用してくださいtry?失敗時に使用される任意の値に変換します?? placeholderすべての詳細を明らかにします。
非同期オーディオおよびビデオ合成
(04:40)
let composition = AVMutableComposition()
try await composition.insertTimeRange(timeRange, of: asset, at: startTime)
キーポイント:
insertTimeRange今はありますawaitバージョン- 内部自動非同期ロード
asset軌道情報 - 最初に手動で行う必要はありません
try await asset.load(.tracks)
ビデオ構成検証は非同期もサポートしています。
(04:57)
let videoComposition = try await AVVideoComposition.videoComposition(withPropertiesOf: asset)
try await videoComposition.isValid(for: asset, timeRange: range, validationDelegate: delegate)
タイプセーフなリソース属性のロード
(05:33)
古い書き方 (時代遅れ):
asset.loadValuesAsynchronously(forKeys: ["duration", "tracks"]) {
guard asset.statusOfValue(forKey: "duration", error: &error) == .loaded else { ... }
guard asset.statusOfValue(forKey: "tracks", error: &error) == .loaded else { ... }
myFunction(thatUses: asset.duration, and: asset.tracks)
}
新しい書き方:
let (duration, tracks) = try await asset.load(.duration, .tracks)
myFunction(thatUses: duration, and: tracks)
キーポイント:
- 古い書き方では文字列を使用します
"duration"、"tracks"キーを作成する場合、コンパイラはスペル ミスのエラーを報告しません。また、実行時の同期読み込みにより遅延が発生します。 - 新しい書き方
.duration、.tracksタイプセーフですAVAsyncProperty、コンパイル中にスペルミスが見つかる可能性があります - 一度
load呼び出しでは複数のプロパティをロードし、結果をタプルとして受け取ることができます - 適用対象
AVAsset、AVAssetTrack、AVMetadataItemとそのサブカテゴリ
コンポジットトラックの同期挿入 (特殊なケース)
(07:06)
let composition = AVMutableComposition()
guard let compositionTrack = composition
.addMutableTrack(withMediaType: .video, preferredTrackID: 1) else { return }
try compositionTrack.insertTimeRange(firstFiveSeconds,
of: videoTrack1, at: .zero)
try compositionTrack.insertTimeRange(firstFiveSeconds,
of: videoTrack2, at: fiveSeconds)
キーポイント:
- シンセトラックの挿入は同期して実行できます。
AVMutableCompositionメモリ内のデータ構造です - ファイル I/O は関係せず、メインスレッドのブロックもありません
- これは、同期されたプロパティ アクセスを保持する数少ないクラスの 1 つです
ローカルのカスタム データ ソースを最適化する
(09:09)
let resourceLoader = asset.resourceLoader
resourceLoader.setDelegate(myDelegate, queue: myQueue)
// 在 delegate 中设置
// resourceLoader.entireLengthAvailableOnDemand = true
キーポイント:
AVAssetResourceLoaderアプリにデータ読み込みロジックをカスタマイズさせますentireLengthAvailableOnDemandデータがすぐに利用可能であることをシステムに伝えます- キャッシュメカニズムをスキップし、メモリ使用量を削減し、再生の起動を高速化します。
- 純粋にローカル データにのみ適用されます。ネットワーク データ ソースをオンにすると、再生が不安定になります。
重要ポイント
1.ビデオエディターのリアルタイムタイムラインプレビュー
- 手順: 動画編集アプリのタイムライン上で、マウスをホバーしたときに対応する時点のサムネイル プレビューを表示します。
- 実行する価値がある理由:
images(for:)非同期シーケンスは、タイムラインのサムネイルを生成するためのストリーミングに当然適しています。requestedTimeToleranceAfterユーザーがドラッグしたときにすぐに応答できるように、適切な許容値を設定します。 - 開始方法: を使用します
AVAssetImageGeneratorのimages(for:)一定間隔(1秒ごとなど)でサムネイルを生成し、保存する方法NSCache再利用
2.ローカル暗号化ビデオプレーヤー
- 対処方法: 教育アプリのオフライン コース パッケージなど、カスタム コンテナに保存されている暗号化されたビデオを再生する
- 実行する価値がある理由:
AVAssetResourceLoader+entireLengthAvailableOnDemand復号化と再生がシームレスになり、キャッシュをスキップした後、ローカル データの開始が速くなります。 - 開始方法: 実装
AVAssetResourceLoaderDelegate、存在するresourceLoader(_:shouldWaitForLoadingOfRequestedResource:)データを復号化して返し、設定しますentireLengthAvailableOnDemand = true
3.ビデオスプライシング/ミックスカットツール
- 対処方法: ユーザーが複数のビデオ クリップを選択し、それらを順番に 1 つのビデオに結合できるようにし、トランジション効果の追加をサポートします。
- 実行する価値がある理由: 非同期
insertTimeRangeそしてAVVideoComposition非同期構築手法により、マルチセグメントビデオ合成が UI に留まることはなくなり、合成結果をリアルタイムでプレビューできるようになります。 - 開始方法: 作成する
AVMutableComposition、非同期を使用するinsertTimeRange各フラグメントを挿入するには、次を使用しますAVVideoComposition.videoComposition(withPropertiesOf:)トランジションを生成するには、次を使用します。AVPlayerItemプレビュー
4.ビデオ情報クイックスキャンツール
- 手順: アルバムまたはフォルダー内のビデオをバッチスキャンして、再生時間、解像度、ビットレートなどのメタデータを抽出します。
- 実行する価値がある理由:
asset.load(.duration, .tracks)タイプセーフ API によりバッチ処理コードの信頼性が向上し、async/await により同時実行制御が容易になります。 - 開始方法: を使用します
TaskGroup複数のビデオのプロパティを同時に読み込むには、次を使用します。load(.formatDescriptions, .naturalSize)技術パラメータを取得する
関連セッション
- 非同期シーケンスについて — Swift 非同期シーケンスの仕組みと連携方法について詳しく学びます。
images(for:)使用 - SwiftUI での同時実行性の発見 — AVFoundation の非同期 API を結合して、SwiftUI で応答性の高いインターフェイスを構築します
- Live Text インタラクションをアプリに追加 — サムネイル生成と組み合わせて使用される、ビデオ フレーム上のオーバーレイ テキスト認識機能
- AVQT の新機能 — メディア処理を補完する AVFoundation ビデオ品質ツールの新機能
コメント
GitHub Issues · utterances