AVAsset是AVFoundation最重要的類,框架把所有代碼設(shè)計(jì)圍繞資源(assert)進(jìn)行,可以說(shuō)AVFoundation就是面向資源開(kāi)發(fā)的世界。
AVAsset屬性和方法的簡(jiǎn)單介紹
//視頻時(shí)長(zhǎng)
@property (nonatomic, readonly) CMTime duration;
//默認(rèn)的速度
@property (nonatomic, readonly) float preferredRate;
//默認(rèn)音量
@property (nonatomic, readonly) float preferredVolume;
//commonMetadata屬性中包含著當(dāng)前視頻常見(jiàn)格式類型的元數(shù)據(jù)
@property (nonatomic, readonly) NSArray<AVMetadataItem *> *commonMetadata;
//metadata屬性中包含當(dāng)前視頻所有格式類型的元數(shù)據(jù)
@property (nonatomic, readonly) NSArray<AVMetadataItem *> *metadata;
//availableMetadataFormats屬性中包含當(dāng)前視頻所有可用元數(shù)據(jù)的格式類型元數(shù)據(jù)的格式類型在AVMetadataFormat中定義了很多種,常見(jiàn)的有title、creator、subject、publisher等
@property (nonatomic, readonly) NSArray<NSString *> *availableMetadataFormats;
//此資源中包含的所有曲目(AVAssetTrack),AVAsset還可以通過(guò)標(biāo)識(shí)符,媒體類型或媒體特征等信息找到相應(yīng)的曲目.
@property (nonatomic, readonly) NSArray*tracks;
//通過(guò)trackID獲得trackAVAssetTrack
-(nullable AVAssetTrack *)trackWithTrackID:(CMPersistentTrackID)trackID;
//通過(guò)指定的媒體類型返回一個(gè)AVAssetTrack數(shù)組,數(shù)組中包含著Asset中所有指定媒體類型的AVAssetTrack。如果Asset中沒(méi)有這個(gè)媒體類型的AVAssetTrack,返回一個(gè)空數(shù)組
-(NSArray<AVAssetTrack *> *)tracksWithMediaType:(NSString *)mediaType;
//通過(guò)指定的媒體特征返回AVAssetTrack數(shù)組,數(shù)組的特性與-tracksWithMediaType:類似,如果Asset中沒(méi)有這個(gè)媒體特征的AVAssetTrack,返回一個(gè)空數(shù)組。
-(NSArray<AVAssetTrack *> *)tracksWithMediaCharacteristic:(NSString *)mediaCharacteristic;
AVAsset是一個(gè)抽象不可變類,把媒體所有靜態(tài)屬性模塊化成為一個(gè)整體,比如:標(biāo)題,時(shí)長(zhǎng),元數(shù)據(jù)等, AVAsset不管你從URL還是照片庫(kù)獲取到媒體,它都會(huì)統(tǒng)一管理。
我們使用 AVAssetTrack 類代表保存在資源中的統(tǒng)一類型媒體,并對(duì)每個(gè)資源建立相應(yīng)的模型。AVAssetTrack 最常見(jiàn)的形態(tài)就是音頻和視頻流,但是它還可以表示文本、副標(biāo)題或隱藏字幕等媒體類型。
資源創(chuàng)建:
NSURL *assetURL = [NSURL URLWithString:@""];
AVAsset *asset = [AVAsset assetWithURL:assetURL];
AVAsset 是一個(gè)抽象類,意味著不能直接被實(shí)例化。當(dāng)創(chuàng)建實(shí)例時(shí),實(shí)際上是創(chuàng)建了它子類的一個(gè)實(shí)例,子類名為 AVURLAsset。有的時(shí)候我們會(huì)直接使用 AVURLAsset 這個(gè)類,因?yàn)樗试S通過(guò)字典來(lái)調(diào)整資源的創(chuàng)建方式。比如允許程序稍長(zhǎng)一點(diǎn)的加載時(shí)間,獲取更準(zhǔn)確的時(shí)長(zhǎng)和時(shí)間信息。
NSURL *assetURL = [NSURL URLWithString:@""];
NSDictionary *options = @{ AVURLAssetPreferPreciseDurationAndTimingKey : @YES };
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:assetURL options:options];
元數(shù)據(jù)格式
Apple 環(huán)境下的媒體類型主要有四種
QuickTime( mov )
MPEG-4 video( mp4 和 m4v )
MPEG-4 audio( m4a )
MPEG-Layer III audio( mp3 )
QuickTime 是由蘋(píng)果公司開(kāi)發(fā)的一種功能強(qiáng)大、跨平臺(tái)的媒體架構(gòu)。
mp4 是對(duì) MPEG-4 媒體的標(biāo)準(zhǔn)擴(kuò)展,m4v、m4a、m4p、m4b 這些變體都使用 MPEG-4 容器格式,但是包含了附加的拓展功能。m4v 文件是帶有針對(duì) FairPlay 加密和 AC3-audio 擴(kuò)展的 MPEG-4 視頻格式,如果不涉及這些 mp4 和 m4v 僅僅是擴(kuò)展名不同而已。m4a 針對(duì)音頻,讓使用者知道該文件只帶有音頻資源。m4p 是蘋(píng)果較舊的 iTunes 音頻格式。m4b 用于有聲讀物,通常包含章節(jié)標(biāo)簽和書(shū)簽功能。
mp3 文件和上面兩種有顯著區(qū)別,mp3 文件不使用容器格式,而使用編碼音頻數(shù)據(jù)。mp3 文件使用一種稱為 ID3v2 格式來(lái)保存關(guān)于音頻內(nèi)容的描述信息,包含的數(shù)據(jù)有歌曲演唱者、所屬唱片和音樂(lè)風(fēng)格等。AV Foundation 支持讀取 ID3v2 標(biāo)簽的所有版本,但是不支持寫(xiě)入,所以 AV Foundation 無(wú)法支持對(duì) mp3 進(jìn)行編碼。
異步載入
AVAsset使用一種高效的設(shè)計(jì)方法,延遲載入資源的屬性,什么時(shí)候使用,什么時(shí)候再加載.雖然這可以解決一些由于直接加載數(shù)據(jù)帶來(lái)的問(wèn)題,但是訪問(wèn)AVAsset的屬性如果耗時(shí)較長(zhǎng),而又發(fā)生在主線程,就會(huì)阻塞主線程,使界面無(wú)法響應(yīng).屬性的訪問(wèn)總是同步發(fā)生的,如果沒(méi)有預(yù)先載入,程序就會(huì)阻塞(我測(cè)了一下查詢資源的availableMetadataFormats大概花了2秒左右),所以這時(shí)候就需要異步查詢資源的屬性。
所以AVAsset和AVAssetTrack都遵守了AVAsynchronousKeyValueLoading協(xié)議,通過(guò)下面兩個(gè)方法實(shí)現(xiàn)異步查詢
-(AVKeyValueStatus)statusOfValueForKey:(NSString *)key error:(NSError * __nullable * __nullable)outError;
-(void)loadValuesAsynchronouslyForKeys:(NSArray<NSString *> *)keys completionHandler:(nullable void (^)(void))handler;
通過(guò)statusOfValueForKey方法查詢一個(gè)給定屬性的狀態(tài),該方法會(huì)返回一個(gè)枚舉類型的AVKeyValueStatus值,用于標(biāo)識(shí)查詢的屬性的狀態(tài),如果狀態(tài)不是AVKeyValueStatusLoaded,意味著此時(shí)請(qǐng)求該屬性會(huì)導(dǎo)致程序卡頓。
通過(guò)調(diào)用loadValuesAsynchronouslyForKeys: completionHandler:方法異步查詢屬性,為其提供一個(gè)屬性的數(shù)組,當(dāng)資源處于回應(yīng)請(qǐng)求狀態(tài)就會(huì)調(diào)用completionHandler代碼塊
AVAsset *asset = [AVAsset assetWithURL:assetUrl];
NSArray *keys = @[@"tracks",
@"playable",
@"duration"];
__weak typeof(asset) weakAsset = asset;
__weak typeof(self) weakSelf = self;
[asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
// check the keys
for (NSString *key in keys) {
NSError *error = nil;
AVKeyValueStatus keyStatus = [weakAsset statusOfValueForKey:key error:&error];
switch (keyStatus) {
case AVKeyValueStatusFailed:{
// failed
break;
}
case AVKeyValueStatusLoaded:{
// success
break;
}case AVKeyValueStatusCancelled:{
// cancelled
break;
}
default:
break;
}
}
}];
-
使用元數(shù)據(jù)
AVAsset 和 AVAssetTrack 都可以實(shí)現(xiàn)查詢相關(guān)元數(shù)據(jù)的功能。一般使用 AVAsset 提供的元數(shù)據(jù),當(dāng)涉及獲取曲目一級(jí)元數(shù)據(jù)等情況時(shí)會(huì)使用 AVAssestTrack。讀取具體資源元數(shù)據(jù)的接口由 AVMetadataItem 的類提供。

上圖中common部分表示,歌名,歌手,插圖等信息,AVFoundation使用鍵空間(key spaces)作為將相關(guān)鍵組合在一起的方法,比如上圖common它表示的歌手歌名就是每種格式的文件都會(huì)有的信息。
- 我們可以通過(guò)查詢資源(AVAsset)或曲目(AVAssetTrack)的commonMetadata屬性從Common鍵空間獲取元數(shù)據(jù)(歌手歌名等信息),這個(gè)屬性會(huì)返回一個(gè)包含所有可用元數(shù)據(jù)的數(shù)組.
- 通過(guò)availableMetedataFormats鍵來(lái)查詢資源中所有的元數(shù)據(jù)的格式(mp4,mp3,mov等),比如com.apple.itunes代表itunes類型文件(.mp4 m4v .m4a)。并給每個(gè)格式調(diào)用metadataForFormat:方法獲指定取元數(shù)據(jù),這個(gè)方法返回一個(gè)包含所有相關(guān)元數(shù)據(jù)信息的NSArray.
看一個(gè)例子
AVAsset *asset = [AVAsset assetWithURL:url];
[asset loadValuesAsynchronouslyForKeys:@[@"availableMetadataFormats"] completionHandler:^{
NSMutableArray *arr = [NSMutableArray array];
//如果直接調(diào)用這個(gè)asset.availableMetadataFormats是同步的,會(huì)卡界面,上面的loadValuesAsynchronouslyForKeys:方法就是先異步加載好這個(gè)availableMetadataFormats屬性
for (NSString *str in asset.availableMetadataFormats) {
[arr addObject:[asset metadataForFormat:str]];
NSLog(@"%@",[asset metadataForFormat:str]);
}
}];
查找元數(shù)據(jù)
使用metadataItemsFromArray: withKey: keySpace:從一堆元數(shù)據(jù)數(shù)組中篩選出需要的一個(gè),雖然返回是一個(gè)數(shù)組,但是其實(shí)里面通常只有一個(gè)元素
當(dāng)我們拿到一個(gè)元數(shù)據(jù)的數(shù)組時(shí),使用AVMetadataItem獲取具體元數(shù)據(jù)的值。
例如:想要拿到一個(gè)m4a音頻文件的歌手信息,需要用如下方法:
NSArray *artistMetadata = [AVMetadataItem metadataItemsFromArray:arr2[0] withKey:AVMetadataiTunesMetadataKeyArtist keySpace:AVMetadataKeySpaceiTunes];
使用AVMetadataItem
AVMetadataItem最基本的形式其實(shí)是一個(gè)封裝鍵值對(duì)的封裝器
可通過(guò)它查詢key或commonKey,查詢其是否存在于Common鍵空間中,最重要的是它對(duì)應(yīng)的value.
value屬性被定義成id<NSObject,NSCopying>形式,不過(guò)它可能是NSString,NSNumber,NSData或者NSDictionary等.
AVAsset *asset = [AVAsset assetWithURL:url];
NSArray *arr = @[@"availableMetadataFormats"];
NSMutableArray *arr2 = [NSMutableArray array];
[asset loadValuesAsynchronouslyForKeys:arr completionHandler:^{
NSError *error=nil;
//如果直接調(diào)用這個(gè)asset.availableMetadataFormats是同步的,會(huì)卡界面,上面的loadValuesAsynchronouslyForKeys:方法就是先異步加載好這個(gè)availableMetadataFormats屬性,然后現(xiàn)在就可以直接讀了。這是apple的優(yōu)化。
AVKeyValueStatus state=[asset statusOfValueForKey:@"availableMetadataFormats" error:&error];
switch (state) {
case AVKeyValueStatusLoaded:
for (NSString *format in asset.availableMetadataFormats)
{
NSArray *meatataArray=[asset metadataForFormat:format];
NSArray *artisArray=[AVMetadataItem metadataItemsFromArray:meatataArray withKey:AVMetadataCommonKeyArtist keySpace:AVMetadataKeySpaceCommon];
for (AVMetadataItem *item in artisArray)
{
NSLog(@"key=%@,value=%@",item.key,item.value);
dispatch_async(dispatch_get_main_queue(), ^{
//
});
}
}
NSLog(@"%@",@"加載成功");
break;
case AVKeyValueStatusFailed:
NSLog(@"%@",@"加載失敗");
break;
default:
break;
}
}];
大部分開(kāi)發(fā)者第一次遇到AVMetadataItem都會(huì)碰到一個(gè)常見(jiàn)問(wèn)題是:如何理解該對(duì)象的key屬性,打開(kāi)AVMetadaFormat.h文件,一看就容易明白。
保存元數(shù)據(jù)
前面提到過(guò),AVAsset是個(gè)不可以變化的類,我們不能修改,我們要使用AVAssetExportSession的類來(lái)導(dǎo)出一個(gè)新的資源副本以及元數(shù)據(jù)的改動(dòng)。AVAssetExportSession是用于將AVAsset內(nèi)容根據(jù)導(dǎo)出預(yù)設(shè)進(jìn)行編碼,并將導(dǎo)出資源寫(xiě)到磁盤(pán)中。有興趣大家可以去研究下。