Assets可以来自用户的iPod库或照片库中的文件或媒体。当你创建一个 Assets 对象的时,可能想要检索的该项目的所有信息都不会立即可用。 一旦你有一个视频 asset,你可以从中提取静止图像,转码为另一种格式,或修剪内容。
创建一个 Asset 对象
要创建asset来表示可以使用URL标识的任何资源,请使用 AVURLAsset。最简单的情况是从一个文件创建一个资产:
NSURL *url = <#A URL that identifies an audiovisual asset such as a movie file#>;
AVURLAsset *anAsset = [[AVURLAsset alloc] initWithURL:url options:nil];
初始化 Asset 的选项
AVURLAsset初始化方法的第二个参数是一个可选字典。在这个字典中为一个的key是AVURLAssetPreferPreciseDurationAndTimingKey。对应的值是一个布尔值(包含在 NSValue 对象中)指明 asset 是否应该准备好指示精确的持续时间,并提供准确的随机访问时间。
获取 asset 的确切持续时间可能需要相当大的处理开销。使用大致的持续时间通常是更低廉的操作并且足够播放。因此:
- 如果你只打算播放 asset, 传递nil代替字典,或者传递包含 key AVURLAssetPreferPreciseDurationAndTimingKey以及对应的 value 为NO。
- 如果你想要添加 asset 到一个组合中(AVMutableComposition),你通常需要精确的随机访问。传递一个字典包含 key 为 AVURLAssetPreferPreciseDurationAndTimingKey 以及对应的 value 值为YES :
NSURL *url = <#A URL that identifies an audiovisual asset such as a movie file#>;
NSDictionary *options = @{ AVURLAssetPreferPreciseDurationAndTimingKey : @YES };
AVURLAsset *anAssetToUseInAComposition = [[AVURLAsset alloc] initWithURL:url options:options];
访问用户的 asset
要访问由iPod库或Photos应用程序管理的 asset,您需要获取所需资产的URL。
为了访问iPod库,你要创建一个 MPMediaQuery 实例对象去寻找你想要的条目,然后使用 MPMediaItemPropertyAssetURL 获取它的URL
了解更多 Media Library 相关信息,查看Multimedia Programming Guide.为了获取由 Photos app管理的 asset,可以使用 ALAssetsLibrary
以下示例展示了如果获得“已保存的照片相册”中的第一个视频的 asset :
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
// Enumerate just the photos and videos group by using ALAssetsGroupSavedPhotos.
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
// Within the group enumeration block, filter to enumerate just videos.
[group setAssetsFilter:[ALAssetsFilter allVideos]];
// For this example, we're only interested in the first item.
[group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:0]
options:0
usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) {
// The end of the enumeration is signaled by asset == nil.
if (alAsset) {
ALAssetRepresentation *representation = [alAsset defaultRepresentation];
NSURL *url = [representation url];
AVAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
// Do something interesting with the AV asset.
}
}];
}
failureBlock: ^(NSError *error) {
// Typically you should handle an error more gracefully than this.
NSLog(@"No groups");
}];
准备一个 asset 以供使用
初始化一个 asset (或者轨道)并不一定意味着可以立即获得您可能希望检索的该项目的所有信息。计算一个项目的持续时间可能需要一些时间(例如,MP3文件可能不包含摘要信息)。当计算一个值的时候不要阻塞当前线程,你应该使用 AVAsynchronousKeyValueLoading 协议来请求值,并稍后通过您使用块定义的完成处理程序返回答案。(AVAsset 和 AVAssetTrack 遵循
AVAsynchronousKeyValueLoading 协议)
你可以通过方法 statusOfValueForKey:error: 测试属性的值是否加载好了。 当一个 asset 第一次加载的,大多数的属性的值为 AVKeyValueStatusUnknown。 为了加载其中一个或者多个属性值,调用 loadValuesAsynchronouslyForKeys:completionHandler: 。在完成处理程序中,根据属性的状态采取适当的操作。你应该始终准备好加载未成功完成,要么因为某些原因(例如基于网络的URL无法访问),要么因为加载被取消而失败。
NSURL *url = <#A URL that identifies an audiovisual asset such as a movie file#>;
AVURLAsset *anAsset = [[AVURLAsset alloc] initWithURL:url options:nil];
NSArray *keys = @[@"duration"];
[asset loadValuesAsynchronouslyForKeys:keys completionHandler:^() {
NSError *error = nil;
AVKeyValueStatus tracksStatus = [asset statusOfValueForKey:@"duration" error:&error];
switch (tracksStatus) {
case AVKeyValueStatusLoaded:
[self updateUserInterfaceForDuration];
break;
case AVKeyValueStatusFailed:
[self reportError:error forAsset:asset];
break;
case AVKeyValueStatusCancelled:
// Do whatever is appropriate for cancelation.
break;
}
}];
如果您要准备 asset 进行播放,则应加载其 tracks 属性。有关播放资产的更多信息,请参阅播放 Playback
从视频中获取图片
从 asset 获取视频播放缩略图,要使用 AVAssetImageGenerator 对象。使用 asset 初始化一个图片生成器。 初始化可能成功可能失败,例如 asset 在初始化的时候没有视觉轨道,因此使用
tracksWithMediaCharacteristic: 测试 asset 是否有视觉轨道是很有必要的
AVAsset anAsset = <#Get an asset#>;
if ([[anAsset tracksWithMediaType:AVMediaTypeVideo] count] > 0) {
AVAssetImageGenerator *imageGenerator =
[AVAssetImageGenerator assetImageGeneratorWithAsset:anAsset];
// Implementation continues...
}
可以配置图像生成器的几个方面,例如,您可以分别指定其生成的图像的最大尺寸和使用maximumSize和apertureMode的光圈模式。然后,您可以在给定的时间或一系列的图片。您必须确保您保留对图像生成器的强烈参考,直到生成所有图像。
生成单张图片
可以使用 copyCGImageAtTime:actualTime:error: 在一个指定的时间点生成单张图片。AVFoundation可能无法在您请求的时间产生图像,因此您可以将第二个参数作为指向CMTime的指针,返回时包含图像实际生成的时间。
AVAsset *myAsset = <#An asset#>];
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:myAsset];
Float64 durationSeconds = CMTimeGetSeconds([myAsset duration]);
CMTime midpoint = CMTimeMakeWithSeconds(durationSeconds/2.0, 600);
NSError *error;
CMTime actualTime;
CGImageRef halfWayImage = [imageGenerator copyCGImageAtTime:midpoint actualTime:&actualTime error:&error];
if (halfWayImage != NULL) {
NSString *actualTimeString = (NSString *)CMTimeCopyDescription(NULL, actualTime);
NSString *requestedTimeString = (NSString *)CMTimeCopyDescription(NULL, midpoint);
NSLog(@"Got halfWayImage: Asked for %@, got %@", requestedTimeString, actualTimeString);
// Do something interesting with the image.
CGImageRelease(halfWayImage);
}
生成一些列图片
为了生成一系列图片,要向图片生成器发送 generateCGImagesAsynchronouslyForTimes:completionHandler: 消息。第一个参数是包含了 NSValue 对象的数组,每个包含一个 CMTime 结构体,指明你想生成图片的时间点。第二个参数是一个 block ,用作为每个生成的图像调用的回调函数。block 的参数提供了一个结果常量,告诉您图像是否已成功创建,或者操作是否已被取消,并且视情况而定:
- 图片
- 请求图片的时间以及图片生成的实际时间
- 描述原因生成失败的错误对象
在你的 block 实现中,检查结果常量以确定图像是否被创建。此外,请确保您保留对图像生成器的强烈参考,直到完成图像创建。
AVAsset *myAsset = <#An asset#>];
// Assume: @property (strong) AVAssetImageGenerator *imageGenerator;
self.imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:myAsset];
Float64 durationSeconds = CMTimeGetSeconds([myAsset duration]);
CMTime firstThird = CMTimeMakeWithSeconds(durationSeconds/3.0, 600);
CMTime secondThird = CMTimeMakeWithSeconds(durationSeconds*2.0/3.0, 600);
CMTime end = CMTimeMakeWithSeconds(durationSeconds, 600);
NSArray *times = @[NSValue valueWithCMTime:kCMTimeZero],
[NSValue valueWithCMTime:firstThird], [NSValue valueWithCMTime:secondThird],
[NSValue valueWithCMTime:end]];
[imageGenerator generateCGImagesAsynchronouslyForTimes:times
completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime actualTime,
AVAssetImageGeneratorResult result, NSError *error) {
NSString *requestedTimeString = (NSString *)
CFBridgingRelease(CMTimeCopyDescription(NULL, requestedTime));
NSString *actualTimeString = (NSString *)
CFBridgingRelease(CMTimeCopyDescription(NULL, actualTime));
NSLog(@"Requested: %@; actual %@", requestedTimeString, actualTimeString);
if (result == AVAssetImageGeneratorSucceeded) {
// Do something interesting with the image.
}
if (result == AVAssetImageGeneratorFailed) {
NSLog(@"Failed with error: %@", [error localizedDescription]);
}
if (result == AVAssetImageGeneratorCancelled) {
NSLog(@"Canceled");
}
}];
您可以通过发送图像生成器 cancelAllCGImageGeneration 消息来取消生成图像序列。
修剪和转码电影
你可以使用 AVAssetExportSession 对象将视频转码为另一个格式,或者修剪视频。工作流程如图1-1所示。一个 export session 对象是管理异步导出 asset 的控制对象。 使用要导出的 asset 初始化 session,并指定要应用导出选项的导出预设的名称(请参阅allExportPresets)。然后配置导出 session 到一个指定的输出 URL 和文件类型,以及可选的其他设置(如元数据)以及输出是否应针对网络使用进行优化。
-
Figure 1-1 The export session workflow
您可以检查是否可以使用 exportPresetsCompatibleWithAsset: 使用给定的预设导出给定资产:如下例所示:
AVAsset *anAsset = <#Get an asset#>;
NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:anAsset];
if ([compatiblePresets containsObject:AVAssetExportPresetLowQuality]) {
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]
initWithAsset:anAsset presetName:AVAssetExportPresetLowQuality];
// Implementation continues.
}
通过提供输出URL(URL必须是文件URL)来完成会话的配置。AVAssetExportSession可以从URL的路径扩展中推断输出文件类型;通常使用 outputFileType 直接设置它。您还可以指定其他属性,例如时间范围,输出文件长度限制,导出的文件是否应针对网络使用进行优化以及视频合成。以下示例说明如何使用 timeRange 属性修剪影片:
exportSession.outputURL = <#A file URL#>;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
CMTime start = CMTimeMakeWithSeconds(1.0, 600);
CMTime duration = CMTimeMakeWithSeconds(3.0, 600);
CMTimeRange range = CMTimeRangeMake(start, duration);
exportSession.timeRange = range;
要创建新文件,请调用 exportAsynchronouslyWithCompletionHandler:。完成处理 block 会在导出操作完成后被调用。在你的实现中,你应该检查session’s的 status 的值以确定导出成功,失败还是被取消了。
[exportSession exportAsynchronouslyWithCompletionHandler:^{
switch ([exportSession status]) {
case AVAssetExportSessionStatusFailed:
NSLog(@"Export failed: %@", [[exportSession error] localizedDescription]);
break;
case AVAssetExportSessionStatusCancelled:
NSLog(@"Export canceled");
break;
default:
break;
}
}];
你也可以发送 cancelExport 消息取消导出。
如果试图重写已存在的文件或者往沙盒外边的文件里导出会导致失败。也可能会因为这些原因失败:
- 有一个来电
- 您的应用程序在后台,另一个应用程序开始播放
在这些情况下,通常应该通知用户导出失败,然后允许用户重新启动导出