Table of Contents
视频缩略图是现代媒体丰富应用的基石,为用户提供了快速的视觉预览,而无需播放完整视频. 在iOS开发中, AVFoundation提供了最能提取缩略图图像的性能和灵活的工具. 本文走过自定义视频缩略图生成器,可以处理真实世界的情景——包括本地和远程文件,缓存,错误处理,以及性能调谐——使用Swift和AVoundation.
理解基金会在缩略图生成中的作用
AVFoundation是苹果公司与视听媒体合作的主要框架。对于缩略图生成,关键类是 AVASSet 和[ AVASSetImageGenerator[。
- AVASet: 代表一个定时视听资产(本地或远程视频),包含音轨信息,持续时间和元数据.
- AVASetImageGenerator:提供在特定时间间隔从资产中获取CGImage Refers的方法,它尊重视频导向,应用色彩空间调整,并支持同步批量生成.
使用这些类直接使您完全控制图像大小,时间准确度,以及变换,与UIKit快捷键不同,后者经常隐藏配置选项.
建立项目
从将 AVFoundation 导入到您的 Swift 文件开始 :
import AVFoundation
您还需要 UIKit 来显示生成的图像。 从 URL 创建 [[FLT: 1]] 。 对于本地文件, 请使用 [[FLT: 2]] 。 对于远程视频, 请使用 [[FLT: 3] 并确保资产可以初始化 :
// 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)
生成一个单缩略图
要在指定时间生成一个缩略图, 请创建 [[FLT: 5]] 并呼叫 [[FLT: 6]]。 这种方法是同步的, 应该运行在主线上, 以避免阻断 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无法访问.
- 错过视频音轨:[ 有些资产只包含音频;检查]].
- 超出范围的时间: 确保所要求的时间在之间,以及资产的期限。
- 网络错误: 远程资产可能无法装入。 在生成缩略图之前使用 。
预装资产属性示例:
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
}
}
自定义缩略图输出
AVASetImageGenerator提供了多个属性来微调输出图像.
最大大小
将生成的图像限制在使用 [[FLT: 15]] 的指定边框内。 这会减少内存的超高, 并加快大型视频的生成 。
imageGenerator.maximumSize = CGSize(width: 640, height: 480)
生成的图像将按比例调整,以适应这些维度,同时保持宽度比。
请求时间容忍
默认情况下, [[FLT: 17] ] 在请求的时间返回精确的框架( 或尽可能接近) 。 您可以通过设置 [[FLT: 18] 和 [[FLT: 19]] 来交换速度的精确度 。 对于缩略图, 0. 5 秒的容积通常可以接受, 速度更快 :
imageGenerator.requestedTimeToleranceBefore = CMTime(seconds: 0.5, preferredTimescale: 600)
imageGenerator.requestedTimeToleranceAfter = CMTime(seconds: 0.5, preferredTimescale: 600)
孔径模式
]控制生成的图像如何处理干净的孔径对生产孔径。使用[去除边缘尘埃并生成更干净的缩略图:
imageGenerator.apertureMode = .cleanAperture
应用转换
如果您需要旋转或翻转图像(例如,对于特定方向),请使用 Core 图形转换。一种方法是捕捉原始图像,然后手动应用 CGAffine Transform 。 或者,您可以将 设置为 (如所示) 并信任资产的自然方向。
高效生成多缩略图
对于视频网格,时间线擦除,或故事板预览,您通常需要数十个缩略图。避免在主线程的循环中调用 。而是使用同步方法。它高效地处理多个时间请求,利用内部平行性。
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 。 如果用户离开屏幕释放资源, 请记住调用 [[FLT: 29] 。
与远程视频合作
当视频 URL 指向远程服务器时, 您必须等待资产被装满或至少可以获取其音轨信息。 使用 [[FLT: 30] ] 适当的缓存头以避免重复下载 。
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)
}
}
将此与图像生成器合并,以避免冗余工作.
业绩考量
生成缩略图是一个CPU的密集操作, 特别是高分辨率的 4K 视频。 遵循这些最佳做法 :
- 总是在背景队列上执行同步调用(例如]).
- 设置一个合理的 [[FLT: 35]] 来减小输出图像尺寸。 大小为 320x240 的缩略图通常足够 。
- 使用 [[FLT: 36]]] ,使生成器可以跳过最近的帧,而不是解码精确.
- 当视图消失时, 取消任何同步生成的不同步 。
- 对于收藏视图中的显示,生成精确单元格大小的缩略图,避免在主线程上重定大小.
与斯威夫特货币(Async/Await)的结合
如果您的应用目标为iOS 15+, 您可以将封杀的呼叫包成一个连续的 干净的 Async/ 等待代码 :
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
生成的缩略图将反映复合输出.
生成活照片缩略图
Live Photos 是视频和静态图像的组合。要从基本视频中提取缩略图,请使用 ] ,并在输入AVFoundation前请求视频URL。
无障碍和用户经验
缩略图增强视觉扫描, 但总是为 VoiceOver 用户提供文本替代。 在图像视图上使用 ], 尽可能描述视频内容 。
imageView.isAccessibilityElement = true
imageView.accessibilityLabel = "Thumbnail of video: \(videoTitle)"
外部资源
为了加深理解,请参考以下内容:
结论
使用 AVFoundation 创建自定义视频缩略图生成器, 使您在图像质量、 时间和性能方面完全自由。 通过同步和同步的[] , 处理错误和缓存结果, 您可以在您的 iOS 应用程序中提供无缝用户体验。 无论您正在处理本地文件还是远程视频, 覆盖的技术都会帮助您快速高效地制作简洁的, 上下文中的缩略图 。