Swiftで動画をトリミングした

#　実行

![on01.gif](https://qiita-image-store.s3.amazonaws.com/0/135794/101a15f0-0dca-60e0-1a54-ea4ed107fe22.gif)

![on.gif](https://qiita-image-store.s3.amazonaws.com/0/135794/c73c255e-6f26-5096-e9f4-5e6dc422d605.gif)

![on03.gif](https://qiita-image-store.s3.amazonaws.com/0/135794/98ecd4fa-b430-9b55-b411-45e2a9387218.gif)

# 補足

映像と音声は別々に合成しています。スタートポイントだけしかまだ実装してないです。エンドポイントを動かしてもスタートポイントの位置が変わります。エンドポイントはスタトート地点から５０秒後に設定しています。viewには動画からサムネイルを生成しています。スライダーを動かすたびにviewに描画されたサムネイル画像が変動するのは、スライダーを動かすたびに、スライダーから取得した秒数で新たにサムネイルを生成してviewに描画しているからです。


# ソース

[gitHub](https://github.com/Ryosuke-Hujisawa/Video-trimming-swift)


# 実装

```swift

import UIKit
import AVFoundation
import MobileCoreServices
import Photos
import AVKit

var int = 0
var trimVideoURL: URL?
var asset : AVAsset?

class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    let imagePickerController = UIImagePickerController()
    var videoURL: URL?

    
    @IBOutlet weak var imageView: UIImageView!
    @IBAction func selectImage(_ sender: Any) {
        print("UIBarButtonItem。カメラロールから動画を選択")
        imagePickerController.sourceType = .photoLibrary
        imagePickerController.delegate = self
        //imagePickerController.mediaTypes = ["public.image", "public.movie"]
        //動画だけ
        imagePickerController.mediaTypes = ["public.movie"]
        //画像だけ
        //imagePickerController.mediaTypes = ["public.image"]
        present(imagePickerController, animated: true, completion: nil)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // 一つめのスライダー
        let startPointSlider = UISlider(frame: CGRect(x:0, y:0, width:350, height:30))
        startPointSlider.layer.position = CGPoint(x:self.view.frame.midX, y:500)
        startPointSlider.backgroundColor = UIColor.white
        startPointSlider.layer.cornerRadius = 10.0
        startPointSlider.layer.shadowOpacity = 0.5
        startPointSlider.layer.masksToBounds = false
        startPointSlider.addTarget(self, action: #selector(self.onStartPointlabel(_:)), for: .valueChanged)
        
        // 最小値と最大値を設定する.
        startPointSlider.minimumValue = 0
        startPointSlider.maximumValue = 100
        
        self.view.addSubview(startPointSlider)
        
        //一つめのラベル
        let startPointlabel = UILabel()
        startPointlabel.text = "start point"
        startPointlabel.sizeToFit()
        startPointlabel.layer.position = CGPoint(x:self.view.frame.midX, y:450)
        self.view.addSubview(startPointlabel)
        
        // 二つめのスライダー
        let endPointSlider = UISlider(frame: CGRect(x:0, y:0, width:350, height:30))
        endPointSlider.layer.position = CGPoint(x:self.view.frame.midX, y:600)
        endPointSlider.backgroundColor = UIColor.white
        endPointSlider.layer.cornerRadius = 10.0
        endPointSlider.layer.shadowOpacity = 0.5
        endPointSlider.layer.masksToBounds = false
        endPointSlider.addTarget(self, action: #selector(self.onEndPointlabel(_:)), for: .valueChanged)
        
        // 最小値と最大値を設定する.
        endPointSlider.minimumValue = 0
        endPointSlider.maximumValue = 100
        
        self.view.addSubview(endPointSlider)
        
        //二つめのラベル
        let endPointlabel = UILabel()
        endPointlabel.text = "end point"
        endPointlabel.sizeToFit()
        endPointlabel.layer.position = CGPoint(x:self.view.frame.midX, y:550)
        self.view.addSubview(endPointlabel)
    }
    
    /*
     Sliderの値が変わった時に呼ばれるメソッド
     */
    
    func onStartPointlabel(_ sender:UISlider!)
    {
        print(floor(sender.value))
        int = Int(floor(sender.value))
        imageView.image = previewImageFromVideo(videoURL!)!
        
    }
    
    func onEndPointlabel(_ sender:UISlider!)
    {
        print(floor(sender.value))
        int = Int(floor(sender.value))
        imageView.image = previewImageFromVideo(videoURL!)!
        
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        videoURL = info["UIImagePickerControllerReferenceURL"] as? URL
        trimVideoURL = (info["UIImagePickerControllerReferenceURL"] as? URL)!
        print(videoURL!)
        print(videoURL!)
        imageView.image = previewImageFromVideo(videoURL!)!
        imageView.contentMode = .scaleAspectFit
        imagePickerController.dismiss(animated: true, completion: nil)

    }
    
    func previewImageFromVideo(_ url:URL) -> UIImage? {
        print("動画からサムネイルを生成する")
        asset = AVAsset(url:url)
        let imageGenerator = AVAssetImageGenerator(asset:asset!)
        imageGenerator.appliesPreferredTrackTransform = true
        var time = asset?.duration
        time?.value = min(30,32)
        do {
            let imageRef = try imageGenerator.copyCGImage(at: CMTimeMake(Int64(int), 1), actualTime: nil)
            return UIImage(cgImage: imageRef)
        } catch {
            return nil
        }
    }
    
    //動画トリミング
    func createAudioFileFromAsset(_ asset: AVAsset){
        
        let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as URL
        let filePath = documentsDirectory.appendingPathComponent("rendered-audio.m4v")
        if let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPreset640x480){
            
            exportSession.canPerformMultiplePassesOverSourceMediaData = true
            exportSession.outputURL = filePath
            exportSession.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration)
            exportSession.outputFileType = AVFileTypeQuickTimeMovie
            exportSession.exportAsynchronously {
                _ in
                print("finished: \(filePath) :  \(exportSession.status.rawValue) ")
            }
        }
        
    }

    @IBAction func trim(_ sender: Any) {

        let url = trimVideoURL
        
        let videoAsset = AVURLAsset(url: url!)
        
        print("videoAssetする")
        print(videoAsset)
        print( type(of: videoAsset) )
        
        let comp = AVMutableComposition()
        let videoAssetSourceTrack = videoAsset.tracks(withMediaType: AVMediaTypeVideo).first! as AVAssetTrack
        let videoCompositionTrack = comp.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid)
        
        let audioAssetSourceTrack1 = videoAsset.tracks(withMediaType: AVMediaTypeAudio).first! as AVAssetTrack
        let audioCompositionTrack = comp.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid)

        do {
            
            try videoCompositionTrack.insertTimeRange(
                CMTimeRangeMake(CMTimeMakeWithSeconds(Float64(int), 10), CMTimeMakeWithSeconds(50, 10)),
                of: videoAssetSourceTrack,
                at: kCMTimeZero)
            
            
            
            try audioCompositionTrack.insertTimeRange(
                CMTimeRangeMake(CMTimeMakeWithSeconds(Float64(int), 10), CMTimeMakeWithSeconds(50, 10)),
                of: audioAssetSourceTrack1,
                at: kCMTimeZero)
            
            
        }catch { print(error) }
        asset = comp
        createAudioFileFromAsset(asset!)
    }
}

```