ios开发音视频

三、资源和元数据 AVAsset

2018-11-14  本文已影响15人  smallLabel

简介

    AV Foundation 最重要的类就是AVAsset,也是AV Foundation设计的核心。AVAsset是一个抽象类和不可变类,定义了媒体资源混合呈现的方式,讲媒体资源的静态属性模块化成一个整体,如标题、时长、元数据、作者等。

    AVAsset由一个或多个带有描述自身元数据的媒体组成。AVAssetTrack类是某种类型的媒体,如音频、视频流等。以下是AVAssetTrack的AVMediaType属性,可以看出还可以表示文本、子标题等。

AVF_EXPORT AVMediaType const AVMediaTypeVideo                 NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMediaType const AVMediaTypeAudio                 NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMediaType const AVMediaTypeText                  NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMediaType const AVMediaTypeClosedCaption         NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMediaType const AVMediaTypeSubtitle              NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMediaType const AVMediaTypeTimecode              NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMediaType const AVMediaTypeMetadata              NS_AVAILABLE(10_8, 6_0);
AVF_EXPORT AVMediaType const AVMediaTypeMuxed                 NS_AVAILABLE(10_7, 4_0);

AVAsset与AVAssetTrack关系如图所示:


AVAsset_track.png

异步载入

如果想要获取一个媒体的某个属性,可能需要读取整个媒体才能获取,如果在主线程就可能造成阻塞。为了提高载入效率。AVAsset可以延迟载入资源的属性,当请求属性时才载入相应的元数据或媒体。
AVAsset和AVAssetTrack都遵循AVAsynchronousKeyValueLoading协议,该协议提供了异步查询属性的接口。

@protocol AVAsynchronousKeyValueLoading
@required
- (AVKeyValueStatus)statusOfValueForKey:(NSString *)key error:(NSError * _Nullable * _Nullable)outError;
- (void)loadValuesAsynchronouslyForKeys:(NSArray<NSString *> *)keys completionHandler:(nullable void (^)(void))handler;
@end

statusOfValueForKey::查询一个给定属性的状态,同时返回一个枚举值,表示当前要请求的属性的状态,如果该属性状态不为AVKeyValueStatusLoaded,则需要调用loadValuesAsynchronouslyForKeys:completionHandler: 异步载入一组给定的属性。当资源加载完成时触发完成block。
需要注意的是:完成回调坑你会在任意一个队列中调用,因此更新UI前需要回到主队列;一次请求多个属性时,完成回调只会触发一次,并且每个属性返回的状态可能都不一样;

媒体文件结构

元数据格式

一般在Apple环境下媒体类型主要有4中,分别是QuickTime(.mov) 、MPEG-4 video(.mp4/.m4v)、 MPEG-4 audio(.m4a) 、MPEG-Layer III audio(.mp3)。
下面以QuickTime为例,我们可以用Atom Inspector工具打开.mov文件,查看文件结构。如图:

atom.png

QuickTime文件由一种称为atoms的数据结构组成。一个atom包含了描述媒体资源某一方面的数据或者包含其他atom。
在moov目录下有一个mvhd,即movie header atom,里面包含有整个视频的描述,如duration、rate、volume、created等基本属性,在AVAsset中都有对应的属性。

@property (nonatomic, readonly) CMTime duration;

/*  indicates the natural rate at which the asset is to be played; often but not always 1.0
*/
@property (nonatomic, readonly) float preferredRate;

/*  indicates the preferred volume at which the audible media of an asset is to be played; often but not always 1.0
*/
@property (nonatomic, readonly) float preferredVolume;

下面两个track,说明该视频文件有两个轨道。


track.png

tkhd中包含有duration、flags、created等属性。其中track id是视频文件中的唯一标识码。AVAsset可以通过该id获取AVAssetTrack.

- (nullable AVAssetTrack *)trackWithTrackID:(CMPersistentTrackID)trackID;

另外还可以通过其他方法获取AVAssetTrack。

@property (nonatomic, readonly) NSArray<AVAssetTrack *> *tracks;
- (NSArray<AVAssetTrack *> *)tracksWithMediaType:(AVMediaType)mediaType;
- (NSArray<AVAssetTrack *> *)tracksWithMediaCharacteristic:(AVMediaCharacteristic)mediaCharacteristic;

MP3是比较特殊的文件。MP3文件不使用容器格式,而是使用编码音频数据,其格式为ID3v2。AV Foundation支持所有ID3v2标签格式的读取操作,但是不支持写入。MP3格式收到专利限制,所以AV Foundation无法支持对MP3或ID3数据进行编码。

使用元数据

获取元数据

AVAsset和AVAssetTrack提种方法可以获取相关的元数据。读取具体元数据的接口由名为AVMetadataItem的类提供,AVMetadataItem其实就是数据模型类,保存这作者时长、创建时间等。获取元数据可通过一下三种方式获取。

/*  Provides access to an array of AVMetadataItems for each common metadata key for which a value is available; items can be filtered according to language via +[AVMetadataItem metadataItemsFromArray:filteredAndSortedAccordingToPreferredLanguages:] and according to identifier via +[AVMetadataItem metadataItemsFromArray:filteredByIdentifier:].        */
@property (nonatomic, readonly) NSArray<AVMetadataItem *> *commonMetadata;

//  Provides access to an array of AVMetadataItems for all metadata identifiers for which a value is available; items can be filtered according to language via +[AVMetadataItem metadataItemsFromArray:filteredAndSortedAccordingToPreferredLanguages:] and according to identifier via +[AVMetadataItem metadataItemsFromArray:filteredByIdentifier:].
@property (nonatomic, readonly) NSArray<AVMetadataItem *> *metadata NS_AVAILABLE(10_10, 8_0);

//  Provides an NSArray of NSStrings, each representing a metadata format that's available to the asset (e.g. ID3, iTunes metadata, etc.). Metadata formats are defined in AVMetadataFormat.h.
@property (nonatomic, readonly) NSArray<NSString *> *availableMetadataFormats;

commonMetadata:当前视频常见格式类型的元数据;
metadata:当前视频所有格式类型的元数据;
availableMetadataFormats:当前视频所有可用元数据的格式类型;
metadataForFormat:查询特定格式的元数据集合。
元数据类型格式可以在AVMetadataFormat.h中查询。如下所示:

// QuickTimeUserData
AVF_EXPORT AVMetadataFormat const AVMetadataFormatQuickTimeUserData   
// ISO UserData
AVF_EXPORT AVMetadataFormat const AVMetadataFormatISOUserData 
// iTunesMetadata
AVF_EXPORT AVMetadataFormat const AVMetadataFormatiTunesMetadata    
// ID3Metadata
AVF_EXPORT AVMetadataFormat const AVMetadataFormatID3Metadata   
// QuickTimeMetadata
AVF_EXPORT AVMetadataFormat const AVMetadataFormatQuickTimeMetadata  
// HTTP Live Streaming metadata
AVF_EXPORT AVMetadataFormat const AVMetadataFormatHLSMetadata   
// Metadata format for AVMetadataItems of unknown provenance. This can occur when metadata is provided generically by an intermediate interface, such as AudioToolbox's AudioFile interface.
AVF_EXPORT AVMetadataFormat const AVMetadataFormatUnknown        

查询元数据内容

如果想要查询元数据中的某个字段,可按照下面的方法获取:

//获取演奏者和唱片元数据
NSArray *items = asset.metadata;
        NSString *keySpace = AVMetadataKeySpaceiTunes;
        NSString *artitsKey = AVMetadataiTunesMetadataKeyArtist;
        NSString *albumKey = AVMetadataiTunesMetadataKeyAlbum;
        NSArray *artistData = [AVMetadataItem metadataItemsFromArray:items withKey:artitsKey keySpace:keySpace];
        NSArray *albumData = [AVMetadataItem metadataItemsFromArray:items withKey:albumKey keySpace:keySpace];
        
        if (artistData.count > 0) {
            NSLog(@"%@", artistData.firstObject);
        }
        if (albumData.count > 0) {
            NSLog(@"%@", albumData.firstObject);
        }

AVMetadataItem最基本的形式是一个封装键值对的封装器,可以通过查询key/commonKey和value获取内容。

/* provides the value of the metadata item */
@property (nonatomic, readonly, copy, nullable) id<NSObject, NSCopying> value;
/* indicates the key of the metadata item */
@property (nonatomic, readonly, copy, nullable) id<NSObject, NSCopying> key;

/* indicates the common key of the metadata item */
@property (nonatomic, readonly, copy, nullable) AVMetadataKey commonKey;

可以看到key和value是id<NSObject, NSCopying>类型。如果提前知道value的类型,那么可以直接调用对应方法转换:

/* provides the value of the metadata item as a string; will be nil if the value cannot be represented as a string */
@property (nonatomic, readonly, nullable) NSString *stringValue;

/* provides the value of the metadata item as an NSNumber. If the metadata item's value can't be coerced to a number, @"numberValue" will be nil. */
@property (nonatomic, readonly, nullable) NSNumber *numberValue;

/* provides the value of the metadata item as an NSDate. If the metadata item's value can't be coerced to a date, @"dateValue" will be nil. */
@property (nonatomic, readonly, nullable) NSDate *dateValue;

/* provides the raw bytes of the value of the metadata item */
@property (nonatomic, readonly, nullable) NSData *dataValue;

这里需要注意的是,如果直接打印key,可能并不会得到预期结果,有可能是一个数字,这时我们需要做转换。打印出来可能是这样的效果:

avmetadataitem.png
- (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>>";
    }
}
上一篇下一篇

猜你喜欢

热点阅读