簡體   English   中英

如何從實時照片中獲取視頻 iOS

[英]How to Get Video from a Live Photo in iOS

我想弄明白,但找不到任何有用的信息。 我只發現了這個:

PHAssetResourceManager.defaultManager().writeDataForAssetResource(assetRes, 
toFile: fileURL, options: nil, completionHandler: 
{
     // Video file has been written to path specified via fileURL
}

但我很慚愧地說我不知道如何進行。 我創建了一個 UIImagePickerController 並從相機膠卷加載了一個圖像。

使用此代碼從實時照片中獲取視頻:

- (void)videoUrlForLivePhotoAsset:(PHAsset*)asset withCompletionBlock:(void (^)(NSURL* url))completionBlock{
    if([asset isKindOfClass:[PHAsset class]]){
        NSString* identifier = [(PHAsset*)asset localIdentifier];
        NSString* filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.mov",[NSString stringWithFormat:@"%.0f",[[NSDate date] timeIntervalSince1970]]]];
        NSURL *fileUrl = [NSURL fileURLWithPath:filePath];

        PHLivePhotoRequestOptions* options = [PHLivePhotoRequestOptions new];
        options.deliveryMode = PHImageRequestOptionsDeliveryModeFastFormat;
        options.networkAccessAllowed = YES;
        [[PHImageManager defaultManager] requestLivePhotoForAsset:asset targetSize:[UIScreen mainScreen].bounds.size contentMode:PHImageContentModeDefault options:options resultHandler:^(PHLivePhoto * _Nullable livePhoto, NSDictionary * _Nullable info) {
            if(livePhoto){
                NSArray* assetResources = [PHAssetResource assetResourcesForLivePhoto:livePhoto];
                PHAssetResource* videoResource = nil;
                for(PHAssetResource* resource in assetResources){
                    if (resource.type == PHAssetResourceTypePairedVideo) {
                        videoResource = resource;
                        break;
                    }
                }
                if(videoResource){
                    [[PHAssetResourceManager defaultManager] writeDataForAssetResource:videoResource toFile:fileUrl options:nil completionHandler:^(NSError * _Nullable error) {
                        if(!error){
                            completionBlock(fileUrl);
                        }else{
                            completionBlock(nil);
                        }
                    }];
                }else{
                    completionBlock(nil);
                }
            }else{
                completionBlock(nil);
            }
        }];
    }else{
        completionBlock(nil);
    }
}

基本上你需要做的是你首先需要從你的PHAsset獲取PHLivePhoto對象。 之后,您必須遍歷實時照片中的所有資產資源,並檢查它是否屬於PHAssetResourceTypePairedVideo類型。

如果是的話,你得到了你的視頻。 現在,您需要像我在這里所做的那樣將其保存到某個臨時目錄中,並將此文件用於您可能擁有的任何目的。

要播放此視頻,您可以使用以下代碼:

NSURL *videoURL = [NSURL fileURLWithPath:fileUrl];
AVPlayer *player = [AVPlayer playerWithURL:videoURL];
AVPlayerViewController *playerViewController = [AVPlayerViewController new];
playerViewController.player = player;
[self presentViewController:playerViewController animated:YES completion:nil];

如果您需要任何說明,請隨時詢問。

PS-我在此方法中做了一些更改以消除對我的應用程序代碼的依賴,因此上述代碼未經測試,但我覺得它應該按預期工作。

斯威夫特 4 版本

import Photos
import MobileCoreServices

// <UIImagePickerControllerDelegate, UINavigationControllerDelegate>
@IBAction func showImagePicker(sender: UIButton) {
    let picker = UIImagePickerController()
    picker.delegate = self;
    picker.allowsEditing = false;
    picker.sourceType = .photoLibrary;
    picker.mediaTypes = [kUTTypeLivePhoto as String, kUTTypeImage as String];

    present(picker, animated: true, completion: nil);
}

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
    guard
        let livePhoto = info[UIImagePickerControllerLivePhoto] as? PHLivePhoto,
        let photoDir = generateFolderForLivePhotoResources()
        else {
            return;
    }

    let assetResources = PHAssetResource.assetResources(for: livePhoto)
    for resource in assetResources {

        // SAVE FROM BUFFER
//            let buffer = NSMutableData()
//            PHAssetResourceManager.default().requestData(for: resource, options: nil, dataReceivedHandler: { (chunk) in
//                buffer.append(chunk)
//            }, completionHandler: {[weak self] error in
//                self?.saveAssetResource(resource: resource, inDirectory: photoDir, buffer: buffer, maybeError: error)
//            })

        // SAVE DIRECTLY
        saveAssetResource(resource: resource, inDirectory: photoDir, buffer: nil, maybeError: nil)
    }

    picker.dismiss(animated: true) {}
}

func saveAssetResource(
    resource: PHAssetResource,
    inDirectory: NSURL,
    buffer: NSMutableData?, maybeError: Error?
    ) -> Void {
    guard maybeError == nil else {
        print("Could not request data for resource: \(resource), error: \(String(describing: maybeError))")
        return
    }

    let maybeExt = UTTypeCopyPreferredTagWithClass(
        resource.uniformTypeIdentifier as CFString,
        kUTTagClassFilenameExtension
        )?.takeRetainedValue()

    guard let ext = maybeExt else {
        return
    }

    guard var fileUrl = inDirectory.appendingPathComponent(NSUUID().uuidString) else {
        print("file url error")
        return
    }

    fileUrl = fileUrl.appendingPathExtension(ext as String)

    if let buffer = buffer, buffer.write(to: fileUrl, atomically: true) {
        print("Saved resource form buffer \(resource) to filepath \(String(describing: fileUrl))")
    } else {
        PHAssetResourceManager.default().writeData(for: resource, toFile: fileUrl, options: nil) { (error) in
            print("Saved resource directly \(resource) to filepath \(String(describing: fileUrl))")
        }
    }
}

func generateFolderForLivePhotoResources() -> NSURL? {
    let photoDir = NSURL(
        // NB: Files in NSTemporaryDirectory() are automatically cleaned up by the OS
        fileURLWithPath: NSTemporaryDirectory(),
        isDirectory: true
        ).appendingPathComponent(NSUUID().uuidString)

    let fileManager = FileManager()
    // we need to specify type as ()? as otherwise the compiler generates a warning
    let success : ()? = try? fileManager.createDirectory(
        at: photoDir!,
        withIntermediateDirectories: true,
        attributes: nil
    )

    return success != nil ? photoDir! as NSURL : nil
}

iOS 上的 Live Photo API 在這里有深入的教程

問題有點混亂

首先,如果您想選擇實時照片並播放實時照片。我建議您使用照片框架而不是UIImagePickerController。 通過這種方式,您可以獲取資產並擁有更多控制權。 然后,您可以通過將startPlayback(with:)hintfull來使用PHLivePhotoView將實時照片播放為 mov 或靜音版本。

您可以在此處參考代碼:

  • github repo LivePreview向您展示了如何選擇實時照片並播放。

其次,如果你想把live photo轉成mov,你粘貼的代碼就可以了,如果你想直接播放mov,你可能需要使用AVPlayer

另外,WWDC 提供了使用照片框架的示例應用程序

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {

 let phAsset = info[.phAsset] as? PHAsset
 imagePickerController.dismiss(animated: true, completion: nil)
 let style = phAsset?.playbackStyle
  if(style != .livePhoto) {
         print("This is not a live photo")
         return
  }
  let filePath = NSTemporaryDirectory() + String(format: "%.0f", NSDate().timeIntervalSince1970) + "_.mov"
  let fileURL = NSURL(fileURLWithPath: filePath)
  let options = PHLivePhotoRequestOptions()
  options.deliveryMode = .fastFormat
  options.isNetworkAccessAllowed = true

PHImageManager.default().requestLivePhoto(for: phAsset!, targetSize: CGSize(width: 1920, height: 1080), contentMode: PHImageContentMode.default, options: options) { livePhoto, info in
        if((livePhoto) != nil) {
           let assetResources = PHAssetResource.assetResources(for: livePhoto!)
           var videoResource : PHAssetResource?
           for resources in assetResources {
               if(resources.type == .pairedVideo) {
                    videoResource = resources
                    break
               }
            }
            guard let videoResource = videoResource else {
                fatalError("video resource is nil")
            }
            PHAssetResourceManager.default().writeData(for: videoResource, toFile: fileURL as URL, options: nil) { error in                   
                let avAsset : AVAsset = AVAsset(url: fileURL as URL)
                 DispatchQueue.main.async { [self] in

                        // Whatever you do using fileURL or avAsset.

                 }
             }
                
         }
    }

}

斯威夫特 5

func videoUrlForLivePhotoAsset(asset: PHAsset, completionHandler: @escaping (_ result: URL?) -> Void) {
            
    print("videoUrlForLivePhotoAsset: \(asset)")
    
    let options : PHLivePhotoRequestOptions = PHLivePhotoRequestOptions.init()
    
    options.deliveryMode = .fastFormat
    options.isNetworkAccessAllowed = true
    
    PHImageManager.default().requestLivePhoto(for: asset, targetSize: UIScreen.main.bounds.size, contentMode: .default, options: options) { (livePhoto, info) in
        
        if livePhoto != nil {
            
            let assetResources : [PHAssetResource] = PHAssetResource.assetResources(for: livePhoto!)
            
            var videoResource : PHAssetResource?
            
            for resource in assetResources {
                
                if resource.type == .pairedVideo {
                    
                    videoResource = resource

                    break
                    
                }
                
            }
            
            guard let photoDir = self.generateFolderForLivePhotoResources() else {

                return

            }

            
            print("videoResource: \(videoResource)")
            
            if videoResource != nil {
                
                self.saveAssetResource(resource: videoResource!, inDirectory: photoDir, buffer: nil, maybeError: nil) { (fileUrl) in
                    
                    completionHandler(fileUrl)

                }
                                                            
            }
            
        } else {
            
            completionHandler(nil)

        }
        
    }
    
}

func saveAssetResource(
    resource: PHAssetResource,
    inDirectory: NSURL,
    buffer: NSMutableData?, maybeError: Error?, completionHandler: @escaping (_ result: URL?) -> Void) {
    
    guard maybeError == nil else {
        print("Could not request data for resource: \(resource), error: \(String(describing: maybeError))")
        return
    }

    let maybeExt = UTTypeCopyPreferredTagWithClass(
        resource.uniformTypeIdentifier as CFString,
        kUTTagClassFilenameExtension
        )?.takeRetainedValue()

    guard let ext = maybeExt else {
        return
    }

    guard var fileUrl = inDirectory.appendingPathComponent(NSUUID().uuidString) else {
        print("file url error")
        return
    }

    fileUrl = fileUrl.appendingPathExtension(ext as String)

    if let buffer = buffer, buffer.write(to: fileUrl, atomically: true) {
        
        print("Saved resource form buffer \(resource) to filepath \(String(describing: fileUrl))")

        completionHandler(fileUrl)
        
    } else {

        PHAssetResourceManager.default().writeData(for: resource, toFile: fileUrl, options: nil) { (error) in

            print("Saved resource directly \(resource) to filepath \(String(describing: fileUrl))")

            if error == nil {
                
                completionHandler(fileUrl)

            } else {

                completionHandler(nil)

            }

        }

    }
    
}

func generateFolderForLivePhotoResources() -> NSURL? {
    
    let photoDir = NSURL(
        // NB: Files in NSTemporaryDirectory() are automatically cleaned up by the OS
        fileURLWithPath: NSTemporaryDirectory(),
        isDirectory: true
        ).appendingPathComponent(NSUUID().uuidString)

    let fileManager = FileManager()

    // we need to specify type as ()? as otherwise the compiler generates a warning

    let success : ()? = try? fileManager.createDirectory(
        at: photoDir!,
        withIntermediateDirectories: true,
        attributes: nil
    )

    return success != nil ? photoDir! as NSURL : nil
    
}

使用以下命令調用:

let asset = PHAsset.init()
                    
self.videoUrlForLivePhotoAsset(asset: asset!) { (url) in
                        
    print("url: \(url)")

}

注意:您需要清理 Temp 和 Documents 目錄,並刪除文件。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM