AVFoundationでアルバムから写真を選択して保存 Swift3

 

環境

  • swift
  • Swift3
  • Xcode
  • Xcode9
  • AVFoundation
  • video

ソース

GitHub

 

実行

on.gif

説明

iOSのアルバムから動画を選択してavfoundationでmacのドキュメントフォルダにアルバムから選択した動画を生成して保存します

実装

//
//  ViewController.swift
//  video-edit
//
//  Created by ryosuke-hujisawa on 2017/10/04.
//  Copyright © 2017年 ryosuke-hujisawa. All rights reserved.
//

import UIKit
import AVFoundation
import AVKit

class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

    let imagePickerController = UIImagePickerController()
    var videoURL: URL?

    @IBAction func ChoiceCamera(_ sender: Any) {

        print("UIBarButtonItem。カメラロールから動画を選択")
        imagePickerController.sourceType = .photoLibrary
        imagePickerController.delegate = self
        imagePickerController.mediaTypes = ["public.image", "public.movie"]
        present(imagePickerController, animated: true, completion: nil)
    }





    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        //アルバムから選択した動画を変数に代入
        let documentsPath = info[UIImagePickerControllerMediaURL] as! URL!
        videoURL =  documentsPath

        //viewを戻す
        picker.dismiss(animated: true, completion: nil)
    }






    @IBAction func SaveMovies(_ sender: Any) {

        // 動画のurlを定義する
        // Define the url of the video
        let url = videoURL
        //URL(string: "http://160.16.120.119/kkk.m4v")

        // 動画URLからアセットを生成
        // Generate assets from video URL
        let videoAsset: AVURLAsset = AVURLAsset(url: url!)

        // アセットからトラックを取得
        // Get a track from an asset
        let videoTrack: AVAssetTrack = videoAsset.tracks(withMediaType: AVMediaType.video)[0]
        let audioTrack: AVAssetTrack = videoAsset.tracks(withMediaType: AVMediaType.audio)[0]

        // ベースとなる動画のコンポジション作成
        // Composition creation of the base video
        let mainComposition : AVMutableComposition = AVMutableComposition()

        // コンポジションのトラック作成
        // Creating composition tracks
        let compositionVideoTrack: AVMutableCompositionTrack = mainComposition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: kCMPersistentTrackID_Invalid)!

        let compositionAudioTrack: AVMutableCompositionTrack = mainComposition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)!

        // コンポジションの設定
        // Composition setting
        try! compositionVideoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), of: videoTrack, at: kCMTimeZero)
        try! compositionAudioTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), of: audioTrack, at: kCMTimeZero)















        // ロゴのCALayerの作成
        // Create logo CALayer
        //let logoImage : UIImage = UIImage(named: "logo")!
        // UIImage インスタンスの生成
        // 画像はAssetsに入れてないのとjpgなので拡張子を入れます
        let image1:UIImage = UIImage(named:"logo.png")!

        // UIImageView 初期化
        let logoImage = UIImageView(image:image1)




        let logoLayer: CALayer = CALayer()
        logoLayer.contents = logoImage
        logoLayer.frame = CGRect(x: 5, y: 25, width: 57, height: 57)
        logoLayer.opacity = 0.9

        // 動画のサイズを取得
        // Get video size
        let videoSize: CGSize = videoTrack.naturalSize


        // 親レイヤーを作成
        // Create parent layer
        let parentLayer: CALayer = CALayer()
        let videoLayer: CALayer = CALayer()
        parentLayer.frame = CGRect(x: 0, y: 0, width: videoSize.width, height: videoSize.height)
        videoLayer.frame = CGRect(x: 0, y: 0, width: videoSize.width, height: videoSize.height)
        parentLayer.addSublayer(videoLayer)
        parentLayer.addSublayer(logoLayer)

        // 合成用コンポジション作成
        // Composition composition creation
        let videoComp = AVMutableVideoComposition()
        videoComp.renderSize = videoSize
        videoComp.frameDuration = CMTimeMake(1, 30)
        videoComp.animationTool = AVVideoCompositionCoreAnimationTool.init(postProcessingAsVideoLayer: videoLayer, in: parentLayer)

        // インストラクション作成
        // Instruction creation
        let instruction: AVMutableVideoCompositionInstruction = AVMutableVideoCompositionInstruction()
        instruction.timeRange = CMTimeRangeMake(kCMTimeZero, mainComposition.duration)
        let layerInstruction: AVMutableVideoCompositionLayerInstruction = AVMutableVideoCompositionLayerInstruction.init(assetTrack: videoTrack)
        instruction.layerInstructions = [layerInstruction]

        // インストラクションを合成用コンポジションに設定
        // Set Instruction to Composition for Composition
        videoComp.instructions = [instruction]















        // 動画のコンポジションをベースにAVAssetExportを生成
        // Generate AVAssetExport based on composition of video
        let assetExport = AVAssetExportSession.init(asset: mainComposition, presetName: AVAssetExportPresetMediumQuality)

        /*



         下記が画像を動画に合成する処理。下記を記述する動画が生成されなくなる。
         下記の処理を消すと、正常にmacのドキュメントフォルダに動画が生成される


         Process below to combine images into moving images. A moving image describing the following will not be generated.
         When the following processing is deleted, a movie is normally generated in the mac document folder



         */
        // 合成用コンポジションを設定
        // Set composition for composition
        assetExport!.videoComposition = videoComp

        // 生成された動画を保存する場所のurlとファイルの名前を設定(今回はmacの中のDocumentsディレクトリに保存)
        // Set the url and file name where the generated video will be saved (this time saved in the Documents directory in mac)
        let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as URL

        //生成する動画の名前
        // Name of video to generate
        let filePath = documentsDirectory.appendingPathComponent("rendered-audio.m4v")

        // エクスポートファイルのビデオタイプの設定
        // Setting the video type of the export file
        assetExport?.outputFileType = AVFileType.mov

        // 出力されるurlの設定
        // Setting output url
        assetExport?.outputURL = filePath

        // これは必要?
        // Is this necessary?

        //assetExport?.shouldOptimizeForNetworkUse = true

        assetExport?.exportAsynchronously {

            //出力されたファイルのパスをprint表示
            //Print the path of the outputted file

            print("finished: \(filePath) :  \(String(describing: assetExport?.status.rawValue)) ")
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        print("Hello World")
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}