אגודל וידאו הם אבן הפינה של יישומים מודרניים מדיה עשיר, המציע למשתמשים תצוגה מקדימה חזותית מהירה מבלי צורך לנגן וידאו מלא.בפיתוח iOS, AVFoundation מספק את הכלים הביצועים וגמישים ביותר עבור תמצית תמונות אגודל. מאמר זה הולך דרך בניית גנרטור אגודל וידאו מותאם אישית שיכול להתמודד עם תרחישים בעולם האמיתי - כולל קבצים מקומיים ומרוחקים, סגסוגת, טיפול, ביצועים - ועידודה.

הבנת תפקידה של AVFoundation בדור Thumbnail

ההקמה של אפל היא המסגרת העיקרית של העבודה עם תקשורת אודיו-ויזואלית.לדור אגודל, המעמדות העיקריים הם FLT:0AVAssetFLT:1 ו-FLT:2AVAsset ImageGenerator (ראה: 3 בינואר)

  • (ב) [ה]: [ה], [ה], [ה],], הוא מייצג נכס אודיו-ויזואלי (וידאו מקומי או מרוחק).
  • (FLT:0)AVAsset ImageGenerator:1אנדר: מספק שיטות להשיג CG ImageRefs מהנכס במרווחי זמן ספציפיים.זה מכבד אוריינטציה וידאו, חל על התאמות חלל צבע, ותומכת בדור גדול סינכרוני.

שימוש בכיתות אלה נותן לך שליטה מלאה על גודל התמונה, דיוק זמן, וטרנספורמציה, בניגוד קיצורי דרך UIKit כי לעתים קרובות להסתיר אפשרויות תצורה.

הקמת הפרויקט

התחל על ידי ייבוא AVFoundation לתוך קובץ Swift שלך:

import AVFoundation

כמו כן, עליך להציג את התמונות שנוצרו על ידי יצירת ההרחבה של ה-FLT:1 מתוך כתובת URL. עבור קבצים מקומיים, להשתמש ב-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)

יצירת יחיד

כדי ליצור אגודל אחד בזמן מוגדר, ליצור AFLT: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)

טעויות ודברים

דור Thumbnail יכול להיכשל מסיבות רבות.תמיד לטפל שגיאות בחסד ולספק נפילה UI.

  • (ב) ניתן להשחית (ב) לפסוקים: (ב) ל[[1924]], או ל[[1924]], [[1924]], [[1924]], [[1924]]]]
  • (ב) ,0) ,מסלול וידאו: 1FLT: חלק מהנכסים מכילים רק אודיו; לבדוק את ה-FLT:11 .
  • (ב) ,0) זמן מתוך גבולות: 1FLT: 1 ודא שהזמן המבוקש הוא בין השנים (FLT:12) לבין משך הנכס.
  • (ב) ,0) שגיאות: נכסים מרוחקים עלולים לא לטעון לשימוש (FLT:13 לפני יצירת אגודל).

דוגמה להורדת נכסים:

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
 }
}

שם הסרטון: Thumbnail Output

AVAsset ImageGenerator מציע כמה תכונות כדי לתקן את התמונה של הפלט.

גודל מקסימלי

הגבלת התמונה הנבנית לקופסא מסוימת של קידוד באמצעות ההרחבה של 15: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)

מצב Aperture

(ה-FLT:21) שולט כיצד הדימוי שנוצר מטפל ביצור נקי לעומת ייצור של אמפרציה (FLT:22) כדי להסיר אבק קצה לייצר אצבע נקייה:

imageGenerator.apertureMode = .cleanAperture

החלת הטרנספורמציות

אם אתה צריך לסובב או להזיז את התמונה (למשל, עבור אוריינטציה מסוימת), להשתמש בגרפיקה הליבה משתנה.גישה אחת היא ללכוד את התמונה הגולמית ולאחר מכן ליישם CGAffine Transform באופן ידני.

יצירת מספר רב של Thumbnails Efficiently

עבור רשתות וידאו, ציר זמן פעוט, או תצוגה מקדימה של לוח התכנון, לעתים קרובות צריך עשרות אלקונאליות. להימנע מלקראת FLT:26 בלולאה על החוט הראשי. במקום, להשתמש בשיטה סינכרונית (FLT:27 זה תהליכים מרובים דורש ביעילות, מינוף מקבילות פנימית.

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
}

עבור יישומי ייצור, לשקול אגודל צ'נג על הדיסק כך גישה חוזרת לאותו וידאו לא מחזירה את התמונה. השתמש ב-FLT:32 או cache מבוסס קובץ באמצעות כתובת ה-URL של וידאו כמפתח.

דוגמה ל- Caching Strategy

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-intensive, במיוחד עבור קטעי וידאו ברזולוציה גבוהה 4K. לעקוב אחר שיטות אלה הטובות ביותר:

  • תמיד לבצע שיחות סינכרוניות על תור רקע (למשל, ; 34).
  • הגדר גרף סביר (FLT:35) כדי להפחית את גודל התמונה של הפלט.גודל של 320x240 הוא לעתים קרובות מספיק עבור אגודל רשימה.
  • השתמש ב-FLT:36 כדי לאפשר גנרטור לדלג על המסגרת הקרובה ביותר במקום לפתור את הקוד המדויק.
  • לבטל כל דור סינכרוני כאשר הנוף נעלם.
  • עבור תצוגה בתצוגת אוסף, ליצור אגודל בגודל התא המדויק ולהימנע מחידוש על החוט הראשי.

היכרות עם 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)
 }
 }
 }
}

התאמה מתקדמת

וידאו: Thumbnails with Video

אם הסרטון שלך משתמש ב-FLT:38 (למשל, מסיימת מים, התאמות צבע), באפשרותך להעביר אותו למכונת הצילום:

imageGenerator.videoComposition = myVideoComposition

האגודל שנוצר ישקף את הפלט המורכב.

יצירת Thumbnails for Live Photos

תמונות חיות הן שילוב של וידאו ותמונות עדיין.כדי להוציא אגודל מהסרטון הבסיסי, להשתמש ב-FLT:40 ולבקש כתובת וידאו לפני האכלה אותו ל- AVFoundation.

נגישות וחווית המשתמש

תמבנאל משפר את סריקת הראייה, אך תמיד מספק חלופה טקסט עבור משתמשי VoiceOver. השתמש ב-FLT:41 על תמונת התמונה, המתאר את תוכן הוידאו במידת האפשר.

imageView.isAccessibilityElement = true
imageView.accessibilityLabel = "Thumbnail of video: \(videoTitle)"

משאבים חיצוניים

להבנה עמוקה יותר, להתייעץ עם הדברים הבאים:

  • (ב) ,0) ,avasset ImageGenerator
  • (ב) ויקרא י"ד:
  • (FLT:0)WDC 2019: AVFoundation Performance Tipsigation Performance Tipsigation)

מסקנה

בניית גנרטור וידאו מותאם אישית עם AVFoundation נותן לך חופש מוחלט על איכות תמונה, תזמון וביצועים. על ידי מינוף FLT:43 הן סינכרוני ו מסונכרן, טיפול שגיאות, תוצאות caching, אתה יכול לספק חוויית משתמש חלקה ביישום iOS שלך.אם אתה מעבד קבצים מקומיים או קטעי וידאו מרוחקים, הטכניקות מכוסה כאן יעזור לך לייצר פריך, הקשר ביעילות ומודע במהירות.