Table of Contents
Unghiile de deget sunt o piatra de temelie a aplicatiilor moderne de media bogata, oferind utilizatorilor o scurta previzualizare vizuala fara a fi nevoie sa joace video complet. In dezvoltarea iOS, AVFoundation ofera cele mai performante si flexibile instrumente pentru extragerea imaginilor miniaturale. Acest articol trece prin construirea unui generator personalizat de miniatura in miniatura video care poate gestiona scenarii din lumea reala . Inclusiv fisiere locale si la distanta, cache, manipularea erorilor si performanta de tuning Swift si AVFoundation.
Înțelegerea AVFoundation
AVFoundation este cadrul primar al Apple pentru lucrul cu mass-media audiovizuală. Pentru generarea unghiilor, clasele cheie sunt AVAsset și AVAssetImageGenerator.
- AVAsset: Reprezintă un activ audiovizual cronometrat (un video local sau de la distanță). Acesta conține informații despre cale, durată și metadate.
- AVASSsetImageGenerator: Oferă metode pentru obținerea CGIMageRefs de la activ la intervale de timp specifice. Respectă orientarea video, aplică ajustări de culoare ale spațiului și suportă generarea de vrac asincronă.
Folosind aceste clase, vă oferă direct controlul complet asupra dimensiunii imaginii, preciziei timpului și transformării, spre deosebire de comenzile rapide UIKit care ascund adesea opțiunile de configurare.
Configurarea proiectului
Începeți prin importul AVFoundation în fișierul dvs. Swift:
import AVFoundation
De asemenea, aveţi nevoie de UIKit pentru a afişa imaginile generate. Creaţi un dintr-un URL. Pentru fişierele locale, folosiţi . Pentru videoclipurile de la distanţă, utilizaţi şi asiguraţi-vă că activ poate fi iniţializat:
// 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)
Generarea unei singure unghii
Pentru a genera o miniatură la un moment dat, creați un și apel . Această metodă este sincronă și trebuie să fie rulat de pe fir principal pentru a evita blocarea 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)")
}
Important: Setarea către asigură imaginea rezultată respectă metadatele de rotație video (portret vs. peisaj). Fără aceasta, un videoclip vertical ar putea apărea lateral.
Alegerea momentului potrivit
Unghiile de deget sunt adesea luate la începutul unui video, dar ia în considerare folosind un timp care reprezintă conținutul . ex. 10% în durată. Pentru a obține un punct de timp semnificativ, interoga durata activului:
let duration = asset.duration
let midTime = CMTimeMultiplyByFloat64(duration, multiplier: 0.1)
Erorile de manipulare și cazurile de evitare
Generarea unghiilor poate eșua din mai multe motive. Întotdeauna se ocupe de erori cu grație și să ofere IU de rezervă.
- Activ invalid: Videoclipul poate fi corupt sau URL-ul inaccesibil.
- Lipsa de cale video: Unele active conțin numai audio; verificați .
- Timpul de ieșire din limite: Asigurați-vă că timpul solicitat este între și durata activelor.
- Erori de rețea: Activele la distanță pot să nu se încarce.Use înainte de a genera miniaturi.
Exemplu de proprietăți ale activelor preîncărcate:
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
}
}
Personalizarea ieșirii miniaturii
AVAssetImageGenerator oferă mai multe proprietăți pentru a regla imaginea de ieșire.
Dimensiune maximă
Restrânge imaginea generată la o anumită cutie de legare folosind . Aceasta reduce memoria deasupra capului și accelerează generarea de videoclipuri mari.
imageGenerator.maximumSize = CGSize(width: 640, height: 480)
Imaginea generată va fi scalată pentru a se potrivi în aceste dimensiuni, păstrând în același timp raportul de aspect.
Toleranță la timp solicitată
In mod implicit, returneaza cadrul exact la ora solicitata (sau cat mai aproape posibil). Puteti schimba precizia pentru viteza prin setarea si . Pentru miniaturi, o toleranta de 0,5 secunde este adesea acceptabila si mai rapida:
imageGenerator.requestedTimeToleranceBefore = CMTime(seconds: 0.5, preferredTimescale: 600)
imageGenerator.requestedTimeToleranceAfter = CMTime(seconds: 0.5, preferredTimescale: 600)
Mod de apertură
controlează modul în care imaginea generată mânuiește deschiderea curată față de deschiderea producției. Utilizați ] pentru a elimina praful de margine și a produce o miniatură mai curată:
imageGenerator.apertureMode = .cleanAperture
Aplicarea transformărilor
Dacă trebuie să rotiți sau să răsturnați imaginea (de exemplu, pentru o anumită orientare), utilizați transformarea graficii de bază. O abordare este de a captura imaginea brută și apoi de a aplica manual o CGAffineTransform. Alternativ, puteți seta la (după cum se arată) și să încredere în orientarea naturală a activului.
Generarea de mai multe unghii de deget eficient
Pentru reţele video, spălare cronologie, sau previzualizare storyboard, aveţi nevoie de multe ori de zeci de miniaturi. Evitaţi apel într-o buclă pe fir principal. În schimb, utilizaţi metoda asincronă . Procesează multiple cereri de timp eficient, pârghie paralelism intern.
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")")
}
}
Această metodă este bună și vă permite să actualizați IU treptat. Amintiți-vă să apelați dacă utilizatorul lasă ecranul la resurse gratuite.
Lucrul cu video la distanță
Când URL-ul video indică către un server de la distanță, trebuie să așteptați ca activul să fie complet încărcat sau cel puțin informațiile sale de cale să fie disponibile. Utilizați ] cu antetele de cache adecvate pentru a evita descărcările repetate.
let urlAsset = AVURLAsset(url: remoteURL, options: [AVURLAssetPreferPreciseDurationAndTimingKey: true])
urlAsset.loadValuesAsynchronously(forKeys: ["tracks"]) {
// Then create image generator and proceed
}
Pentru aplicații de producție, ia în considerare cache-uri pe disc, astfel încât accesul repetat la același video nu re-fetth imaginea. Utilizați sau cache-ul pe bază de fișier folosind URL-ul video ca o cheie.
Exemplu de strategie de 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)
}
}
Combină asta cu generatorul de imagine pentru a evita munca redundantă.
Considerații privind performanța
Generarea miniaturilor este o operatie intensiva de procesor, in special pentru videoclipuri 4K de înaltă rezolutie. Urmati aceste bune practici:
- Întotdeauna efectuați apeluri sincrone pe o coadă de fundal (de exemplu, ].
- Setează o imagine rezonabilă pentru a reduce dimensiunile imaginii de ieșire. O dimensiune de 320x240 este adesea suficientă pentru miniatura listei.
- Utilizați ] pentru a permite generatorului să sară peste cel mai apropiat cadru în loc de rezolvarea decodorului exact.
- Anulează orice generaţie asincronă în curs de dezvoltare când vederea dispare.
- Pentru afișare într-o vizualizare de colecție, genera unghii la dimensiunea exactă a celulei și pentru a evita redimensionarea pe fir principal.
Integrarea cu Swift Convailment (Async/Await)
Dacă aplicaţia ta vizează iOS 15+, poţi încheia apelul de blocare într-o continuare pentru codul Async/Wait curat:
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)
}
}
}
}
Personalizări avansate
Unghii cu compozitie video
Dacă videoclipul utilizează un (de exemplu, suprapusul filigranului, ajustările de culoare), îl puteți trece la generatorul de imagine:
imageGenerator.videoComposition = myVideoComposition
Unghia degetului va reflecta rezultatul compozit.
Generarea de miniaturi pentru fotografii live
Pentru a extrage o miniatură din videoclipul de bază, utilizați și solicitați un URL video înainte de a-l alimenta în AVFoundation.
Accesibilitate și experiență de utilizare
Unghiile de deget îmbunătăţesc scanarea vizuală, dar oferă întotdeauna o alternativă de text pentru utilizatorii VoceOver. Utilizaţi ] pe vizualizarea imaginii, descriind conţinutul video, dacă este posibil.
imageView.isAccessibilityElement = true
imageView.accessibilityLabel = "Thumbnail of video: \(videoTitle)"
Resurse externe
Pentru o înţelegere mai profundă, consultaţi următoarele:
- AVASSsetImageGenerator Documentaţie oficială
- AvAsset Reference
- WDC 2019: AVFoundation Performance Tips
Concluzie
Construirea unui generator personalizat de miniatură video cu AVFoundation vă oferă libertate completă asupra calității imaginii, a calendarului și a performanței. Prin pârghie atât sincron, cât și asincronic, erori de manipulare și rezultate de caching, puteți oferi o experiență de utilizare fără probleme în aplicația dvs. iOS. Fie că procesați fișiere locale sau videouri la distanță, tehnicile acoperite aici vă vor ajuta să produceți rapid și eficient miniaturi clare, context-aware.