AVF 5 - 资源和元数据
文章目录
一、简要说明
相关类
- AVAsset
- AVURLAsset
- AVFragmentedAsset
- AVMetadataItem 具体资源元数据
- AVAssetTrack 曲目等一级元数据
- AVAssetTrackGroup
二、使用
1、获取 AVAsset 信息
@implementation AssetManager
- (void)setup{
NSString *fileName = @"Drums.m4a";
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:fileName
withExtension:nil];
AVURLAsset *asset = [AVURLAsset assetWithURL:fileURL];
[self getInfoOfAsset:asset]; //
}
- (void)getInfoOfAsset:(AVAsset *)asset{
CMTime dur = asset.duration; //
CMTime minimumTimeOffsetFromLive = asset.minimumTimeOffsetFromLive;
NSLog(@"dur : %lld, %d",dur.value, dur.timescale); // dur : 314368, 44100
NSLog(@"preferredRate : %f ",asset.preferredRate ); // 1.000000
NSLog(@"preferredVolume : %f ",asset.preferredVolume ); // 1.000000
NSLog(@"naturalSize : %@ ",NSStringFromSize(asset.naturalSize) ); // {0, 0}
// NSLog(@"preferredDisplayCriteria : %@ ",asset.preferredDisplayCriteria ); //
NSLog(@"providesPreciseDurationAndTiming : %d ",asset.providesPreciseDurationAndTiming ); //
NSLog(@"referenceRestrictions : %lu ",(unsigned long)asset.referenceRestrictions ); // 0
AVMetadataItem *creationDate = asset.creationDate; //
[self getInfoOfMetadataItem:creationDate]; //
NSLog(@"creationDate : %@ ",asset.creationDate );
/*
AVMetadataItem: 0x600000024050, identifier=common/creationDate, keySpace=comn, key class = __NSCFConstantString, key=creationDate, commonKey=creationDate, extendedLanguageTag=(null), dataType=(null), time={INVALID}, duration={INVALID}, startDate=(null), extras={ }, value class=__NSTaggedDate, value=2015-05-28 22:50:49 +0000>
*/
NSLog(@"preferredVolume : %f ",asset.preferredVolume ); // 1.000000
NSArray *tracks = asset.tracks;
NSLog(@"tracks : %@ ",asset.tracks );
/*
tracks : (
"<AVAssetTrack: 0x6000000242a0, trackID = 1, mediaType = soun>"
)
*/
for (AVAssetTrack *track in tracks) {
[self getInfoOfAssetTrack:track]; //
}
NSArray *trackGroups = asset.trackGroups; //
for (AVAssetTrackGroup *trackGroup in trackGroups) {
[self getInfoOfAssetTrackGroup:trackGroup]; //
}
NSLog(@"lyrics : %@ ",asset.lyrics ); // (null)
NSArray<AVMetadataItem *> *commonMetadata = asset.commonMetadata; //
for (AVMetadataItem *item in commonMetadata) {
[self getInfoOfMetadataItem:item]; //
}
NSArray<AVMetadataItem *> *metadata = asset.metadata; //
for (AVMetadataItem *item in metadata) {
[self getInfoOfMetadataItem:item]; //
}
NSArray<AVMetadataFormat> *availableMetadataFormats = asset.availableMetadataFormats; //
for (AVMetadataFormat format in availableMetadataFormats) {
// [self getInfoOfMetadataFormat:format]; //
NSLog(@"MetadataFormat : %@",format); // com.apple.itunes, org.mp4ra
}
}
- (void)getInfoOfAssetTrack:(AVAssetTrack *)assetTrack{
NSLog(@"\n---------------assetTrack"); //
NSLog(@"trackID : %d ",assetTrack.trackID); // 1
NSLog(@"mediaType : %@ ",assetTrack.mediaType); // soun
NSLog(@"formatDescriptions : %@ ",assetTrack.formatDescriptions);
/*
(
"<CMAudioFormatDescription 0x600003305a40 [0x7fff915fb690]> {\n\tmediaType:'soun' \n\tmediaSubType:'aac ' \n\tmediaSpecific: {\n\t\tASBD: {\n\t\t\tmSampleRate: 44100.000000 \n\t\t\tmFormatID: 'aac ' \n\t\t\tmFormatFlags: 0x0 \n\t\t\tmBytesPerPacket: 0 \n\t\t\tmFramesPerPacket: 1024 \n\t\t\tmBytesPerFrame: 0 \n\t\t\tmChannelsPerFrame: 2 \n\t\t\tmBitsPerChannel: 0 \t} \n\t\tcookie: {<CFData 0x6000029057a0 [0x7fff915fb690]>{length = 39, capacity = 39, bytes = 0x03808080220000000480808014401500 ... 1210068080800102}} \n\t\tACL: {Stereo (L R)}\n\t\tFormatList Array: {\n\t\t\tIndex: 0 \n\t\t\tChannelLayoutTag: 0x650002 \n\t\t\tASBD: {\n\t\t\tmSampleRate: 44100.000000 \n\t\t\tmFormatID: 'aac ' \n\t\t\tmFormatFlags: 0x0 \n\t\t\tmBytesPerPacket: 0 \n\t\t\tmFramesPerPacket: 1024 \n\t\t\tmBytesPerFrame: 0 \n\t\t\tmChannelsPerFrame: 2 \n\t\t\tmBitsPerChannel: 0 \t}} \n\t} \n\textensions: {{\n VerbatimISOSampleEntry = {length = 87, bytes = 0x00000057 6d703461 00000000 00000001 ... 12100680 80800102 };\n}}\n}"
)
*/
NSLog(@"playable : %d ",assetTrack.playable); // 1
NSLog(@"decodable : %d ",assetTrack.decodable); // 1
NSLog(@"enabled : %d ",assetTrack.enabled); // 1
NSLog(@"selfContained : %d ",assetTrack.selfContained); // 1
NSLog(@"totalSampleDataLength : %lld ",assetTrack.totalSampleDataLength); // 170679
NSLog(@"decodable : %d ",assetTrack.decodable); // 1
NSLog(@"timeRange : %d",assetTrack.timeRange); // 1
NSLog(@"naturalTimeScale : %d",assetTrack.naturalTimeScale); // 44100
NSLog(@"estimatedDataRate : %f",assetTrack.estimatedDataRate); // 191544.781250
NSLog(@"languageCode : %@",assetTrack.languageCode); // und
NSLog(@"extendedLanguageTag : %@",assetTrack.extendedLanguageTag); // (null)
NSLog(@"naturalSize : %@",NSStringFromSize(assetTrack.naturalSize)); // {0, 0}
NSLog(@"preferredVolume : %f",assetTrack.preferredVolume); // 1.000000
NSLog(@"hasAudioSampleDependencies : %d",assetTrack.hasAudioSampleDependencies); // 0
NSLog(@"nominalFrameRate : %f",assetTrack.nominalFrameRate); // 43.066406
NSLog(@"requiresFrameReordering : %d",assetTrack.requiresFrameReordering); // 0
NSArray<AVAssetTrackSegment *> *segments = assetTrack.segments; // 1
for (AVAssetTrackSegment *segment in segments) {
// [self getInfoOfMetadataFormat:format]; //
}
// NSArray<AVMetadataItem *> *commonMetadata = asset.commonMetadata; //
// for (AVMetadataItem *item in commonMetadata) {
// [self getInfoOfMetadataItem:item]; //
// }
//
// NSArray<AVMetadataItem *> *metadata = asset.metadata; //
// for (AVMetadataItem *item in metadata) {
// [self getInfoOfMetadataItem:item]; //
// }
//
// NSArray<AVMetadataFormat> *availableMetadataFormats = asset.availableMetadataFormats; //
// for (AVMetadataFormat format in availableMetadataFormats) {
// [self getInfoOfMetadataFormat:format]; //
// }
NSLog(@"canProvideSampleCursors : %d",assetTrack.canProvideSampleCursors); // 1
}
- (void)getInfoOfAssetTrackGroup:(AVAssetTrackGroup *)assetTrackGroup{
NSLog(@"assetTrackGroup.trackIDs : %@",assetTrackGroup.trackIDs); //
}
- (void)getInfoOfMetadataItem:(AVMetadataItem *)item{
NSLog(@"\n---------------AVMetadataItem"); //
NSLog(@"identifier : %@ ",item.identifier); // itlk/com.apple.iTunes.iTunSMPB
NSLog(@"extendedLanguageTag : %@ ",item.extendedLanguageTag); // (null)
NSLog(@"locale : %@ ",item.locale); //
NSLog(@"dataType : %@ ",item.dataType); // com.apple.metadata.datatype.UTF-8
NSLog(@"extraAttributes : %@ ",item.extraAttributes);
/*
{
dataType = 1;
dataTypeNamespace = "com.apple.itunes";
}
*/
NSLog(@"startDate : %@ ",item.startDate); //
NSLog(@"stringValue : %@ ",item.stringValue); // 00000000 00000CC6 00000193 000000000004BDA7 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
NSLog(@"numberValue : %@ ",item.numberValue); //
NSLog(@"dateValue : %@ ",item.dateValue); // Fri May 29 06:50:49 2015
NSLog(@"key : %@ ",item.key); // com.apple.iTunes.iTunSMPB
NSLog(@"commonKey : %@ ",item.commonKey);
NSLog(@"keySpace : %@ ",item.keySpace); // itlk
NSLog(@"dateValue : %@ ",item.dateValue);
}
2、使用 AVMetadataItem
- (void)getMetadata{
NSString *fileName = @"Drums.m4a";
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:fileName
withExtension:nil];
AVURLAsset *asset = [AVURLAsset assetWithURL:fileURL];
NSArray *formats = [asset availableMetadataFormats];
NSLog(@"formats : %@",formats);
/*
formats : (
"com.apple.itunes",
"org.mp4ra"
)
*/
for (AVMetadataFormat item in formats) {
NSArray *medata = [asset metadataForFormat:item];
for (AVMetadataItem *mitem in medata) {
NSLog(@"%@ : %@",mitem.key,mitem.value);
// com.apple.iTunes.iTunSMPB : 00000000 00000CC6 00000193 000000000004BDA7 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
}
}
}
3、AVAsynchronousKeyValueLoading 懒惰加载
由于多媒体文件一般比较大,获取或计算出Asset中的属性非常耗时,apple 对Asset的属性采用了懒惰加载模式。
在创建 AVAsset 的时候,只生成一个实例,并不初始化属性。
只有当第一次访问属性时,系统才会根据多媒体中的数据初始化这个属性。
由于不用同时加载所有属性,耗时问题得到了一定缓解。
但是属性加载在计算量比较大的时候仍旧可能会阻塞线程。为了解决这个问题,AVFoundation提供了 AVAsynchronousKeyValueLoading 协议,可以异步加载属性:
-loadValuesAsynchronouslyForKeys:completionHandler:
方法用来异步加载属性,通过keys传入要加载的key数组,在handler中做加载完成的操作。
-statusOfValueForKey:error:
方法可以获得属性的加载状态,如果是AVKeyValueStatusLoaded状态,表示已经加载完成。
- (void)cancelLoading;
取消加载
使用示例
AVKeyValueStatus commonStatus =
[self.asset statusOfValueForKey:@"commonMetadata" error:nil];
AVKeyValueStatus formatsStatus =
[self.asset statusOfValueForKey:@"availableMetadataFormats" error:nil];
/* ------ */
NSArray *keys = @[@"commonMetadata", @"availableMetadataFormats"];
[self.asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
AVKeyValueStatus commonStatus =
[self.asset statusOfValueForKey:COMMON_META_KEY error:nil];
AVKeyValueStatus formatsStatus =
[self.asset statusOfValueForKey:AVAILABLE_META_KEY error:nil];
self.prepared = (commonStatus == AVKeyValueStatusLoaded) && // 3
(formatsStatus == AVKeyValueStatusLoaded);
if (self.prepared) {
for (AVMetadataItem *item in self.asset.commonMetadata) { // 4
//NSLog(@"%@: %@", item.keyString, item.value);
[self.metadata addMetadataItem:item withKey:item.commonKey];
}
for (id format in self.asset.availableMetadataFormats) { // 5
if ([self.acceptedFormats containsObject:format]) {
NSArray *items = [self.asset metadataForFormat:format];
for (AVMetadataItem *item in items) {
//NSLog(@"%@: %@", item.keyString, item.value);
[self.metadata addMetadataItem:item
withKey:item.keyString];
}
}
}
}
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(self.prepared);
});
4、数据转换
- (id)displayValueFromMetadataItem:(AVMetadataItem *)item {
NSNumber *number = nil;
NSNumber *count = nil;
if ([item.value isKindOfClass:[NSString class]]) { // 1
NSArray *components =
[item.stringValue componentsSeparatedByString:@"/"];
number = @([components[0] integerValue]);
count = @([components[1] integerValue]);
}
else if ([item.value isKindOfClass:[NSData class]]) { // 2
NSData *data = item.dataValue;
if (data.length == 6) {
uint16_t *values = (uint16_t *)[data bytes];
if (values[1] > 0) {
number = @(CFSwapInt16BigToHost(values[1])); // 3
}
if (values[2] > 0) {
count = @(CFSwapInt16BigToHost(values[2])); // 4
}
}
}
NSMutableDictionary *dict = [NSMutableDictionary dictionary]; // 5
[dict setObject:number ?: [NSNull null] forKey:THMetadataKeyDiscNumber];
[dict setObject:count ?: [NSNull null] forKey:THMetadataKeyDiscCount];
return dict;
}
5、保存元数据 AVAssetExportSession
- (void)save{
NSString *fileName = @"Drums.m4a";
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:fileName
withExtension:nil];
AVURLAsset *asset = [AVURLAsset assetWithURL:fileURL];
// AVAssetExportPresetPassthrough
NSString *presetName = AVAssetExportPresetAppleM4A; // 注意此处格式,如果不对应也会转换失败
AVAssetExportSession *session =
[[AVAssetExportSession alloc] initWithAsset:asset
presetName:presetName];
NSURL *outputURL = [NSURL fileURLWithPath:@"/Users/user/Desktop/001.m4a"];
session.outputURL = outputURL;
session.outputFileType = [self fileTypeForURL:fileURL];
// session.metadata = [self.metadata metadataItems];
[session exportAsynchronouslyWithCompletionHandler:^{
AVAssetExportSessionStatus status = session.status;
BOOL success = (status == AVAssetExportSessionStatusCompleted);
if (success) {
[[NSWorkspace sharedWorkspace] selectFile:outputURL.path inFileViewerRootedAtPath:nil];
}
NSLog(@"export : %d",success);
}];
}