動画をトリミングする方法 swift
説明
PryntTrimmerViewというフレームワークを使います。下記の実装ではユーザーインターフェース上で画像を切り取る範囲を指定しているだけで実際にはトリミングの機能は、まだありません。動画、及び音声をトリミングしたければ、別途AVFoundationを使ってください。その実装はここに書いてあります。
実装
ViewController.swift
import UIKit
import AVFoundation
import MobileCoreServices
import PryntTrimmerView
import Photos
import AVKit
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
@IBOutlet weak var trimmerView: TrimmerView!
@IBOutlet weak var playerView: UIView!
var player: AVPlayer?
var playbackTimeCheckerTimer: Timer?
var trimmerPositionChangedTimer: Timer?
var imagePickerController = UIImagePickerController()
var videoURL: URL?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
@IBAction func selectAsset(_ sender: Any) {
print("カメラロールから動画を選択する")
imagePickerController.sourceType = .photoLibrary
imagePickerController.delegate = self
imagePickerController.mediaTypes = ["public.movie"]
present(imagePickerController, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
videoURL = info["UIImagePickerControllerReferenceURL"] as? URL
print(videoURL!)
//playerView.image = previewImageFromVideo(videoURL!)!
//playerView.contentMode = .scaleAspectFit
//動画を洗濯したら元の画面に戻る
imagePickerController.dismiss(animated: true, completion: nil)
//関数実行
previewImageFromVideo(videoURL!)!
}
func previewImageFromVideo(_ url:URL) -> UIImage? {
print("動画からサムネイルを生成する")
let asset = AVAsset(url:url)
print(asset)
print(type(of: asset))
trimmerView.asset = asset
trimmerView.delegate = self
addVideoPlayer(with: asset, playerView: playerView)
//下いらなくね
let imageGenerator = AVAssetImageGenerator(asset:asset)
imageGenerator.appliesPreferredTrackTransform = true
var time = asset.duration
time.value = min(time.value,2)
do {
let imageRef = try imageGenerator.copyCGImage(at: time, actualTime: nil)
return UIImage(cgImage: imageRef)
} catch {
return nil
}
}
// override func loadAsset(_ asset: AVAsset) {
//
// trimmerView.asset = asset
// trimmerView.delegate = self
// addVideoPlayer(with: asset, playerView: playerView)
//
//
// print(asset)
// print( type(of: asset) )
// // 出力 -> AVURLAsset
//
// print("i can do it")
// }
private func addVideoPlayer(with asset: AVAsset, playerView: UIView) {
let playerItem = AVPlayerItem(asset: asset)
player = AVPlayer(playerItem: playerItem)
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.itemDidFinishPlaying(_:)),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: playerItem)
let layer: AVPlayerLayer = AVPlayerLayer(player: player)
layer.backgroundColor = UIColor.white.cgColor
layer.frame = CGRect(x: 0, y: 0, width: playerView.frame.width, height: playerView.frame.height)
layer.videoGravity = AVLayerVideoGravityResizeAspectFill
playerView.layer.sublayers?.forEach({$0.removeFromSuperlayer()})
playerView.layer.addSublayer(layer)
}
func itemDidFinishPlaying(_ notification: Notification) {
if let startTime = trimmerView.startTime {
player?.seek(to: startTime)
}
}
func startPlaybackTimeChecker() {
stopPlaybackTimeChecker()
playbackTimeCheckerTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self,
selector:
#selector(ViewController.onPlaybackTimeChecker), userInfo: nil, repeats: true)
}
func stopPlaybackTimeChecker() {
playbackTimeCheckerTimer?.invalidate()
playbackTimeCheckerTimer = nil
}
func onPlaybackTimeChecker() {
guard let startTime = trimmerView.startTime, let endTime = trimmerView.endTime, let player = player else {
return
}
let playBackTime = player.currentTime()
trimmerView.seek(to: playBackTime)
if playBackTime >= endTime {
player.seek(to: startTime, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)
trimmerView.seek(to: startTime)
}
}
@IBAction func play(_ sender: Any) {
guard let player = player else { return }
if !player.isPlaying {
player.play()
startPlaybackTimeChecker()
} else {
player.pause()
stopPlaybackTimeChecker()
}
}
@IBAction func trim(_ sender: Any) {
}
}
extension ViewController: TrimmerViewDelegate {
func positionBarStoppedMoving(_ playerTime: CMTime) {
player?.seek(to: playerTime, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)
player?.play()
startPlaybackTimeChecker()
}
func didChangePositionBar(_ playerTime: CMTime) {
stopPlaybackTimeChecker()
player?.pause()
player?.seek(to: playerTime, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)
let duration = (trimmerView.endTime! - trimmerView.startTime!).seconds
print(duration)
}
}
extension AVPlayer {
var isPlaying: Bool {
return self.rate != 0 && self.error == nil
}
}
AssetSelectionViewController.swift
import UIKit
import Photos
class AssetSelectionViewController: UIViewController {
var fetchResult: PHFetchResult<PHAsset>?
override func viewDidLoad() {
super.viewDidLoad()
loadLibrary()
}
func loadLibrary() {
PHPhotoLibrary.requestAuthorization { (status) in
if status == .authorized {
self.fetchResult = PHAsset.fetchAssets(with: .video, options: nil)
}
}
}
func loadAssetRandomly() {
guard let fetchResult = fetchResult, fetchResult.count > 0 else {
print("Error loading assets.")
return
}
let randomAssetIndex = Int(arc4random_uniform(UInt32(fetchResult.count - 1)))
let asset = fetchResult.object(at: randomAssetIndex)
PHCachingImageManager().requestAVAsset(forVideo: asset, options: nil) { (avAsset, audioMix, info) in
DispatchQueue.main.async {
if let avAsset = avAsset {
self.loadAsset(avAsset)
}
}
}
}
func loadAsset(_ asset: AVAsset) {
// override in subclass
}
}