Génie civil & structural
Création d'un générateur de vignette vidéo personnalisé en Ios avec Avfoundation
Table of Contents
Les vignettes vidéo sont une pierre angulaire des applications modernes riches en médias, offrant aux utilisateurs un aperçu visuel rapide sans avoir à lire la vidéo complète. Dans le développement iOS, AVFoundation fournit les outils les plus performants et flexibles pour extraire des images miniatures. Cet article se déplace dans la construction d'un générateur de vignettes vidéo personnalisé qui peut gérer des scénarios du monde réel – y compris les fichiers locaux et distants, la mise en cache, la manipulation des erreurs et le réglage des performances – en utilisant Swift et AVFoundation.
Comprendre le rôle de la Fondation AV dans la génération de vignettes
AVFoundation est le cadre primaire d'Apple pour travailler avec les médias audiovisuels. Pour la génération de vignettes, les classes clés sont AVAsset et AVAssetImageGenerator.
- AVAsset: Représente un actif audiovisuel chronométré (une vidéo locale ou distante) qui contient des informations sur les pistes, la durée et les métadonnées.
- AVAssetImageGenerator: Fournit des méthodes pour obtenir CGImageRefs de l'actif à des intervalles de temps précis. Il respecte l'orientation vidéo, applique des ajustements d'espace de couleur, et supporte la génération de vrac asynchrone.
L'utilisation de ces classes vous donne directement le contrôle total de la taille de l'image, de la précision du temps et de la transformation, contrairement aux raccourcis UIKit qui masquent souvent les options de configuration.
Mise en place du projet
Commencez par importer AVFoundation dans votre fichier Swift :
import AVFoundation
Vous avez également besoin d'UIKit pour afficher les images générées. Créez un à partir d'une URL. Pour les fichiers locaux, utilisez . Pour les vidéos distantes, utilisez et assurez-vous que l'actif peut être initialisé :
// 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)
Générer un seul pouce
Pour générer une vignette à un moment donné, créez un et appelez . Cette méthode est synchrone et devrait être lancée hors du thread principal pour éviter de bloquer l'interface utilisateur.
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)")
}
Important: Le réglage à permet de s'assurer que l'image obtenue respecte les métadonnées de rotation de la vidéo (portrait vs paysage).
Choisir le bon moment
Les vignettes sont souvent prises au début d'une vidéo, mais envisager d'utiliser un temps qui représente le contenu – par exemple, 10% dans la durée. Pour obtenir un point de temps significatif, demandez la durée de l'actif:
let duration = asset.duration
let midTime = CMTimeMultiplyByFloat64(duration, multiplier: 0.1)
Traitement des erreurs et des cas d'Edge
La génération de vignettes peut échouer pour plusieurs raisons. Toujours gérer les erreurs gracieusement et fournir l'interface utilisateur de repli.
- Actif invalide: La vidéo peut être corrompue ou l'URL inaccessible.
- Station vidéo manquante:[ Certains actifs ne contiennent que du son; vérifiez .
- Délai hors limites: S'assurer que le délai demandé est entre et la durée de l'actif.
- Erreurs réseau: Les actifs distants peuvent ne pas être chargés. Utilisez avant de générer des vignettes.
Exemple de préchargement des biens :
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
}
}
Personnalisation de la sortie des vignettes
AVAssetImageGenerator offre plusieurs propriétés pour affiner l'image de sortie.
Taille maximale
Limiter l'image générée à une boîte de délimitation spécifique en utilisant , ce qui réduit les frais généraux de mémoire et accélère la génération pour les grandes vidéos.
imageGenerator.maximumSize = CGSize(width: 640, height: 480)
L'image générée sera mise à l'échelle pour s'adapter à ces dimensions tout en préservant le rapport d'aspect.
Tolérance du temps demandé
Par défaut, retourne le cadre exact au moment demandé (ou aussi près que possible). Vous pouvez échanger la précision de vitesse en réglant et . Pour les vignettes, une tolérance de 0,5 secondes est souvent acceptable et plus rapide :
imageGenerator.requestedTimeToleranceBefore = CMTime(seconds: 0.5, preferredTimescale: 600)
imageGenerator.requestedTimeToleranceAfter = CMTime(seconds: 0.5, preferredTimescale: 600)
Mode d'ouverture
contrôle la façon dont l'image générée gère l'ouverture propre par rapport à l'ouverture de production. Utilisez pour enlever la poussière de bord et produire une vignette plus propre:
imageGenerator.apertureMode = .cleanAperture
Application des transformations
Si vous devez tourner ou retourner l'image (p. ex. pour une orientation spécifique), utilisez Core Graphics transforms. Une approche consiste à capturer l'image brute et ensuite appliquer manuellement une CGaffineTransform. Vous pouvez aussi définir à (comme indiqué) et faire confiance à l'orientation naturelle de l'actif.
Générer efficacement plusieurs vignettes
Pour les grilles vidéo, les prévisualisations de la timeline ou des storyboards, il faut souvent des dizaines de vignettes. Évitez d'appeler dans une boucle sur le thread principal. Utilisez plutôt la méthode asynchrone . Elle traite efficacement les demandes de temps multiples, en tirant parti du parallélisme interne.
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")")
}
}
Cette méthode s'évalue bien et vous permet de mettre à jour l'interface utilisateur de manière progressive. N'oubliez pas d'appeler si l'utilisateur quitte l'écran pour des ressources libres.
Travailler avec des vidéos distantes
Lorsque l'URL vidéo pointe vers un serveur distant, vous devez attendre que l'actif soit complètement chargé ou au moins que ses informations de piste soient disponibles. Utilisez avec les en-têtes de cache appropriés pour éviter les téléchargements répétés.
let urlAsset = AVURLAsset(url: remoteURL, options: [AVURLAssetPreferPreciseDurationAndTimingKey: true])
urlAsset.loadValuesAsynchronously(forKeys: ["tracks"]) {
// Then create image generator and proceed
}
Pour les applications de production, envisagez de mettre en cache des vignettes sur disque de sorte que l'accès répété à la même vidéo ne ré-fetch l'image. Utilisez ou cache basé sur des fichiers en utilisant l'URL vidéo comme clé.
Exemple de stratégie de mise en cache
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)
}
}
Combinez ceci avec le générateur d'image pour éviter les travaux redondants.
Considérations relatives aux performances
Générer des vignettes est une opération intensive du CPU, surtout pour les vidéos 4K haute résolution. Suivez ces meilleures pratiques :
- Effectuez toujours des appels synchrones sur une file d'attente de fond (p. ex. .
- Définir un raisonnable pour réduire les dimensions de sortie de l'image. Une taille de 320x240 est souvent suffisante pour les vignettes de liste.
- Utilisez pour permettre au générateur de sauter le cadre le plus proche au lieu de résoudre le décodage exact.
- Annuler toute génération en cours d'avancement asynchrone lorsque la vue disparaît.
- Pour l'affichage dans une vue de collection, générer des vignettes à la taille exacte de la cellule et éviter de redimensionner le fil principal.
Intégration avec la comptabilisation rapide (Async/Await)
Si votre application cible iOS 15+, vous pouvez enrouler l'appel de blocage dans une suite pour un code async/attendu propre:
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)
}
}
}
}
Personnalisations avancées
Miniatures avec composition vidéo
Si votre vidéo utilise un (par exemple, superposition de filigrane, réglages de couleur), vous pouvez le transmettre au générateur d'image:
imageGenerator.videoComposition = myVideoComposition
La vignette générée reflétera la sortie composite.
Générer des vignettes pour des photos en direct
Live Photos est une combinaison d'une vidéo et d'une image fixe. Pour extraire une vignette de la vidéo sous-jacente, utilisez le et demandez une URL vidéo avant de la alimenter en AVFoundation.
Accessibilité et expérience utilisateur
Les vignettes améliorent la numérisation visuelle, mais offrent toujours une alternative textuelle aux utilisateurs de VoiceOver. Utilisez le sur la vue d'image, décrivant le contenu vidéo si possible.
imageView.isAccessibilityElement = true
imageView.accessibilityLabel = "Thumbnail of video: \(videoTitle)"
Ressources extérieures
Pour une compréhension plus approfondie, consultez les documents suivants :
- AVAssetImageGenerator Documentation officielle
- Référence de l'AVAsset
- WWDC 2019: Conseils sur les performances de la Fondation AV
Conclusion
En tirant parti de à la fois de manière synchrone et asynchrone, en manipulant les erreurs et en enregistrant les résultats, vous pouvez offrir une expérience utilisateur transparente dans votre application iOS. Que vous traitiez des fichiers locaux ou des vidéos à distance, les techniques décrites ici vous aideront à produire des vignettes croustillantes et contextuelles rapidement et efficacement.