INVideoExportController.swift 37 KB

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