How to Get Video from a Live Photo in iOS
Asked Answered
L

5

12

I'm trying to figure it out, but can't find any useful information. I only found this:

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

but I'm ashamed to say I have no idea how to play it out. I've created a UIImagePickerController and loaded an Image from the Camera Roll.

Longevous answered 19/12, 2016 at 13:47 Comment(0)
Q
13

Use this code to get the video from live photo:

- (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);
    }
}

Basically what you have to do is that you first need to fetch the PHLivePhoto object from your PHAsset. After that, you will have to traverse all the asset resources within your live photo and check if it is of type PHAssetResourceTypePairedVideo.

If yes, you got your video. Now you will require to save it to some temporary directory as I did here and use this file for whatever purpose you may have.

To Play this video, you can use the following code:

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

Feel free to ask if you need any clarification.

P.S.- I made a few changes in this method to remove dependency of my application's code so the above code is untested, however I feel it should work as expected.

Quinn answered 8/8, 2017 at 12:27 Comment(0)
O
3

Swift 4 version

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
}

In depth tutorial here Live Photo API on iOS

Orgasm answered 25/4, 2018 at 12:5 Comment(0)
G
2

The question is a little confusing

First, If you want to pick live photo and play live photo.I recommend you use the Photos Framework instead of UIImagePickerController. This way you can fetch the asset and have more control. Then you can play the live photo as mov or the muted version with PHLivePhotoView by setting the startPlayback(with:) to hint or full.

You can refer the code here :

  • a github repo LivePreview show you how to select live photo and play it.

Second, if you want convert live photo to mov, the code you pasted will work, and if you want to play mov directly, you may want use AVPlayer

Plus, WWDC provides Example app using Photos framework

Grady answered 10/3, 2017 at 20:22 Comment(0)
F
1
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.

                 }
             }
                
         }
    }

}
Flowerage answered 26/8, 2021 at 5:2 Comment(0)
D
0

Swift 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
    
}

Invoke with the following:

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

}

Note: You need to clean up the Temp and Documents directory, and remove the files.

Dramatic answered 25/4, 2021 at 23:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.