寫在前面
喜歡AVFoundation資料的同學(xué)可以關(guān)注我的專題:《AVFoundation》專輯
也可以關(guān)注我的簡書賬號
正文
Asset可以來自用戶的iPod庫或照片庫中的文件或媒體。創(chuàng)建asset對象時,你可能無法立即獲取可能要為該項(xiàng)檢索的所有信息。擁有電影資源后,你可以從中提取靜態(tài)圖像,將其轉(zhuǎn)碼為其他格式或修剪內(nèi)容。
創(chuàng)建一個Asset對象
要創(chuàng)建Asset以表示你可以使用URL識別的任何Asset,你可以使用AVURLAsset。最簡單的情況是從文件創(chuàng)建asset:
NSURL *url = <#A URL that identifies an audiovisual asset such as a movie file#>;
AVURLAsset *anAsset = [[AVURLAsset alloc] initWithURL:url options:nil];
初始化Asset的選項(xiàng)
AVURLAsset初始化方法將選項(xiàng)字典作為其第二個參數(shù)。字典中使用的唯一鍵是AVURLAssetPreferPreciseDurationAndTimingKey。相應(yīng)的值是布爾值(包含在NSValue對象中),指示asset是否應(yīng)準(zhǔn)備好指示精確的持續(xù)時間并按時間提供精確的隨機(jī)訪問。
獲得asset的確切持續(xù)時間可能需要大量的處理開銷。使用近似持續(xù)時間通常是更便宜的操作并且足以用于回放。從而:
如果你只打算播放
asset,則傳遞nil而不是字典,或者傳遞包含AVURLAssetPreferPreciseDurationAndTimingKey鍵和相應(yīng)值NO(包含在NSValue對象中)的字典。-
如果要將
asset添加到合成(AVMutableComposition),通常需要精確的隨機(jī)訪問。傳遞包含AVURLAssetPreferPreciseDurationAndTimingKey鍵和相應(yīng)值YES的字典(包含在NSValue對象中 -recallNSNumber從NSValue繼承):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]
訪問用戶的Assets
要訪問由iPod庫或照片應(yīng)用程序管理的asset,你需要獲取所需Asset的URL。
要訪問
iPod庫,你需要創(chuàng)建一個MPMediaQuery實(shí)例來查找所需的item,然后使用MPMediaItemPropertyAssetURL獲取其URL。
有關(guān)媒體庫的更多信息,請參閱Multimedia Programming Guide。要訪問
Photos應(yīng)用程序管理的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");
}];
準(zhǔn)備可供使用的Asset
初始化asset(或track)并不一定意味著你可能要為該item檢索的所有信息都可立即使用。甚至可能需要一些時間來計(jì)算item的持續(xù)時間(MP3文件,例如,可能不包含摘要信息)。你應(yīng)該使用AVAsynchronousKeyValueLoading協(xié)議來詢問值,然后通過使用block獲得回調(diào),而不是在計(jì)算值時阻塞當(dāng)前線程。 (AVAsset和AVAssetTrack符合AVAsynchronousKeyValueLoading協(xié)議。)
你可以測試是否為使用statusOfValueForKey:error:函數(shù)的屬性加載了值。首次加載asset時,其大部分或全部屬性的值為AVKeyValueStatusUnknown。要為一個或多個屬性加載值,請調(diào)用loadValuesAsynchronouslyForKeys:completionHandler:。在完成處理程序中,你可以根據(jù)屬性的狀態(tài)采取適當(dāng)?shù)牟僮?。你?yīng)該始終準(zhǔn)備好加載到未成功完成,因?yàn)樗捎谀承┰蚴。缁诰W(wǎng)絡(luò)的URL無法訪問,或者因?yàn)樨?fù)載被取消。
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;
}
}];
如果要準(zhǔn)備要播放的資源,則應(yīng)加載其track屬性。有關(guān)播放資源的更多信息,請參閱AVFoundation編程指南04-Playback。
從視頻中獲取靜態(tài)圖片
要從資源中獲取靜態(tài)圖像(如縮略圖)以進(jìn)行播放,請使用 [AVAssetImageGenerator](https://developer.apple.com/documentation/avfoundation/avassetimagegenerator)對象。你使用asset初始化圖像生成器。但是,初始化可能會成功,即使asset在初始化時沒有視覺軌跡,因此如果有必要,你應(yīng)該使用tracksWithMediaCharacteristic:來測試asset是否具有具有視覺特征的track。
AVAsset anAsset = <#Get an asset#>;
if ([[anAsset tracksWithMediaType:AVMediaTypeVideo] count] > 0) {
AVAssetImageGenerator *imageGenerator =
[AVAssetImageGenerator assetImageGeneratorWithAsset:anAsset];
// Implementation continues...
}
你可以配置圖像生成器的多個方面,例如,你可以分別使用maximumSize和apertureMode指定它生成的圖像的最大尺寸和aperture模式。然后,你可以在給定時間生成單個圖像,或者一系列圖片。你必須確保在生成所有圖像之前保留對圖像生成器的強(qiáng)引用。
生成單個圖像
你使用copyCGImageAtTime:actualTime:error:在特定時間生成單個圖像。 AVFoundation可能無法在你請求的時間生成圖像,因此你可以將指向CMTime的指針作為第二個參數(shù)傳遞,返回時包含實(shí)際生成圖像的時間。
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);
}
生成圖像序列
要生成一系列圖像,需要向圖像生成器發(fā)送generateCGImagesAsynchronouslyForTimes:completionHandler:消息。第一個參數(shù)是NSValue對象數(shù)組,每個對象包含一個CMTime結(jié)構(gòu),指定要為其生成圖像的asset時間。第二個參數(shù)是一個block,用作為生成的每個圖像調(diào)用的回調(diào)。block參數(shù)提供結(jié)果常量,告訴你圖像是否已成功創(chuàng)建或操作是否已取消,并且視情況而定:
- 圖像
- 你請求圖像的時間以及生成圖像的實(shí)際時間
- 描述生成失敗原因的錯誤對象
在block的實(shí)現(xiàn)中,需要檢查結(jié)果常量以確定是否已創(chuàng)建映像。此外,需要確保在完成圖像創(chuàng)建之前對圖像生成器保持強(qiáng)引用。
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");
}
}];
你可以通過向圖像生成器發(fā)送cancelAllCGImageGeneration消息來取消生成圖像序列。
修剪和轉(zhuǎn)碼視頻
你可以使用AVAssetExportSession對象將影片從一種格式轉(zhuǎn)碼為另一種格式,并修剪影片。工作流程如圖1-1所示。導(dǎo)出會話是管理asset異步導(dǎo)出的控制器對象。你可以使用要導(dǎo)出的asset和導(dǎo)出預(yù)設(shè)的名稱初始化會話,該導(dǎo)出預(yù)設(shè)指示要應(yīng)用的導(dǎo)出選項(xiàng)(請參閱allExportPresets)。然后,配置導(dǎo)出會話以指定輸出URL和文件類型,以及可選的其他設(shè)置,例如元數(shù)據(jù)以及是否應(yīng)針對網(wǎng)絡(luò)使用優(yōu)化輸出。

你可以使用exportPresetsCompatibleWithAsset:檢查是否可以使用給定預(yù)設(shè)導(dǎo)出給定asset:如下例所示:
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的路徑擴(kuò)展中推斷輸出文件類型;但是,通常使用outputFileType直接設(shè)置它。你還可以指定其他屬性,例如時間范圍,輸出文件長度的限制,是否應(yīng)針對網(wǎng)絡(luò)使用優(yōu)化導(dǎo)出的文件以及視頻合成。以下示例說明如何使用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;
要創(chuàng)建新文件,請調(diào)用exportAsynchronouslyWithCompletionHandler:。導(dǎo)出操作完成時調(diào)用完成處理程序block;在執(zhí)行處理程序時,應(yīng)檢查會話的status值以確定導(dǎo)出是成功,失敗還是已取消:
[exportSession exportAsynchronouslyWithCompletionHandler:^{
switch ([exportSession status]) {
case AVAssetExportSessionStatusFailed:
NSLog(@"Export failed: %@", [[exportSession error] localizedDescription]);
break;
case AVAssetExportSessionStatusCancelled:
NSLog(@"Export canceled");
break;
default:
break;
}
}];
你可以通過向會話發(fā)送cancelExport消息來取消導(dǎo)出。
如果你嘗試覆蓋現(xiàn)有文件,或者在應(yīng)用程序的沙箱之外寫入文件,則導(dǎo)出將失敗。它可能也會失敗,如果:
- 有一個來電
- 你的應(yīng)用程序在后臺,另一個應(yīng)用程序開始播放
在這些情況下,你通常應(yīng)該通知用戶導(dǎo)出失敗,然后允許用戶重新啟動導(dǎo)出。
| 上一章 | 目錄 | 下一章 |
|---|