LivePhotoから動画や画像を取り出す方法

この記事は[学生エンジニア Advent Calendar](http://www.adventar.org/calendars/1158)19日目の記事です．

## 背景
iPhone6，Iphone6Sから新たに使えるようになったLivePhoto，皆様使っているでしょうか．
忘年会の多いこのシーズン，盛り上がってしまった友達を撮るために僕はよく使っています．

![screenshot1.gif](https://qiita-image-store.s3.amazonaws.com/0/14569/d610eca7-78d6-1ae3-bebe-251dc038f56e.gif)

(* 本人の許可を得て掲載しております）

しかし，ここで問題となるのがLivePhotoで撮った写真を如何に共有するかということ．
iPhone6S, 6S+以外を利用している人に共有するとただの画像として扱われてしまい，彼の躍動感も失われてしまいます。
LivePhotoを動画やGIFに変換するアプリもありましたが有料だったり制限があったりということで，LivePhotoを動画やGIFに変換する方法を調べてみました．

## 環境
Mac OS X(10.10.5)
Xcode (7.1)

## PHLivePhotoクラス
LivePhotoを扱うためのクラス[PHLivePhoto](https://developer.apple.com/library/ios/documentation/Photos/Reference/PHLivePhoto_Class/)が用意されていたので，そのリファレンスを眺めてみました．
プロパティとして用意されているのはSizeのみで，構成する動画や画像を取り出すためのAPIは存在しません．

ということで，まずはLivePhotoクラスに設定されているプロパティを公開されていないものも含めて見てみます．

```swift

import ObjectiveC
var outCount:uint = 0
let propertyList = class_copyPropertyList(PHLivePhoto.self, &outCount)

for i in 0..<Int(outCount) {
    let property = propertyList[i]
    let propertyName = property_getName(property)
    let propertyType = property_getAttributes(property)
            
    print("\(String.fromCString(UnsafePointer<CChar>(propertyType))!):\(String.fromCString(UnsafePointer<CChar>(propertyName))!)")
}

```

すると，以下の様な結果が得られました．

```text:出力
T{CGSize=dd},R,N,V_size:size
T@"UIImage",R,N,V_image:image
T@"AVAsset",R,N,V_videoAsset:videoAsset
T{?=qiIq},R,N,V_photoTime:photoTime
T@"NSString",R,C,N,V_assetLocalIdentifier:assetLocalIdentifier
T@"NSString",R,V_uniqueIdentifier:uniqueIdentifier
T@"NSURL",R,N,V_imageURL:imageURL
T@"PHSandboxExtensionWrapper",R,N,V_imageURLSandboxExtensionWrapper:imageURLSandboxExtensionWrapper
T@"NSURL",R,N,V_videoURL:videoURL
T@"PHSandboxExtensionWrapper",R,N,V_videoURLSandboxExtensionWrapper:videoURLSandboxExtensionWrapper
T@"NSString",R,N:originalFilename
T@"NSString",R,N:imageTypeIdentifier
T@"NSString",R,N:videoTypeIdentifier
T@?,R,N:imageFileLoader
T@?,R,N:videoFileLoader
T{CGSize=dd},R,N,V_targetSize:targetSize
Tq,R,N,V_contentMode:contentMode
T@"PHImageManager",W,N,V_imageManager:imageManager
T@"PHAsset",W,N,V_asset:asset

```

怪しいものにKVCを使ってアクセスしてその出力を見たいのですが，
その前にまず自分のカメラロール内のライブフォトにアクセスしたいので以下の様にしてカメラロールから取得します．

## カメラロールからライブフォトの取り込み

適当なボタンをタップするとカメラロールを表示できるようにします．

```swift:ViewController.swift
import MobileCoreServices

//中略

 func didTapButton(button: UIButton) {
    let imagePickerController = UIImagePickerController()
    imagePickerController.delegate = self
    imagePickerController.sourceType = .PhotoLibrary
    imagePickerController.mediaTypes = [kUTTypeImage as String, kUTTypeLivePhoto as String]
    presentViewController(imagePickerController, animated: true, completion: nil)
}
```
そして写真を選択した時に呼ばれるdelegateメソッド内で以下のようにするとライブフォトを取り出すことが出来ます．

```swift:ViewController.swift

import Photos

//中略

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
    if let livePhoto = info[UIImagePickerControllerLivePhoto] as? PHLivePhoto {
        //この中で使える
    }
}
```

これでカメラロール内のライブフォトを扱えるようになったので早速，先ほど怪しかったpropertyのvalueを取り出してみた結果，
videoAssetに設定されているAVURLAssetのインスタンスにライブフォトのムービーファイルのURLが格納されていることがわかりました．

(ちなみに`imageURL`に表示されている画像のURLが格納されています)

```swift

if let urlAsset = livePhoto.valueForKey("videoAsset") as? AVURLAsset {
    print(urlAsset.URL)
}
```

```text:出力
file:///var/mobile/Media/DCIM/105APPLE/IMG_5491.MOV
```

動画ファイル名が分かってしまえばこっちのもので，

```swift
UISaveVideoAtPathToSavedPhotosAlbum(urlAsset.URL.path!, self, "video:didFinishSavingWithError:contextInfo:", nil)
```
動画をカメラロールに保存するためのメソッドを呼び出してあげれば

![screenshot2.gif](https://qiita-image-store.s3.amazonaws.com/0/14569/04fa8f78-06ef-b9b6-848f-18dba9ff80fe.gif)

無事に動画として保存されました！

次にGIFとして保存する方法ですが，動画の場所が分かっているので，
[NSGIF](http://cocoadocs.org/docsets/NSGIF/1.1.1/)というmovファイルをgifに変換してくれるライブラリがございますのでそちらを使えば良いと思います。

## 感想
さくっとできるだろ〜って思ってたらまさかのPHLivePhotoで公開されているプロパティがサイズしかないということで思ったより時間がかかってしまいました。

忘年会の多いこのシーズン、あなたが撮った素敵なライブフォトを動画やGIFで共有したい場合，ぜひぜひご活用ください！
## おまけ
動画から時系列に30枚くらい画像を切り出してみましたのでおまけで置いておきます．

```swift

if let image = livePhoto.valueForKey("videoAsset") as? AVURLAsset {
    let generator = AVAssetImageGenerator(asset: image)
    generator.maximumSize = livePhoto.size
    let defaultTime = image.duration
    let numberOfImage: Int64 = 30
    for i in 0..<numberOfImage {
        let targetTime = CMTimeMake((defaultTime.value / numberOfImage) * i, defaultTime.timescale)
        let imageRef: CGImageRef = try! generator.copyCGImageAtTime(targetTime, actualTime: nil)
        let image = UIImage(CGImage: imageRef)
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
    }
}
```
![screenshot](https://dl2.pushbulletusercontent.com/chmkPahtOuh7EdhJWMQe6ASNQuXIYGRZ/picture.jpg)
こんなにいらない