文章目錄:
- 1. Using Assets - 使用Assets
Using Assets - 使用Assets
Assets can come from a file or from media in the user’s iPod library or Photo library. When you create an asset object all the information that you might want to retrieve for that item is not immediately available. Once you have a movie asset, you can extract still images from it, transcode it to another format, or trim the contents.
Assets
可以來自文件或者媒體用戶的iPod庫、圖片庫。當你創建一個 asset
對象時,所有你可能想要檢索該項目的信息不是立即可用的。一旦你有了一個電影 asset
,你可以從里面提取靜態圖像,轉換到另一個格式,或者對內容就行修剪。
Creating an Asset Object - 創建一個Asset對象
To create an asset to represent any resource that you can identify using a URL, you use AVURLAsset. The simplest case is creating an asset from a file:
為了創建一個 asset
,去代表任何你能用一個 URL
識別的資源,你可以使用 AVURLAsset .最簡單的情況是從一個文件創建一個 asset
。
NSURL *url = <#A URL that identifies an audiovisual asset such as a movie file#>;
AVURLAsset *anAsset = [[AVURLAsset alloc] initWithURL:url options:nil];
Options for Initializing an Asset - 初始化一個Asset的選擇
The AVURLAsset initialization methods take as their second argument an options dictionary. The only key used in the dictionary is AVURLAssetPreferPreciseDurationAndTimingKey. The corresponding value is a Boolean (contained in an NSValue object) that indicates whether the asset should be prepared to indicate a precise duration and provide precise random access by time.
AVURLAsset
初始化方法作為它們的第二個參數選項字典。本字典中唯一被使用的 key
是 AVURLAssetPreferPreciseDurationAndTimingKey. 相應的值是一個布爾值(包含在一個 NSValue
對象中),這個布爾值指出是否該 asset
應該準備標出一個精確的時間和提供一個以時間為種子的隨機存取。
Getting the exact duration of an asset may require significant processing overhead. Using an approximate duration is typically a cheaper operation and sufficient for playback. Thus:
- If you only intend to play the asset, either pass nil instead of a dictionary, or pass a dictionary that contains the AVURLAssetPreferPreciseDurationAndTimingKey key and a corresponding value of NO (contained in an NSValue object).
- If you want to add the asset to a composition (AVMutableComposition), you typically need precise random access. Pass a dictionary that contains theAVURLAssetPreferPreciseDurationAndTimingKey key and a corresponding value of YES (contained in an NSValue object—recall that NSNumberinherits from NSValue):
獲得一個asset的確切持續時間可能需要大量的處理開銷。使用一個近似的持續時間通常是一個更便宜的操作并且對于播放已經足夠了。因此:
- 如果你只打算播放這個
asset
, 要么傳遞一個nil
代替dictionary
,或者傳遞一個字典,這個字典包含AVURLAssetPreferPreciseDurationAndTimingKey
的key
和相應NO
(包含在一個NSValue
對象) 的值。 - 如果你想要把
asset
添加給一個composition
(AVMutableComposition), 通常你需要精確的隨機存取。傳遞一個字典(這個字典包含AVURLAssetPreferPreciseDurationAndTimingKey
key) 和一個相應的 YES 的值(YES 包含在一個NSValue
對象中,回憶一下繼承自NSValue
的 NSNmuber)
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];
Accessing the User’s Assets - 訪問用戶的Assets
To access the assets managed by the iPod library or by the Photos application, you need to get a URL of the asset you want.
- To access the iPod Library, you create an MPMediaQuery instance to find the item you want, then get its URL using MPMediaItemPropertyAssetURL.For more about the Media Library, see Multimedia Programming Guide.
- To access the assets managed by the Photos application, you use ALAssetsLibrary.
The following example shows how you can get an asset to represent the first video in the Saved Photos Album.
為了訪問由 iPod 庫或者照片應用程序管理的 assets
,你需要得到你想要 asset
的一個 URL
。
- 為了訪問 iPod 庫,創建一個 MPMediaQuery 實例去找到你想要的項目,然后使用MPMediaItemPropertyAssetURL得到它的
URL
, - 為了訪問有照片應用程序管理的
assets
,可以使用 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");
}];
Preparing an Asset for Use - 將 Asset
準備好使用
Initializing an asset (or track) does not necessarily mean that all the information that you might want to retrieve for that item is immediately available. It may require some time to calculate even the duration of an item (an MP3 file, for example, may not contain summary information). Rather than blocking the current thread while a value is being calculated, you should use the AVAsynchronousKeyValueLoading protocol to ask for values and get an answer back later through a completion handler you define using a block. (AVAsset and AVAssetTrack conform to the AVAsynchronousKeyValueLoading protocol.)
初始化一個 asset
(或者軌道)并不意味著你可能想要檢索該項的所有信息是立即可用的。這可能需要一些時間來計算一個項目的持續時間(例如一個 MP3 文件可能不包含摘要信息)。當一個值被計算的時候不應該阻塞當前線程,你應該使用AVAsynchronousKeyValueLoading 協議去請求值,通過完成處理你定義使用的一個 block
后得到答復。(AVAsset
and AVAssetTrack
遵循 AVAsynchronousKeyValueLoading
協議.)
You test whether a value is loaded for a property using statusOfValueForKey:error:. When an asset is first loaded, the value of most or all of its properties is AVKeyValueStatusUnknown. To load a value for one or more properties, you invoke loadValuesAsynchronouslyForKeys:completionHandler:. In the completion handler, you take whatever action is appropriate depending on the property’s status. You should always be prepared for loading to not complete successfully, either because it failed for some reason such as a network-based URL being inaccessible, or because the load was canceled.
測試一個值是否是使用 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;
}
}];
If you want to prepare an asset for playback, you should load its tracks property. For more about playing assets, see Playback.
如果你想準備一個 asset
去播放,你應該加載它的軌道屬性。更多有關播放 assets
,請看 Playback
Getting Still Images From a Video - 從視頻中獲取靜態圖像
To get still images such as thumbnails from an asset for playback, you use an AVAssetImageGenerator object. You initialize an image generator with your asset. Initialization may succeed, though, even if the asset possesses no visual tracks at the time of initialization, so if necessary you should test whether the asset has any tracks with the visual characteristic using tracksWithMediaCharacteristic:.
為了從一個準備播放的 asset
中得到靜態圖像,比如縮略圖,可以使用 AVAssetImageGenerator 對象。用你的 asset
初始化一個圖像發生器。不過即使 asset
進程在初始化的時候沒有視覺跟蹤,也可以成功,所以如果有必要,你應該測試一下, asset
是否有軌道有使用 tracksWithMediaCharacteristic 的視覺特征。
AVAsset anAsset = <#Get an asset#>;
if ([[anAsset tracksWithMediaType:AVMediaTypeVideo] count] > 0) {
AVAssetImageGenerator *imageGenerator =
[AVAssetImageGenerator assetImageGeneratorWithAsset:anAsset];
// Implementation continues...
}
You can configure several aspects of the image generator, for example, you can specify the maximum dimensions for the images it generates and the aperture mode using maximumSize and apertureMode respectively.You can then generate a single image at a given time, or a series of images. You must ensure that you keep a strong reference to the image generator until it has generated all the images.
你可以配置幾個圖像發生器的部分,例如,可以指定生成的圖像采用最大值,并且光圈的模式分別使用 maximumSize 和 apertureMode 。然后可以在給定的時間生成一個單獨的圖像,或者一系列圖像。你必須確定,在生成所有圖像之前,必須對圖像生成器保持一個強引用。
Generating a Single Image - 生成一個單獨的圖像
You use copyCGImageAtTime:actualTime:error: to generate a single image at a specific time. AVFoundation may not be able to produce an image at exactly the time you request, so you can pass as the second argument a pointer to a CMTime that upon return contains the time at which the image was actually generated.
使用 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);
s
Generating a Sequence of Images - 生成一系列圖像
To generate a series of images, you send the image generator a generateCGImagesAsynchronouslyForTimes:completionHandler: message. The first argument is an array of NSValue objects, each containing a CMTime structure, specifying the asset times for which you want images to be generated. The second argument is a block that serves as a callback invoked for each image that is generated. The block arguments provide a result constant that tells you whether the image was created successfully or if the operation was canceled, and, as appropriate:
- The image
- The time for which you requested the image and the actual time for which the image was generated
- An error object that describes the reason generation failed
In your implementation of the block, check the result constant to determine whether the image was created. In addition, ensure that you keep a strong reference to the image generator until it has finished creating the images.
生成一系列圖像,可以給圖像生成器發送 generateCGImagesAsynchronouslyForTimes:completionHandler: 消息。第一個參數是一個 NSValue
對象的數組,每個都包含一個 CMTime
結構體,指定了圖像想要被生成的 asset
時間。block
參數提供了一個結果,這個結果包含了告訴你是否圖像被成功生成,或者操作某些情況下被取消。結果:
- 圖像
- 你要求的圖像和圖像生成的實際時間
- 一個
error
對象,描述了生成失敗的原因
在 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");
}
}]s
You can cancel the generation of the image sequence by sending the image generator a cancelAllCGImageGeneration message.
你發送給圖像生成器一個 cancelAllCGImageGeneration 消息,可以取消隊列中的圖像生成。
Trimming and Transcoding a Movie - 微調和轉化為一個電影
You can transcode a movie from one format to another, and trim a movie, using an AVAssetExportSession object. The workflow is shown in Figure 1-1. An export session is a controller object that manages asynchronous export of an asset. You initialize the session using the asset you want to export and the name of a export preset that indicates the export options you want to apply (see allExportPresets). You then configure the export session to specify the output URL and file type, and optionally other settings such as the metadata and whether the output should be optimized for network use.
asset
一律使用“資產”代碼,切換還要加``略麻煩
你可以使用 AVAssetExportSession 對象,將一個電影的編碼進行轉換,并且對電影進行微調。工作流程如圖1-1所示。一個 export session
是一個控制器對象,管理一個資產的異步導出。使用想要導出的資產初始化一個 session
和輸出設定的名稱,這個輸出設定表明你想申請的導出選項(allExportPresets)。然后配置導出會話去指定輸出的 URL 和文件類型,以及其他可選的設定,比如元數據,是否將輸出優化用于網絡使用。
You can check whether you can export a given asset using a given preset using exportPresetsCompatibleWithAsset: as illustrated in this example:
你可以檢查你能否用給定的預設導出一個給定的資產,使用 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.
}
You complete the configuration of the session by providing the output URL (The URL must be a file URL.) AVAssetExportSession can infer the output file type from the URL’s path extension; typically, however, you set it directly using outputFileType. You can also specify additional properties such as the time range, a limit for the output file length, whether the exported file should be optimized for network use, and a video composition. The following example illustrates how to use the timeRange property to trim the movie:
完成會話的配置,是由輸出的 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;
To create the new file, you invoke exportAsynchronouslyWithCompletionHandler:. The completion handler block is called when the export operation finishes; in your implementation of the handler, you should check the session’s status value to determine whether the export was successful, failed, or was canceled:
調用 exportAsynchronouslyWithCompletionHandler: 創建新的文件。當導出操作完成的時候完成處理的 block
被調用,你應該檢查會話的 status 值,去判斷導出是否成功、失敗或者被取消。
[exportSession exportAsynchronouslyWithCompletionHandler:^{
switch ([exportSession status]) {
case AVAssetExportSessionStatusFailed:
NSLog(@"Export failed: %@", [[exportSession error] localizedDescription]);
break;
case AVAssetExportSessionStatusCancelled:
NSLog(@"Export canceled");
break;
default:
break;
}
}];
You can cancel the export by sending the session a cancelExport message.
The export will fail if you try to overwrite an existing file, or write a file outside of the application’s sandbox. It may also fail if:
- There is an incoming phone call
- Your application is in the background and another application starts playback
In these situations, you should typically inform the user that the export failed, then allow the user to restart the export.
你可以通過給會話發送一個 cancelExport 消息來取消導出。
如果你嘗試覆蓋一個現有的文件或者在應用程序的沙盒外部寫一個文件,都將會是導出失敗。如果發生下面情況也可能失敗:
- 有一個來電
- 你的應用程序在后臺并且另一個程序開始播放
在這種情況下,你通常應該通知用戶導出失敗,然后允許用戶重新啟動導出。