AVAsset
AVAsset
是一个抽象类,定义了媒体资源混合呈现的方式。它本身并不是媒体资源,但可以作为时基媒体(实时地接收并处理数据)的容器。
从用户资源库中的视频创建一个 AVAsset
:
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
//设置相册组的筛选条件,ALAssetsFilter类表示筛选条件,allPhotos代表相册只包含相片,allVideos代表只包含视频,allAssets代表包含所有资源
[group setAssetsFilter:[ALAssetsFilter allVideos]];
//按遍历顺序获取指定索引的资源,遍历顺序可以是先序或倒序
/*
enum {
NSEnumerationConcurrent = (1UL << 0),
NSEnumerationReverse = (1UL << 1),
};
typedef NSUInteger NSEnumerationOptions;
*/
[group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:0] options:0 usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (result) {
//返回一个ALAssetRepresentation
id representation = [result defaultRepresentation];
//提供一个创建AVAsset的URL
NSURL *url= [representation url];
AVAsset *asset = [AVAsset assetWithURL:url];
// 保存视频
}
}];
} failureBlock:^(NSError *error) {
NSLog(@" error: %@",[error localizedDescription]);
}];
iOS 9以后使用 PhotoKit
代替 ALAssetsLibrary
来管理相册资源,如何保存视频,如下:
NSURL * url = [NSURL URLWithString:@""];
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetChangeRequest *createAssetRequest = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:url];
PHFetchResult *result=[PHAsset fetchAssetsWithALAssetURLs:[NSArray arrayWithObject:url] options:nil];
PHAssetCollection *assetColection=[[PHAssetCollection fetchAssetCollectionsContainingAsset:[result lastObject] withType:PHAssetCollectionTypeAlbum options:nil] lastObject];
PHAssetCollectionChangeRequest *albumChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:assetColection];
PHObjectPlaceholder *assetPlaceholder = [createAssetRequest placeholderForCreatedAsset];
[albumChangeRequest addAssets:@[assetPlaceholder]];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
NSLog(@"Finished adding asset. %@", (success ? @"Success" : error));
}];
在某人的某唱片中查找一首歌,创建一个 AVAsset
:
MPMediaPropertyPredicate *artistPredicate = [MPMediaPropertyPredicate predicateWithValue:@"Leona" forProperty:MPMediaItemPropertyArtist];
MPMediaPropertyPredicate *ablumPredicate = [MPMediaPropertyPredicate predicateWithValue:@"my favor" forProperty:MPMediaItemPropertyAlbumArtist];
MPMediaPropertyPredicate *songPredicate = [MPMediaPropertyPredicate predicateWithValue:@"Because of you" forProperty:MPMediaItemPropertyTitle];
MPMediaQuery *query = [[MPMediaQuery alloc] init];
[query addFilterPredicate:ablumPredicate];
[query addFilterPredicate:artistPredicate];
[query addFilterPredicate:songPredicate];
NSArray *result = [query items];
if (result.count > 0) {
MPMediaItem *item = result[0];
NSURL *url = [item valueForProperty:MPMediaItemPropertyAssetURL];
AVAsset *asset = [AVAsset assetWithURL:url];
//...
}
异步载入
为了防止应用出现卡顿,导致系统监视器介入,并终止应用程序的运行,我们应该使用异步的方式来查询资源的属性。
AVAsset
和 AVAssetTrack
都采用了 AVAsynchronousKeyValueLoading
协议。
通过下面的协议方法实现异步查询属性的功能:
- (AVKeyValueStatus)statusOfValueForKey:(NSString *)key error:(NSError * _Nullable * _Nullable)outError;
- (void)loadValuesAsynchronouslyForKeys:(NSArray<NSString *> *)keys completionHandler:(nullable void (^)(void))handler;
NSURL * url = [[NSBundle mainBundle] URLForResource:@"sunShine" withExtension:@"mov"];
AVAsset *asset = [AVAsset assetWithURL:url];
NSArray *keys = @[@"tracks"];
//只会调用一次completionHandler块 异步载入tracks 属性
[asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
NSError *error = nil;
//需要为每个请求的属性调用 statusOfValueForKey:error: 方法 不能假设所有的属性都返回相同的属性值
AVKeyValueStatus status = [asset statusOfValueForKey:@"tracks" error:&error];
switch (status) {
case AVKeyValueStatusLoaded:
//...
break;
case AVKeyValueStatusFailed:
//...
break;
case AVKeyValueStatusCancelled:
//...
break;
default:
break;
}
}];
媒体元数据
常用的元数据保存格式有 4
种:
- QuickTime (mov)定义了.mov文件的内部结构
- MPEG-4 音频(m4a)
- 视频 (mp4 和 m4v)
- MP3 受专利限制, AV Foundation 无法支持对其编码
使用元数据:
NSURL *url = [NSURL URLWithString:@"文件地址"];
AVAsset *asset = [AVAsset assetWithURL:url];
NSArray *keys = @[@"availableMetadataFormats"];
[asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
NSMutableArray *metaData = [NSMutableArray array];
for (NSString *format in asset.availableMetadataFormats) {
//访问指定格式的元数据需要调用metadataForFormat方法
[metaData arrayByAddingObjectsFromArray:[asset metadataForFormat:format]];
}
}];
查找元数据:
NSArray *metadata = @[];
NSString *keySpace = AVMetadataKeySpaceiTunes;
NSString *artistkey = AVMetadataiTunesMetadataKeyArtist;
NSString *albumKey = AVMetadataiTunesMetadataKeyAlbum;
//通过metadataItemsFromArray方法来对集合进行筛选,得出那些匹配键和键空间标准的对象,返回一个数组只包含单个AVMetadataItem实例
NSArray *artistMetaData = [AVMetadataItem metadataItemsFromArray:metadata withKey:artistkey keySpace:keySpace];
NSArray *albumMetadata =[AVMetadataItem metadataItemsFromArray:metadata withKey:albumKey keySpace:keySpace];
AVMetadataItem *artistItem, *albumItem;
if (artistMetaData.count > 0) {
artistItem = artistMetaData[0];
}
if (albumMetadata.count > 0) {
albumItem = albumMetadata[0];
}
使用AVMetadataItem
:
AVMetadataItem
最基本的形式其实是一个封装键值对的封装器。可通过它查询 key
或 commonKey
, 查询其是否存在于commonKey
键空间中,最重要的是它对应的 value
。
但是 key
属性返回的整数形式无法知道它的含义。
#import "AVMetadataItem+DLKeyString.h"
@implementation AVMetadataItem (DLKeyString)
- (NSString *)keyString
{
//如果key是字符串直接返回
if ([self.key isKindOfClass:[NSString class]]) {
return (NSString *)self.key;
}
//如果key是无符号整型
else if ([self.key isKindOfClass:[NSNumber class ]]){
uint32_t keyValue = [(NSNumber *) self.key unsignedIntValue];
size_t length = sizeof(uint32_t);
if ((keyValue >> 24) == 0 ) -- length;
if ((keyValue >> 16) == 0 ) -- length;
if ((keyValue >> 8) == 0 ) -- length;
if ((keyValue >> 0) == 0 ) -- length;
long address = (unsigned long) &keyValue;
address += (sizeof(uint32_t) - length);
keyValue = CFSwapInt32BigToHost(keyValue);
char cstring[length];
strncpy(cstring, (char *)address, length);
cstring[length] = '\0';
if (cstring[0] == '\xA9') {
cstring[0] = '@';
}
//使用stringWithCString 初始化起将字符数组转化成一个字符串
return [NSString stringWithCString:(char *)cstring encoding:NSUTF8StringEncoding];
}
else
{
return @"<<unknow>>";
}
}
网友评论