iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど
今年最後の投稿が中途半端な内容となり残念だ。
動画の画面を90度回転させて保存するアプリをSwiftで書いてみたのだが、保存で失敗しまう。なぜだろう。
import UIKit
import AVFoundation
import AssetsLibrary
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
@IBOutlet weak var rotateButton: UIButton!
@IBAction func rotate(sender:AnyObject) {
println(__FUNCTION__)
if UIImagePickerController.isSourceTypeAvailable(.SavedPhotosAlbum) == false {
return
}
let mediaUI = UIImagePickerController()
mediaUI.sourceType = .SavedPhotosAlbum
if let mediaTypes = UIImagePickerController.availableMediaTypesForSourceType(.SavedPhotosAlbum) {
mediaUI.mediaTypes = mediaTypes
}
//mediaUI.mediaTypes = [kUTTypeMovie as NSString]
//mediaUI.mediaTypes = [kUTTypeMovie!]
//mediaUI.mediaTypes = [kUTTypeMovie]
//mediaUI.mediaTypes = NSArray(object: kUTTypeImage)
//mediaUI.mediaTypes = [String(kUTTypeMovie)]
mediaUI.allowsEditing = false
mediaUI.delegate = self
self.presentViewController(mediaUI, animated: true, completion: nil)
}
var mutableComposition: AVMutableComposition?
var mutableVideoComposition: AVMutableVideoComposition?
var exportSession: AVAssetExportSession?
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject: AnyObject]) {
println(__FUNCTION__)
if info[UIImagePickerControllerMediaURL] != nil {
/* アセットオブジェクトの作成 */
let url: NSURL = info[UIImagePickerControllerMediaURL] as NSURL
var options = [String: Bool]()
options[AVURLAssetPreferPreciseDurationAndTimingKey] = true
var asset = AVURLAsset(URL: url, options: options)
/* アセットから動画/音声トラックを取り出す */
let assetVideoTrack: AVAssetTrack = asset.tracksWithMediaType(AVMediaTypeVideo)[0] as AVAssetTrack
let assetAudioTrack: AVAssetTrack = asset.tracksWithMediaType(AVMediaTypeAudio)[0] as AVAssetTrack
let insertionPoint: CMTime = kCMTimeZero
var error: NSError? = nil
/* コンポジションを作成 */
if mutableComposition == nil {
mutableComposition = AVMutableComposition()
/* 動画コンポジショントラックの作成 */
let compositionVideoTrack: AVMutableCompositionTrack = mutableComposition!.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid))
/* 動画データをコンポジションに追加 */
compositionVideoTrack.insertTimeRange(CMTimeRangeMake(insertionPoint, assetVideoTrack.timeRange.duration), ofTrack: assetVideoTrack, atTime: kCMTimeZero, error: &error)
/* 音声コンポジショントラックの作成 */
let compositionAudioTrack: AVMutableCompositionTrack = mutableComposition!.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid))
/* 音声データをコンポジションに追加 */
compositionAudioTrack.insertTimeRange(CMTimeRangeMake(insertionPoint, assetAudioTrack.timeRange.duration), ofTrack: assetAudioTrack, atTime: kCMTimeZero, error: &error)
}
var instruction: AVMutableVideoCompositionInstruction
var layerInstruction: AVMutableVideoCompositionLayerInstruction
/* 移動して回転 */
let t1: CGAffineTransform = CGAffineTransformMakeTranslation(assetVideoTrack.naturalSize.height, 0.0)
let t2: CGAffineTransform = CGAffineTransformRotate(t1, ((90.0 / 180.0) * 3.14159265358979323846264338327950288))
if mutableVideoComposition == nil {
/* 動画コンポジションの作成 */
mutableVideoComposition = AVMutableVideoComposition()
mutableVideoComposition!.renderSize = CGSizeMake(assetVideoTrack.naturalSize.height, assetVideoTrack.naturalSize.width)
mutableVideoComposition!.frameDuration = CMTimeMake(1, 30);
/* 動画コンポジション命令 */
instruction = AVMutableVideoCompositionInstruction()
instruction.timeRange = CMTimeRangeMake(kCMTimeZero, mutableComposition!.duration);
layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: assetVideoTrack);
layerInstruction.setTransform(t2, atTime: kCMTimeZero)
}
else {
mutableVideoComposition!.renderSize = CGSizeMake(mutableVideoComposition!.renderSize.height, mutableVideoComposition!.renderSize.width);
/* 動画コンポジション命令の抽出 */
instruction = mutableVideoComposition!.instructions[0] as AVMutableVideoCompositionInstruction
layerInstruction = instruction.layerInstructions[0] as AVMutableVideoCompositionLayerInstruction
/* 内容の確認 */
var existingTransform = CGAffineTransform(a: 0.0, b: 0.0, c: 0.0, d: 0.0, tx: 0.0, ty: 0.0)
if layerInstruction.getTransformRampForTime(mutableComposition!.duration, startTransform: &existingTransform, endTransform: nil, timeRange: nil) == false {
layerInstruction.setTransform(t2, atTime: kCMTimeZero)
}
else {
/* 原点補償 */
let t3: CGAffineTransform = CGAffineTransformMakeTranslation(-1.0 * assetVideoTrack.naturalSize.height / 2.0, 0.0)
let newTransform: CGAffineTransform = CGAffineTransformConcat(existingTransform, CGAffineTransformConcat(t2, t3))
layerInstruction.setTransform(newTransform, atTime: kCMTimeZero)
}
}
/* コンポジションに命令を追加 */
instruction.layerInstructions = [layerInstruction]
mutableVideoComposition!.instructions = [instruction]
/* 出力URL */
var documentsPath: NSString = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString
//NSFileManager.defaultManager().createDirectoryAtPath(documentsPath, withIntermediateDirectories: true, attributes: nil, error: &error)
var exportPath: NSString = documentsPath.stringByAppendingPathComponent("output.mp4")
//NSFileManager.defaultManager().removeItemAtPath(exportPath, error: &error)
var exportUrl: NSURL = NSURL.fileURLWithPath(exportPath)!
/* セッションを作成し、フォトライブラリに書き出す */
exportSession = AVAssetExportSession(asset: mutableComposition!.copy() as AVAsset, presetName: AVAssetExportPresetHighestQuality)
exportSession!.videoComposition = mutableVideoComposition
exportSession!.outputURL = exportUrl
exportSession!.outputFileType = AVFileTypeQuickTimeMovie
exportSession!.exportAsynchronouslyWithCompletionHandler({
() -> Void in
NSLog("%@", __FUNCTION__)
//NSFileManager.defaultManager().removeItemAtPath(self.filePath(self.mov_extenstion), error: nil)
switch self.exportSession!.status {
case AVAssetExportSessionStatus.Completed:
NSLog("%@ AVAssetExportSessionStatus.Completed", __FUNCTION__)
let assetsLib = ALAssetsLibrary()
assetsLib.writeVideoAtPathToSavedPhotosAlbum(exportUrl, completionBlock: {
(nsurl, error) -> Void in
NSLog("%@ error:%@", __FUNCTION__, error)
})
case AVAssetExportSessionStatus.Failed:
NSLog("%@ AVAssetExportSessionStatus.Failed exporter:%@ error:%@", __FUNCTION__, self.exportSession!, self.exportSession!.error)
case AVAssetExportSessionStatus.Cancelled:
NSLog("%@ AVAssetExportSessionStatus.Cancelled exporter:%@ error:%@", __FUNCTION__, self.exportSession!, self.exportSession!.error)
default:
NSLog("%@ none exporter:%@", __FUNCTION__, self.exportSession!)
}
})
}
picker.dismissViewControllerAnimated(true, completion: nil);
}
}