トップ 最新 追記

Cocoa練習帳

iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど

2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|

2014-12-31 [iOS]動画を90度回転させる

今年最後の投稿が中途半端な内容となり残念だ。

動画の画面を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);
    }
 
}

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/ios/AVEditor - GitHub

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)

トップ 最新 追記