INVideoExportController.swift 33 KB

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