AVFoundation框架解析(四)—— 幾個(gè)關(guān)鍵問(wèn)題之AVFoundation探索(一)

版本記錄

版本號(hào) 時(shí)間
V1.0 2017.08.29

前言

AVFoundation框架是ios中很重要的框架,所有與視頻音頻相關(guān)的軟硬件控制都在這個(gè)框架里面,接下來(lái)這幾篇就主要對(duì)這個(gè)框架進(jìn)行介紹和講解。感興趣的可以看我上幾篇。
1. AVFoundation框架解析(一)—— 基本概覽
2. AVFoundation框架解析(二)—— 實(shí)現(xiàn)視頻預(yù)覽錄制保存到相冊(cè)
3. AVFoundation框架解析(三)—— 幾個(gè)關(guān)鍵問(wèn)題之關(guān)于框架的深度概括

AVFoundation探索

在例子Building a Basic Playback App中展示了,使用AVKit創(chuàng)建播放應(yīng)用程序是多么的容易,對(duì)于基本的視頻播放,那一章的例子中也許就有所有你需要的,但是為了利用好AVKit的所有特性和優(yōu)勢(shì),你應(yīng)該好好理解AVFoundation框架對(duì)象驅(qū)動(dòng)播放的,本章探討AVFoundation的基本知識(shí),并提供您使用AVKit和AVFoundation構(gòu)建全功能視頻播放應(yīng)用程序所需的信息。


Asset模型的理解

AVFoundation的許多主要特征和功能與播放和處理媒體資源有關(guān),框架模型資源使用AVAsset類(lèi),是一個(gè)抽象不可變的類(lèi),代表了單個(gè)的媒體資源。它提供了媒體資源的綜合視圖,對(duì)整個(gè)媒體的靜態(tài)方面進(jìn)行了建模。AVAsset的一個(gè)實(shí)例可以建?;诒镜匚募拿襟w,例如QuickTime影片或MP3音頻文件,但也可以表示從遠(yuǎn)程主機(jī)逐步下載或使用HTTP Live Streaming(HLS)流式傳輸?shù)馁Y產(chǎn)。

AVAsset以?xún)煞N重要的方式處理多媒體文件。第一:它提供了與媒體格式的獨(dú)立性水平。 它為您提供了一個(gè)一致的界面,用于管理和與媒體進(jìn)行互動(dòng),無(wú)論其底層類(lèi)型如何。使用容器格式和編解碼器類(lèi)型的細(xì)節(jié)留在框架中,讓您專(zhuān)注于如何在app中使用這些資源。其次,AVAsset提供了與媒體位置的獨(dú)立性。 您可以通過(guò)使用媒體的URL初始化資源實(shí)例來(lái)創(chuàng)建資產(chǎn)實(shí)例。 這可能是本地URL,例如包含在應(yīng)用程序包或文件系統(tǒng)中其他位置的本地URL,也可能是遠(yuǎn)程服務(wù)器上托管的HLS流等資源。

在這兩種情況下,框架執(zhí)行必要的工作,以便及時(shí)有效地檢索和加載媒體。 消除處理媒體格式和位置的負(fù)擔(dān)大大簡(jiǎn)化了視聽(tīng)媒體的處理。

AVAsset是由AVAssetTrack的一個(gè)或多個(gè)實(shí)例組成的容器對(duì)象,它對(duì)資產(chǎn)的均勻類(lèi)型的媒體流進(jìn)行建模。 最常用的軌道類(lèi)型是音頻和視頻軌道,但AVAssetTrack還可以建模其他補(bǔ)充軌道,例如隱藏字幕,字幕和定時(shí)元數(shù)據(jù)。

AVAsset組成

您使用其軌道屬性檢索資源的軌道集合。 在許多情況下,您將需要對(duì)資產(chǎn)軌道的子集合執(zhí)行操作,而不是對(duì)其完整集合執(zhí)行操作。 在這些情況下,AVAsset還提供了例如基于標(biāo)識(shí)符,媒體類(lèi)型或特征等標(biāo)準(zhǔn)來(lái)檢索軌道子集的方法。


創(chuàng)建Asset

通過(guò)使用指向媒體資源的本地或遠(yuǎn)程URL對(duì)其進(jìn)行初始化來(lái)創(chuàng)建AVAsset,如以下示例所示:

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

AVAsset是一個(gè)抽象類(lèi),所以當(dāng)您創(chuàng)建一個(gè)資源時(shí),如下面實(shí)例所示,您實(shí)際上正在創(chuàng)建一個(gè)名為AVURLAsset的具體子類(lèi)的實(shí)例。在許多情況下,這是創(chuàng)建資產(chǎn)的合適方式,但是當(dāng)您需要對(duì)其初始化進(jìn)行更細(xì)粒度的控制時(shí),也可以直接實(shí)例化AVURLAsset。 AVURLAsset的初始化程序接受一個(gè)選項(xiàng)字典,可以讓您根據(jù)特定用例定制資產(chǎn)的初始化。例如,如果您正在為HLS流創(chuàng)建資源,則當(dāng)用戶連接到蜂窩網(wǎng)絡(luò)時(shí),您可能希望阻止它獲取媒體。 你可以這樣做,如下面的例子所示:

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

AVURLAssetAllowsCellularAccessKey選項(xiàng)傳遞值為false表示您希望此資產(chǎn)僅在用戶連接到Wi-Fi網(wǎng)絡(luò)時(shí)才能檢索其媒體。


準(zhǔn)備Asset

您可以使用AVAsset的屬性來(lái)確定其特性和功能,例如其適用于播放,持續(xù)時(shí)間,創(chuàng)建日期和元數(shù)據(jù)。 創(chuàng)建資源不會(huì)自動(dòng)加載其屬性或?yàn)槿魏翁囟ㄓ猛咀鰷?zhǔn)備。相反,資產(chǎn)的屬性值的加載被推遲到被請(qǐng)求為止。 因?yàn)閷傩栽L問(wèn)是同步的,如果以前沒(méi)有加載請(qǐng)求的屬性,則框架可能需要執(zhí)行大量的工作才能返回值。在macOS中,如果從主線程訪問(wèn)卸載的屬性,則可能導(dǎo)致無(wú)響應(yīng)的用戶界面。 在iOS和tvOS中,由于媒體操作是由共享媒體服務(wù)守護(hù)進(jìn)程執(zhí)行的,所以情況會(huì)更加嚴(yán)重。如果檢索卸載的屬性值的請(qǐng)求被阻止太久,則超時(shí)會(huì)導(dǎo)致媒體服務(wù)終止。 為了防止這種情況發(fā)生,請(qǐng)異步加載資產(chǎn)的屬性。

AVAsset和AVAssetTrack采用AVAsynchronousKeyValueLoading協(xié)議,它定義了用于查詢(xún)屬性的當(dāng)前加載狀態(tài)的方法,并根據(jù)需要異步加載一個(gè)或多個(gè)屬性值。 協(xié)議定義了兩種方法:

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

你可以使用方法loadValuesAsynchronouslyForKeys:completionHandler:異步的加載一個(gè)或者多個(gè)屬性值,你傳遞給一個(gè)鍵組成的數(shù)組,它們是要加載的屬性的名字,和在確定狀態(tài)之后調(diào)用的完成塊。 以下示例顯示如何異步加載資產(chǎn)的可播放屬性。

// 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
    }
}

你檢查屬性的狀態(tài),在使用方法statusOfValueForKey:error:的完成塊調(diào)用中。AVKeyValueStatusLoaded狀態(tài)表示屬性值成功的加載了,可以不用block塊檢索。AVKeyValueStatusFailed狀態(tài)表示屬性值不可用,因?yàn)樵趪L試加載數(shù)據(jù)時(shí)發(fā)生了錯(cuò)誤。你可以通過(guò)NSError指針確定失敗的原因。在所有情況下,請(qǐng)注意,在任意后臺(tái)隊(duì)列中調(diào)用完成回調(diào)。 在執(zhí)行任何與用戶界面相關(guān)的操作之前,將調(diào)度方法調(diào)用回主隊(duì)列。


處理元數(shù)據(jù)Metadata

媒體容器格式可以存儲(chǔ)有關(guān)其媒體的描述性元數(shù)據(jù)。 作為開(kāi)發(fā)人員,使用元數(shù)據(jù)往往具有挑戰(zhàn)性,因?yàn)槊總€(gè)容器格式都有自己獨(dú)特的元數(shù)據(jù)格式。您通常需要對(duì)讀取和寫(xiě)入容器元數(shù)據(jù)的格式進(jìn)行低級(jí)的了解,但AVFoundation可以通過(guò)使用其AVMetadataItem類(lèi)來(lái)簡(jiǎn)化使用元數(shù)據(jù)。

在其最基本的形式中,AVMetadataItem的一個(gè)實(shí)例是表示單個(gè)元數(shù)據(jù)值的鍵值對(duì),例如電影的標(biāo)題或?qū)]嫷淖髌贰?以與AVAsset提供媒體標(biāo)準(zhǔn)化視圖相同的方式,AVMetadataItem提供其關(guān)聯(lián)元數(shù)據(jù)的歸一化視圖。

1. 檢索元數(shù)據(jù)集合

要有效地使用AVMetadataItem,您應(yīng)該了解AVFoundation如何組織元數(shù)據(jù)。 為了簡(jiǎn)化查找和過(guò)濾元數(shù)據(jù)項(xiàng),框架將相關(guān)元數(shù)據(jù)分組成關(guān)鍵空間。

  • 格式特定的鍵空格
    • 框架定義了一些特定于格式的關(guān)鍵空間。 這些與特定的容器或文件格式大致相關(guān),例如QuickTime(Quicktime元數(shù)據(jù)和用戶數(shù)據(jù))或MP3(ID3)。 但是,單個(gè)資產(chǎn)可能包含跨多個(gè)關(guān)鍵空間的元數(shù)據(jù)值。 您可以使用其元數(shù)據(jù)屬性來(lái)檢索資產(chǎn)的格式特定元數(shù)據(jù)的完整集合。
  • 公共密鑰空間
    • 有許多常見(jiàn)的元數(shù)據(jù)值,例如電影的創(chuàng)建日期或描述,可以跨多個(gè)關(guān)鍵空間存在。 為了幫助規(guī)范對(duì)這個(gè)公共元數(shù)據(jù)的訪問(wèn),框架提供了一個(gè)公共密鑰空間,可以訪問(wèn)幾個(gè)密鑰空間共有的有限的一組元數(shù)據(jù)值。這樣可以方便地檢索常用的元數(shù)據(jù),而不用考慮特定的格式。 您可以使用其commonMetadata屬性來(lái)檢索資產(chǎn)的公共元數(shù)據(jù)集合。

您可以通過(guò)調(diào)用其可用的MetadataFormats屬性來(lái)確定資產(chǎn)包含的元數(shù)據(jù)格式。 此屬性返回其包含的每個(gè)元數(shù)據(jù)格式的字符串標(biāo)識(shí)符數(shù)組。 然后,使用其metadataForFormat:方法通過(guò)傳遞適當(dāng)?shù)母袷綐?biāo)識(shí)符來(lái)檢索特定于格式的元數(shù)據(jù)值,如下所示:

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. 發(fā)現(xiàn)和使用元數(shù)據(jù)值

檢索到元數(shù)據(jù)的集合后,下一步是查找其中的特定值。 您使用AVMetadataItem的各種類(lèi)方法將元數(shù)據(jù)集合過(guò)濾到一組離散值。 查找特定元數(shù)據(jù)項(xiàng)目的最簡(jiǎn)單方法是按標(biāo)識(shí)符進(jìn)行過(guò)濾,將標(biāo)識(shí)符和密鑰的概念分組到一個(gè)單元中。 以下示例顯示如何從公共密鑰空間中檢索標(biāo)題項(xiàng)。

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

這里還要注意:AVMetadataItem的過(guò)濾方法返回項(xiàng)目的集合而不是單個(gè)實(shí)例。 在許多情況下,返回的集合包含單個(gè)元素,但是如果介質(zhì)包含本地化的元數(shù)據(jù),或者如果從公共密鑰空間檢索數(shù)據(jù),并且多個(gè)密鑰空間中存在相同的值,則會(huì)返回與每個(gè)區(qū)域設(shè)置或密鑰空間匹配的不同值。

檢索到特定元數(shù)據(jù)項(xiàng)后,下一步是調(diào)用其value屬性。 返回的值是采用NSObjectNSCopying協(xié)議的對(duì)象類(lèi)型。 您可以手動(dòng)將值轉(zhuǎn)換為適當(dāng)?shù)念?lèi)型,但使用元數(shù)據(jù)項(xiàng)的類(lèi)型強(qiáng)制屬性更安全和更容易。 您可以使用其stringValue,numberValue,dateValue和dataValue屬性來(lái)輕松將該值強(qiáng)制為適當(dāng)?shù)念?lèi)型。 例如,以下示例顯示如何檢索與iTunes音軌相關(guān)聯(lián)的藝術(shù)作品。

// 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
    }
}

元數(shù)據(jù)在許多媒體app中起著重要的作用。 本指南的后續(xù)部分將介紹如何使用靜態(tài)和定時(shí)元數(shù)據(jù)來(lái)增強(qiáng)播放app的功能。

后記

未完,待續(xù)~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,494評(píng)論 3 416
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事?!?“怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,283評(píng)論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,953評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,714評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,186評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,410評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,940評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,776評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,976評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,210評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,642評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,878評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,654評(píng)論 3 391
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,958評(píng)論 2 373

推薦閱讀更多精彩內(nèi)容