ビデオサムネイルは、フルビデオを再生することなく、ユーザーがすばやく視覚的なプレビューを提供する、現代のメディアリッチなアプリケーションのコーナーストーンです。 iOS開発では、AVFoundationは、サムネイル画像の抽出のための最も実行可能で柔軟なツールを提供します。 この記事では、ローカルおよびリモートファイル、キャッシュ、エラー処理、およびパフォーマンスチューニングを含む現実的なシナリオを処理することができるカスタムビデオサムネイルジェネレータを構築し、歩く。 SwiftとAVFoundationを使用して。

サムネイル生成におけるAVFoundationのロールを理解する

AVFoundationは、オーディオビジュアルメディアと連携するためのAppleの第一次フレームワークです。サムネイル生成のために、キークラスは]]AVAssetAVAssetImageGeneratorです。

  • [AVAsset]:タイムされた視聴覚アセット(ローカルまたはリモートビデオ)を表します。 追跡情報、期間、メタデータが含まれています。
  • [AVAssetImageGenerator:特定の時間間隔で、CGImageRefsをアセットから取得するためのメソッドを提供します。 ビデオの向きを尊重し、色のスペース調整を適用し、非同期のバルク生成をサポートしています。

これらのクラスを使用して、直接、設定オプションを隠すUIKitのショートカットとは異なり、画像サイズ、時間精度、および変換を完全に制御できます。

プロジェクトの設定

SwiftファイルへのAVFoundationをインポートして起動します。

import AVFoundation

また、生成された画像を表示するには UIKit が必要です。URL から を作成します。ローカルファイルの場合は、 を使用します。リモートビデオの場合は、 を使用し、アセットを初期化できるようにします。

// Local
let localURL = Bundle.main.url(forResource: "sample", withExtension: "mp4")!
let asset = AVAsset(url: localURL)

// Remote
let remoteURL = URL(string: "https://example.com/video.mp4")!
let remoteAsset = AVAsset(url: remoteURL)

単一のサムネイルを生成する

指定された時間にサムネイルを1つ生成するには、 を作成し、[ を呼び出します。この方法は同期であり、メインスレッドをオフにしてUIをブロックすることを避けるべきです。

let imageGenerator = AVAssetImageGenerator(asset: asset)
imageGenerator.appliesPreferredTrackTransform = true

let time = CMTime(seconds: 2.0, preferredTimescale: 600) // 2 seconds into the video

do {
 let cgImage = try imageGenerator.copyCGImage(at: time, actualTime: nil)
 let thumbnail = UIImage(cgImage: cgImage)
 // Update UI on main thread
 DispatchQueue.main.async {
 imageView.image = thumbnail
 }
} catch {
 print("Thumbnail generation failed: \(error.localizedDescription)")
}

[]:[]]]]を[]に設定すると、その結果の画像は、ビデオの回転メタデータ(ポートレート対ランド)を尊重します。 これなしで、直立したビデオは横に表示されることがあります。

正しい時間を選ぶ

サムネイルは、多くの場合、ビデオの先頭に撮影されますが、コンテンツを表す時間を使用して検討します。たとえば、10% 期間。 意味のある時間ポイントを取得するには、アセットの期間を尋ねます。

let duration = asset.duration
let midTime = CMTimeMultiplyByFloat64(duration, multiplier: 0.1)

エラーとエッジケースの処理

サムネイル生成は、いくつかの理由で失敗することができます。 常にエラーを優雅に処理し、フォールバックUIを提供します。

  • []無効なアセット:[]]]] は、この動画は破損したり、URLが到達不能な可能性があります。
  • []ビデオトラックをissing:[]]] 一部のアセットには音声のみが含まれています。 [] を確認してください。
  • ] 境界から時刻:[] の時刻が と資産の期間の間にあることを確認します。
  • ネットワークエラー:]リモートアセットがロードできないことがあります。サムネイルを生成する前にを使用してください。

アセットプロパティのプリロード例:

let assetKeys = ["tracks", "duration", "playable"]
asset.loadValuesAsynchronously(forKeys: assetKeys) {
 var error: NSError?
 let status = asset.statusOfValue(forKey: "tracks", error: &error)
 if status == .loaded {
 // Safe to generate thumbnails
 } else {
 // Handle error
 }
}

サムネイル出力をカスタマイズ

AVAssetImageGeneratorは、出力イメージを微調整するためにいくつかのプロパティを提供しています。

最大サイズ

生成された画像をを使用して特定の境界ボックスに制限します。これにより、メモリのオーバーヘッドを削減し、大規模なビデオの生成をスピードアップします。

imageGenerator.maximumSize = CGSize(width: 640, height: 480)

生成された画像は、アスペクト比を維持しながら、これらの寸法内で収まるようにスケールされます。

要求された時間の許容

デフォルトでは、[]は、要求された時間(または可能な限り近い)で正確なフレームを返します。 ]と]を設定することで、速度の精度を取引できます。 サムネイルの場合、0.5秒の許容度は、多くの場合、許容範囲と高速です。

imageGenerator.requestedTimeToleranceBefore = CMTime(seconds: 0.5, preferredTimescale: 600)
imageGenerator.requestedTimeToleranceAfter = CMTime(seconds: 0.5, preferredTimescale: 600)

絞りモード

は、生成された画像がクリーンな開き対生産の開きを処理する方法を制御します。 ] を使用して、エッジのほこりを取り除き、クリーナーのサムネイルを生成します。

imageGenerator.apertureMode = .cleanAperture

変革の応用

画像を回転または反転する必要がある場合(例えば、特定の方向のために)、コアグラフィックスが変換します。 1つのアプローチは、生の画像をキャプチャし、手動でCGAffineTransformを適用することです。 または、を[]]に設定し、資産の自然な方向を信頼することができます。

複数のサムネイルを効率的に生成

ビデオグリッド、タイムラインスクラブ、またはストーリーボードプレビューでは、しばしば何十ものサムネイルが必要です。メインスレッドのループでを呼び出すことを避けます。代わりに、非同期メソッドを使用します。それは、内部の並列を効果的に使用し、複数の時間要求を処理する。

var times: [NSValue] = []
for i in 0..<10 {
 let time = CMTime(seconds: Double(i) * 2.0, preferredTimescale: 600)
 times.append(NSValue(time: time))
}

imageGenerator.generateCGImagesAsynchronously(forTimes: times) { requestedTime, cgImage, actualTime, result, error in
 if result == .succeeded, let cgImage = cgImage {
 let thumbnail = UIImage(cgImage: cgImage)
 DispatchQueue.main.async {
 // Append to array or update collection view cell
 }
 } else {
 print("Failed at \(requestedTime): \(error?.localizedDescription ?? "unknown")")
 }
}

このメソッドはうまくスケールアップし、UIを増分的に更新できます。ユーザーが画面を離れると、画面をフリーリソースに残すと、を呼び出してください。

リモートビデオで作業

リモートサーバにビデオURLがポイントされると、アセットが完全にロードされるか、少なくとも利用可能なトラック情報が待ちつなければなりません。 繰り返しダウンロードを避けるために、適切なキャッシュヘッダーでを使用してください。

let urlAsset = AVURLAsset(url: remoteURL, options: [AVURLAssetPreferPreciseDurationAndTimingKey: true])
urlAsset.loadValuesAsynchronously(forKeys: ["tracks"]) {
 // Then create image generator and proceed
}

プロダクションアプリでは、同じビデオへのリピートされたアクセスがイメージを再取得しないため、ディスク上のサムネイルをキャッシュするのを検討してください。 ] またはビデオURLを使用してファイルベースのキャッシュをキーとして使用してください。

キャッシュ戦略例

class ThumbnailCache {
 static let shared = ThumbnailCache()
 private let cache = NSCache<NSString, UIImage>()

 func thumbnail(for url: URL) -> UIImage? {
 return cache.object(forKey: url.absoluteString as NSString)
 }

 func setThumbnail(_ image: UIImage, for url: URL) {
 cache.setObject(image, forKey: url.absoluteString as NSString)
 }
}

冗長作業を避けるために画像ジェネレーターと組み合わせてください。

パフォーマンスの考慮事項

サムネイルを生成することは、特に高解像度4KビデオのCPU - 集中操作です。 これらのベストプラクティスに従ってください:

  • 常にバックグラウンドキュー(例、)で同期呼び出しを実行します。
  • 出力イメージの寸法を小さくするために、合理的な[を設定してください。 320x240のサイズは、リストサムネイルに十分です。
  • 発電機が正確なデコードを解決するのではなく、最も近いフレームをスキップできるようにするために[[を使用します。
  • 視界が消えるときに、進展非同期生成をキャンセルします。
  • コレクションビューに表示するには、正確なセルサイズでサムネイルを生成し、メインスレッドで再サイズを避けます。

Swift Concurrencyとの統合(Async/Await)

アプリがiOS 15 +をターゲットにしている場合は、コードをクリーンな非同期/待ち受けるための継続でブロック呼び出しをラップできます。

func generateThumbnail(forVideoAt url: URL, at time: CMTime) async throws -> UIImage {
 let asset = AVAsset(url: url)
 let imageGenerator = AVAssetImageGenerator(asset: asset)
 imageGenerator.appliesPreferredTrackTransform = true
 imageGenerator.maximumSize = CGSize(width: 640, height: 480)

 return try await withCheckedThrowingContinuation { continuation in
 let times = [NSValue(time: time)]
 imageGenerator.generateCGImagesAsynchronously(forTimes: times) { _, cgImage, _, result, error in
 if let cgImage = cgImage, result == .succeeded {
 let image = UIImage(cgImage: cgImage)
 continuation.resume(returning: image)
 } else {
 continuation.resume(throwing: error ?? ThumbnailError.generationFailed)
 }
 }
 }
}

高度なカスタマイズ

ビデオ構成によるサムネイル

動画が(例:透かしオーバーレイ、色調整)を使用している場合は、画像ジェネレータに渡します。

imageGenerator.videoComposition = myVideoComposition

生成されたサムネイルは、合成された出力を反映します。

ライブ写真のサムネイルを生成する

ライブフォトは、ビデオと静止画の組み合わせです。 基礎的なビデオからサムネイルを抽出するには、を使用して、AVFoundationにそれを供給する前にビデオURLを要求します。

アクセシビリティとユーザーエクスペリエンス

サムネイルは視覚的なスキャンを強化しますが、常にボイスオーバーユーザーのためのテキストの代替手段を提供します。 画像ビューでを使用して、ビデオコンテンツを記述可能であれば。

imageView.isAccessibilityElement = true
imageView.accessibilityLabel = "Thumbnail of video: \(videoTitle)"

外部リソース

より深い理解のために、次の事項を相談して下さい:

コンテンツ

AVFoundation でカスタムビデオサムネイルジェネレータを作成すると、画像の品質、タイミング、およびパフォーマンスの自由が得られるようになります。 ] を同期的に、非同期に組み合わせて、エラーを処理し、結果をキャッシュすることで、iOS アプリでシームレスなユーザーエクスペリエンスを配信できます。 ローカルファイルやリモート動画を処理する場合でも、ここでカバーされた技術は、鮮明で文的なサムネイルを迅速かつ効率的に生成するのに役立ちます。