实现的功能:视频1的视频 + 视频2的音频,并导出视频文件
//
// ViewController.swift
// MovieEdit
//
// Created by caoyicheng on 2023/2/12.
//
import UIKit
import AVKit
let kScreenWidth = UIScreen.main.bounds.width
let kScreenHeight = UIScreen.main.bounds.height
class ViewController: UIViewController {
var player = AVPlayer()
var showView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
let view1 = UIView.init(frame: CGRect.init(x: 0, y: 100, width: kScreenWidth, height: kScreenWidth*0.5))
view1.backgroundColor = .systemPink
self.view.addSubview(view1)
self.showView = view1
let btn1 = UIButton.init(frame: CGRect.init(x: (kScreenWidth - 200)/2.0, y: view1.c_bottom() + 50, width: 200, height: 50))
btn1.setTitle("添加视频1", for: .normal)
btn1.setTitleColor(.white, for: .normal)
btn1.titleLabel?.font = .systemFont(ofSize: 16)
btn1.backgroundColor = .systemBlue
btn1.c_radius(radius: btn1.c_height()/2.0)
btn1.addTarget(self, action: #selector(button1Action(btn:)), for: .touchUpInside)
self.view.addSubview(btn1)
let btn2 = UIButton.init(frame: CGRect.init(x: (kScreenWidth - 200)/2.0, y: btn1.c_bottom() + 30, width: 200, height: 50))
btn2.setTitle("添加视频2", for: .normal)
btn2.setTitleColor(.white, for: .normal)
btn2.titleLabel?.font = .systemFont(ofSize: 16)
btn2.backgroundColor = .systemBlue
btn2.c_radius(radius: btn2.c_height()/2.0)
btn2.addTarget(self, action: #selector(button2Action(btn:)), for: .touchUpInside)
self.view.addSubview(btn2)
let btn3 = UIButton.init(frame: CGRect.init(x: (kScreenWidth - 200)/2.0, y: btn2.c_bottom() + 30, width: 200, height: 50))
btn3.setTitle("播放视频", for: .normal)
btn3.setTitleColor(.white, for: .normal)
btn3.titleLabel?.font = .systemFont(ofSize: 16)
btn3.backgroundColor = .systemBlue
btn3.c_radius(radius: btn3.c_height()/2.0)
btn3.addTarget(self, action: #selector(button3Action(btn:)), for: .touchUpInside)
self.view.addSubview(btn3)
}
// MARK: 添加视频1
@objc func button1Action(btn: UIButton) {
// 任务:视频1的视频 + 视频2的音频
// 1、加载视频资源
let path1 = Bundle.main.path(forResource: "movie1", ofType: "mov")
let videoAsset1 = AVAsset.init(url: URL.init(fileURLWithPath: path1!))
let path2 = Bundle.main.path(forResource: "movie2", ofType: "mov")
let videoAsset2 = AVAsset.init(url: URL.init(fileURLWithPath: path2!))
// 2、取出视频轨道、音频轨道
let videoAssetTrack = videoAsset1.tracks(withMediaType: .video).first
let audioAssetTrack = videoAsset2.tracks(withMediaType: .audio).first
// 3、创建工程,将素材的轨道数据导入到工程文件的轨道中
let mutableComposition = AVMutableComposition.init()
let videoTrack = mutableComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)
let audioTrack = mutableComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)
do {
try videoTrack!.insertTimeRange(.init(start: .zero, end: videoAsset1.duration), of: videoAssetTrack!, at: .zero)
try audioTrack!.insertTimeRange(.init(start: .zero, end: videoAsset2.duration), of: audioAssetTrack!, at: .zero)
// // 4、计算需要裁剪的头和尾的range,然后将音频轨道和视频轨道都进行裁剪
// let start = CMTimeMakeWithSeconds(1, preferredTimescale: videoAsset1.duration.timescale)
// let end = CMTimeMakeWithSeconds(3, preferredTimescale: videoAsset1.duration.timescale)
// let duration = CMTimeMakeWithSeconds(1, preferredTimescale: videoAsset1.duration.timescale)
// let deleteRange1 = CMTimeRangeMake(start: .zero, duration: start)
// let deleteRange2 = CMTimeRangeMake(start: end, duration: duration)
// videoTrack!.removeTimeRange(deleteRange1)
// videoTrack!.removeTimeRange(deleteRange2)
// audioTrack!.removeTimeRange(deleteRange1)
// audioTrack!.removeTimeRange(deleteRange2)
// // 5、我们已经将工程文件中的视频进行了裁剪,我们可以直接使用AVPlayer进行播放看裁剪成功没有
// let item = AVPlayerItem.init(asset: mutableComposition)
// self.player = AVPlayer.init(playerItem: item)
// let playerLayer = AVPlayerLayer.init(player: self.player)
// playerLayer.frame = self.showView.bounds
// self.showView.layer.addSublayer(playerLayer)
// self.player.play()
// 6、使用AVAssetExportSession将工程文件转码导出
let exportSession = AVAssetExportSession.init(asset: mutableComposition, presetName: AVAssetExportPresetPassthrough)
if let savePath = self.getSavePath() {
let outPath = NSURL.fileURL(withPath: savePath)
exportSession!.outputURL = outPath
exportSession!.outputFileType = .mp4
exportSession!.exportAsynchronously {
if exportSession!.status == .failed {
print("失败")
print(exportSession!.error!.localizedDescription)
} else if exportSession!.status == .cancelled {
print("取消")
} else if exportSession!.status == .completed {
print("完成")
print(savePath)
} else {
print("未知")
}
}
} else {
print("获取保存路径失败")
}
} catch {
print(error)
}
}
// MARK: 添加视频2
@objc func button2Action(btn: UIButton) {
}
// MARK: 播放视频
@objc func button3Action(btn: UIButton) {
// 播放视频
let path = Bundle.main.path(forResource: "movie1", ofType: "mov")
let playerItem = AVPlayerItem.init(url: URL.init(fileURLWithPath: path!))
self.player = AVPlayer.init(playerItem: playerItem)
let playerLayer = AVPlayerLayer.init(player: self.player)
playerLayer.frame = self.showView.bounds
self.showView.layer.addSublayer(playerLayer)
self.player.play()
}
// MARK: 获取保存路劲
@objc func getSavePath() -> String? {
let savePath = String.init(format: "%@/DraftVideo", NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)[0])
do {
let fileManager = FileManager.default
if fileManager.fileExists(atPath: savePath) == false {
// 文件夹不存在,则创建
try fileManager.createDirectory(at: URL(fileURLWithPath: savePath), withIntermediateDirectories: true, attributes: nil)
} else {
// 文件夹存在
}
let videoPath = String.init(format: "%@/%.0f.mp4", savePath, Date().timeIntervalSince1970)
return videoPath
} catch {
}
return nil
}
}
网友评论