如何使用PhotoKit访问慢动作video的NSData / NSURL

使用新的Photo框架,我可以使用requestImageDataForAsset访问PHAssets的NSData 。 我也可以使用返回的info NSDictionaryPHImageFileURLKey来访问文件的URL。

 [[PHImageManager defaultManager] requestImageDataForAsset:asset options:nil resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { //imageData contains the correct data for images and videos NSLog(@"info - %@", info); NSURL* fileURL = [info objectForKey:@"PHImageFileURLKey"]; }]; 

这适用于图像和普通video。

但是,当资产是PHAssetMediaSubtypeVideoHighFrameRate(慢动作video)时,返回的数据对应于包含video的第一帧(NSData,dataUTI和info字典指向相同的jpg文件)的JPG文件。 例如,这是为慢动作video返回的URL和dataUTI:

PHImageFileURLKey =“file:///var/mobile/Media/PhotoData/Metadata/DCIM/100APPLE/IMG_0642.JPG”; PHImageFileUTIKey =“public.jpeg”;

为什么发生这种情况? 如何访问慢动作videoNSData / NSURL,而不是这个JPG预览?

经过坚果和testing每一个选项,我发现这个问题。

为慢动作video返回JPG图像的响应是PHImageRequestOptions.version属性的默认PHImageRequestOptionsVersionCurrent值。

只需将版本分配给PHImageRequestOptionsVersionUnadjusted或PHImageRequestOptionsVersionOriginal将返回原始的慢动作video。

 PHImageRequestOptions * imageRequestOptions = [[PHImageRequestOptions alloc] init]; imageRequestOptions.version = PHImageRequestOptionsVersionUnadjusted; // or imageRequestOptions.version = PHImageRequestOptionsVersionOriginal; 

我认为这是一个意外的行为,因为我并不期待“慢动作video”的“当前”版本是静止图像(可能是应用了慢动作效果的video,而不是照片)。

希望这对某人有用。

请务必注意,慢动作video属于AVComposition而不是AVURLAssettypes。 AVComposition对象将来自多个来源的媒体数据组合在一起。

导出慢动作video

为了达到这个目标,我基本上经历了三个步骤:

  1. 为video创build一个输出url
  2. configuration导出会话
  3. 导出video并抓取url!

 PHVideoRequestOptions *options = [PHVideoRequestOptions new]; options.networkAccessAllowed = YES; [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) { if(([asset isKindOfClass:[AVComposition class]] && ((AVComposition *)asset).tracks.count == 2)){ //slow motion videos. See Here: https://overflow.buffer.com/2016/02/29/slow-motion-video-ios/ //Output URL of the slow motion file. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = paths.firstObject; NSString *myPathDocs = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"mergeSlowMoVideo-%d.mov",arc4random() % 1000]]; NSURL *url = [NSURL fileURLWithPath:myPathDocs]; //Begin slow mo video export AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetHighestQuality]; exporter.outputURL = url; exporter.outputFileType = AVFileTypeQuickTimeMovie; exporter.shouldOptimizeForNetworkUse = YES; [exporter exportAsynchronouslyWithCompletionHandler:^{ dispatch_async(dispatch_get_main_queue(), ^{ if (exporter.status == AVAssetExportSessionStatusCompleted) { NSURL *URL = exporter.outputURL; self.filePath=URL.absoluteString; // NSData *videoData = [NSData dataWithContentsOfURL:URL]; // //// Upload //[self uploadSelectedVideo:video data:videoData]; } }); }]; } }]; 

请在iOS中查看这个美妙的博客,了解慢动作video。

// video slo-mo

 PHVideoRequestOptions *options=[[PHVideoRequestOptions alloc]init]; options.version=PHVideoRequestOptionsVersionOriginal; 

从PHImageManager请求AVAsset

 [[PHImageManager defaultManager] requestAVAssetForVideo:videoAsset options:options resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) { if ([asset isKindOfClass:[AVURLAsset class]]) { // use URL to get file content NSURL *URL = [(AVURLAsset *)asset URL]; NSData *videoData=[NSData dataWithContentsOfURL:URL]; NSNumber *fileSizeValue = nil; [URL getResourceValue:&fileSizeValue forKey:NSURLFileSizeKey error:nil]; } } 

以下Swift 3/4的代码片段

 PHImageManager.default().requestAVAsset(forVideo: asset, options: nil, resultHandler: { (asset, _, _) in // AVAsset has two sub classes: AVComposition and AVAssetURL // AVComposition for slow mo vid // AVAssetURL for normal videos // For slow motion video checking for AVCompostion // Creating an exporter to write the video into local file path and using the same to play/upload if asset!.isKind(of: AVComposition.self){ let avCompositionAsset = asset as! AVComposition if avCompositionAsset.tracks.count > 1{ let exporter = AVAssetExportSession(asset: avCompositionAsset, presetName: AVAssetExportPresetHighestQuality) exporter!.outputURL = self.fetchOutputURL() exporter!.outputFileType = AVFileTypeMPEG4 exporter!.shouldOptimizeForNetworkUse = true exporter!.exportAsynchronously { DispatchQueue.main.sync { // Use this url for uploading or playing a video let url = exporter!.outputURL } } } }else{ // Normal video, are stored as AVAssetURL let url = (asset as! AVURLAsset).url } }) // Fetch local path func fetchOutputURL() -> URL{ let documentDirectory = getDocumentsDirectory() as NSString let path = documentDirectory.appendingPathComponent("test.mp4") return URL(fileURLWithPath:path) }