INVideoExportController.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. //
  2. // INVideoExportController.swift
  3. // Introduce
  4. //
  5. // Created by 胡志强 on 2021/11/29.
  6. //
  7. import Foundation
  8. import BFUIKit
  9. import Photos
  10. import BFRecordScreenKit
  11. import UIKit
  12. import BFCommonKit
  13. class INVideoExportController: BFBaseViewController {
  14. var videoAsset : AVURLAsset?
  15. var avplayerTimeObserver: NSKeyValueObservation?
  16. let backV = UIView()
  17. var hasExportAll = false
  18. var hasExportOnly = false
  19. var hasSaveAll = false
  20. var hasSaveOnly = false
  21. var saveAllUlr = URL(fileURLWithPath: "aaa")
  22. var saveOnlyUlr = URL(fileURLWithPath: "aaa")
  23. var isExporting = false
  24. // 播放进度
  25. lazy var progreddL : UILabel = {
  26. let l = UILabel(frame: CGRect(x: 0, y: cDevice_iPhoneStatusBarHei, width: cScreenWidth, height: 14))
  27. l.textAlignment = .center
  28. l.font = UIFont.systemFont(ofSize: 10)
  29. l.textColor = .white
  30. l.shadowColor = .black
  31. l.shadowOffset = CGSize(width: 1, height: 1)
  32. return l
  33. }()
  34. // 合成进度指示条
  35. lazy var progressView : UIView = {
  36. let v = UIView()
  37. v.backgroundColor = ThemeStyleColor
  38. return v
  39. }()
  40. // 合成进度百分比lable
  41. lazy var progressL : UILabel = {
  42. let la = UILabel()
  43. la.textColor = .white
  44. la.textAlignment = .center
  45. la.text = "0%"
  46. la.font = UIFont.systemFont(ofSize: 28)
  47. la.shadowColor = .black
  48. la.shadowOffset = CGSize(width: 1, height: 1)
  49. return la
  50. }()
  51. lazy var saveAllBtn:UIButton = {
  52. let btn = UIButton()
  53. btn.setImage(UIImage(named: "export_saveall_n"), for: .normal)
  54. btn.setImage(UIImage(named: "export_saveall_h"), for: .selected)
  55. btn.addTarget(self, action: #selector(saveAllAction(btn:)), for: .touchUpInside)
  56. return btn
  57. }()
  58. lazy var saveOnlyBtn:UIButton = {
  59. let btn = UIButton()
  60. btn.setImage(UIImage(named: "export_saveonly_n"), for: .normal)
  61. btn.setImage(UIImage(named: "export_saveonly_h"), for: .selected)
  62. btn.addTarget(self, action: #selector(saveOnlyAction(btn:)), for: .touchUpInside)
  63. btn.isSelected = true
  64. return btn
  65. }()
  66. lazy var exportBtn : UIButton = {
  67. let btn = UIButton()
  68. btn.setTitle(" 保存到相册", for: .normal)
  69. btn.setTitleColor(.white, for: .normal)
  70. btn.setImage(UIImage(named: "export_btn"), for: .normal)
  71. btn.backgroundColor = ThemeStyleColor
  72. btn.addCorner(roundingCorners: .allCorners, corner: 8)
  73. btn.addTarget(self, action: #selector(saveToPhotoNow), for: .touchUpInside)
  74. return btn
  75. }()
  76. lazy var bottomView:UIView = {
  77. let vv = UIView()
  78. vv.backgroundColor = .black
  79. return vv
  80. }()
  81. lazy var avplayer : AVPlayer = {
  82. let avplayer = AVPlayer()
  83. avplayerTimeObserver = avplayer.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 100), queue: DispatchQueue.global()) {[weak self, weak avplayer] time in
  84. // 进度监控
  85. if let item = avplayer?.currentItem {
  86. DispatchQueue.main.async { [weak self] in
  87. self?.progreddL.text = String(format: "%@ / %@", CMTimeGetSeconds(time).formatDurationToHMS(), CMTimeGetSeconds(item.duration).formatDurationToHMS())
  88. }
  89. }
  90. } as? NSKeyValueObservation
  91. NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: avplayer.currentItem, queue: .main) { [weak avplayer] notify in
  92. avplayer?.seek(to: CMTime.zero)
  93. }
  94. return avplayer
  95. }()
  96. lazy var export : BFRecordExport = {
  97. let export = BFRecordExport()
  98. export.progress = {[weak self] progress in
  99. DispatchQueue.main.async { [weak self] in
  100. if let sself = self {
  101. // let width = export.data?.first?.width ?? 0
  102. sself.progressL.text = String(format: "%d%%", Int(progress*100))
  103. sself.progressView.snp.updateConstraints { make in
  104. make.width.equalTo(cScreenWidth * CGFloat(progress))
  105. }
  106. }
  107. }
  108. }
  109. export.exportCompletion = {[weak self] (error, url) in
  110. DispatchQueue.main.async { [weak self] in
  111. guard let sself = self else {
  112. return
  113. }
  114. self?.isExporting = false
  115. sself.bottomView.isHidden = false
  116. sself.progreddL.isHidden = false
  117. sself.progressView.isHidden = true
  118. sself.progressL.isHidden = true
  119. if let fileUrl = url {
  120. let item = AVPlayerItem(url: fileUrl)
  121. sself.avplayer.replaceCurrentItem(with: item)
  122. sself.avplayer.play()
  123. if sself.saveAllBtn.isSelected {
  124. sself.saveAllUlr = fileUrl
  125. sself.hasExportAll = true
  126. }
  127. if sself.saveOnlyBtn.isSelected {
  128. sself.saveOnlyUlr = fileUrl
  129. sself.hasExportOnly = true
  130. }
  131. }
  132. }
  133. }
  134. return export
  135. }()
  136. deinit {
  137. avplayerTimeObserver?.invalidate()
  138. NotificationCenter.default.removeObserver(self)
  139. }
  140. override func viewWillAppear(_ animated: Bool) {
  141. super.viewWillAppear(animated)
  142. showNavigation()
  143. }
  144. override func backBtnClick() {
  145. if !(hasExportAll || hasExportOnly) {
  146. }
  147. export.cancelExport()
  148. super.backBtnClick()
  149. }
  150. override func viewDidLoad() {
  151. super.viewDidLoad()
  152. view.backgroundColor = .black
  153. navHeadImageView?.backgroundColor = .black
  154. leftButton(image: nil, imageName: nil, tintColor: UIColor.white)
  155. // backV.frame = CGRect(x: 0, y: navHeadImageView?.bottomY ?? 0, width: cScreenWidth, height: cScreenWidth)
  156. backV.backgroundColor = .clear
  157. backV.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(play)))
  158. addSubviews()
  159. // export.startExprot()
  160. DispatchQueue.main.asyncAfter(deadline: .now()+0.2) {[weak self] in
  161. self?.exportNow()
  162. }
  163. }
  164. func addSubviews(){
  165. view.addSubview(backV)
  166. view.addSubview(bottomView)
  167. let playerLayer = AVPlayerLayer(player: avplayer)
  168. backV.layer.addSublayer(playerLayer)
  169. backV.addSubview(progressView)
  170. backV.addSubview(progressL)
  171. backV.addSubview(progreddL)
  172. bottomView.addSubview(saveAllBtn)
  173. bottomView.addSubview(saveOnlyBtn)
  174. bottomView.addSubview(exportBtn)
  175. backV.snp.makeConstraints { make in
  176. make.left.right.equalToSuperview()
  177. make.top.equalTo(navHeadImageView!.snp.bottom)
  178. make.bottom.equalTo(bottomView.snp.top)
  179. }
  180. progressView.snp.makeConstraints { make in
  181. make.left.top.height.equalToSuperview()
  182. make.width.equalTo(0)
  183. }
  184. progressL.snp.makeConstraints { make in
  185. make.center.width.equalToSuperview()
  186. make.height.equalTo(20)
  187. }
  188. progreddL.snp.makeConstraints { make in
  189. make.bottom.equalToSuperview().offset(-10)
  190. make.centerX.equalTo(backV)
  191. make.width.equalTo(100)
  192. make.height.equalTo(20)
  193. }
  194. bottomView.snp.makeConstraints { make in
  195. make.left.right.equalToSuperview()
  196. if #available(iOS 11.0, *) {
  197. make.bottom.equalTo(view.safeAreaLayoutGuide.snp.bottom)
  198. } else {
  199. make.bottom.equalToSuperview()
  200. }
  201. }
  202. exportBtn.snp.makeConstraints { make in
  203. make.left.equalTo(saveAllBtn)
  204. make.right.equalTo(saveOnlyBtn)
  205. make.height.equalTo(50)
  206. make.bottom.equalTo(-40)
  207. }
  208. saveAllBtn.snp.makeConstraints { make in
  209. make.left.equalTo(16)
  210. make.bottom.equalTo(exportBtn.snp.top).offset(-21)
  211. make.height.equalTo(70)
  212. make.top.equalTo(28)
  213. }
  214. saveOnlyBtn.snp.makeConstraints { make in
  215. make.left.equalTo(saveAllBtn.snp.right).offset(16)
  216. make.top.height.width.equalTo(saveAllBtn)
  217. make.right.equalTo(-16)
  218. }
  219. DispatchQueue.main.asyncAfter(deadline: .now()+0) {[weak self] in
  220. playerLayer.frame = self!.backV.bounds
  221. }
  222. }
  223. //MARK: - 按钮事件
  224. func exportNow(){
  225. if bottomView.isHidden {
  226. cShowHUB(superView: nil, msg: "正在合成中。。。")
  227. return
  228. }
  229. bottomView.isHidden = true
  230. progressView.isHidden = false
  231. progressL.isHidden = false
  232. progreddL.isHidden = true
  233. export.startExprot(synthesisAll:saveAllBtn.isSelected)
  234. }
  235. @objc func saveToPhotoNow(){
  236. if (self.saveAllBtn.isSelected && self.hasSaveAll) || self.saveOnlyBtn.isSelected && self.hasSaveOnly {
  237. cShowHUB(superView: nil, msg: "已保存过了")
  238. return
  239. }
  240. if let url = (avplayer.currentItem?.asset as? AVURLAsset)?.url{
  241. PHPhotoLibrary.shared().performChanges {
  242. PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: url)
  243. } completionHandler: { isFinished, _ in
  244. if isFinished {
  245. DispatchQueue.main.async {
  246. cShowHUB(superView: nil, msg: "保存成功")
  247. }
  248. if self.saveAllBtn.isSelected {
  249. self.hasSaveAll = true
  250. }else if self.saveOnlyBtn.isSelected {
  251. self.hasSaveOnly = true
  252. }
  253. }
  254. }
  255. }
  256. }
  257. @objc func saveAllAction(btn:UIButton){
  258. if btn.isSelected {
  259. return
  260. }
  261. btn.isSelected = true
  262. saveOnlyBtn.isSelected = false
  263. avplayer.pause()
  264. if !hasExportAll{
  265. exportNow()
  266. }else{
  267. let item = AVPlayerItem(url: saveAllUlr)
  268. avplayer.replaceCurrentItem(with: item)
  269. avplayer.play()
  270. }
  271. }
  272. @objc func saveOnlyAction(btn:UIButton){
  273. if btn.isSelected {
  274. return
  275. }
  276. btn.isSelected = true
  277. saveAllBtn.isSelected = false
  278. avplayer.pause()
  279. if !hasExportOnly {
  280. exportNow()
  281. }else{
  282. let item = AVPlayerItem(url: saveOnlyUlr)
  283. avplayer.replaceCurrentItem(with: item)
  284. avplayer.play()
  285. }
  286. }
  287. @objc func play(){
  288. if isExporting {
  289. self.avplayer.pause()
  290. return
  291. }
  292. if self.avplayer.currentItem != nil {
  293. if self.avplayer.timeControlStatus == .playing {
  294. self.avplayer.pause()
  295. }else if self.avplayer.timeControlStatus == .paused{
  296. self.avplayer.play()
  297. }
  298. }
  299. }
  300. }