一、資源AVAsset
AVAsset是一個抽象類和不可變類,定義媒體資源混合呈現的方式,將媒體資源的靜態屬性模塊化成為一個整體,比如標題、時長和元數據等。
AVAsset不需要考慮媒體資源所具有的兩個重要范疇:1、提供了對基本媒體格式的層抽象,不需要關注具體格式,只關注資源這個概念。2、隱藏資源的位置信息。
AVAsset本身不是媒體資源,但他可以作為時基媒體的容器,由一個或多個帶有描述自身元數據的媒體組成。
AVAssetTrack類代表保存在資源的統一類型媒體,并對每個資源建立相應的模型。
資源的曲目可以通過tracks屬性進行訪問。
二、創建資源
為現有媒體資源創建AVAsset對象時,可以通過URL對齊進行初始化來實現,可以是本地文件URL,也可以是遠程資源的URL
NSString *path = @"";
NSURL *fileUrl = [NSURL fileURLWithPath:path];
NSDictionary *dict = @{AVURLAssetPreferPreciseDurationAndTimingKey : @YES};
AVAsset *asset = [AVAsset assetWithURL:fileUrl];
AVAsset是一個抽象類,意味著它不能直接被實例化,使用assetWithURL
創建實例時,實際上是創建了他子類AVURLAsset
的一個實例。一般會直接創建這個類,因為可以通過傳遞選項字典來調整資源的創建方式。
NSURL *fileUrl;
NSDictionary *dict = @{AVURLAssetPreferPreciseDurationAndTimingKey : @YES};
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:fileUrl options:dict];
AVURLAssetPreferPreciseDurationAndTimingKey
這個布爾值支出這個asset是否應該準備標出一個準確的時間和提供一個以時間為種子的隨機存取。
只是播放asset,options傳遞nil,或者字典里對應的值是NO(包含在NSValue對象中)
給asset添加一個composition,需要精確的隨機存取,傳遞一個字典,對應值是YES(包含在NSValue對象中)
1、iOS Assets庫
用戶使用系統子弟的Camera程序或者第三方視頻捕捉程序捕捉的侍寢,通常先保存在用戶照片庫中。iOS提供的Assets庫可以實現從照片庫這種讀寫的功能。 iOS9.0以后,<AssetsLibrary/AssetsLibrary.h>
被取消。
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
[group setAssetsFilter:[ALAssetsFilter allVideos]];
NSLog(@"bbb");
if ([group numberOfAssets] > 0) {
[group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:0] options:0 usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (result) {
id representation = [result defaultRepresentation];
NSURL *url = [representation url];
AVAsset *asset = [AVAsset assetWithURL:url];
}
}];
}
} failureBlock:^(NSError *error) {
}];
2、iOS iPod庫
iPod Library歌曲(也稱作本地音樂庫歌曲)也就是用戶從iTunes中導入的歌曲。MediaPlayer
框架提供了API,實現在這個庫中查詢和獲取條目。當需要的條目找到后可以獲取其URL并使用這個URL初始化一個資源:
MediaPlayer
框架提供了一個名為MPMediaPropertyPredicate
的類,它用于構建幫助用戶在iPod苦衷查找具體內容所用的查詢語句。需要導入庫文件<MediaPlayer/MediaPlayer.h>
.
MPMediaPropertyPredicate *artistPredicate = [MPMediaPropertyPredicate predicateWithValue:@"劉德華" forProperty:MPMediaItemPropertyArtist];
MPMediaPropertyPredicate *albumPredicate = [MPMediaPropertyPredicate predicateWithValue:@"真永遠" forProperty:MPMediaItemPropertyArtist];
MPMediaPropertyPredicate *songPredicate = [MPMediaPropertyPredicate predicateWithValue:@"今天" forProperty:MPMediaItemPropertyArtist];
MPMediaQuery *query = [[MPMediaQuery alloc] init];
[query addFilterPredicate:artistPredicate];
[query addFilterPredicate:albumPredicate];
[query addFilterPredicate:songPredicate];
NSArray *results = [query items];
if (results.count > 0) {
MPMediaItem *item = results[0];
NSURL *assetURL = [item valueForProperty:MPMediaItemPropertyAssetURL];
AVAsset *asset = [AVAsset assetWithURL:assetURL];
}
三、異步載入
AVAsset
使用一種高效的設計方法,即延遲載入資源的睡醒,知道請求時才載入。這樣可以快速創建資源,而不用考慮因為立即載入相關媒體或元數據所帶來的問題。有一點,屬性的訪問總是同步發生的,如果正在請求的屬性沒有預先載入,程序就會阻塞,知道可以做出適當的響應。要解決這個問題,應該使用異步的方法來查詢資源的屬性。
AVAsset
和AVAssetTrack
都采用了AVAsynchornousKeyValueLoading
協議,該協議通過下面給出的方法實現了一步查詢屬性的功能:
NSURL *assetUrl = [[NSBundle mainBundle] URLForResource:@"崔健-假行僧" withExtension:@"mp3"];
AVAsset *asset = [AVAsset assetWithURL:assetUrl];
NSArray *keys = @[@"tracks"];
[asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
NSError *error;
AVKeyValueStatus status = [asset statusOfValueForKey:@"tracks" error:&error];
switch (status) {
case AVKeyValueStatusLoaded:
//已經加載,繼續處理
NSLog(@"loaded");
NSLog(@"%@",asset.tracks);
break;
case AVKeyValueStatusFailed:
NSLog(@"failure");
break;
case AVKeyValueStatusCancelled:
NSLog(@"canceld");
break;
case AVKeyValueStatusUnknown:
NSLog(@"unknown");
break;
default:
NSLog(@"default");
break;
}
}];
可使用statusOfValueForKey: error:
方法查詢一個給定屬性的狀態,該方法會返回一個枚舉類型的AVKeyValueStatus
值,用于表示當前所請求的屬性的狀態。如果狀態不是AVKeyValueStatusLoaded
,意味著此時請求該屬性可能導致程序卡杜,需要異步載入一個給定的屬性,可以調用loadValuesAsynchronouslyForKeys: completionHandler:
方法,為其提供一個具有一個或多個key的數組(資源的屬性名)和一個completionHandler
塊,當資源處于回應請求狀態時,就會回調這個塊方法。
注意:
completionHandler
可能會在任意一個隊列中被調用,在對用戶界面做出相應更新之前,必須先回到主隊列中。在請求多個屬性時,每次調用
loadValuesAsynchronouslyForKeys: completionHandler:
方法只會調用一次completionHandler
,調用該方法的次數并不是根據傳遞給這個方法的鍵的個數決定的。需要為每個請求的屬性調用
statusOfValueForKey: error:
不能假設所有屬性都返回相同的狀態值。
四、媒體元數據
對于元數據的使用有一定的挑戰,每個媒體類型就具有唯一的格式,并且通常要求開發者對相應格式讀寫操作的底層技術有所了解。
AVFoundation提供一套統一的方法,用來處理媒體元數據,可以讓開發者不需要考慮大多數特定格式的細節。
1、元數據格式
Apple環境下遇到的媒體類型主要有4種,分別是:QuickTime(mov)、MPEG-4 video(mp4或m4v)、MPEG-4 Audio(m4a)、MPEG-Layer III audio(mp3)。
QuickTime
MPEG-4音頻和視頻
MP3
五、使用元數據
AVAsset
和AVAssetTrack
都可以實現查詢相關元數據的功能,大部分情況使用AVAsset
提供的元數據,不過涉及獲取曲目一級元數據等情況時會使用AVAssetTrack
。
讀取具體資源元數據的接口由AVMetadataItem
提供。提供一個面向對象的接口,可以對存儲于QuickTime、MPEG-4 atom和ID3幀中的元素進行訪問。
- 鍵空間(key spaces): AV中使用鍵空間作為將相關鍵組合在一起的方法,可以實現對
AVMetadataItem
實例集合的篩選,每個資源至少包含兩個鍵空間,供從中獲取元數據。
common鍵空間用來定義所有支持的媒體類型的鍵,包括諸如曲名、歌手和插圖信息等常見元素。可以通過查詢資源或曲目的[asset commonMetadata]
屬性從common鍵空間獲取與數據,這個屬性會返回一個包含所有可用元數據的數據.
metadataForFormat:
訪問指定格式的元數據格式,返回一個包含所有相關元數據信息的NSArray。
[asset availableMetadataFormats]
返回資源中包含的所有元數據格式
1、查找元數據
2、使用AVMetadataItem
AVMetadataItem
最基本的形式是一個封裝鍵值對的封裝器。而已通過它查詢key或commonKey。value屬性被定義成id<NSObject,NSCopying>
形式,AVMetadataItem
還提供了三個類型強制屬性stringValue
、numberValue
、dataValue
,如果已經提前知道value類型,可以強制轉換。
NSURL *assetUrl = [[NSBundle mainBundle] URLForResource:@"今天" withExtension:@"mp3"];
AVAsset *asset = [AVAsset assetWithURL:assetUrl];
for (AVMetadataFormat item in [asset availableMetadataFormats]) {
NSArray *medata = [asset metadataForFormat:item];
for (AVMetadataItem *mitem in medata) {
NSLog(@"%@:%@",mitem.key,mitem.value);
}
}
// TPE1:劉德華
// TALB:真永遠
// TIT2:今天
// TYER:1995-08-01
六、創建MetaManager應用程序
七、保存元數據
AVAsset是一個不可變類,如果要保存元數據的修改,使用AVAssetExportSession
導出一個新的資源副本以及元數據改動。
AVAssetExportSession
用于將AVAsset
內容根據導出預設條件進行轉碼,并將導出資源寫到磁盤。它提供了多個功能來實現將一種格式轉換為另一種格式、修訂資源的內容、修改資源的音頻和視頻行為、寫入新的元數據。
創建一個AVAssetExportSession
實例需要提供資源和導出預設。導出預設用于確定導出內容的質量、大小等屬性。創建導出會話后,還要指定導出內容地址outputURL
,并且給出一個outputFileType
表示要導出的格式。最后調用exportAsynchronouslyWithCompletionHandler:
開始導出。
NSString *presetName = AVAssetExportPresetPassthrough;
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:self.asset presetName:presetName];
NSURL *outputUrl ;
exportSession.outputURL = outputUrl;
exportSession.outputFileType = @"";
// exportSession.metadata = [_asset availableMetadataFormats]
[exportSession exportAsynchronouslyWithCompletionHandler:^{
AVAssetExportSessionStatus status = exportSession.status;
BOOL success = (status == AVAssetExportSessionStatusCompleted);
if (success) {
}
}];