動画のアップロード機能を実装していると、iOS標準の写真.appでクリッピングやトリミングしている編集済みの動画を選択した場合に、編集しているにもかかわらず編集前のオリジナル動画がアップロードされてしまう現象に気が付いた。
動画を選択するのに UIImagePickerController
を使っている。未編集の動画を選択した場合とクリッピングした動画を選択した場合で違いがあるのかについて調査した。
結論から書くとUIImagePickerController
は関係なく、PHAsset
からAVAsset
を取り出す際のPHVideoRequestOptions
の指定方法に誤りがあることがわかった。
編集済みの動画の場合はPHAsset#adjustedが1になっている
UIImagePickerControllerで動画や写真を選択するとUIImagePickerControllerDelegate.imagePickerController(_:didFinishPickingMediaWithInfo:)
が呼ばれる。未編集動画と編集済み動画でどんな差異があるのか、info の中身をまず調べることにした。
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) { // ここでinfoの中身を調べた print("\(info)") }
調査の結果、未編集の動画と編集済みの動画との違いはPHAsset#adjusted
の値の違いだけであることがわかった。少なくとも info[.editedPhAsset]
などからデータを取り出さないといけないわけではない。PHAssetから「編集前のオリジナル動画」ではなく「編集済みの動画」を取得するにはどうしたらよいのか?については後述する。
あまり重要な情報ではないが、それぞれの動画を選択した場合のログを載せてく。
編集済みの動画を選択した場合には、info[.phAsset]
で取り出したPHAssetのadjusted
が1
になっている。
[ __C.UIImagePickerControllerInfoKey(_rawValue: UIImagePickerControllerMediaType): public.movie, __C.UIImagePickerControllerInfoKey(_rawValue: UIImagePickerControllerPHAsset): <PHAsset: 0x13024f720> A6BC4CB3-E92F-4BB3-A967-5EA773F1DCDF/L0/001 mediaType=2/0, sourceType=1, (1920x1080), creationDate=2022-01-14 13:59:02 +0000, location=1, hidden=0, favorite=0, adjusted=1 , __C.UIImagePickerControllerInfoKey(_rawValue: UIImagePickerControllerReferenceURL): assets-library://asset/asset.MOV?id=A6BC4CB3-E92F-4BB3-A967-5EA773F1DCDF&ext=MOV, __C.UIImagePickerControllerInfoKey(_rawValue: UIImagePickerControllerMediaURL): file:///private/var/mobile/Containers/Data/PluginKitPlugin/F543103D-63B7-4566-B24D-758E8D85CA17/tmp/trim.E02629B0-C7EE-4FA1-998E-3EFE22F9D590.MOV ]
未編集の動画を選択した場合には、info[.phAsset]
で取り出したPHAssetのadjusted
が0
になっている。
[ __C.UIImagePickerControllerInfoKey(_rawValue: UIImagePickerControllerPHAsset): <PHAsset: 0x135141e30> D82F6058-C843-45E6-B82F-3D0614A6A5A5/L0/001 mediaType=2/0, sourceType=1, (1920x1080), creationDate=2022-01-14 13:59:02 +0000, location=1, hidden=0, favorite=0, adjusted=0 , __C.UIImagePickerControllerInfoKey(_rawValue: UIImagePickerControllerMediaType): public.movie, __C.UIImagePickerControllerInfoKey(_rawValue: UIImagePickerControllerReferenceURL): assets-library://asset/asset.MOV?id=D82F6058-C843-45E6-B82F-3D0614A6A5A5&ext=MOV, __C.UIImagePickerControllerInfoKey(_rawValue: UIImagePickerControllerMediaURL): file:///private/var/mobile/Containers/Data/PluginKitPlugin/F543103D-63B7-4566-B24D-758E8D85CA17/tmp/trim.20082119-CD92-4146-A76D-3AE767B38FCC.MOV ]
問題はUIImagePickerController
で選択した動画情報(PHAsset)の取り出し方にあるわけではなくて、PHAssetからAVAssetを取得する際のオプションの指定の仕方が悪いことがわかった。
PHAssetからAVAssetの取得時にオプションでオリジナル動画かクリッピングした動画かを選択する
既存の実装では、PHAsset
からAVAsset
を取り出す際のPHVideoRequestOptions
の指定を PHVideoRequestOptionsVersion.original
としていた。このため編集前のオリジナル動画がアップロードされていたことがわかった。
let options: PHVideoRequestOptions = PHVideoRequestOptions() // 編集前のオリジナル動画がほしい場合 //options.version = PHVideoRequestOptionsVersion.original // 編集後の動画がほしい場合 options.version = PHVideoRequestOptionsVersion.current PHImageManager.default().requestAVAsset(forVideo: self, options: options, resultHandler: {(asset: AVAsset?, _: AVAudioMix?, _: [AnyHashable: Any]?) -> Void in DispatchQueue.main.async { if let urlAsset = asset as? AVURLAsset { let localVideoUrl: URL = urlAsset.url as URL completionHandler(localVideoUrl) } else { completionHandler(nil) } } })
写真.appで編集したとしても同じ動画として保存した場合には、写真.appで見た場合にサムネイルが変わっていたとしても、オリジナル動画が上書きされるわけではなくて少なくともファイルパス等はそのまま残っている。編集後の動画が欲しい場合には PHVideoRequestOptions#version
の指定を PHVideoRequestOptionsVersion.current
とする必要がある。
以上で問題は解決した。