Article / 2026/05/29
AVAsset 完整API文档
AVAsset 是 AVFoundation 框架中的核心类,用于表示音视频资源的抽象基类。它提供了访问媒体资源元数据、轨道信息和时间属性的接口,但不直接用于播放(播放需要使用 AVPlayerItem 和 AVPlayer)。
🚀 AVAsset 完整API文档
概述
AVAsset 是 AVFoundation 框架中的核心类,用于表示音视频资源的抽象基类。它提供了访问媒体资源元数据、轨道信息和时间属性的接口,但不直接用于播放(播放需要使用 AVPlayerItem 和 AVPlayer)。
初始化方法
init(url:)
从 URL 创建资源对象。
convenience init(url: URL)
示例:
let url = URL(fileURLWithPath: "/path/to/video.mp4")
let asset = AVAsset(url: url)
// 或网络URL
let remoteURL = URL(string: "https://example.com/video.mp4")!
let remoteAsset = AVAsset(url: remoteURL)
init(url:options:)
使用选项从 URL 创建资源。
convenience init(url: URL, options: [String : Any]?)
常用选项:
let options = [
AVURLAssetPreferPreciseDurationAndTimingKey: true, // 精确时长
AVURLAssetHTTPHeaderFieldsKey: ["User-Agent": "MyApp"], // HTTP头
AVURLAssetAllowsCellularAccessKey: false // 禁止蜂窝网络
]
let asset = AVAsset(url: url, options: options)
核心属性
duration
var duration: CMTime { get }
资源的总时长。
示例:
let duration = asset.duration
let durationInSeconds = duration.seconds
print("时长: \(durationInSeconds) 秒")
// 检查是否有效
if duration.isValid && !duration.isIndefinite {
print("有效时长")
}
providesPreciseDurationAndTiming
var providesPreciseDurationAndTiming: Bool { get }
是否提供精确的时长和时间信息。
naturalSize (已废弃)
@available(*, deprecated)
var naturalSize: CGSize { get }
替代方案: 使用 tracks 获取视频轨道的自然尺寸。
// 现代方法
if let videoTrack = try? await asset.loadTracks(withMediaType: .video).first {
let size = try? await videoTrack.load(.naturalSize)
}
preferredRate
var preferredRate: Float { get }
首选播放速率(通常为 1.0)。
preferredVolume
var preferredVolume: Float { get }
首选音量(0.0 到 1.0)。
preferredTransform
var preferredTransform: CGAffineTransform { get }
首选变换矩阵(用于处理视频旋转)。
let transform = asset.preferredTransform
let isPortrait = transform.a == 0 && abs(transform.b) == 1
isPlayable
var isPlayable: Bool { get }
资源是否可播放。
if asset.isPlayable {
print("可以播放")
}
isExportable
var isExportable: Bool { get }
资源是否可导出。
isReadable
var isReadable: Bool { get }
资源是否可读取。
isComposable
var isComposable: Bool { get }
资源是否可用于合成。
hasProtectedContent
var isProtectedContent: Bool { get }
资源是否包含受保护的内容(DRM)。
轨道相关 API
tracks
var tracks: [AVAssetTrack] { get }
获取所有轨道(已废弃,建议使用异步方法)。
loadTracks(withMediaType:)
func loadTracks(withMediaType mediaType: AVMediaType) async throws -> [AVAssetTrack]
异步加载特定类型的轨道。
Task {
do {
// 加载视频轨道
let videoTracks = try await asset.loadTracks(withMediaType: .video)
// 加载音频轨道
let audioTracks = try await asset.loadTracks(withMediaType: .audio)
print("视频轨道数: \(videoTracks.count)")
print("音频轨道数: \(audioTracks.count)")
} catch {
print("加载轨道失败: \(error)")
}
}
loadTracks(withMediaCharacteristic:)
func loadTracks(withMediaCharacteristic mediaCharacteristic: AVMediaCharacteristic) async throws -> [AVAssetTrack]
加载具有特定特征的轨道。
// 加载可视轨道
let visualTracks = try await asset.loadTracks(withMediaCharacteristic: .visual)
// 加载可听轨道
let audibleTracks = try await asset.loadTracks(withMediaCharacteristic: .audible)
track(withTrackID:)
func track(withTrackID trackID: CMPersistentTrackID) -> AVAssetTrack?
根据轨道 ID 获取特定轨道。
if let track = asset.track(withTrackID: 1) {
print("找到轨道")
}
loadTrack(withTrackID:)
func loadTrack(withTrackID trackID: CMPersistentTrackID) async throws -> AVAssetTrack?
异步加载特定 ID 的轨道。
tracksWithMediaType(_:)
func tracksWithMediaType(_ mediaType: AVMediaType) -> [AVAssetTrack]
获取指定媒体类型的轨道(同步,已废弃)。
tracksWithMediaCharacteristic(_:)
func tracksWithMediaCharacteristic(_ mediaCharacteristic: AVMediaCharacteristic) -> [AVAssetTrack]
获取具有特定特征的轨道(同步,已废弃)。
trackGroups
var trackGroups: [AVAssetTrackGroup] { get }
获取轨道组信息。
元数据 API
metadata
var metadata: [AVMetadataItem] { get }
资源的元数据数组。
for item in asset.metadata {
if let key = item.commonKey?.rawValue,
let value = item.value {
print("\(key): \(value)")
}
}
commonMetadata
var commonMetadata: [AVMetadataItem] { get }
通用格式的元数据。
availableMetadataFormats
var availableMetadataFormats: [AVMetadataFormat] { get }
可用的元数据格式列表。
metadata(forFormat:)
func metadata(forFormat format: AVMetadataFormat) -> [AVMetadataItem]
获取特定格式的元数据。
let id3Metadata = asset.metadata(forFormat: .id3Metadata)
availableChapterLocales
var availableChapterLocales: [Locale] { get }
可用的章节语言环境。
chapterMetadataGroups(withTitleLocale:containingItemsWithCommonKeys:)
func chapterMetadataGroups(
withTitleLocale locale: Locale,
containingItemsWithCommonKeys commonKeys: [AVMetadataKey]?
) -> [AVTimedMetadataGroup]
获取章节元数据组。
chapterMetadataGroups(bestMatchingPreferredLanguages:)
func chapterMetadataGroups(
bestMatchingPreferredLanguages preferredLanguages: [String]
) -> [AVTimedMetadataGroup]
根据首选语言获取最佳匹配的章节元数据。
媒体选择 API
availableMediaCharacteristicsWithMediaSelectionOptions
var availableMediaCharacteristicsWithMediaSelectionOptions: [AVMediaCharacteristic] { get }
具有媒体选择选项的可用特征。
let characteristics = asset.availableMediaCharacteristicsWithMediaSelectionOptions
if characteristics.contains(.audible) {
print("有多个音轨可选")
}
mediaSelectionGroup(forMediaCharacteristic:)
func mediaSelectionGroup(
forMediaCharacteristic mediaCharacteristic: AVMediaCharacteristic
) -> AVMediaSelectionGroup?
获取特定特征的媒体选择组。
if let audioGroup = asset.mediaSelectionGroup(forMediaCharacteristic: .audible) {
for option in audioGroup.options {
print("音轨: \(option.displayName)")
}
}
if let subtitleGroup = asset.mediaSelectionGroup(forMediaCharacteristic: .legible) {
for option in subtitleGroup.options {
print("字幕: \(option.displayName)")
}
}
preferredMediaSelection
var preferredMediaSelection: AVMediaSelection { get }
首选的媒体选择。
异步加载 API (Modern Concurrency)
load(_:)
func load<T>(_ property: AVAsyncProperty<AVAsset, T>) async throws -> T
Swift 并发模型中加载属性的现代方法。
Task {
do {
// 加载单个属性
let duration = try await asset.load(.duration)
let isPlayable = try await asset.load(.isPlayable)
let tracks = try await asset.load(.tracks)
print("时长: \(duration.seconds)")
print("可播放: \(isPlayable)")
// 加载多个属性
let (dur, playable) = try await asset.load(.duration, .isPlayable)
} catch {
print("加载失败: \(error)")
}
}
可加载的属性:
.duration.isPlayable.isExportable.isReadable.isComposable.tracks.metadata.commonMetadata.preferredRate.preferredVolume.preferredTransform- 等等…
资源状态检查
statusOfValue(forKey:error:)
func statusOfValue(forKey key: String, error outError: NSErrorPointer) -> AVKeyValueStatus
检查属性的加载状态(旧方法)。
状态值:
.unknown- 未知.loading- 加载中.loaded- 已加载.failed- 失败.cancelled- 已取消
var error: NSError?
let status = asset.statusOfValue(forKey: "duration", error: &error)
switch status {
case .loaded:
print("已加载")
case .loading:
print("加载中")
case .failed:
print("失败: \(error?.localizedDescription ?? "")")
default:
break
}
loadValuesAsynchronously(forKeys:completionHandler:)
func loadValuesAsynchronously(
forKeys keys: [String],
completionHandler handler: (() -> Void)?
)
异步加载属性值(旧方法)。
asset.loadValuesAsynchronously(forKeys: ["duration", "tracks"]) {
var error: NSError?
let status = asset.statusOfValue(forKey: "duration", error: &error)
if status == .loaded {
let duration = asset.duration
print("时长: \(duration.seconds)")
}
}
时间范围相关
unusedTrackID
func unusedTrackID() -> CMPersistentTrackID
获取一个未使用的轨道 ID。
引用限制 API
referenceRestrictions
var referenceRestrictions: AVAssetReferenceRestrictions { get }
资源的引用限制。
选项:
.forbidNone- 无限制.forbidRemoteReferenceToLocal- 禁止远程引用本地.forbidLocalReferenceToRemote- 禁止本地引用远程.forbidCrossSiteReference- 禁止跨站引用.forbidAll- 禁止所有引用
完整使用示例
示例 1: 获取视频基本信息
import AVFoundation
func getVideoInfo(url: URL) async {
let asset = AVAsset(url: url)
do {
// 使用现代异步API加载属性
let duration = try await asset.load(.duration)
let isPlayable = try await asset.load(.isPlayable)
let tracks = try await asset.load(.tracks)
print("=== 视频信息 ===")
print("时长: \(duration.seconds) 秒")
print("可播放: \(isPlayable)")
print("总轨道数: \(tracks.count)")
// 获取视频轨道信息
let videoTracks = try await asset.loadTracks(withMediaType: .video)
if let videoTrack = videoTracks.first {
let size = try await videoTrack.load(.naturalSize)
let frameRate = try await videoTrack.load(.nominalFrameRate)
print("\n=== 视频轨道 ===")
print("分辨率: \(size.width) x \(size.height)")
print("帧率: \(frameRate) fps")
}
// 获取音频轨道信息
let audioTracks = try await asset.loadTracks(withMediaType: .audio)
print("\n=== 音频轨道 ===")
print("音轨数量: \(audioTracks.count)")
} catch {
print("获取视频信息失败: \(error)")
}
}
// 使用
Task {
await getVideoInfo(url: videoURL)
}
示例 2: 读取视频元数据
func readMetadata(from asset: AVAsset) async {
do {
let metadata = try await asset.load(.commonMetadata)
print("=== 元数据 ===")
for item in metadata {
if let key = item.commonKey?.rawValue,
let value = item.value as? String {
print("\(key): \(value)")
}
}
// 读取特定元数据
let titleItems = AVMetadataItem.metadataItems(
from: metadata,
filteredByIdentifier: .commonIdentifierTitle
)
if let title = titleItems.first?.stringValue {
print("标题: \(title)")
}
let artistItems = AVMetadataItem.metadataItems(
from: metadata,
filteredByIdentifier: .commonIdentifierArtist
)
if let artist = artistItems.first?.stringValue {
print("艺术家: \(artist)")
}
} catch {
print("读取元数据失败: \(error)")
}
}
示例 3: 检查视频方向
func checkVideoOrientation(url: URL) async {
let asset = AVAsset(url: url)
do {
let videoTracks = try await asset.loadTracks(withMediaType: .video)
guard let videoTrack = videoTracks.first else {
print("没有视频轨道")
return
}
let transform = try await videoTrack.load(.preferredTransform)
let videoAngle = atan2(transform.b, transform.a)
let degrees = videoAngle * 180 / .pi
print("视频旋转角度: \(degrees)°")
// 判断方向
if degrees == 0 {
print("方向: 正常")
} else if degrees == 90 {
print("方向: 向右旋转90°")
} else if degrees == -90 || degrees == 270 {
print("方向: 向左旋转90°")
} else if abs(degrees) == 180 {
print("方向: 倒置")
}
} catch {
print("检查方向失败: \(error)")
}
}
示例 4: 多语言音轨和字幕选择
func listAvailableMediaOptions(asset: AVAsset) {
print("=== 可用媒体选项 ===")
// 检查音轨
if let audioGroup = asset.mediaSelectionGroup(forMediaCharacteristic: .audible) {
print("\n音轨选项:")
for (index, option) in audioGroup.options.enumerated() {
print("\(index + 1). \(option.displayName)")
if let languageCode = option.extendedLanguageTag {
print(" 语言: \(languageCode)")
}
}
}
// 检查字幕
if let subtitleGroup = asset.mediaSelectionGroup(forMediaCharacteristic: .legible) {
print("\n字幕选项:")
for (index, option) in subtitleGroup.options.enumerated() {
print("\(index + 1). \(option.displayName)")
if let languageCode = option.extendedLanguageTag {
print(" 语言: \(languageCode)")
}
}
}
}
示例 5: 使用旧版回调方式加载属性
func loadAssetPropertiesLegacy(url: URL) {
let asset = AVAsset(url: url)
let keys = ["duration", "tracks", "playable"]
asset.loadValuesAsynchronously(forKeys: keys) {
DispatchQueue.main.async {
var error: NSError?
// 检查 duration
let durationStatus = asset.statusOfValue(forKey: "duration", error: &error)
if durationStatus == .loaded {
print("时长: \(asset.duration.seconds) 秒")
} else if durationStatus == .failed {
print("加载时长失败: \(error?.localizedDescription ?? "")")
}
// 检查 tracks
let tracksStatus = asset.statusOfValue(forKey: "tracks", error: &error)
if tracksStatus == .loaded {
print("轨道数: \(asset.tracks.count)")
}
// 检查 playable
let playableStatus = asset.statusOfValue(forKey: "playable", error: &error)
if playableStatus == .loaded {
print("可播放: \(asset.isPlayable)")
}
}
}
}
示例 6: 创建带选项的 Asset
func createAssetWithOptions(url: URL) {
let options: [String: Any] = [
// 精确时长和时间
AVURLAssetPreferPreciseDurationAndTimingKey: true,
// HTTP 请求头
AVURLAssetHTTPHeaderFieldsKey: [
"User-Agent": "MyVideoPlayer/1.0",
"Authorization": "Bearer token123"
],
// 禁止使用蜂窝网络
AVURLAssetAllowsCellularAccessKey: false,
// 引用限制
AVURLAssetReferenceRestrictionsKey: AVAssetReferenceRestrictions.forbidAll.rawValue
]
let asset = AVAsset(url: url, options: options)
// 使用 asset...
}
常用元数据标识符
// 通用标识符
.commonIdentifierTitle // 标题
.commonIdentifierCreator // 创建者
.commonIdentifierSubject // 主题
.commonIdentifierDescription // 描述
.commonIdentifierPublisher // 发布者
.commonIdentifierContributor // 贡献者
.commonIdentifierCreationDate // 创建日期
.commonIdentifierLastModifiedDate // 最后修改日期
.commonIdentifierType // 类型
.commonIdentifierFormat // 格式
.commonIdentifierAssetIdentifier // 资源标识符
.commonIdentifierSource // 来源
.commonIdentifierLanguage // 语言
.commonIdentifierRelation // 关系
.commonIdentifierLocation // 位置
.commonIdentifierCopyrights // 版权
.commonIdentifierAlbumName // 专辑名
.commonIdentifierAuthor // 作者
.commonIdentifierArtist // 艺术家
.commonIdentifierArtwork // 封面
.commonIdentifierMake // 制造商
.commonIdentifierModel // 型号
.commonIdentifierSoftware // 软件
AVAsset 子类
AVURLAsset
用于基于 URL 的资源,提供额外功能。
let urlAsset = AVURLAsset(url: videoURL)
// 访问 URL
print(urlAsset.url)
// 检查资源是否可用
let isCompatible = urlAsset.isCompatibleWithSavedPhotosAlbum
AVComposition
用于组合多个媒体资源。
let composition = AVMutableComposition()
// 添加轨道和时间范围...
最佳实践
1. 使用现代异步 API
// ✅ 推荐 - Swift Concurrency
let duration = try await asset.load(.duration)
// ❌ 避免 - 旧的回调方式
asset.loadValuesAsynchronously(forKeys: ["duration"]) { ... }
2. 批量加载属性
// ✅ 一次加载多个属性
let (duration, playable, tracks) = try await asset.load(
.duration,
.isPlayable,
.tracks
)
// ❌ 避免多次单独加载
let duration = try await asset.load(.duration)
let playable = try await asset.load(.isPlayable)
let tracks = try await asset.load(.tracks)
3. 错误处理
do {
let duration = try await asset.load(.duration)
if duration.isValid && !duration.isIndefinite {
print("有效时长: \(duration.seconds)")
}
} catch {
print("加载失败: \(error.localizedDescription)")
}
4. 检查资源可用性
let isPlayable = try await asset.load(.isPlayable)
if !isPlayable {
print("资源不可播放")
return
}
常见问题
1. duration 为什么是 invalid?
某些格式(如流媒体)可能没有确定的时长,需要:
let options = [AVURLAssetPreferPreciseDurationAndTimingKey: true]
let asset = AVAsset(url: url, options: options)
2. 如何获取视频的真实尺寸?
let videoTracks = try await asset.loadTracks(withMediaType: .video)
if let track = videoTracks.first {
let size = try await track.load(.naturalSize)
let transform = try await track.load(.preferredTransform)
// 考虑旋转后的尺寸
let rotatedSize = size.applying(transform)
let actualSize = CGSize(
width: abs(rotatedSize.width),
height: abs(rotatedSize.height)
)
}
3. 如何判断是否是视频文件?
let videoTracks = try await asset.loadTracks(withMediaType: .video)
let hasVideo = !videoTracks.isEmpty
相关类型
AVAssetTrack- 资源轨道AVMetadataItem- 元数据项AVMediaSelectionGroup- 媒体选择组AVMediaSelectionOption- 媒体选择选项AVPlayerItem- 播放项(用于实际播放)AVPlayer- 播放器CMTime- 时间表示AVAssetImageGenerator- 图像生成器AVAssetExportSession- 导出会话
Giscus 未启用:请在
src/site.config.ts中配置 repoId 与 categoryId。