众所周知,AVFoundation给我们提供了一个非常大的可扩展的框架,我们可以通过这个框架,对媒体资源进行捕捉,组合,播放,处理等功能.这篇文章,则是主要介绍AVFoundation框架中,一个非常重要的类AVAsset.
1.何为AVAsset
AVAsset是一个抽象类和不可变类,定义了媒体资源混合呈现的方式.可以让我们开发者在处理时基媒体提供了一种简单统一的方式,它并不是媒体资源,但是它可以作为时基媒体的容器.
2.创建方法
当我们想为一个媒体资源创建AVAsset对象时,可以通过URL对它进行初始化,URL可以是本地资源也可以是一个网络资源
<code>NSURLassetUrl = [NSURL URLWithString:@"1234"];
AVAssetasset = [AVAsset asetWithURL:assetUrl];</code>
通过对asset打印可以得知,当它通过asetWithURL方法进行创建时,实际上是创建了它子类AVUrlAsset的一个实例,而AVAsset是一个抽象类,不能直接被实例化.
通过AVUrlAsset我们可以创建一个带选项(optional)的asset,以提供更精确的时长和计时信息
An instance of NSDictionary that contains keys for specifying options for the initialization of the AVURLAsset. See** AVURLAssetPreferPreciseDurationAndTimingKey** and AVURLAssetReferenceRestrictionsKey above.
- (instancetype)URLAssetWithURL:(NSURL*)URL options:(nullableNSDictionary *)options;<
NSURL*assetUrl = [NSURLURLWithString:@"1234"];
NSDictionary*optional =@{@"AVURLAssetPreferPreciseDurationAndTimingKey":@(YES)};
AVURLAsset*urlAsset = [[AVURLAssetalloc]initWithURL:assetUrloptions:optional];
3.属性
AVAsset具有多种有用的方法和属性,比如时长,创建日期和元数据等
/* Indicates the duration of the asset. If @"providesPreciseDurationAndTiming" is NO, a best-available estimate of the duration is returned. The degree of precision preferred for timing-related properties can be set at initialization time for assets initialized with URLs. See AVURLAssetPreferPreciseDurationAndTimingKey for AVURLAsset below.
*/
@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;
/*!
@property tracks
@abstract Provides the array of AVAssetTracks contained by the asset
*/
@property (nonatomic, readonly) NSArray<AVAssetTrack *> *tracks;
@property (nonatomic, readonly, nullable) AVMetadataItem *creationDate
其中duration的属性是一个CMTime的结构体
typedef struct
{
CMTimeValue value; /*! @field value The value of the CMTime. value/timescale = seconds. */
CMTimeScale timescale; /*! @field timescale The timescale of the CMTime. value/timescale = seconds. */
CMTimeFlags flags; /*! @field flags The flags, eg. kCMTimeFlags_Valid, kCMTimeFlags_PositiveInfinity, etc. */
CMTimeEpoch epoch; /*! @field epoch Differentiates between equal timestamps that are actually different because
of looping, multi-item sequencing, etc.
Will be used during comparison: greater epochs happen after lesser ones.
Additions/subtraction is only possible within a single epoch,
however, since epoch length may be unknown/variable. */
} CMTime;
获取一个asset的时长用value/timescale即可
4.异步载入
由于是当创建时,资源就是对媒体文件进行处理.AVAsset使用一种高效的设计方法,延迟载入资源的属性,什么时候使用,什么时候再加载.虽然这可以解决一些由于直接加载数据带来的问题,但是访问AVAsset的属性如果耗时较长,而又发生在主线程,就会阻塞主线程,使界面无法响应.
所以AVAsset和AVAssetTrack都遵守了**
AVAsynchronousKeyValueLoading**协议
这是协议的两个方法
- (AVKeyValueStatus)statusOfValueForKey:(NSString *)key error:(NSError * __nullable * __nullable)outError;
- (void)loadValuesAsynchronouslyForKeys:(NSArray<NSString *> *)keys completionHandler:(nullable void (^)(void))handler;
通过statusOfValueForKey方法查询一个给定属性的状态,该方法会返回一个枚举类型的AVKeyValueStatus值,用于标识状态
通过调用loadValuesAsynchronouslyForKeys: completionHandler:方法,为其提供一个属性的数组,当资源出狱回应请求状态就会调用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:^{
dispatch_async(dispatch_get_main_queue(), ^{
// 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;
}
}
// check playable
if (!weakAsset.playable) { // 不能播放
return;
}
});
}];
通常情况下只需监听tracks一个属性即可,但是官方文档上有这么一段话,所以在代码里监听了三个属性
The completion states of the keys you specify in keys
are not necessarily the same—some may be loaded, and others may have failed. You must check the status of each key individually using thestatusOfValue(forKey:error:)
method.
当然我们亦可以通过苹果提供的方法取消监听
/*!
@method cancelLoading
@abstract Cancels the loading of all values for all observers.
@discussion Deallocation or finalization of an instance of AVAsset will implicitly cancel loading if any loading requests are still outstanding.
*/
- (void)cancelLoading;