Civil &: строительная инженерия
Создание пользовательского генератора миниатюр видео в Ios с Avfoundation
Table of Contents
Видео миниатюры являются краеугольным камнем современных мультимедийных приложений, предлагая пользователям быстрый визуальный предварительный просмотр без необходимости проигрывать полное видео. В разработке iOS AVFoundation предоставляет наиболее эффективные и гибкие инструменты для извлечения миниатюрных изображений. Эта статья проходит через создание пользовательского генератора миниатюр видео, который может обрабатывать реальные сценарии, включая локальные и удаленные файлы, кэширование, обработку ошибок и настройку производительности, используя Swift и AVFoundation.
Понимание роли AVFoundation в поколении миниатюр
AVFoundation является основной структурой Apple для работы с аудиовизуальными носителями. Для миниатюрной генерации ключевыми классами являются AVAsset и AVAssetImageGenerator.
- AVAsset: представляет собой временной аудиовизуальный актив (локальное или удаленное видео). Он содержит информацию о треке, продолжительность и метаданные.
- AVAssetImageGenerator: Предоставляет способы получения CGImageRefs из актива через определённые промежутки времени. Он уважает ориентацию видео, применяет корректировки цветового пространства и поддерживает асинхронную генерацию объёмов.
Использование этих классов напрямую дает вам полный контроль над размером изображения, точностью времени и преобразованием, в отличие от ярлыков UIKit, которые часто скрывают параметры конфигурации.
Настройка проекта
Начните с импорта AVFoundation в ваш файл Swift:
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)
Создание одного миниатюрного
Чтобы создать один миниатюрный элемент в определенное время, создайте и вызовите . Этот метод синхронен и должен быть запущен из основного потока, чтобы избежать блокировки пользовательского интерфейса.
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)
Обработка ошибок и Edge Cases
Генерация миниатюр может потерпеть неудачу по нескольким причинам. Всегда исправляйте ошибки и обеспечивайте резервный пользовательский интерфейс.
- Недействительный актив: Видео может быть повреждено или 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
}
}
Настройка миниатюры Output
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
Применение трансформаций
Если вам нужно повернуть или перевернуть изображение (например, для определенной ориентации), используйте преобразования Core Graphics. Один из подходов заключается в захвате необработанного изображения, а затем примените 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")")
}
}
Этот метод хорошо масштабируется и позволяет постепенно обновлять пользовательский интерфейс. Не забудьте позвонить , если пользователь покидает экран, чтобы освободить ресурсы.
Работа с удаленными видео
Когда 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. Следуйте этим лучшим практикам:
- Всегда выполняйте синхронные вызовы в фоновой очереди (например, ).
- Установите разумный для уменьшения выходных размеров изображения. Размер 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
Сгенерированный миниатюрный рисунок будет отражать композитный выход.
Создание миниатюр для живых фотографий
Live Photos - это комбинация видео и неподвижного изображения. Чтобы извлечь миниатюру из базового видео, используйте и запросите URL-адрес видео, прежде чем подавать его в AVFoundation.
Доступность и пользовательский опыт
Миниатюры улучшают визуальное сканирование, но всегда предоставляют текстовую альтернативу для пользователей VoiceOver. Используйте на просмотре изображения, описывая видеоконтент, если это возможно.
imageView.isAccessibilityElement = true
imageView.accessibilityLabel = "Thumbnail of video: \(videoTitle)"
Внешние ресурсы
Для более глубокого понимания, проконсультируйтесь со следующим:
- AVAssetImageGenerator Официальная документация
- Для сравнения:
- WWDC 2019: AVFoundation Performance Tips
Заключение
Создание пользовательского генератора миниатюр видео с AVFoundation дает вам полную свободу в отношении качества изображения, времени и производительности. Используя как синхронно, так и асинхронно, обработку ошибок и результаты кэширования, вы можете обеспечить бесшовный пользовательский опыт в вашем приложении iOS. Независимо от того, обрабатываете ли вы локальные файлы или удаленные видео, методы, описанные здесь, помогут вам быстро и эффективно создавать четкие, контекстно-осведомленные миниатюры.