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

版本記錄

版本號 時間
V1.0 2017.08.29

前言

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

AVFoundation探索

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


Asset模型的理解

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

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

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

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

AVAsset組成

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


創(chuàng)建Asset

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

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

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

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

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


準(zhǔn)備Asset

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

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

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

你可以使用方法loadValuesAsynchronouslyForKeys:completionHandler:異步的加載一個或者多個屬性值,你傳遞給一個鍵組成的數(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)表示屬性值不可用,因為在嘗試加載數(shù)據(jù)時發(fā)生了錯誤。你可以通過NSError指針確定失敗的原因。在所有情況下,請注意,在任意后臺隊列中調(diào)用完成回調(diào)。 在執(zhí)行任何與用戶界面相關(guān)的操作之前,將調(diào)度方法調(diào)用回主隊列。


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

媒體容器格式可以存儲有關(guān)其媒體的描述性元數(shù)據(jù)。 作為開發(fā)人員,使用元數(shù)據(jù)往往具有挑戰(zhàn)性,因為每個容器格式都有自己獨特的元數(shù)據(jù)格式。您通常需要對讀取和寫入容器元數(shù)據(jù)的格式進行低級的了解,但AVFoundation可以通過使用其AVMetadataItem類來簡化使用元數(shù)據(jù)。

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

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

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

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

您可以通過調(diào)用其可用的MetadataFormats屬性來確定資產(chǎn)包含的元數(shù)據(jù)格式。 此屬性返回其包含的每個元數(shù)據(jù)格式的字符串標(biāo)識符數(shù)組。 然后,使用其metadataForFormat:方法通過傳遞適當(dāng)?shù)母袷綐?biāo)識符來檢索特定于格式的元數(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的各種類方法將元數(shù)據(jù)集合過濾到一組離散值。 查找特定元數(shù)據(jù)項目的最簡單方法是按標(biāo)識符進行過濾,將標(biāo)識符和密鑰的概念分組到一個單元中。 以下示例顯示如何從公共密鑰空間中檢索標(biāo)題項。

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

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

檢索到特定元數(shù)據(jù)項后,下一步是調(diào)用其value屬性。 返回的值是采用NSObjectNSCopying協(xié)議的對象類型。 您可以手動將值轉(zhuǎn)換為適當(dāng)?shù)念愋?,但使用元?shù)據(jù)項的類型強制屬性更安全和更容易。 您可以使用其stringValue,numberValue,dateValue和dataValue屬性來輕松將該值強制為適當(dāng)?shù)念愋汀?例如,以下示例顯示如何檢索與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ù)據(jù)來增強播放app的功能。

后記

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容