媒体元数据
媒体容器格式会存储有关其媒体的描述性元数据。对于开发人员来说,使用元数据具有一定的挑战性,因为每种容器格式都有自己独特的元数据格式,需要对相应格式读写操作的底层技术有所了解。不过 AV Foundation 让这一切变得简单,它提供了 AVMetadataItem
类用于统一处理媒体元数据,使得开发者不需要考虑大多数特定格式的细节。
元数据格式
加载资产的元数据
AVAsset
和 AVAssetTrack
提供了三种方法可以获取相关的元数据,要了解这三种方法的适用范围,首先要了解 keySpace
的含义。AV Foundation 使用 AVMetadataKeySpace
将各个键组合在一起的方法,可以实现对 AVMetadataItem
实例集合的筛选。
CommonMetadata
每个资源至少有一个 AVMetadataKeySpaceCommon
通用键空间供从中获取元数据。AVMetadataKeySpaceCommon
用来定义所有支持的媒体类型的键,包括诸如名称,作者,描述等常见元素,这提供了一种对所有支持的媒体格式进行一定级别的元数据标准化的过程。开发者可以通过查询 AVAsset
或者 AVAssetTrack
的 commonMetadata
属性获取元数据。
NSArray *keys = @[@"commonMetadata"];
[anAsset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
NSLog(@"CommonMetadata:%ld\n",anAsset.commonMetadata.count);
for (AVMetadataItem *item in anAsset.commonMetadata) {
NSLog(@"CommonMetadata,%@:%@\n",item.key,item.value);
}
}];
metadataForFormat
访问指定格式的元数据需要在 AVAsset
或者 AVAssetTrack
上调用 metadataForFormat
方法。这个方法包含一个用于定义数据格式的 NSString
对象返回一个包含所有相关元数据信息的 NSArray
。AVMetadataFormat.h
文件为不同的元数据格式提供对应的字符串常量。与硬编码某个具体的元数据格式字符串不同,可以通过 availableMetadataFormats
获取包含的所有元数据格式。
NSArray *keys = @[@"availableMetadataFormats"];
[anAsset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
AVKeyValueStatus status = [anAsset statusOfValueForKey:@"availableMetadataFormats" error:&error];
if(statuc == AVKeyValueStatusLoaded){
NSMutableArray *availableMetadatas = [NSMutableArray array];
for (NSString *format in anAsset.availableMetadataFormats) {
[availableMetadatas addObjectsFromArray:[anAsset metadataForFormat:format]];
}
NSLog(@"availablemetadatas.count:%ld\n",availableMetadatas.count);
for (AVMetadataItem *item in availableMetadatas) {
NSLog(@"availablemetadatas,%@:%@:%@\n",item.keySpace,item.key,item.value);
}
}
}];
注意: 调用
metadataForFormat:
时要确保availableMetadataFormats
已经加载
metadata
AV Foundation 在 iOS 8.0 提供了 metadata
方法查询 AVAsset
所有可用的元数据数组。
NSArray *keys = @[@"metadata"];
[anAsset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
NSError *error;
AVKeyValueStatus status = [anAsset statusOfValueForKey:@"metadata" error:&error];
if(status == AVKeyValueStatusLoaded){
NSLog(@"metadata:%ld\n",anAsset.metadata.count);
for (AVMetadataItem *item in anAsset.metadata) {
NSLog(@"metadatas,%@:%@\n",item.key,item.value);
}
}
}];
查找元数据
当我们得到一个包含元数据项的数组时,通常希望找到所需的具体元数据值。一个特别有效的方法是使用 AVMetadataItem
提供的便利方法,获取结果集合并对其进行筛选。 AVMetadataItem
在早期通过metadataItemsFromArray:metadatawithKey:keySpace:
过滤指定的元数据,例如,如果开发者希望获得一个 .MOV
视频文件的标题,需要按如下方法获取:
NSArray *metadata = <#AVMetadataItem 的集合#>;
NSString * keySpace = AVMetadataKeySpaceCommon;
NSString *titleKey = AVMetadataCommonKeyTitle;
NSArray *titleMetadata = [AVMetadataItem metadataItemsFromArray:metadatawithKey:AVMetadataCommonKeyTitle keySpace:AVMetadataKeySpaceCommon];
后来,上述方法已经不建议使用。提供了新的方法用于查找指定的元数据:
metadataItemsFromArray:filteredAndSortedAccordingToPreferredLanguages:
metadataItemsFromArray:filteredByIdentifier:
metadataItemsFromArray:filteredByMetadataItemFilter:
例如,查找特定元数据项的最简单方法是按 AVMetadataIdentifier
(标识符)过滤,它将键空间和键组合成一个单元。以下示例显示了如何从公共键空间中检索标题项:
NSArray *metadata = <#AVMetadataItem 的集合#>;
NSArray *metadatas = [AVMetadataItem metadataItemsFromArray:availableMetadatas filteredByIdentifier:AVMetadataCommonIdentifierTitle];
使用元数据
AVMetadataItem
最基本的形式其实是一个封装键值对的容器。可通过它定义的 AVMetadataKey commonKey
,查询其是否存在于公共键空间内,而 key
和 value
都被定义成 id <NSObject, NSCopying>
形式,它可能是 NSString
, NSNumber
等情况。如果开发者已经提前知道 value
的类型,AVMetadataItem
提供三个类型强制属性stringValue
、numberValue
和 dataValue
。
由于 AVMetadataItem
的 key
是泛类型,我们在使用时可能存在获取错误的情况,因此可以在 AVMetadataItem
上添加一个名为 keyString
的分类方法从而获取 key
的字符串:
- (NSString *)keyString {
if ([self.key isKindOfClass:[NSString class]]) { // 1
return (NSString *)self.key;
}
else if ([self.key isKindOfClass:[NSNumber class]]) {
UInt32 keyValue = [(NSNumber *) self.key unsignedIntValue]; // 2
// Most, but not all, keys are 4 characters ID3v2.2 keys are
// only be 3 characters long. Adjust the length if necessary.
size_t length = sizeof(UInt32); // 3
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) - length);
// keys are stored in big-endian format, swap
keyValue = CFSwapInt32BigToHost(keyValue); // 4
char cstring[length]; // 5
strncpy(cstring, (char *) address, length);
cstring[length] = '\0';
// Replace '©' with '@' to match constants in AVMetadataFormat.h
if (cstring[0] == '\xA9') { // 6
cstring[0] = '@';
}
return [NSString stringWithCString:(char *) cstring // 7
encoding:NSUTF8StringEncoding];
}
else {
return @"<<unknown>>";
}
}