Files

507 lines
18 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// ViewController.swift
// SimpleVideoEditor
//
// Created by RenZhu Macro on 2020/7/2.
// Copyright © 2020 RenZhu Macro. All rights reserved.
//
import UIKit
<<<<<<< Updated upstream
import MetalVideoProcess
import AVFoundation
class ViewController: UIViewController {
@IBOutlet weak var renderView: MetalVideoProcessRenderView!
@IBOutlet weak var progress: UISlider!
var player: MetalVideoProcessPlayer?
var beauty1: MetalVideoProcessBeautyFilter?
var beauty2: MetalVideoProcessBeautyFilter?
var blur1: MetalVideoProcessGaussianBlurFilter?
var blur2: MetalVideoProcessGaussianBlurFilter?
var grayFilter: MetalVideoProcessLuminance?
override func viewDidLoad() {
super.viewDidLoad()
let asset1 = AVAsset(url: Bundle.main.url(forResource: "853", withExtension: "mp4")!)
let asset2 = AVAsset(url: Bundle.main.url(forResource: "cute", withExtension: "mp4")!)
let item1 = MetalVideoEditorItem(asset: asset1)
let item2 = MetalVideoEditorItem(asset: asset2)
//set transition before build editor
let transitionDuration = CMTime.init(seconds: 2.0, preferredTimescale: 600)
item1.videoTransition = TransitionDuration(duration: transitionDuration)
item1.audioTransition = FadeInOutAudioTransition(duration: transitionDuration)
do {
let editor = try MetalVideoEditor(videoItems: [item1, item2],
customVideoCompositorClass: MetalVideoProcessCompositor.self)
let playerItem = editor.buildPlayerItem()
self.progress.maximumValue = Float(playerItem.duration.seconds)
let player = try MetalVideoProcessPlayer(playerItem: playerItem)
let beautyFilter1 = MetalVideoProcessBeautyFilter()
beautyFilter1.saveUniformSettings(forTimelineRange: item1.timeRange, trackID: item1.trackID)
beautyFilter1.isEnable = false
let beautyFilter2 = MetalVideoProcessBeautyFilter()
beautyFilter2.saveUniformSettings(forTimelineRange: item2.timeRange, trackID: item2.trackID)
beautyFilter2.isEnable = false
let blurFilter1 = MetalVideoProcessGaussianBlurFilter()
blurFilter1.saveUniformSettings(forTimelineRange: item1.timeRange, trackID: item1.trackID)
blurFilter1.isEnable = false
let blurFilter2 = MetalVideoProcessGaussianBlurFilter()
blurFilter2.saveUniformSettings(forTimelineRange: item2.timeRange, trackID: item2.trackID)
blurFilter2.isEnable = false
let gray = MetalVideoProcessLuminance()
gray.isEnable = false
self.grayFilter = gray
self.beauty1 = beautyFilter1
self.beauty2 = beautyFilter2
self.blur1 = blurFilter1
self.blur2 = blurFilter2
let transitionTimeRange = item1.timeRange.intersection(item2.timeRange)
let fadeTransition = MetalVideoProcessFadeTransition()
//
fadeTransition.mainTrackIDs.append(item1.trackID)
fadeTransition.mainTrackIDs.append(item2.trackID)
// item1item2intersection
fadeTransition.saveUniformSettings(forTimelineRange: transitionTimeRange, trackID: 0)
item1.transitoin = fadeTransition
//Begin build pipeline
player.addTarget(beautyFilter1, atTargetIndex: nil, trackID: item1.trackID, targetTrackId: 0)
player.addTarget(beautyFilter2, atTargetIndex: nil, trackID: item2.trackID, targetTrackId: item2.trackID)
//mapping trackId on mainTrack 0
beautyFilter1 --> blurFilter1 --> fadeTransition
beautyFilter2 --> blurFilter2 --> fadeTransition --> gray --> renderView
//Done
self.player = player
self.player?.playerDelegate = self
} catch {
debugPrint("init error")
}
}
@IBAction func play(_ sender: Any) {
self.player?.play()
}
@IBAction func pause(_ sender: Any) {
self.player?.pause()
}
=======
import AVFoundation
import MobileCoreServices
import MetalVideoProcess
class ViewController: UIViewController {
@IBOutlet weak var renderView: MetalVideoProcessRenderView!
@IBOutlet weak var mainEditButton: UIButton!
@IBOutlet weak var mainDeleteButton: UIButton!
@IBOutlet weak var subEditButton: UIButton!
@IBOutlet weak var subDeleteButton: UIButton!
var mainPicker: UIImagePickerController?
var subPicker: UIImagePickerController?
var mainLayer: MetalVideoProcessBlendFilter?
var player: MetalVideoProcessPlayer?
var movieWriter: MetalVideoProcessMovieWriter?
var videoBackground: MetalVideoProcessBackground = MetalVideoProcessBackground(trackID: 0)
var pop: PopupDialog?
var progressHUD: MBProgressHUD?
var mainResources: [ResourceItem] = []
var lastMainLayerItem: ResourceItem?
@IBOutlet weak var mainTableView: UITableView!
var mainDragger: TableViewDragger?
var videoEditor: MetalVideoEditor?
var mainSelectedItem: ResourceItem?
var subResources: [ResourceItem] = []
@IBOutlet weak var subTableView: UITableView!
var subDragger: TableViewDragger?
var subSelectedItem: ResourceItem?
@IBOutlet weak var progress: UISlider!
private var rotGes: UIRotationGestureRecognizer?
private var pinchGes: UIPinchGestureRecognizer?
var currentPostion: Position = Position(0.0, 0.0)
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.setNavigationBarHidden(true, animated: false)
self.mainTableView.register(ResourceItemTableViewCell.self, forCellReuseIdentifier: "mainCell")
self.mainTableView.allowsSelection = true
self.mainTableView.dataSource = self
self.mainTableView.delegate = self
self.subTableView.register(ResourceItemTableViewCell.self, forCellReuseIdentifier: "pipCell")
self.subTableView.allowsSelection = true
self.subTableView.dataSource = self
self.subTableView.delegate = self
self.mainDragger = TableViewDragger(tableView: self.mainTableView)
self.mainDragger?.delegate = self
self.subDragger = TableViewDragger(tableView: self.subTableView)
self.subDragger?.delegate = self
let asset1 = AVAsset(url: Bundle.main.url(forResource: "cute", withExtension: "mp4")!)
let asset2 = AVAsset(url: Bundle.main.url(forResource: "movie", withExtension: "mov")!)
let sampleMainItem = ResourceItem(asset: asset1)
let sampleSubItem = ResourceItem(asset: asset2)
self.lastMainLayerItem = sampleMainItem
let backgroundFrame = self.renderView.frame
let backgroundSize = CGSize(width: backgroundFrame.size.width * 2.0,
height: backgroundFrame.size.height * 2.0)
MetalVideoProcessBackground.canvasSize = backgroundSize
videoBackground.canvasSizeType = .Type720p
videoBackground.aspectRatioType = .Ratio16_9
videoBackground.setBackgroundType(type: .Blur)
self.mainResources.append(sampleMainItem)
self.subResources.append(sampleSubItem)
self.progress.minimumValue = 0.0
sampleMainItem.fillType = .aspectToFit
sampleMainItem.roi = CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0)
sampleSubItem.fillType = .aspectToFit
sampleSubItem.roi = CGRect(x: 0.5, y: 0.5, width: 0.5, height: 0.5)
self.rebuildPipeline()
self.mainTableView.reloadData()
self.subTableView.reloadData()
self.renderView.isUserInteractionEnabled = true
rotGes = UIRotationGestureRecognizer(target: self,
action: #selector(self.rotateAction))
rotGes!.delegate = self;
pinchGes = UIPinchGestureRecognizer(target: self, action: #selector(self.pinchAction))
pinchGes!.delegate = self;
let panGes = UIPanGestureRecognizer(target: self, action: #selector(self.panAction))
panGes.delegate = self
self.renderView.addGestureRecognizer(rotGes!)
self.renderView.addGestureRecognizer(pinchGes!)
self.renderView.addGestureRecognizer(panGes)
}
@IBAction func changeFillType(_ sender: UIButton) {
let tag = sender.tag
switch tag {
case 0:
self.videoBackground.aspectRatioType = .Ratio16_9
break
case 1:
self.videoBackground.aspectRatioType = .Ratio9_16
case 2:
self.videoBackground.aspectRatioType = .Ratio1_1
case 3:
self.videoBackground.aspectRatioType = .Ratio4_3
case 4:
self.videoBackground.aspectRatioType = .Ratio3_4
default:
break
}
}
@IBAction func play(_ sender: Any) {
self.player?.play()
}
@IBAction func pause(_ sender: Any) {
self.player?.pause()
}
@IBAction func importMain(_ sender: Any) {
let picker = UIImagePickerController()
picker.delegate = self
picker.sourceType = .photoLibrary
var pickerTypes: [String] = []
pickerTypes.append(kUTTypeMovie as String)
picker.mediaTypes = pickerTypes
picker.allowsEditing = true
self.navigationController?.present(picker, animated: true, completion: nil)
self.mainPicker = picker
}
@IBAction func importPIP(_ sender: Any) {
let picker = UIImagePickerController()
picker.delegate = self
picker.sourceType = .photoLibrary
var pickerTypes: [String] = []
pickerTypes.append(kUTTypeMovie as String)
picker.mediaTypes = pickerTypes
picker.allowsEditing = true
self.navigationController?.present(picker, animated: true, completion: nil)
self.subPicker = picker
}
>>>>>>> Stashed changes
@IBAction func progressChanged(_ sender: UISlider) {
let value = sender.value
self.player?.seekTo(time: Float64(value))
}
<<<<<<< Updated upstream
@IBAction func filterOn(_ sender: UISwitch) {
var operation: MetalVideoProcessOperation?
switch sender.tag {
case 0:
operation = self.beauty1
break
case 1:
operation = self.blur1
break
case 2:
operation = self.beauty2
break
case 3:
operation = self.blur2
break
case 4:
operation = self.grayFilter
break
default:
break
}
operation?.isEnable = sender.isOn
}
}
extension ViewController: MetalVideoProcessPlayerDelegate {
func playbackFrameTimeChanged(frameTime time: CMTime, player: AVPlayer) {
DispatchQueue.main.async {
self.progress.value = Float(time.seconds)
}
}
func playEnded(currentPlayer player: AVPlayer) {
}
func finishExport(error: NSError?) {
}
func exportProgressChanged(_ progress: Float) {
}
=======
@IBAction func editItem(_ sender: UIButton) {
let resourceEditView = ResourceItemEditView(nibName: "ResourceItemEditView", bundle: nil)
self.pop = PopupDialog(viewController: resourceEditView, buttonAlignment: .horizontal, transitionStyle: .bounceDown, tapGestureDismissal: true, panGestureDismissal: false)
let btnCancel = CancelButton(title: "Cancel", height: 60) {
self.pop?.viewController.removeFromParent()
self.pop?.removeFromParent()
self.pop = nil
return
}
let btnOK = DefaultButton(title: "OK", height: 60) {
self.rebuildPipeline()
self.mainTableView.reloadData()
self.subTableView.reloadData()
self.pop?.viewController.removeFromParent()
self.pop?.removeFromParent()
self.pop = nil
return
}
pop?.addButtons([btnOK, btnCancel])
if sender == self.mainEditButton {
guard let item = self.mainSelectedItem else {
return
}
self.present(pop!
, animated: true) {
resourceEditView.loadResourceItem(item)
}
} else {
guard let item = self.subSelectedItem else {
return
}
self.present(pop!
, animated: true) {
resourceEditView.loadResourceItem(item, isPipItem: true)
}
}
}
@IBAction func deleteItem(_ sender: UIButton) {
if sender == self.mainDeleteButton {
self.mainResources.removeAll { $0.trackID == self.mainSelectedItem?.trackID ?? 0 }
} else {
self.subResources.removeAll { $0.trackID == self.subSelectedItem?.trackID ?? 0 }
}
self.rebuildPipeline()
self.mainTableView.reloadData()
self.subTableView.reloadData()
self.subEditButton.isEnabled = self.subResources.count > 0
self.subDeleteButton.isEnabled = self.subResources.count > 0
self.mainEditButton.isEnabled = self.mainResources.count > 0
self.mainDeleteButton.isEnabled = self.mainResources.count > 0
}
func rebuildPipeline() {
self.videoEditor = nil
//rebuild player
self.player?.suspend()
self.player?.dispose()
self.player?.removeAllTargets()
self.player = nil
//clear all sources
renderView.sources.sources.removeAll()
do {
let currentEditor = try MetalVideoEditor(videoItems: self.mainResources, overlayItems: self.subResources)
self.videoEditor = currentEditor
try currentEditor.updateChannel()
print("before build PlayerItem mainItems:", currentEditor.editorItems)
print("before build PlayerItem overlayItems:", currentEditor.overlayItems)
let playerItem = currentEditor.buildPlayerItem()
print("after build PlayerItem mainItems:", currentEditor.editorItems)
print("after build PlayerItem overlayItems:", currentEditor.overlayItems)
self.progress.maximumValue = Float(playerItem.duration.seconds)
let player = try MetalVideoProcessPlayer(playerItem: playerItem)
let mainLayer = MetalVideoProcessBlendFilter()
mainLayer.debugName = "mainLayer"
var lastTransform: MetalVideoProcessTransformFilter? = nil
for mainItem in self.mainResources {
let transform = MetalVideoProcessTransformFilter()
transform.saveUniformSettings(forTimelineRange: mainItem.timeRange, trackID: mainItem.trackID)
transform.roi = mainItem.roi
transform.debugName = "main transform trackId:\(mainItem.trackID)"
//render blur background with every main track
player.addTarget(videoBackground, atTargetIndex: nil, trackID: mainItem.trackID, targetTrackId: 0)
mainItem.transformFilter = transform
transform.rotate = mainItem.rotate
transform.translation = mainItem.translation
transform.scale = mainItem.scale
mainItem.currentLayer = mainLayer
if let lt = lastTransform {
//link every main transform in mainLayer
lt --> transform
}
lastTransform = transform
}
// make sure mapping every items trackID on main track to main trackContainer with trackID 0,
// if you don't do this, player can't render every item's texutre to item's target
if let firstTransform = self.mainResources.first?.transformFilter {
self.mainResources.forEach { (item) in
player.addTarget(firstTransform, atTargetIndex: nil, trackID: item.trackID, targetTrackId: 0)
}
}
guard let lt = lastTransform else {
// zero items
self.player?.suspend()
self.player?.dispose()
self.player?.removeAllTargets()
self.player = nil
return
}
videoBackground --> mainLayer
// place lt on videoBackground, secondary source to mainLayer
lt --> mainLayer
var lastLayer: MetalVideoProcessBlendFilter = mainLayer
for subItem in self.subResources {
let pipLayer = MetalVideoProcessBlendFilter()
// render pipLayer in subItem.timeRange
pipLayer.saveUniformSettings(forTimelineRange: subItem.timeRange, trackID: subItem.trackID)
let transform = MetalVideoProcessTransformFilter()
// transform subItem's texture in subItem.timeRange
transform.saveUniformSettings(forTimelineRange: subItem.timeRange, trackID: subItem.trackID)
transform.roi = subItem.roi
// create a new targetContainer for subItem & pipLayer
player.addTarget(transform, atTargetIndex: nil, trackID: subItem.trackID, targetTrackId: subItem.trackID)
subItem.currentLayer = pipLayer
subItem.transformFilter = transform
transform.rotate = subItem.rotate
transform.translation = subItem.translation
transform.scale = subItem.scale
//compositing two layer (warning!!! place transform to secondary source to pipLayer)
lastLayer --> pipLayer
transform --> pipLayer
lastLayer = pipLayer
}
//render the final layer
lastLayer --> renderView
player.playerDelegate = self
self.player = player
} catch {
}
}
>>>>>>> Stashed changes
}