AVFoundation編程指南1-使用 Assets

轉載自:chenjiang3的技術博客html

AVFoundation Programming Guide

建立assert對象

爲了建立一個由URL標識的表明任何資源的assert對象,可使用AVURLAssert,最簡單的是從文件裏建立一個assert對象:ios

NSURL *url = <#A URL that identifies an audiovisual asset such as a movie file#>;
AVURLAsset *anAsset = [[AVURLAsset alloc] initWithURL:url options:nil];

Asset 初始化的可選操做

AVURLAsset初始化方法的第二個參數使用一個dictionary,這個dictionary裏的惟一一個key是 AVURLAssetPreferPreciseDurationAndTimingKey,它的value是一個boolean類型(用NSValue包裝的對象),這個值表示asset是否提供一個精確的duration。
獲取asset精確的duration須要不少處理時間,使用一個預估的duration效率比較高而且對播放來講足夠。所以:
· 若是你想要播放asset,初始化方法傳nil就好了,而不是一個dictionry,或者傳一個以AVURLAssetPreferPreciseDurationAndTimingKeydictionary爲key,值爲NO的一個dictionary。
· 若是你想把asset加到一個composition中,你須要一個精確的訪問權限,這時你能夠傳一個dictionary,這個dictionary的一組鍵值對爲數組

AVURLAssetPreferPreciseDurationAndTimingKey和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 library 和相冊裏asset,你須要獲取asset的URL。
· 爲了訪問ipod Library,須要建立MPMediaQuery對象來找到你想要對象,而後經過MPMediaItemPropertyAssetURL獲取它的URL。要想獲取更多關於Media Library,查看Multimedia Programming Guide.
· 爲了訪問相冊,可使用ALAssetsLibrary.
下面的例子用來獲取相冊裏面的第一個視頻:
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(或者track)並非表示asset裏面全部的信息都是立刻可用的。它須要一些時間去計算,即便是durtation(好比沒有摘要信息的mp3文件),你應該使用AVAsynchronousKeyValueLoading協議獲取這些值,經過- loadValuesAsynchronouslyForKeys:completionHandler:在handler裏面獲取你要的值。
你能夠用statusOfValueForKey:error:測試一個屬性的value是否成功獲取,當一個assert第一次被加載,大多數屬性的值是AVKeyValueStatusUnknown狀態,爲了獲取一個或多個屬性的值,你要調用loadValuesAsynchronouslyForKeys:completionHandler:,在comletiton handler裏面,你能夠根據屬性的狀態作任何合適的處理。你要處理加載沒有成功的狀況,可能由於一些緣由好比網絡不可鏈接,又或者loading被取消。session

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屬性。app

從video中獲取靜態圖片

從asset中獲取靜態圖片(好比說縮略圖),你能夠用AVAssetImageGenerator對象。能夠用asset初始化一個AVAssetImageGenerator對象.即便asset在初始化的時候沒有可見的track也能成功,因此你應該檢測asset是否有track,使用tracksWithMediaCharacteristic:異步

AVAsset anAsset = <#Get an asset#>;
if ([[anAsset tracksWithMediaType:AVMediaTypeVideo] count] > 0) {
    AVAssetImageGenerator *imageGenerator =
        [AVAssetImageGenerator assetImageGeneratorWithAsset:anAsset];
    // Implementation continues...
}

你還能夠設置imagegenerator的其餘屬性,好比,你能夠指定生成圖片的最大的分辨率,你能夠生成指定時間的一張圖片,或者一系列圖片。你必須一直持有generator的引用,直到生成全部的圖片。ide

生成單個圖片

你可使用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提供一個result的參數告訴你圖片是否成功生成或者圖片生成操做是否取消。ui

在你的block實現中,須要檢查result,判斷image是否成功生成,另外,確保你持有image generator直到生成圖片的操做結束。

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");
                }
  }];

你也能夠取消生成圖片的操做,經過想generator發送cancelAllCGImageGeneration的消息。

裁剪視頻和對視頻轉碼

你能夠對視頻進行轉碼、裁剪,經過使用AVAssetExportSession對象。這個流程以下圖所示,

一個export session是一個控制對象,能夠異步的生成一個asset。能夠用你須要生成的asset和presetName來初始化一個session,presetName指明你要生成的asset的屬性。接下來你能夠配置export session,好比能夠指定輸出的URL和文件類型,以及其餘的設置,好比metadata等等。
你能夠先檢測設置的preset是否可用,經過使用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.
}

你能夠配置session的輸出的url(這個url必須是文件url),AVAssetExportSession能夠推斷經過url的擴展名出輸出文件的類型。固然,你能夠直接設置文件類型,使用outputFileType。你還能夠指定其餘屬性,好比time range,輸出文件的長度等等,下面是列子:

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;

生成一個新的asset,能夠調用exportAsynchronouslyWithCompletionHandler:,當生成操做結束後會回調block,在這個block中你須要經過檢查session的status來判斷是否成功,以下:

[exportSession exportAsynchronouslyWithCompletionHandler:^{
 
        switch ([exportSession status]) {
            case AVAssetExportSessionStatusFailed:
                NSLog(@"Export failed: %@", [[exportSession error] localizedDescription]);
                break;
            case AVAssetExportSessionStatusCancelled:
                NSLog(@"Export canceled");
                break;
            default:
                break;
        }
    }];

你能夠取消這個生成操做,經過給session發送 cancelExport 消息。
若是導出的文件存在,或者導出的url在沙盒以外,這個導出操做會失敗。還有兩種狀況也可能致使失敗:
· 來了一個電話
· 你的程序在後臺運行而且其餘的應用開始播放。
這種狀況下,你應該通知用戶export失敗,而且從新export。

參考:

https://developer.apple.com/library/prerelease/ios/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/01_UsingAssets.html

相關文章
相關標籤/搜索