AVFoundation框架解析(四)—— 几个关键问题之AVFoundation探索(一)

版本记录

版本号 时间
V1.0 2017.08.29

前言

AVFoundation框架是ios中很重要的框架,所有与视频音频相关的软硬件控制都在这个框架里面,接下来这几篇就主要对这个框架进行介绍和讲解。感兴趣的可以看我上几篇。
1. AVFoundation框架解析(一)—— 基本概览
2. AVFoundation框架解析(二)—— 实现视频预览录制保存到相册
3. AVFoundation框架解析(三)—— 几个关键问题之关于框架的深度概括

AVFoundation探索

在例子Building a Basic Playback App中展示了,使用AVKit创建播放应用程序是多么的容易,对于基本的视频播放,那一章的例子中也许就有所有你需要的,但是为了利用好AVKit的所有特性和优势,你应该好好理解AVFoundation框架对象驱动播放的,本章探讨AVFoundation的基本知识,并提供您使用AVKit和AVFoundation构建全功能视频播放应用程序所需的信息。


Asset模型的理解

AVFoundation的许多主要特征和功能与播放和处理媒体资源有关,框架模型资源使用AVAsset类,是一个抽象不可变的类,代表了单个的媒体资源。它提供了媒体资源的综合视图,对整个媒体的静态方面进行了建模。AVAsset的一个实例可以建模基于本地文件的媒体,例如QuickTime影片或MP3音频文件,但也可以表示从远程主机逐步下载或使用HTTP Live Streaming(HLS)流式传输的资产。

AVAsset以两种重要的方式处理多媒体文件。第一:它提供了与媒体格式的独立性水平。 它为您提供了一个一致的界面,用于管理和与媒体进行互动,无论其底层类型如何。使用容器格式和编解码器类型的细节留在框架中,让您专注于如何在app中使用这些资源。其次,AVAsset提供了与媒体位置的独立性。 您可以通过使用媒体的URL初始化资源实例来创建资产实例。 这可能是本地URL,例如包含在应用程序包或文件系统中其他位置的本地URL,也可能是远程服务器上托管的HLS流等资源。

在这两种情况下,框架执行必要的工作,以便及时有效地检索和加载媒体。 消除处理媒体格式和位置的负担大大简化了视听媒体的处理。

AVAsset是由AVAssetTrack的一个或多个实例组成的容器对象,它对资产的均匀类型的媒体流进行建模。 最常用的轨道类型是音频和视频轨道,但AVAssetTrack还可以建模其他补充轨道,例如隐藏字幕,字幕和定时元数据。

AVAsset组成

您使用其轨道属性检索资源的轨道集合。 在许多情况下,您将需要对资产轨道的子集合执行操作,而不是对其完整集合执行操作。 在这些情况下,AVAsset还提供了例如基于标识符,媒体类型或特征等标准来检索轨道子集的方法。


创建Asset

通过使用指向媒体资源的本地或远程URL对其进行初始化来创建AVAsset,如以下示例所示:

let url: URL = // Local or Remote Asset URL
let asset = AVAsset(url: url)

AVAsset是一个抽象类,所以当您创建一个资源时,如下面实例所示,您实际上正在创建一个名为AVURLAsset的具体子类的实例。在许多情况下,这是创建资产的合适方式,但是当您需要对其初始化进行更细粒度的控制时,也可以直接实例化AVURLAsset。 AVURLAsset的初始化程序接受一个选项字典,可以让您根据特定用例定制资产的初始化。例如,如果您正在为HLS流创建资源,则当用户连接到蜂窝网络时,您可能希望阻止它获取媒体。 你可以这样做,如下面的例子所示:

let url: URL = // Remote Asset URL
let options = [AVURLAssetAllowsCellularAccessKey: false]
let asset = AVURLAsset(url: url, options: options)

AVURLAssetAllowsCellularAccessKey选项传递值为false表示您希望此资产仅在用户连接到Wi-Fi网络时才能检索其媒体。


准备Asset

您可以使用AVAsset的属性来确定其特性和功能,例如其适用于播放,持续时间,创建日期和元数据。 创建资源不会自动加载其属性或为任何特定用途做准备。相反,资产的属性值的加载被推迟到被请求为止。 因为属性访问是同步的,如果以前没有加载请求的属性,则框架可能需要执行大量的工作才能返回值。在macOS中,如果从主线程访问卸载的属性,则可能导致无响应的用户界面。 在iOS和tvOS中,由于媒体操作是由共享媒体服务守护进程执行的,所以情况会更加严重。如果检索卸载的属性值的请求被阻止太久,则超时会导致媒体服务终止。 为了防止这种情况发生,请异步加载资产的属性。

AVAsset和AVAssetTrack采用AVAsynchronousKeyValueLoading协议,它定义了用于查询属性的当前加载状态的方法,并根据需要异步加载一个或多个属性值。 协议定义了两种方法:

public func loadValuesAsynchronously(forKeys keys: [String], completionHandler handler: (() -> Void)?)
public func statusOfValue(forKey key: String, error outError: NSErrorPointer) -> AVKeyValueStatus

你可以使用方法loadValuesAsynchronouslyForKeys:completionHandler:异步的加载一个或者多个属性值,你传递给一个键组成的数组,它们是要加载的属性的名字,和在确定状态之后调用的完成块。 以下示例显示如何异步加载资产的可播放属性。

// URL of a bundle asset called 'example.mp4'
let url = Bundle.main.url(forResource: "example", withExtension: "mp4")!
let asset = AVAsset(url: url)
let playableKey = "playable"
 
// Load the "playable" property
asset.loadValuesAsynchronously(forKeys: [playableKey]) {
    var error: NSError? = nil
    let status = asset.statusOfValue(forKey: playableKey, error: &error)
    switch status {
    case .loaded:
        // Sucessfully loaded. Continue processing.
    case .failed:
        // Handle error
    case .cancelled:
        // Terminate processing
    default:
        // Handle all other cases
    }
}

你检查属性的状态,在使用方法statusOfValueForKey:error:的完成块调用中。AVKeyValueStatusLoaded状态表示属性值成功的加载了,可以不用block块检索。AVKeyValueStatusFailed状态表示属性值不可用,因为在尝试加载数据时发生了错误。你可以通过NSError指针确定失败的原因。在所有情况下,请注意,在任意后台队列中调用完成回调。 在执行任何与用户界面相关的操作之前,将调度方法调用回主队列。


处理元数据Metadata

媒体容器格式可以存储有关其媒体的描述性元数据。 作为开发人员,使用元数据往往具有挑战性,因为每个容器格式都有自己独特的元数据格式。您通常需要对读取和写入容器元数据的格式进行低级的了解,但AVFoundation可以通过使用其AVMetadataItem类来简化使用元数据。

在其最基本的形式中,AVMetadataItem的一个实例是表示单个元数据值的键值对,例如电影的标题或专辑的作品。 以与AVAsset提供媒体标准化视图相同的方式,AVMetadataItem提供其关联元数据的归一化视图。

1. 检索元数据集合

要有效地使用AVMetadataItem,您应该了解AVFoundation如何组织元数据。 为了简化查找和过滤元数据项,框架将相关元数据分组成关键空间。

  • 格式特定的键空格
    • 框架定义了一些特定于格式的关键空间。 这些与特定的容器或文件格式大致相关,例如QuickTime(Quicktime元数据和用户数据)或MP3(ID3)。 但是,单个资产可能包含跨多个关键空间的元数据值。 您可以使用其元数据属性来检索资产的格式特定元数据的完整集合。
  • 公共密钥空间
    • 有许多常见的元数据值,例如电影的创建日期或描述,可以跨多个关键空间存在。 为了帮助规范对这个公共元数据的访问,框架提供了一个公共密钥空间,可以访问几个密钥空间共有的有限的一组元数据值。这样可以方便地检索常用的元数据,而不用考虑特定的格式。 您可以使用其commonMetadata属性来检索资产的公共元数据集合。

您可以通过调用其可用的MetadataFormats属性来确定资产包含的元数据格式。 此属性返回其包含的每个元数据格式的字符串标识符数组。 然后,使用其metadataForFormat:方法通过传递适当的格式标识符来检索特定于格式的元数据值,如下所示:

let url = Bundle.main.url(forResource: "audio", withExtension: "m4a")!
let asset = AVAsset(url: url)
let formatsKey = "availableMetadataFormats"
asset.loadValuesAsynchronously(forKeys: [formatsKey]) {
    var error: NSError? = nil
    let status = asset.statusOfValue(forKey: formatsKey, error: &error)
    if status == .loaded {
        for format in asset.availableMetadataFormats {
            let metadata = asset.metadata(forFormat: format)
            // process format-specific metadata collection
        }
    }
}

2. 发现和使用元数据值

检索到元数据的集合后,下一步是查找其中的特定值。 您使用AVMetadataItem的各种类方法将元数据集合过滤到一组离散值。 查找特定元数据项目的最简单方法是按标识符进行过滤,将标识符和密钥的概念分组到一个单元中。 以下示例显示如何从公共密钥空间中检索标题项。

let metadata = asset.commonMetadata
let titleID = AVMetadataCommonIdentifierTitle
let titleItems = AVMetadataItem.metadataItems(from: metadata, filteredByIdentifier: titleID)
if let item = titleItems.first {
    // process title item
}

这里还要注意:AVMetadataItem的过滤方法返回项目的集合而不是单个实例。 在许多情况下,返回的集合包含单个元素,但是如果介质包含本地化的元数据,或者如果从公共密钥空间检索数据,并且多个密钥空间中存在相同的值,则会返回与每个区域设置或密钥空间匹配的不同值。

检索到特定元数据项后,下一步是调用其value属性。 返回的值是采用NSObjectNSCopying协议的对象类型。 您可以手动将值转换为适当的类型,但使用元数据项的类型强制属性更安全和更容易。 您可以使用其stringValue,numberValue,dateValue和dataValue属性来轻松将该值强制为适当的类型。 例如,以下示例显示如何检索与iTunes音轨相关联的艺术作品。

// Collection of "common" metadata

let metadata = asset.commonMetadata
// Filter metadata to find the asset's artwork
let artworkItems =
    AVMetadataItem.metadataItems(from: metadata,
                                 filteredByIdentifier: AVMetadataCommonIdentifierArtwork)
if let artworkItem = artworkItems.first {
    // Coerce the value to an NSData using its dataValue property
    if let imageData = artworkItem.dataValue {
        let image = UIImage(data: imageData)
        // process image
    } else {
        // No image data found
    }
}

元数据在许多媒体app中起着重要的作用。 本指南的后续部分将介绍如何使用静态和定时元数据来增强播放app的功能。

后记

未完,待续~~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容