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:

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.