INVideoExportController.swift 35 KB

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