Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

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 ↗

Transcript all transcripts

Code shown on screen · 7 snippets

Generate a thumbnail swift · at 1:41 ↗
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 swift · at 2:56 ↗
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 swift · at 3:49 ↗
func timelineThumbnails(for times: [CMTime]) async {
    for await result in generator.images(for: times) {
        updateThumbnail(for: result.requestedTime, with: (try? result.image) ?? placeholder)
    }
}
AVMutableComposition swift · at 4:40 ↗
let composition = AVMutableComposition()
try await composition.insertTimeRange(timeRange, of: asset, at: startTime)
AVVideoComposition swift · at 4:57 ↗
let videoComposition = try await AVVideoComposition .videoComposition(withPropertiesOf: asset)

try await videoComposition.isValid(for: asset, timeRange: range, validationDelegate: delegate)
Asset inspection swift · at 5:33 ↗
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 swift · at 7:06 ↗
// 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