2022 Audio & Video
WWDC22 · 10 min · Audio & Video
Create a more responsive media app
Discover how you can use AVFoundation to keep people focused on your media app’s content — not your loading spinner. We’ll show you how to support a responsive and fluid interface in your app, all while you create rich audiovisual compositions, load audiovisual assets, and prepare media thumbnails. Find out how you can perform these tasks on your app’s main thread while I/O processes in parallel, learn how to get top-notch playback performance when loading data from custom storage, and more. To get the most out of this session, we recommend first watching "What’s new in AVFoundation” from WWDC21.
Watch at developer.apple.com ↗Code shown on screen · 7 snippets
Generate a thumbnail
func thumbnail() async throws -> UIImage {
let generator = AVAssetImageGenerator(asset: asset)
generator.requestedTimeToleranceBefore = .zero
generator.requestedTimeToleranceAfter = CMTime(seconds: 3, preferredTimescale: 600)
let thumbnail = try await generator.image(at: time).image
return UIImage(cgImage: thumbnail)
} Generate a series of thumbnails
func timelineThumbnails(for times: [CMTime]) async {
for await result in generator.images(for: times) {
switch result {
case .success(requestedTime: let requestedTime, image: let image, actualTime: _):
updateThumbnail(for: requestedTime, with: image)
case .failed(requestedTime: let requestedTime, error: _):
updateThumbnail(for: requestedTime, with: placeholder)
}
}
} Generate a series of thumbnails
func timelineThumbnails(for times: [CMTime]) async {
for await result in generator.images(for: times) {
updateThumbnail(for: result.requestedTime, with: (try? result.image) ?? placeholder)
}
} AVMutableComposition
let composition = AVMutableComposition()
try await composition.insertTimeRange(timeRange, of: asset, at: startTime) AVVideoComposition
let videoComposition = try await AVVideoComposition .videoComposition(withPropertiesOf: asset)
try await videoComposition.isValid(for: asset, timeRange: range, validationDelegate: delegate) Asset inspection
asset.loadValuesAsynchronously(forKeys: ["duration", "tracks"]) {
guard asset.statusOfValue(forKey: "duration", error: &error) == .loaded else { ... }
guard asset.statusOfValue(forKey: "tracks", error: &error) == .loaded else { ... }
myFunction(thatUses: asset.duration, and: asset.tracks)
}
let (duration, tracks) = try await asset.load(.duration, .tracks)
myFunction(thatUses: duration, and: tracks) Synchronously insert track segments into a composition
// videoTrack1: AVAssetTrack, videoTrack2: AVAssetTrack
// Create a composition and add an empty track
let composition = AVMutableComposition()
guard let compositionTrack = composition
.addMutableTrack(withMediaType: .video,
preferredTrackID: 1) else { return }
// Append the first 5 seconds of track 1
try compositionTrack
.insertTimeRange(firstFiveSeconds,
of: videoTrack1, at: .zero)
// Append the first 5 seconds of track 2
try compositionTrack
.insertTimeRange(firstFiveSeconds,
of: videoTrack2, at: fiveSeconds)
myFunction(thatUses: composition.duration,
and: composition.tracks) Resources
Related sessions
-
14 min -
17 min -
1h 1m