AVFoundation Programming Guide – Swift – Using Assets

Using Assets

Assets can come from a file or from media in the user’s iPod library or Photo library. When you create an asset object all the information that you might want to retrieve for that item is not immediately available. Once you have a movie asset, you can extract still images from it, transcode it to another format, or trim the contents.‌

Creating an Asset Object

To create an asset to represent any resource that you can identify using a URL, you use AVURLAsset. The simplest case is creating an asset from a file:

//A URL that identifies an audiovisual asset such as a movie file
let url = Bundle.main.url(forResource: "test", withExtension: "mp4")
let anAsset = AVURLAsset(url: url!, options: nil)

Options for Initializing an Asset

The AVURLAsset initialization methods take as their second argument an options dictionary. The only key used in the dictionary is AVURLAssetPreferPreciseDurationAndTimingKey. The corresponding value is a Boolean (contained in an NSValue object) that indicates whether the asset should be prepared to indicate a precise duration and provide precise random access by time.‌

Getting the exact duration of an asset may require significant processing overhead. Using an approximate duration is typically a cheaper operation and sufficient for playback. Thus:‌

  • If you only intend to play the asset, either pass nil instead of a dictionary, or pass a dictionary that contains the AVURLAssetPreferPreciseDurationAndTimingKey key and a corresponding value of NO (contained in an NSValue object).
  • If you want to add the asset to a composition (AVMutableComposition), you typically need precise random access. Pass a dictionary that contains the AVURLAssetPreferPreciseDurationAndTimingKey key and a corresponding value of YES (contained in an NSValue object—recall that NSNumber inherits from NSValue):
let url = Bundle.main.url(forResource: "test", withExtension: "mp4")//A URL that identifies an audiovisual asset such as a movie file;
let theOpts: [String : Any] =[AVURLAssetPreferPreciseDurationAndTimingKey : true] 
let anAssetToUseInAComposition = AVURLAsset(url: url!, options: theOpts)

Accessing the User’s Assets‌

To access the assets managed by the iPod library or by the Photos application, you need to get a URL of the asset you want.‌

The following example shows how you can get an asset to represent the first video in the Saved Photos Album.exit: ⌘↩

//未完成.....

Preparing an Asset for Use

Initializing an asset (or track) does not necessarily mean that all the information that you might want to retrieve for that item is immediately available. It may require some time to calculate even the duration of an item (an MP3 file, for example, may not contain summary information). Rather than blocking the current thread while a value is being calculated, you should use the AVAsynchronousKeyValueLoading protocol to ask for values and get an answer back later through a completion handler you define using a block. (AVAsset and AVAssetTrack conform to the AVAsynchronousKeyValueLoading protocol.)‌

You test whether a value is loaded for a property using statusOfValueForKey:error:. When an asset is first loaded, the value of most or all of its properties is AVKeyValueStatusUnknown. To load a value for one or more properties, you invoke loadValuesAsynchronouslyForKeys:completionHandler:. In the completion handler, you take whatever action is appropriate depending on the property’s status. You should always be prepared for loading to not complete successfully, either because it failed for some reason such as a network-based URL being inaccessible, or because the load was canceled.

// URL of a bundle asset called 'example.mp4'
let url = Bundle.main.url(forResource: "example", withExtension: "mp4")
let asset = AVAsset(url: url!)
let durationKey = "duration"

// Load the "playable" property
asset.loadValuesAsynchronously(forKeys: [durationKey]) {
    var error: NSError? = nil
    let status = asset.statusOfValue(forKey: durationKey, error: &error)
    switch status {
        case .loaded:
            // Sucessfully loaded. Continue processing.
            break
        case .failed:
            // Handle error
            break
        case .cancelled:
            // Terminate processing
            break
        default:
            // Handle all other cases
            break
     }
}

Ref: https://developer.apple.com/documentation/avfoundation/avasynchronouskeyvalueloading

If you want to prepare an asset for playback, you should load its tracks property. For more about playing assets, see Playback.‌

Getting Still Images From a Video

//未完成….‌

Trimming and Transcoding a Movie

You can transcode a movie from one format to another, and trim a movie, using an AVAssetExportSession object. The workflow is shown in Figure 1-1. An export session is a controller object that manages asynchronous export of an asset. You initialize the session using the asset you want to export and the name of a export preset that indicates the export options you want to apply (see allExportPresets). You then configure the export session to specify the output URL and file type, and optionally other settings such as the metadata and whether the output should be optimized for network use.‌

Figure 1-1 The export session workflow

Enter a caption for this image (optional)

You can check whether you can export a given asset using a given preset using exportPresetsCompatibleWithAsset: as illustrated in this example:

let url = Bundle.main.url(forResource: "test", withExtension: "mp4")
let asset = AVAsset(url: url!)
let compatiblePresets = AVAssetExportSession.exportPresets(compatibleWith: asset)
if compatiblePresets.contains("AVAssetExportPresetLowQuality") {
     let exportSession = AVAssetExportSession(asset: asset, presetName: "AVAssetExportPresetLowQuality")
     // Implementation continues.
}

You complete the configuration of the session by providing the output URL (The URL must be a file URL.) AVAssetExportSession can infer the output file type from the URL’s path extension; typically, however, you set it directly using outputFileType. You can also specify additional properties such as the time range, a limit for the output file length, whether the exported file should be optimized for network use, and a video composition. The following example illustrates how to use the timeRange property to trim the movie:

exportSession.outputFileType = AVFileType.mov //AVFileTypeQuickTimeMovie
exportSession.outputURL = <#A file URL#>
let start: CMTime = CMTimeMakeWithSeconds(1.0, preferredTimescale: 600)
let duration: CMTime = CMTimeMakeWithSeconds(3.0, preferredTimescale: 600)
let range: CMTimeRange = CMTimeRangeMake(start: start, duration: duration)
exportSession.timeRange = range

To create the new file, you invoke exportAsynchronouslyWithCompletionHandler:. The completion handler block is called when the export operation finishes; in your implementation of the handler, you should check the session’s status value to determine whether the export was successful, failed, or was canceled:

exportSession.exportAsynchronously { () -> Void in
    // Handle export results.
    switch exportSession.status {
    case .failed:
        os_log("Export failed: %@", type: .error, exportSession.error?.localizedDescription ?? "error")
        break
    case .cancelled:
        os_log("Export canceled", type: .debug)
        break
    default:
        break
    }
}

You can cancel the export by sending the session a cancelExport message.‌

The export will fail if you try to overwrite an existing file, or write a file outside of the application’s sandbox. It may also fail if:‌

  • There is an incoming phone call
  • Your application is in the background and another application starts playback

In these situations, you should typically inform the user that the export failed, then allow the user to restart the export.‌

Ref:

Exporting Video to Alternative Formats: https://developer.apple.com/documentation/avfoundation/media_assets_playback_and_editing/asset_file_import_and_export/exporting_video_to_alternative_formats