INVideoExportController.swift 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867
  1. //
  2. // INVideoExportController.swift
  3. // Introduce
  4. //
  5. // Created by 胡志强 on 2021/11/29.
  6. //
  7. import BFAnalyzeKit
  8. import BFCommonKit
  9. import BFRecordScreenKit
  10. import BFUIKit
  11. import Foundation
  12. import Photos
  13. import SwiftUI
  14. import UIKit
  15. class INVideoExportController: BFBaseViewController {
  16. var avplayerTimeObserver: NSKeyValueObservation?
  17. let backV : UIView = {
  18. let v = UIView()
  19. v.backgroundColor = UIColor.hexColor(hexadecimal: "#1A1A1A")
  20. return v
  21. }()
  22. let coverIV : UIImageView = {
  23. let iv = UIImageView()
  24. iv.backgroundColor = .black
  25. iv.alpha = 0.5
  26. iv.contentMode = .scaleAspectFit
  27. return iv
  28. }()
  29. var hasExportAll = false
  30. var hasExportOnly = false
  31. var hasSaveAll = false
  32. var hasSaveOnly = false
  33. var saveAllUlr = URL(fileURLWithPath: "aaa")
  34. var saveOnlyUlr = URL(fileURLWithPath: "aaa")
  35. var playViewFrame: CGRect = CGRect.zero
  36. var isExporting = false {
  37. didSet {
  38. if isExporting {
  39. avplayer.pause()
  40. }
  41. }
  42. }
  43. var enterBackgroundCancleExport = false
  44. // 预览播放进度
  45. var sliderView: BFVideoPlayerSliderView?
  46. // 合成进度指示条
  47. lazy var progressView: UIView = {
  48. let v = UIView()
  49. v.backgroundColor = ThemeStyleColor
  50. v.alpha = 0.5
  51. return v
  52. }()
  53. // 合成进度百分比lable
  54. lazy var progressL: UILabel = {
  55. let la = UILabel()
  56. la.textColor = .white
  57. la.textAlignment = .center
  58. la.text = "0%"
  59. la.font = UIFont.systemFont(ofSize: 28)
  60. la.shadowColor = .black
  61. la.shadowOffset = CGSize(width: 1, height: 1)
  62. return la
  63. }()
  64. lazy var saveAllBtn: UIButton = {
  65. let btn = UIButton()
  66. btn.setImage(UIImage(named: "export_saveall_n"), for: .normal)
  67. btn.setImage(UIImage(named: "export_saveall_h"), for: .selected)
  68. btn.addTarget(self, action: #selector(saveAllAction(btn:)), for: .touchUpInside)
  69. return btn
  70. }()
  71. lazy var saveOnlyBtn: UIButton = {
  72. let btn = UIButton()
  73. btn.setImage(UIImage(named: "export_saveonly_n"), for: .normal)
  74. btn.setImage(UIImage(named: "export_saveonly_h"), for: .selected)
  75. btn.addTarget(self, action: #selector(saveOnlyAction(btn:)), for: .touchUpInside)
  76. return btn
  77. }()
  78. lazy var completeBtn: UIButton = {
  79. let btn = UIButton()
  80. btn.setTitle("回到首页", for: .normal)
  81. btn.titleLabel?.font = UIFont.systemFont(ofSize: 14)
  82. btn.setTitleColor(UIColor.hexColor(hexadecimal: "#6B6B6B"), for: .normal)
  83. btn.setTitleColor(.gray, for: .disabled)
  84. btn.addTarget(self, action: #selector(completeAction), for: .touchUpInside)
  85. btn.isHidden = true
  86. return btn
  87. }()
  88. lazy var saveToPhotoBtn: UIButton = {
  89. let btn = UIButton()
  90. btn.setTitle(" 保存到相册", for: .normal)
  91. btn.setTitleColor(.white, for: .normal)
  92. btn.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
  93. btn.setImage(UIImage(named: "export_btn"), for: .normal)
  94. // btn.backgroundColor = ThemeStyleColor
  95. btn.setBackgroundImage(UIImage(color: ThemeStyleColor), for: .normal)
  96. btn.setBackgroundImage(UIImage(color: UIColor.hexColor(hexadecimal: "#1C4D80")), for: .highlighted)
  97. btn.adjustsImageWhenHighlighted = false
  98. btn.adjustsImageWhenDisabled = false
  99. btn.isEnabled = false
  100. btn.alpha = 0.3
  101. btn.addCorner(roundingCorners: .allCorners, corner: 8)
  102. btn.addTarget(self, action: #selector(saveToPhotoNow), for: .touchUpInside)
  103. return btn
  104. }()
  105. lazy var bottomView: UIView = {
  106. let vv = UIView()
  107. vv.backgroundColor = .black
  108. // vv.isHidden = true
  109. return vv
  110. }()
  111. lazy var errorView : UIView = {
  112. let errbackV = UIView()
  113. errbackV.backgroundColor = .black
  114. errbackV.isHidden = true
  115. let titleL = UILabel()
  116. titleL.text = "合成失败"
  117. titleL.font = UIFont.systemFont(ofSize: 36)
  118. titleL.textColor = .white
  119. titleL.textAlignment = .center
  120. errbackV.addSubview(titleL)
  121. let subTitleL = UILabel()
  122. subTitleL.text = "请重新尝试"
  123. subTitleL.tag = 33000
  124. subTitleL.font = UIFont.systemFont(ofSize: 24)
  125. subTitleL.textColor = UIColor.hexColor(hexadecimal: "#A6A6A6")
  126. subTitleL.textAlignment = .center
  127. subTitleL.contentCompressionResistancePriority(for: NSLayoutConstraint.Axis.horizontal)
  128. subTitleL.numberOfLines = 0
  129. errbackV.addSubview(subTitleL)
  130. let retryBtn = UIButton()
  131. retryBtn.backgroundColor = ThemeStyleColor
  132. retryBtn.setTitle("重试", for: .normal)
  133. retryBtn.tag = 33001
  134. retryBtn.setTitleColor(.white, for: .normal)
  135. retryBtn.addCorner(corner:5)
  136. retryBtn.addTarget(self, action: #selector(retryAction(sender:)), for: .touchUpInside)
  137. errbackV.addSubview(retryBtn)
  138. retryBtn.snp.makeConstraints { make in
  139. make.width.equalTo(245)
  140. make.height.equalTo(50)
  141. make.centerX.equalToSuperview()
  142. make.centerY.equalToSuperview().offset(10)
  143. }
  144. subTitleL.snp.makeConstraints { make in
  145. make.left.right.equalToSuperview()
  146. make.bottom.equalTo(retryBtn.snp.top).offset(-30)
  147. }
  148. titleL.snp.makeConstraints { make in
  149. make.left.right.equalToSuperview()
  150. make.height.equalTo(70)
  151. make.bottom.equalTo(subTitleL.snp.top)
  152. }
  153. return errbackV
  154. }()
  155. var playerLayer: AVPlayerLayer!
  156. lazy var avplayer: AVPlayer = {
  157. let avplayer = AVPlayer()
  158. avplayerTimeObserver = avplayer.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 100), queue: DispatchQueue.global()) { [weak self, weak avplayer] _ in
  159. // 进度监控
  160. if let item = avplayer?.currentItem, !(self?.sliderView?.isDragingProgressSlder ?? false) {
  161. DispatchQueue.main.async { [weak self, weak avplayer] in
  162. if avplayer?.currentItem?.status != .readyToPlay {
  163. self?.sliderView?.progress = 0
  164. }else{
  165. self?.sliderView?.progress = CMTimeGetSeconds(item.currentTime()) / CMTimeGetSeconds(item.duration)
  166. }
  167. }
  168. }
  169. } as? NSKeyValueObservation
  170. NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: avplayer.currentItem, queue: .main) { [weak avplayer, weak self] _ in
  171. avplayer?.seek(to: CMTime.zero)
  172. self?.sliderView?.playEnd()
  173. // self?.changeToOriginalFrame()
  174. }
  175. return avplayer
  176. }()
  177. lazy var export: BFRecordExport = {
  178. let export = BFRecordExport()
  179. export.progress = { [weak self] progress in
  180. DispatchQueue.main.async { [weak self] in
  181. if let sself = self {
  182. // let width = export.data?.first?.width ?? 0
  183. sself.progressL.text = String(format: "%d%%", Int(progress * 100))
  184. sself.progressView.snp.updateConstraints { make in
  185. make.width.equalTo(sself.playerLayer.bounds.width * CGFloat(progress))
  186. }
  187. }
  188. }
  189. }
  190. export.exportCompletion = { [weak self] error, url in
  191. DispatchQueue.main.async { [weak self] in
  192. guard let sself = self else {
  193. return
  194. }
  195. UIApplication.shared.isIdleTimerDisabled = false
  196. sself.resetViewStatus()
  197. sself.coverIV.isHidden = true
  198. sself.progressView.snp.updateConstraints { make in
  199. make.width.equalTo(0)
  200. }
  201. sself.progressL.text = "0%"
  202. if let fileUrl = url {
  203. sself.sliderView?.isHidden = false
  204. sself.sliderView?.playStart()
  205. sself.completeBtn.isHidden = false
  206. let item = AVPlayerItem(url: fileUrl)
  207. sself.avplayer.replaceCurrentItem(with: item)
  208. usleep(1000)
  209. sself.avplayer.play()
  210. sself.playerLayer.borderColor = ThemeStyleColor.cgColor
  211. if sself.saveAllBtn.isSelected {
  212. sself.saveAllUlr = fileUrl
  213. sself.hasExportAll = true
  214. }
  215. if sself.saveOnlyBtn.isSelected {
  216. sself.saveOnlyUlr = fileUrl
  217. sself.hasExportOnly = true
  218. }
  219. // 添加播放进度视图
  220. self?.addVideoSliderView()
  221. }else {
  222. if let l = sself.errorView.viewWithTag(33000) as? UILabel, let err = error as NSError?,let b = sself.errorView.viewWithTag(33001) as? UIButton {
  223. switch err.code {
  224. case ExportError.FileNotExist.rawValue :
  225. l.text = "原视频/图片文件被删除"
  226. b.setTitle("重新制作", for: .normal)
  227. case ExportError.DiskNoSpace.rawValue :
  228. l.text = "存储空间不足:" + (err.userInfo["msg"] as! String)
  229. b.setTitle("重试", for: .normal)
  230. case ExportError.VoiceLost.rawValue :
  231. l.text = "手机录音文件丢失"
  232. b.setTitle("重试", for: .normal)
  233. default:
  234. l.text = "请重新尝试"
  235. b.setTitle("重试", for: .normal)
  236. }
  237. l.sizeToFit()
  238. }
  239. sself.errorView.isHidden = false
  240. }
  241. }
  242. // 合成成功上报
  243. BFEventTrackAdaptor.baseReportUpload(businessType: nil, objectType: .ot_composeSuccess, pageSource: .sp_composePage, commonParams: commonParams())
  244. }
  245. return export
  246. }()
  247. deinit {
  248. avplayerTimeObserver?.invalidate()
  249. NotificationCenter.default.removeObserver(self)
  250. }
  251. override func viewWillAppear(_ animated: Bool) {
  252. super.viewWillAppear(animated)
  253. PQNotification.addObserver(self, selector: #selector(enterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
  254. PQNotification.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
  255. //进入活跃状态
  256. PQNotification.addObserver(self, selector: #selector(didBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
  257. //进入非活跃状态
  258. PQNotification.addObserver(self, selector: #selector(willResignActive), name: UIApplication.willResignActiveNotification, object: nil)
  259. showNavigation()
  260. // 曝光上报
  261. BFEventTrackAdaptor.baseReportUpload(businessType: .bt_pageView, objectType: nil, pageSource: .sp_composePage, commonParams: commonParams())
  262. }
  263. @objc func didBecomeActive(){
  264. BFLog(message: "进入活跃状态")
  265. if let sbtn = sliderView?.viewWithTag(1) as? UIButton ,!isExporting {
  266. sbtn.isSelected = false
  267. avplayer.play()
  268. }
  269. }
  270. @objc func willResignActive(){
  271. BFLog(message: "进入非活跃状态")
  272. if let sbtn = sliderView?.viewWithTag(1) as? UIButton , !isExporting{
  273. sbtn.isSelected = true
  274. avplayer.pause()
  275. }
  276. }
  277. @objc func enterBackground() {
  278. BFLog(message: "进入到后台")
  279. if isExporting {
  280. enterBackgroundCancleExport = true
  281. self.export.cancelExport()
  282. }else{
  283. if let sbtn = sliderView?.viewWithTag(1) as? UIButton {
  284. sbtn.isSelected = true
  285. avplayer.pause()
  286. }
  287. }
  288. }
  289. @objc func willEnterForeground() {
  290. BFLog(message: "进入到前台")
  291. if enterBackgroundCancleExport {
  292. export.startExprot(synthesisAll: saveAllBtn.isSelected)
  293. enterBackgroundCancleExport = false
  294. }else {
  295. //重启播放器
  296. if let sbtn = sliderView?.viewWithTag(1) as? UIButton {
  297. sbtn.isSelected = false
  298. avplayer.play()
  299. }
  300. }
  301. }
  302. override func viewWillDisappear(_ animated: Bool) {
  303. super.viewWillDisappear(animated)
  304. PQNotification.removeObserver(self)
  305. UIApplication.shared.isIdleTimerDisabled = false
  306. }
  307. override func backBtnClick() {
  308. if isExporting {
  309. whetherCancelExport (msg: "正在合成中,是否取消?"){
  310. self.export.cancelExport()
  311. super.backBtnClick()
  312. }
  313. return
  314. }
  315. if !(hasExportAll || hasExportOnly) {}
  316. export.cancelExport()
  317. super.backBtnClick()
  318. }
  319. override func viewDidLoad() {
  320. super.viewDidLoad()
  321. view.backgroundColor = .black
  322. navHeadImageView?.backgroundColor = .clear
  323. leftButton(image: nil, imageName: nil, tintColor: UIColor.white)
  324. addSubviews()
  325. // 默认保留录音合成
  326. saveAllBtn.isSelected = true
  327. DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
  328. self?.exportNow()
  329. }
  330. }
  331. func addSubviews() {
  332. view.addSubview(bottomView)
  333. view.addSubview(backV)
  334. view.addSubview(errorView)
  335. playerLayer = AVPlayerLayer(player: avplayer)
  336. playerLayer.borderWidth = 2
  337. playerLayer.borderColor = UIColor.hexColor(hexadecimal: "#1A1A1A").cgColor
  338. backV.layer.addSublayer(playerLayer)
  339. backV.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(playAction)))
  340. backV.addSubview(coverIV)
  341. backV.addSubview(progressView)
  342. backV.addSubview(progressL)
  343. bottomView.addSubview(saveAllBtn)
  344. bottomView.addSubview(saveOnlyBtn)
  345. bottomView.addSubview(saveToPhotoBtn)
  346. bottomView.addSubview(completeBtn)
  347. errorView.snp.makeConstraints { make in
  348. make.edges.equalToSuperview()
  349. }
  350. coverIV.snp.makeConstraints { make in
  351. make.bottom.centerX.equalToSuperview()
  352. make.width.equalTo(coverIV.snp.height).multipliedBy(cScreenWidth / cScreenHeigth)
  353. make.height.equalToSuperview().offset(-1 * cDevice_iPhoneNavBarAndStatusBarHei + 30)
  354. }
  355. progressView.snp.makeConstraints { make in
  356. make.top.left.bottom.equalTo(coverIV)
  357. make.width.equalTo(0)
  358. }
  359. progressL.snp.makeConstraints { make in
  360. make.width.centerX.centerY.equalTo(coverIV)
  361. make.height.equalTo(28)
  362. }
  363. backV.snp.makeConstraints { make in
  364. make.left.right.top.equalToSuperview()
  365. make.bottom.equalTo(bottomView.snp.top)
  366. }
  367. bottomView.snp.makeConstraints { make in
  368. make.left.right.equalToSuperview()
  369. if #available(iOS 11.0, *) {
  370. make.bottom.equalTo(view.safeAreaLayoutGuide.snp.bottom)
  371. } else {
  372. make.bottom.equalToSuperview()
  373. }
  374. }
  375. completeBtn.snp.makeConstraints { make in
  376. // make.left.equalTo(16)
  377. // make.right.equalTo(-16)
  378. make.centerX.equalToSuperview()
  379. make.bottom.equalToSuperview().offset(-7)
  380. make.height.equalTo(20)
  381. }
  382. saveToPhotoBtn.snp.makeConstraints { make in
  383. make.left.right.equalTo(completeBtn)
  384. make.height.equalTo(46)
  385. make.bottom.equalTo(completeBtn.snp.top).offset(-25)
  386. }
  387. saveAllBtn.snp.makeConstraints { make in
  388. make.left.equalTo(completeBtn)
  389. make.bottom.equalTo(saveToPhotoBtn.snp.top).offset(-15)
  390. make.height.equalTo(70)
  391. make.width.equalTo(164)
  392. make.top.equalTo(28)
  393. make.centerX.equalToSuperview().offset(-82 - 6.5)
  394. }
  395. saveOnlyBtn.snp.makeConstraints { make in
  396. // make.left.equalTo(saveAllBtn.snp.right).offset(16)
  397. make.top.height.width.equalTo(saveAllBtn)
  398. make.left.equalTo(saveAllBtn.snp.right).offset(13)
  399. make.right.equalTo(completeBtn)
  400. }
  401. // DispatchQueue.main.asyncAfter(deadline: .now() + 0) { [weak self] in
  402. // guard let sself = self else {
  403. // return
  404. // }
  405. //
  406. // }
  407. }
  408. override func viewDidLayoutSubviews() {
  409. super.viewDidLayoutSubviews()
  410. playerLayer.frame = coverIV.frame
  411. }
  412. /// 添加播放进度视图
  413. func addVideoSliderView() {
  414. if sliderView == nil {
  415. sliderView = BFVideoPlayerSliderView(frame: CGRect(x: (backV.frame.width - playerLayer.frame.width) / 2, y: backV.frame.height - 60, width: playerLayer.frame.width, height: 50),isShowFullScreen: false)
  416. sliderView?.playerBtn.setImage(UIImage(named: "play_0"), for: .normal)
  417. sliderView?.playerBtn.setImage(UIImage(named: "play_1"), for: .selected)
  418. sliderView?.valueChangeBloc = { [weak self,weak sliderView] sender in
  419. let cmtime = CMTime(value: CMTimeValue(Float64(sender.value) * Float64(self?.avplayer.currentItem?.asset.duration.seconds ?? 0) * 1000.0), timescale: CMTimeScale(1000.0))
  420. BFLog(message: "cmtime == \(cmtime),\(cmtime.seconds)")
  421. self?.avplayer.seek(to: cmtime, toleranceBefore: CMTime(seconds: 1, preferredTimescale: 1000), toleranceAfter: CMTime(seconds: 1, preferredTimescale: 1000))
  422. sliderView?.playStart()
  423. self?.avplayer.play()
  424. }
  425. sliderView?.slideBeginDrag = { [weak self] in
  426. self?.avplayer.pause()
  427. }
  428. sliderView?.btnClickBloc = { [weak self] sender in
  429. // 按钮点击
  430. if sender.tag == 1 {
  431. self?.play(sender: sender)
  432. } else if sender.tag == 2 {
  433. if sender.isSelected {
  434. self?.changeToFullScreen()
  435. } else {
  436. self?.changeToOriginalFrame()
  437. }
  438. }
  439. }
  440. }
  441. sliderView?.duration = avplayer.currentItem?.asset.duration.seconds ?? 0
  442. if sliderView?.superview == nil {
  443. backV.addSubview(sliderView!)
  444. }
  445. }
  446. func resetViewStatus() {
  447. isExporting = false
  448. // sself.bottomView.isHidden = false
  449. saveAllBtn.setImage(UIImage(named: "export_saveall_n"), for: .normal)
  450. saveOnlyBtn.setImage(UIImage(named: "export_saveonly_n"), for: .normal)
  451. saveToPhotoBtn.isEnabled = true
  452. saveToPhotoBtn.alpha = 1
  453. completeBtn.isEnabled = true
  454. progressView.isHidden = true
  455. progressView.snp.updateConstraints { make in
  456. make.width.equalTo(0)
  457. }
  458. progressL.isHidden = true
  459. progressL.text = "0%"
  460. }
  461. // MARK: - 按钮事件
  462. @objc func retryAction(sender:UIButton){
  463. if sender.currentTitle == "重新制作" {
  464. export.cancelExport()
  465. navigationController?.popToRootViewController(animated: true)
  466. } else {
  467. errorView.isHidden = true
  468. exportNow()
  469. }
  470. }
  471. func exportNow() {
  472. if isExporting {
  473. cShowHUB(superView: nil, msg: "正在合成中。。。")
  474. return
  475. }
  476. isExporting = true
  477. coverIV.isHidden = false
  478. playerLayer.borderColor = UIColor.hexColor(hexadecimal: "#1A1A1A").cgColor
  479. avplayer.replaceCurrentItem(with: nil)
  480. // bottomView.isHidden = true
  481. if saveAllBtn.isSelected {
  482. // saveAllBtn.setImage(UIImage(named: "export_saveall_h"), for: .disabled)
  483. saveAllBtn.setImage(UIImage(named: "export_saveall_h"), for: .normal)
  484. saveOnlyBtn.setImage(UIImage(named: "export_saveonly_n"), for: .normal)
  485. }else{
  486. // saveAllBtn.setImage(UIImage(named: "export_saveall_n"), for: .disabled)
  487. saveAllBtn.setImage(UIImage(named: "export_saveall_n"), for: .normal)
  488. saveOnlyBtn.setImage(UIImage(named: "export_saveonly_h"), for: .normal)
  489. }
  490. saveToPhotoBtn.isEnabled = false
  491. saveToPhotoBtn.alpha = 0.3
  492. completeBtn.isEnabled = false
  493. progressView.isHidden = false
  494. progressL.isHidden = false
  495. sliderView?.isHidden = true
  496. export.startExprot(synthesisAll: saveAllBtn.isSelected)
  497. UIApplication.shared.isIdleTimerDisabled = true
  498. // 开始合成上报
  499. BFEventTrackAdaptor.baseReportUpload(businessType: nil, objectType: .ot_startCompose, pageSource: .sp_composePage, commonParams: commonParams())
  500. }
  501. @objc func saveToPhotoNow() {
  502. if (saveAllBtn.isSelected && hasSaveAll) || saveOnlyBtn.isSelected && hasSaveOnly {
  503. cShowHUB(superView: nil, msg: "已保存过了")
  504. return
  505. }
  506. if let url = (avplayer.currentItem?.asset as? AVURLAsset)?.url {
  507. PHPhotoLibrary.shared().performChanges {
  508. PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: url)
  509. } completionHandler: { [weak self] isFinished, _ in
  510. guard let sself = self else {
  511. return
  512. }
  513. if isFinished {
  514. DispatchQueue.main.async {
  515. cShowHUB(superView: nil, msg: "保存成功")
  516. }
  517. if sself.saveAllBtn.isSelected {
  518. sself.hasSaveAll = true
  519. } else if sself.saveOnlyBtn.isSelected {
  520. sself.hasSaveOnly = true
  521. }
  522. }
  523. }
  524. }
  525. // 点击保存至相册上报
  526. BFEventTrackAdaptor.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_saveToAblum, pageSource: .sp_composePage, extParams: ["saveMode": saveAllBtn.isSelected ? "saveAll" :"saveRecord"], commonParams: commonParams())
  527. }
  528. @objc func saveAllAction(btn: UIButton) {
  529. if btn.isSelected {
  530. return
  531. }
  532. if isExporting {
  533. whetherCancelExport {[weak self] in
  534. self?.export.cancelExport()
  535. self?.resetViewStatus()
  536. self?.saveAllAction(btn: btn)
  537. }
  538. return
  539. }
  540. btn.isSelected = true
  541. saveOnlyBtn.isSelected = false
  542. avplayer.pause()
  543. if !hasExportAll {
  544. exportNow()
  545. } else {
  546. coverIV.isHidden = true
  547. avplayer.pause()
  548. let item = AVPlayerItem(url: saveAllUlr)
  549. avplayer.replaceCurrentItem(with: item)
  550. if let sbtn = sliderView?.viewWithTag(1) as? UIButton {
  551. sbtn.isSelected = false
  552. avplayer.play()
  553. sliderView?.duration = avplayer.currentItem?.asset.duration.seconds ?? 0
  554. }
  555. }
  556. }
  557. @objc func saveOnlyAction(btn: UIButton) {
  558. if btn.isSelected {
  559. return
  560. }
  561. if isExporting {
  562. whetherCancelExport {[weak self ] in
  563. self?.export.cancelExport()
  564. self?.resetViewStatus()
  565. self?.saveOnlyAction(btn: btn)
  566. }
  567. return
  568. }
  569. btn.isSelected = true
  570. saveAllBtn.isSelected = false
  571. avplayer.pause()
  572. if !hasExportOnly {
  573. exportNow()
  574. } else {
  575. coverIV.isHidden = true
  576. avplayer.pause()
  577. let item = AVPlayerItem(url: saveOnlyUlr)
  578. avplayer.replaceCurrentItem(with: item)
  579. if let sbtn = sliderView?.viewWithTag(1) as? UIButton {
  580. sbtn.isSelected = false
  581. avplayer.play()
  582. sliderView?.duration = avplayer.currentItem?.asset.duration.seconds ?? 0
  583. }
  584. }
  585. }
  586. @objc func playAction(){
  587. if !isExporting {
  588. if let sbtn = sliderView?.viewWithTag(1) as? UIButton{
  589. sbtn.isSelected = !sbtn.isSelected
  590. if sbtn.isSelected {
  591. avplayer.pause()
  592. }else{
  593. avplayer.play()
  594. }
  595. }
  596. }
  597. }
  598. @objc func completeAction() {
  599. // MARK: 删除所有录制资源. 现在放在了选择相册展示时清理cache
  600. if (!hasSaveOnly && !saveOnlyUlr.absoluteString.contains("aaa"))
  601. || (!hasSaveAll && !saveAllUlr.absoluteString.contains("aaa")) {
  602. let alertController = UIAlertController(title: "你还未保存视频哦,是否需要保存视频再离开?",
  603. message: "", preferredStyle: .alert)
  604. let cancelAction = UIAlertAction(title: "不保存", style: .default){[weak self] action in
  605. guard let sself = self else {
  606. return
  607. }
  608. sself.export.cancelExport()
  609. sself.navigationController?.popToRootViewController(animated: true)
  610. }
  611. let okAction = UIAlertAction(title: "保存视频", style: .default) {[weak self] action in
  612. guard let sself = self else {
  613. return
  614. }
  615. if sself.hasExportOnly {
  616. PHPhotoLibrary.shared().performChanges {
  617. PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: sself.saveOnlyUlr)
  618. } completionHandler: { _, _ in
  619. }
  620. }
  621. if sself.hasExportAll {
  622. PHPhotoLibrary.shared().performChanges {
  623. PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: sself.saveAllUlr)
  624. } completionHandler: { _, _ in
  625. }
  626. }
  627. sself.export.cancelExport()
  628. sself.navigationController?.popToRootViewController(animated: true)
  629. }
  630. alertController.addAction(cancelAction)
  631. alertController.addAction(okAction)
  632. self.present(alertController, animated: true, completion: nil)
  633. //
  634. // let remindData = BFBaseModel()
  635. // remindData.summary = "合成的视频尚未保存到相册"
  636. // let alertV = BFRemindView(frame: view.bounds)
  637. // alertV.isBanned = true
  638. // alertV.confirmBtn.setTitle("不保存", for: .normal)
  639. // alertV.cancelBtn.setTitle("确认保存", for: .normal)
  640. // alertV.remindData = remindData
  641. // alertV.remindBlock = { [weak self] item, _ in
  642. // guard let sself = self else {
  643. // return
  644. // }
  645. // if item.tag == 1 { // 确定返回到上一层
  646. // if sself.hasExportOnly {
  647. // PHPhotoLibrary.shared().performChanges {
  648. // PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: sself.saveOnlyUlr)
  649. // } completionHandler: { _, _ in
  650. // }
  651. // }
  652. // if sself.hasExportAll {
  653. // PHPhotoLibrary.shared().performChanges {
  654. // PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: sself.saveAllUlr)
  655. // } completionHandler: { _, _ in
  656. // }
  657. // }
  658. // }
  659. //
  660. // sself.export.cancelExport()
  661. // sself.navigationController?.popToRootViewController(animated: true)
  662. // }
  663. // UIApplication.shared.keyWindow?.addSubview(alertV)
  664. } else {
  665. export.cancelExport()
  666. navigationController?.popToRootViewController(animated: true)
  667. }
  668. }
  669. @objc func play(sender: UIButton) {
  670. if isExporting {
  671. avplayer.pause()
  672. return
  673. }
  674. if avplayer.currentItem != nil {
  675. if avplayer.timeControlStatus == .playing || sender.isSelected {
  676. avplayer.pause()
  677. } else if avplayer.timeControlStatus == .paused {
  678. avplayer.play()
  679. }
  680. }
  681. }
  682. func whetherCancelExport(msg:String = "正在合成中,是否切换?", comfirm: (() -> Void)?) {
  683. let alertController = UIAlertController(title: msg,
  684. message: "", preferredStyle: .alert)
  685. let cancelAction = UIAlertAction(title: "取消", style: .default, handler: nil)
  686. let okAction = UIAlertAction(title: "确定", style: .default) {[weak self] action in
  687. guard let sself = self else {
  688. return
  689. }
  690. sself.export.cancelExport()
  691. comfirm?()
  692. }
  693. alertController.addAction(cancelAction)
  694. alertController.addAction(okAction)
  695. self.present(alertController, animated: true, completion: nil)
  696. // let remindData = BFBaseModel()
  697. // remindData.summary = "正在合成中,是否取消?"
  698. // let alertV = BFRemindView(frame: view.bounds)
  699. // alertV.isBanned = true
  700. // alertV.confirmBtn.setTitle("继续合成", for: .normal)
  701. // alertV.cancelBtn.setTitle("取消合成", for: .normal)
  702. // alertV.remindData = remindData
  703. // alertV.remindBlock = { [weak self] item, _ in
  704. // guard let sself = self else {
  705. // return
  706. // }
  707. // if item.tag == 1 { // 确定返回到上一层
  708. // sself.export.cancelExport()
  709. // comfirm?()
  710. // }
  711. // }
  712. // UIApplication.shared.keyWindow?.addSubview(alertV)
  713. }
  714. }
  715. extension INVideoExportController {
  716. @objc func changeToOriginalFrame() {
  717. if !(sliderView?.isFullScreen ?? false) {
  718. isHiddenStatus = false // (0.0, 64.0, 375.0, 401.0)
  719. navHeadImageView?.isHidden = false
  720. backV.frame = CGRect(x: 0, y: navHeadImageView?.frame.maxY ?? 0, width: view.width, height: bottomView.frame.minY - (navHeadImageView?.frame.maxY ?? 0))
  721. let width = backV.height * (cScreenWidth / cScreenHeigth)
  722. playerLayer.frame = CGRect(x: (backV.width - width)/2, y: 0, width: width, height: backV.height)
  723. // playerLayer.frame = CGRect(x: 0, y: 0, width: view.width, height: bottomView.frame.minY - (navHeadImageView?.frame.maxY ?? 0))
  724. sliderView?.frame = CGRect(x: 0, y: backV.frame.height - 60, width: view.frame.width, height: 50)
  725. UIView.animate(withDuration: 0.2, animations: { [weak self] in
  726. self?.changeOrientation(orientation: .portrait)
  727. self?.backV.center = CGPoint(x: (self?.backV.width ?? 0) / 2, y: (self?.backV.height ?? 0) / 2 + (self?.navHeadImageView?.frame.maxY ?? 0))
  728. }) { [weak self] _ in
  729. }
  730. }
  731. }
  732. func changeToFullScreen() {
  733. if sliderView?.isFullScreen ?? false {
  734. isHiddenStatus = true
  735. navHeadImageView?.isHidden = true
  736. backV.frame = CGRect(x: 0, y: 0, width: view.frame.height, height: view.frame.width)
  737. let width = view.width * (cScreenWidth / cScreenHeigth)
  738. playerLayer.frame = CGRect(x: (view.height - width)/2, y: 0, width: width, height: backV.width)
  739. // playerLayer.frame = backV.bounds
  740. sliderView?.frame = CGRect(x: 0, y: backV.frame.height - 60, width: backV.frame.width, height: 50)
  741. UIView.animate(withDuration: 0.2, animations: { [weak self] in
  742. let orientation = UIDevice.current.orientation
  743. if orientation == .landscapeRight {
  744. self?.changeOrientation(orientation: .landscapeLeft)
  745. } else {
  746. self?.changeOrientation(orientation: .landscapeRight)
  747. }
  748. self?.backV.center = CGPoint(x: (self?.view.frame.width ?? 0) / 2, y: (self?.view.frame.height ?? 0) / 2)
  749. }) { [weak self] _ in
  750. }
  751. }
  752. }
  753. func changeOrientation(orientation: UIInterfaceOrientation) {
  754. UIView.animate(withDuration: 0.2, animations: { [weak self] in
  755. self?.backV.transform = self?.transformRotation(orientation: orientation) as! CGAffineTransform
  756. }) { [weak self] _ in
  757. }
  758. }
  759. @objc func transformRotation(orientation: UIInterfaceOrientation) -> CGAffineTransform {
  760. if orientation == .portrait {
  761. return .identity
  762. } else if orientation == .landscapeLeft {
  763. return CGAffineTransform(rotationAngle: -CGFloat.pi / 2)
  764. } else if orientation == .landscapeRight {
  765. return CGAffineTransform(rotationAngle: CGFloat.pi / 2)
  766. }
  767. return .identity
  768. }
  769. }