Edited at

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

More than 1 year has passed since last update.

この記事は学生エンジニア Advent Calendar19日目の記事です.


背景

iPhone6,Iphone6Sから新たに使えるようになったLivePhoto,皆様使っているでしょうか.

忘年会の多いこのシーズン,盛り上がってしまった友達を撮るために僕はよく使っています.

screenshot1.gif

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

しかし,ここで問題となるのがLivePhotoで撮った写真を如何に共有するかということ.

iPhone6S, 6S+以外を利用している人に共有するとただの画像として扱われてしまい,彼の躍動感も失われてしまいます。

LivePhotoを動画やGIFに変換するアプリもありましたが有料だったり制限があったりということで,LivePhotoを動画やGIFに変換する方法を調べてみました.


環境

Mac OS X(10.10.5)

Xcode (7.1)


PHLivePhotoクラス

LivePhotoを扱うためのクラスPHLivePhotoが用意されていたので,そのリファレンスを眺めてみました.

プロパティとして用意されているのはSizeのみで,構成する動画や画像を取り出すためのAPIは存在しません.

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


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))!)")
}

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


出力

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を使ってアクセスしてその出力を見たいのですが,

その前にまず自分のカメラロール内のライブフォトにアクセスしたいので以下の様にしてカメラロールから取得します.


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

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


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メソッド内で以下のようにするとライブフォトを取り出すことが出来ます.


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が格納されています)


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


出力

file:///var/mobile/Media/DCIM/105APPLE/IMG_5491.MOV


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

UISaveVideoAtPathToSavedPhotosAlbum(urlAsset.URL.path!, self, "video:didFinishSavingWithError:contextInfo:", nil)

動画をカメラロールに保存するためのメソッドを呼び出してあげれば

screenshot2.gif

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

次にGIFとして保存する方法ですが,動画の場所が分かっているので,

NSGIFというmovファイルをgifに変換してくれるライブラリがございますのでそちらを使えば良いと思います。


感想

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

忘年会の多いこのシーズン、あなたが撮った素敵なライブフォトを動画やGIFで共有したい場合,ぜひぜひご活用ください!


おまけ

動画から時系列に30枚くらい画像を切り出してみましたのでおまけで置いておきます.


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

こんなにいらない