12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592 |
- //
- // PQStuckPointPublicController.swift
- // PQSpeed
- //
- // Created by SanW on 2021/5/6.
- // Copyright © 2021 BytesFlow. All rights reserved.
- //
- import ObjectMapper
- import Photos
- import UIKit
- import WechatOpenSDK
- class PQStuckPointPublicController: PQBaseViewController {
- private var isShared: Bool = false // 是否在分享
- private var isExportSuccess: Bool = false // 是否导出完成
- private var isSaveDraftSuccess: Bool = false // 是否保存草稿完成
- private var isSaveProjectSuccess: Bool = false // 是否保存项目完成
- private var isUploadSuccess: Bool = false // 是否上传完成
- private var isPublicSuccess: Bool = false // 是否发布完成
- private var exportLocalURL: URL? // 导出的地址
- // 再创作数据
- private var reCreateData: PQReCreateModel?
- // 确定上传的数据
- private var uploadData: PQUploadModel?
- // 发布成功的视频数据
- private var videoData: PQVideoListModel?
- // 视频创作埋点数据
- private var eventTrackData: PQVideoMakeEventTrackModel?
- // 选中的总时长-统计使用
- var selectedTotalDuration: Float64 = 0
- // 选择的总数-统计使用
- var selectedDataCount: Int = 0
- // 选择的图片总数-统计使用
- var selectedImageDataCount: Int = 0
- // 最大的宽度
- private var maxWidth: CGFloat = cScreenWidth
- // 最大的高度
- private var maxHeight: CGFloat = 385
- // 开始导出的时间
- private let startExportDate: Float64 = Date().timeIntervalSince1970
- // 导出结束的时间
- private var exportEndDate: Float64 = Date().timeIntervalSince1970
- // 取到的封面 给发布界面使用
- private var coverImage: UIImage?
- // 导出视频工具类
- private var exporter: PQCompositionExporter!
- // 导出进度
- private var exportProgrss = 0
- var mStickers: [PQEditVisionTrackMaterialsModel]?
- var remindView: PQRemindView?
- // 已经选择标题内容,加一个属性接收 使用有不在主线不能直接使用 titleLabel text
- var selectTitle: String = ""
- // add by ak 玩法类型 调用 producevideo/saveProject 时使用
- var rhythmMode: createStickersModel = .createStickersModelPoint
- // add by ak 设置的速度
- var syncedUpVideoSpeedMax: Float = 0.0
- var syncedUpVideoSpeedMin: Float = 0.0
- // add by ak 是否是再创作模式
- var isReCreate: Bool = false
- // 最终使用的音频时长,用于拼接音乐使用
- var finallyUserAudioTime: Float = 0.0
- // 拼接音乐的开始和结束位置
- var clipAudioRange: CMTimeRange = CMTimeRange.zero
- // 导出的开始的开始和结束时间
- var playeTimeRange: CMTimeRange = CMTimeRange()
-
- //add by ak 保存系统相册使用的变量
- // 导出保存相册视频工具类
- private var saveMovieExporter: PQCompositionExporter!
- private var saveMovieExportLocalURL: URL? // 导出的地址
- //----------------------------
- // 预览大小
- private var preViewSize: CGSize {
- switch aspectRatio {
- case let .origin(width, height):
- var tempHeight: CGFloat = 0
- var tempWidth: CGFloat = 0
- if width > height {
- tempWidth = maxWidth
- tempHeight = (maxWidth * height / width)
- if tempHeight > maxHeight {
- tempHeight = maxHeight
- tempWidth = (maxHeight * width / height)
- }
- } else {
- tempHeight = maxHeight
- tempWidth = (maxHeight * width / height)
- if tempWidth > maxWidth {
- tempWidth = maxWidth
- tempHeight = (maxWidth * height / width)
- }
- }
- if tempHeight.isNaN || tempWidth.isNaN {
- return CGSize.zero
- } else {
- return CGSize(width: tempWidth, height: tempHeight)
- }
- case .oneToOne:
- if maxWidth > maxHeight {
- return CGSize(width: maxHeight, height: maxHeight)
- } else {
- return CGSize(width: maxWidth, height: maxWidth)
- }
- case .sixteenToNine:
- return CGSize(width: maxWidth, height: maxWidth * 9.0 / 16.0)
- case .nineToSixteen:
- return CGSize(width: maxHeight * 9.0 / 16.0, height: maxHeight)
- default:
- break
- }
- return CGSize(width: maxHeight, height: maxHeight)
- }
- // 背景音乐
- var audioMixModel: PQVoiceModel?
- // 画面比例
- var aspectRatio: aspectRatio?
- // 导出的项目数据
- var editProjectModel: PQEditProjectModel? {
- didSet {
- aspectRatio = PQPlayerViewModel.videoCanvasTypeToAspectRatio(projectModel: editProjectModel)
- var totalDuration: Float64 = 0
- if editProjectModel?.sData?.sections.count ?? 0 > 0 {
- for section in (editProjectModel?.sData?.sections)! {
- totalDuration = totalDuration + section.sectionDuration
- }
- }
- editProjectModel?.sData?.videoMetaData?.duration = totalDuration
- if editProjectModel?.sData?.sections != nil, (editProjectModel?.sData?.sections.count ?? 0) > 0 {
- // 查找出背景图并设置
- var coverImageMaterialsModel: PQEditVisionTrackMaterialsModel?
- for section in (editProjectModel?.sData?.sections)! {
- if coverImageMaterialsModel != nil {
- break
- }
- coverImageMaterialsModel = section.sectionTimeline?.visionTrack?.getEnableVisionTrackMaterials().first
- }
- if coverImageMaterialsModel != nil {
- coverImage = coverImageMaterialsModel?.getCoverImage()
- playerHeaderView.image = coverImage
- playerHeaderView.contentMode = coverImageMaterialsModel!.canvasFillType == stickerContentMode.aspectFitStr.rawValue ? .scaleAspectFill : .scaleAspectFit
- }
- }
- }
- }
- /// 所有需要导出的filter
- var filters: Array = Array<ImageProcessingOperation>.init()
- /// 预览背景页
- lazy var bgTopView: UIView = {
- let bgTopView = UIView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: maxHeight))
- bgTopView.backgroundColor = PQBFConfig.shared.styleBackGroundColor
- return bgTopView
- }()
- // 预览界面
- var playerHeaderView: UIImageView = {
- let playerHeaderView = UIImageView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: 0))
- playerHeaderView.isUserInteractionEnabled = true
- playerHeaderView.contentMode = .scaleAspectFit
- playerHeaderView.clipsToBounds = true
- return playerHeaderView
- }()
- // add by ak 播放器的封面 为了不和原有的播放器层级单独添加一个 view
- lazy var playerHeaderCoverImageView: UIImageView = {
- let playerHeaderCoverImageView = UIImageView()
- playerHeaderCoverImageView.isUserInteractionEnabled = true
- playerHeaderCoverImageView.contentMode = .scaleAspectFit
- playerHeaderCoverImageView.clipsToBounds = true
- let playBtn = UIButton(type: .custom)
- playBtn.setImage(UIImage().BF_Image(named: "icon_video_play"), for: .normal)
- playBtn.tag = 4
- playBtn.isUserInteractionEnabled = false
- playerHeaderCoverImageView.addSubview(playBtn)
- playerHeaderCoverImageView.isHidden = true
- return playerHeaderCoverImageView
- }()
- /// 播放器
- lazy var avPlayer: AVPlayer = {
- let avPlayer = AVPlayer()
- NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: avPlayer.currentItem, queue: .main) { [weak self] notify in
- BFLog(message: "AVPlayerItemDidPlayToEndTime = \(notify)")
- avPlayer.seek(to: CMTime.zero)
- if self?.playerHeaderCoverImageView.image != nil {
- self?.playerHeaderCoverImageView.isHidden = false
- }
- self?.playBtn.isHidden = false
- }
- NotificationCenter.default.addObserver(forName: .AVPlayerItemNewErrorLogEntry, object: avPlayer.currentItem, queue: .main) { notify in
- BFLog(message: "AVPlayerItemNewErrorLogEntry = \(notify)")
- }
- NotificationCenter.default.addObserver(forName: .AVPlayerItemFailedToPlayToEndTime, object: avPlayer.currentItem, queue: .main) { notify in
- BFLog(message: "AVPlayerItemFailedToPlayToEndTime = \(notify)")
- }
- NotificationCenter.default.addObserver(forName: .AVPlayerItemPlaybackStalled, object: avPlayer.currentItem, queue: .main) { notify in
- BFLog(message: "AVPlayerItemPlaybackStalled = \(notify)")
- }
- avPlayer.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 1000), queue: .main) { [weak self] _ in
- let progress = CMTimeGetSeconds(avPlayer.currentItem?.currentTime() ?? CMTime.zero) / CMTimeGetSeconds(avPlayer.currentItem?.duration ?? CMTime.zero)
- if progress >= 1 {
- self?.playBtn.isHidden = false
- }
- }
- return avPlayer
- }()
- /// 预览layer
- lazy var playerLayer: AVPlayerLayer = {
- let playerLayer = AVPlayerLayer(player: avPlayer)
- playerLayer.frame = playerHeaderView.bounds
- return playerLayer
- }()
- /// 播放按钮
- lazy var playBtn: UIButton = {
- let playBtn = UIButton(type: .custom)
- playBtn.frame = CGRect(x: (preViewSize.width - cDefaultMargin * 5) / 2, y: (preViewSize.height - cDefaultMargin * 5) / 2, width: cDefaultMargin * 5, height: cDefaultMargin * 5)
- playBtn.setImage(UIImage().BF_Image(named: "icon_video_play"), for: .normal)
- playBtn.tag = 4
- playBtn.isHidden = true
- playBtn.isUserInteractionEnabled = false
- return playBtn
- }()
- // progressTipsLab
- lazy var progressTipsLab: UILabel = {
- let progressTipsLab = UILabel()
- progressTipsLab.textAlignment = .center
- progressTipsLab.font = UIFont.systemFont(ofSize: 14, weight: .medium)
- progressTipsLab.numberOfLines = 2
- progressTipsLab.textColor = UIColor.white
- let attributedText = NSMutableAttributedString(string: "0%\n视频正在处理中,请勿离开")
- attributedText.addAttributes([.font: UIFont.systemFont(ofSize: 34)], range: NSRange(location: 0, length: 2))
- progressTipsLab.attributedText = attributedText
- progressTipsLab.addShadow()
- return progressTipsLab
- }()
- // 进度条
- lazy var progressView: UIProgressView = {
- let progressView = UIProgressView(progressViewStyle: .default)
- progressView.trackTintColor = UIColor(white: 0, alpha: 0.5)
- progressView.progressTintColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue)
- progressView.transform = CGAffineTransform(scaleX: 1.0, y: playerHeaderView.frame.height)
- return progressView
- }()
- // 提示
- lazy var remindLab: UILabel = {
- let remindLab = UILabel()
- remindLab.font = UIFont.boldSystemFont(ofSize: 18)
- remindLab.textColor = PQBFConfig.shared.styleTitleColor
- remindLab.textAlignment = .center
- remindLab.numberOfLines = 2
- remindLab.backgroundColor = .clear
- remindLab.text = "为你的大作起个响亮的标题\n分享秀一下🎉"
- return remindLab
- }()
- // 输入框背景
- lazy var inputBackView: UIView = {
- let inputBackView = UIView()
- inputBackView.backgroundColor = .clear
- inputBackView.layer.cornerRadius = 7
- inputBackView.layer.borderWidth = 2
- inputBackView.layer.borderColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue).cgColor
- return inputBackView
- }()
- // 手势提示
- lazy var pinView: UIImageView = {
- let pinView = UIImageView()
- pinView.kf.setImage(with: URL(fileURLWithPath: Bundle().BF_mainbundle().path(forResource: "editCoverPin", ofType: ".gif")!))
- return pinView
- }()
- // 封面
- lazy var coverImageView: UIImageView = {
- let coverImageView = UIImageView()
- coverImageView.isUserInteractionEnabled = true
- coverImageView.backgroundColor = .clear
- coverImageView.contentMode = .scaleToFill
- return coverImageView
- }()
- // 封面标题
- lazy var coverImageTitle: UILabel = {
- let coverImageTitle = UILabel()
- coverImageTitle.text = "换封面"
- coverImageTitle.textAlignment = .center
- coverImageTitle.backgroundColor = UIColor(red: 0.22, green: 0.26, blue: 0.35, alpha: 0.5)
- coverImageTitle.isUserInteractionEnabled = true
- coverImageTitle.textColor = .white
- coverImageTitle.font = UIFont.boldSystemFont(ofSize: 12)
- return coverImageTitle
- }()
- // 标题
- lazy var titleLabel: UILabel = {
- let titleLabel = UILabel()
- titleLabel.numberOfLines = 2
- titleLabel.isUserInteractionEnabled = true
- titleLabel.textColor = UIColor.hexColor(hexadecimal: "#ABABAB")
- titleLabel.textAlignment = .left
- titleLabel.font = UIFont.systemFont(ofSize: 17)
- let ges = UITapGestureRecognizer(target: self, action: #selector(titleLabelClick))
- titleLabel.addGestureRecognizer(ges)
- return titleLabel
- }()
- // 编辑发布标题
- lazy var publicTitleView: PQEditPublicTitleView = {
- let publicTitleView = PQEditPublicTitleView()
- publicTitleView.isHidden = true
- publicTitleView.confirmBtnClock = { [weak self] title in
- BFLog(message: "传出的 title is :\(String(describing: title))")
- if title?.count != 0 && title != self?.titleLabel.text {
- self?.changPlayerIsPause(isPause: false)
- // 判断文字是否有效
- var inputText = ""
- inputText = title?.replacingOccurrences(of: "\n", with: "") ?? ""
- inputText = inputText.replacingOccurrences(of: " ", with: "")
- if inputText.count > 0 {
- self?.setTitleText(text: title ?? "", textColor: .black)
- // 更新数据
- self?.videoData?.title = title
- self?.updateCoverImagegOrTitle()
- }
- }
- }
- publicTitleView.viewIsHiddenCallBack = { [weak self] in
- self?.changPlayerIsPause(isPause: false)
- }
- return publicTitleView
- }()
- // 编辑发布封面
- lazy var publicEditCoverView: PQEditPublicCoverImageView = {
- let publicEditCoverView = PQEditPublicCoverImageView(frame: CGRect(x: 0, y: 0, width: cScreenWidth, height: cScreenHeigth))
- publicEditCoverView.isHidden = true
- return publicEditCoverView
- }()
- // 分享到朋友圈
- lazy var shareWechatBtn: UIButton = {
- let shareWechatBtn = UIButton(type: .custom)
- shareWechatBtn.frame = CGRect(x: 0, y: 0, width: 70, height: 70)
- shareWechatBtn.setImage(UIImage().BF_Image(named: "reCreate_opration_wechat"), for: .normal)
- shareWechatBtn.backgroundColor = PQBFConfig.shared.styleBackGroundColor
- shareWechatBtn.addCorner(corner: 6)
- shareWechatBtn.tag = 2
- shareWechatBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
- return shareWechatBtn
- }()
- // 分享到好友
- lazy var shareFriendBtn: UIButton = {
- let shareFriendBtn = UIButton(type: .custom)
- shareFriendBtn.frame = CGRect(x: 0, y: 0, width: 70, height: 70)
- shareFriendBtn.setImage(UIImage().BF_Image(named: "reCreate_opration_friend"), for: .normal)
- shareFriendBtn.backgroundColor = PQBFConfig.shared.styleBackGroundColor
- shareFriendBtn.addCorner(corner: 6)
- shareFriendBtn.tag = 1
- shareFriendBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
- return shareFriendBtn
- }()
- // 关闭
- lazy var finishedBtn: UIButton = {
- let finishedBtn = UIButton(type: .custom)
- finishedBtn.setTitle("完成", for: .normal)
- finishedBtn.setTitleColor(UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue), for: .normal)
- finishedBtn.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .medium)
- finishedBtn.backgroundColor = .clear
- finishedBtn.tag = 3
- finishedBtn.addCorner(corner: 3)
- finishedBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
- return finishedBtn
- }()
- /// 背景View
- lazy var oprationBgView: UIView = {
- let oprationBgView = UIView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: view.frame.height - cDevice_iPhoneNavBarAndStatusBarHei))
- oprationBgView.backgroundColor = .clear
- return oprationBgView
- }()
- // 除了播放器以外的 下半部分操作区
- lazy var bottomOprationBgView: UIView = {
- let bottomOprationBgView = UIView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei + maxHeight, width: cScreenWidth, height: view.frame.height - cDevice_iPhoneNavBarAndStatusBarHei - maxHeight))
- bottomOprationBgView.backgroundColor = .clear
- bottomOprationBgView.isHidden = true
- return bottomOprationBgView
- }()
- override func backBtnClick() {
- if isExportSuccess {
- navigationController?.popViewController(animated: true)
- } else {
- view.endEditing(true)
- let remindData = PQBaseModel()
- remindData.title = "编辑的内容,将不会被保存"
- remindView = PQRemindView(frame: CGRect(x: 0, y: 0, width: cScreenWidth, height: cScreenHeigth))
- remindView?.isBanned = true
- remindView?.confirmBtn.setTitle("确认", for: .normal)
- remindView?.cancelBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#333333"), for: .normal)
- remindView?.confirmBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#EE0051"), for: .normal)
- UIApplication.shared.keyWindow?.addSubview(remindView!)
- remindView?.remindData = remindData
- remindView?.remindBlock = { [weak self] item, _ in
- if item.tag == 2 {
- // 取消导出
- if self?.exporter != nil {
- self?.exporter.cancel()
- }
- self?.navigationController?.popViewController(animated: true)
- }
- }
- }
- }
- override func viewDidLoad() {
- super.viewDidLoad()
- // 注册上传成功的通知
- addNotification(self, selector: #selector(uploadSuccess(notify:)), name: cUploadSuccessKey, object: nil)
- PQNotification.addObserver(self, selector: #selector(didBecomeActiveNotification), name: UIApplication.didBecomeActiveNotification, object: nil)
- leftButton(image: "icon_detail_back", tintColor: PQBFConfig.shared.styleTitleColor)
- navHeadImageView?.backgroundColor = UIColor.clear
- lineView?.removeFromSuperview()
- view.addSubview(bgTopView)
- playerHeaderView.frame = CGRect(origin: CGPoint(x: (cScreenWidth - preViewSize.width) / 2, y: (maxHeight - preViewSize.height) / 2), size: preViewSize)
- let ges = UITapGestureRecognizer(target: self, action: #selector(playVideo))
- playerHeaderView.addGestureRecognizer(ges)
- if playerLayer.superlayer == nil {
- playerHeaderView.layer.insertSublayer(playerLayer, at: 0)
- }
- playerHeaderView.addSubview(playBtn)
- playerHeaderView.addSubview(progressView)
- view.addSubview(oprationBgView)
- oprationBgView.addSubview(progressTipsLab)
- // 添加导出view
- bgTopView.addSubview(playerHeaderView)
- playerHeaderCoverImageView.frame = playerHeaderView.frame
- playerHeaderCoverImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(playVideo)))
- (playerHeaderCoverImageView.viewWithTag(4))?.frame =
- CGRect(x: (preViewSize.width - cDefaultMargin * 5) / 2, y: (preViewSize.height - cDefaultMargin * 5) / 2, width: cDefaultMargin * 5, height: cDefaultMargin * 5)
- bgTopView.addSubview(playerHeaderCoverImageView)
- view.addSubview(bottomOprationBgView)
- bottomOprationBgView.addSubview(remindLab)
- bottomOprationBgView.addSubview(shareWechatBtn)
- bottomOprationBgView.addSubview(shareFriendBtn)
- bottomOprationBgView.addSubview(finishedBtn)
- bottomOprationBgView.addSubview(inputBackView)
- bottomOprationBgView.addSubview(pinView)
- inputBackView.addSubview(coverImageView)
- coverImageView.addSubview(coverImageTitle)
- inputBackView.addSubview(titleLabel)
- view.addSubview(publicTitleView)
- view.addSubview(publicEditCoverView)
- coverImageTitle.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(settingCoverImage)))
- coverImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(settingCoverImage)))
- progressView.snp.makeConstraints { make in
- make.left.right.centerY.equalTo(playerHeaderView)
- make.height.equalTo(3)
- }
- progressTipsLab.snp.makeConstraints { make in
- make.centerX.equalToSuperview()
- make.top.equalToSuperview().offset(((preViewSize.height - 90) / 2) + ((maxHeight - preViewSize.height) / 2))
- make.width.equalToSuperview()
- make.height.equalTo(90)
- }
- finishedBtn.snp.makeConstraints { make in
- make.centerX.equalToSuperview()
- make.bottom.equalToSuperview().offset(-cSafeAreaHeight)
- make.width.equalTo(100)
- make.height.equalTo(22)
- }
- shareWechatBtn.snp.makeConstraints { make in
- make.right.equalTo(view.snp_centerX).offset(-cDefaultMargin)
- make.width.equalTo(164)
- make.height.equalTo(52)
- make.bottom.equalTo(finishedBtn.snp_top).offset(-32)
- }
- shareFriendBtn.snp.makeConstraints { make in
- make.left.equalTo(view.snp_centerX).offset(cDefaultMargin)
- make.width.bottom.height.equalTo(shareWechatBtn)
- }
- inputBackView.snp.makeConstraints { make in
- make.centerX.equalToSuperview()
- make.bottom.equalTo(shareWechatBtn.snp_top).offset(-16)
- make.width.equalTo(343)
- make.height.equalTo(109)
- }
- // 根据横竖屏设置不同的 UI
- let isWidth: Bool = (Float(editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) / Float(editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0)) >= 1
- var coverImageViewHeight = 50.0 * Float(editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) / Float(editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0)
- if coverImageViewHeight > 89 {
- coverImageViewHeight = 89
- }
- coverImageView.snp.makeConstraints { make in
- make.left.equalToSuperview().offset(12)
- make.width.equalTo(50)
- make.top.equalToSuperview().offset(10)
- make.height.equalTo(coverImageViewHeight)
- }
- coverImageTitle.snp.makeConstraints { make in
- make.left.equalToSuperview()
- make.width.equalTo(50)
- make.top.equalTo(coverImageView.snp_bottom).offset(isWidth ? 0 : -23)
- make.height.equalTo(23)
- }
- remindLab.snp.makeConstraints { make in
- make.centerX.equalToSuperview()
- make.bottom.equalTo(inputBackView.snp_top).offset(-16).priorityHigh()
- make.height.equalTo(44)
- make.top.greaterThanOrEqualTo(5)
- make.bottom.lessThanOrEqualTo(inputBackView.snp_top).offset(-5)
- }
- titleLabel.snp.makeConstraints { make in
- make.height.equalTo(48)
- make.left.equalTo(coverImageView.snp_right).offset(12)
- make.right.equalToSuperview().offset(-14)
- make.top.equalToSuperview().offset(10)
- }
- pinView.snp.makeConstraints { make in
- make.height.width.equalTo(72)
- make.right.equalToSuperview()
- make.bottom.equalTo(inputBackView.snp_bottom)
- }
- publicTitleView.snp.makeConstraints { make in
- make.height.equalTo(cScreenHeigth)
- make.width.equalTo(cScreenWidth)
- make.bottom.equalToSuperview()
- }
- // 取消所有的导出
- PQSingletoMemoryUtil.shared.allExportSession.forEach { _, exportSession in
- exportSession.cancelExport()
- }
- // 开始导出
- appendAudio()
- /// 保存草稿
- saveDraftbox()
- // 曝光上报:窗口曝光
- PQEventTrackViewModel.baseReportUpload(businessType: .bt_windowView, objectType: .ot_view_publishSyncedUp, pageSource: .sp_stuck_publishSyncedUp, extParams: nil, remindmsg: "卡点视频数据上报-(曝光上报:窗口曝光)")
- // 取推荐标题
- getTitles()
- }
- override func viewWillAppear(_ animated: Bool) {
- super.viewWillAppear(animated)
- PQNotification.addObserver(self, selector: #selector(enterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
- PQNotification.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
- UIApplication.shared.isIdleTimerDisabled = true
- // 从相册选择一个照片后回调
- addNotification(self, selector: #selector(imageSelectedImage(notify:)), name: cSelectedImageSuccessKey, object: nil)
- #if swift(>=4.2)
- let memoryNotification = UIApplication.didReceiveMemoryWarningNotification
- _ = UIApplication.willTerminateNotification
- _ = UIApplication.didEnterBackgroundNotification
- #else
- let memoryNotification = NSNotification.Name.UIApplicationDidReceiveMemoryWarning
- let terminateNotification = NSNotification.Name.UIApplicationWillTerminate
- let enterbackgroundNotification = NSNotification.Name.UIApplicationDidEnterBackground
- #endif
- NotificationCenter.default.addObserver(
- self, selector: #selector(clearMemoryCache), name: memoryNotification, object: nil
- )
- }
- @objc public func clearMemoryCache() {
- BFLog(message: "收到内存警告")
- }
- override func viewWillDisappear(_ animated: Bool) {
- super.viewWillDisappear(animated)
- UIApplication.shared.isIdleTimerDisabled = false
- }
- deinit {
- view.endEditing(true)
- PQNotification.removeObserver(self)
- // 取消导出
- if exporter != nil {
- exporter.cancel()
- }
- avPlayer.pause()
- avPlayer.replaceCurrentItem(with: nil)
- // 点击上报:返回按钮
- PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_back, pageSource: .sp_stuck_publishSyncedUp, extParams: nil, remindmsg: "卡点视频数据上报-(点击上报:返回按钮)")
- }
- }
- // MARK: - 导出/上传/下载及其他方法
- /// 导出/上传/下载及其他方法
- extension PQStuckPointPublicController {
- /// fp1 - 导出视频
- /// 开始导出视频
- /// 合并声音
- /// - Parameter urls: 所有音频的URL 是全路径方便复用
- /// - Parameter completeHander: 返回的 URL 全路径的 URL 如果要保存替换掉前缀
- func mergeAudios(originAsset: AVURLAsset, mTotalDuration: Float, clipAudioRange: CMTimeRange = CMTimeRange.zero, mStartTime _: CMTime = .zero, completeHander: @escaping (_ fileURL: URL?) -> Void) {
- let timeInterval: TimeInterval = Date().timeIntervalSince1970
- let composition = AVMutableComposition()
- let originaDuration = CMTimeGetSeconds(clipAudioRange.duration)
- BFLog(message: "处理主音频 原始时长startTime = \(originaDuration) 要显示时长totalDuration = \(mTotalDuration)")
- //originaDuration = 37.616768 mTotalDuration = 37.616776 TODO 都用 INT 微秒级
- if Float64(String(format: "%.3f",mTotalDuration)) ?? 0.0 <= Float64(String(format: "%.3f",originaDuration)) ?? 0.0 {
- BFLog(message: "不用拼接音频文件 \(originAsset.url) 时长is \(CMTimeGetSeconds(originAsset.duration))")
- completeHander(originAsset.url)
- return
- }
- // 整倍数
- let count = Int(mTotalDuration) / Int(originaDuration)
- // 有余数多 clip 一整段
- let row = mTotalDuration - Float(count) * Float(originaDuration)
- // 已经拼接的总时长
- var totalDuration: CMTime = .zero
- // 第一段的时长
- var duration: CMTime = .zero
- // 第一段的区间
- var timeRange: CMTimeRange = CMTimeRange.zero
- if count > 0 {
- for index in 0 ..< count {
- // 第0段从0开始到推荐的结束,播放器的开始时间不是从0开始的
- duration = CMTime(value: CMTimeValue((CMTimeGetSeconds(clipAudioRange.end)) * Double(playerTimescaleInt)), timescale: playerTimescaleInt)
- BFLog(message: "每一个文件的 duration \(CMTimeGetSeconds(duration))")
- var timeRange = CMTimeRangeMake(start: .zero, duration: duration)
- if index != 0 {
- // (CMTimeGetSeconds(clipAudioRange.end) - CMTimeGetSeconds(mStartTime))为用户选择的第一段时长
- timeRange = clipAudioRange
- }
- BFLog(message: "合并的文件地址: \(originAsset.url)")
- let audioAsset = originAsset
- let tracks = audioAsset.tracks(withMediaType: .audio)
- if tracks.count == 0 {
- BFLog(message: "音频数据无效不进行合并,所有任务结束要确保输入的数据都正常! \(originAsset.url)")
- completeHander(URL(string: ""))
- return
- }
- let assetTrack: AVAssetTrack = tracks[0]
- let compositionAudioTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: CMPersistentTrackID())!
- do {
- //
- try compositionAudioTrack.insertTimeRange(timeRange, of: assetTrack, at: totalDuration)
- } catch {
- BFLog(message: "error is \(error)")
- completeHander(URL(string: ""))
- return
- }
- totalDuration = CMTimeAdd(totalDuration, timeRange.duration)
- }
- }
- if row > 0 {
- duration = CMTime(value: CMTimeValue(Float(CMTimeGetSeconds(totalDuration)) * Float(playerTimescaleInt)), timescale: playerTimescaleInt)
- timeRange = CMTimeRange(start: clipAudioRange.start, duration: CMTime(value: Int64(Double(row) * Double(playerTimescaleInt)), timescale: playerTimescaleInt))
- BFLog(message: "合并的文件地址: \(originAsset.url)")
- let audioAsset = originAsset
- let tracks = audioAsset.tracks(withMediaType: .audio)
- if tracks.count == 0 {
- BFLog(message: "音频数据无效不进行合并,所有任务结束要确保输入的数据都正常! \(originAsset.url)")
- completeHander(URL(string: ""))
- return
- }
- let assetTrack: AVAssetTrack = tracks[0]
- let compositionAudioTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: CMPersistentTrackID())!
- do {
- //
- try compositionAudioTrack.insertTimeRange(timeRange, of: assetTrack, at: totalDuration)
- } catch {
- BFLog(message: "合并音频 error is \(error)")
- completeHander(URL(string: ""))
- return
- }
- totalDuration = CMTimeAdd(totalDuration, timeRange.duration)
- }
- let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetAppleM4A)
- BFLog(message: "assetExport.supportedFileTypes is \(String(describing: assetExport?.supportedFileTypes))")
- assetExport?.outputFileType = .m4a
- // XXXX 注意文件名的后缀要和outputFileType 一致 否则会导出失败
- var audioFilePath = exportAudiosDirectory
- if !directoryIsExists(dicPath: audioFilePath) {
- BFLog(message: "文件夹不存在")
- createDirectory(path: audioFilePath)
- }
- audioFilePath.append("merge_\(timeInterval).m4a")
- let fileUrl = URL(fileURLWithPath: audioFilePath)
- assetExport?.outputURL = fileUrl
- assetExport?.exportAsynchronously {
- if assetExport!.status == .completed {
- let audioAsset = AVURLAsset(url: fileUrl, options: avAssertOptions)
- BFLog(1, message: "拼接声音文件 完成 \(fileUrl) 时长is \(CMTimeGetSeconds(audioAsset.duration))")
- completeHander(fileUrl)
- } else {
- print("拼接出错 \(String(describing: assetExport?.error))")
- completeHander(URL(string: ""))
- }
- }
- }
- func appendAudio() {
- //更新一下假进度
- updatePublicCurrentProgress(useProgress: 0.01)
- let inputAsset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + (audioMixModel?.localPath ?? "")), options: nil)
- let startMergeTime = CFAbsoluteTimeGetCurrent()
- mergeAudios(originAsset: inputAsset, mTotalDuration: finallyUserAudioTime, clipAudioRange: clipAudioRange, mStartTime: CMTime(value: CMTimeValue((mStickers?.first?.timelineIn ?? 0.0) * Float64(playerTimescaleInt)), timescale: playerTimescaleInt)) { [weak self] completURL in
- if completURL != nil {
- let asset = AVURLAsset(url: completURL!, options: nil)
- BFLog(message: "拼接后音频时长\(asset.duration.seconds) url is \(String(describing: completURL)) 用时\(CFAbsoluteTimeGetCurrent() - startMergeTime)")
- //导出不带水印的正片
- self?.beginExport(inputAsset: asset)
- //导出带水印的正片
- self?.beginExportSaveMovie(inputAsset:asset)
- }else{
- cShowHUB(superView: self?.view, msg: "合成失败请重试。")
- }
- }
- }
- func beginExport(inputAsset: AVURLAsset!) {
- if !(editProjectModel?.sData?.sections != nil && (editProjectModel?.sData?.sections.count ?? 0) > 0) {
- BFLog(message: "项目段落错误❌")
- return
- }
- // 输出视频地址
- var outPutMP4Path = exportVideosDirectory
- if !directoryIsExists(dicPath: outPutMP4Path) {
- BFLog(message: "文件夹不存在")
- createDirectory(path: outPutMP4Path)
- }
- outPutMP4Path.append("video_\(String.qe.timestamp()).mp4")
- let outPutMP4URL = URL(fileURLWithPath: outPutMP4Path)
- BFLog(message: "导出视频地址 \(outPutMP4URL)")
- exporter = PQCompositionExporter(asset: inputAsset, videoComposition: nil, audioMix: nil, filters: nil, stickers: mStickers, animationTool: nil, exportURL: outPutMP4URL)
- var orgeBitRate = (editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) * (editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) * 3
- if mStickers != nil {
- for stick in mStickers! {
- if stick.type == StickerType.VIDEO.rawValue {
- let asset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + stick.locationPath), options: avAssertOptions)
- let cbr = asset.tracks(withMediaType: .video).first?.estimatedDataRate
- if Int(cbr ?? 0) > orgeBitRate {
- orgeBitRate = Int(cbr ?? 0)
- }
- }
- }
- }
- BFLog(message: "导出设置的码率为:\(orgeBitRate)")
- exporter.showGaussianBlur = true
- if exporter.prepare(videoSize: CGSize(width: editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0, height: editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0), videoAverageBitRate: orgeBitRate) {
- BFLog(message: "开始导出 \(String(describing: playeTimeRange.start)) 结束 \(String(describing: playeTimeRange.end))")
- exporter.start(playeTimeRange: playeTimeRange)
- BFLog(message: "开始导出")
- }
- exporter.progressClosure = { [weak self] _, _, progress in
- BFLog(message: "合成进度 \(progress)")
- let useProgress = progress > 1 ? 1 : progress
- if progress > 0, Int(useProgress * 100) > (self?.exportProgrss ?? 0) {
- // 更新进度
- self?.updatePublicCurrentProgress(useProgress: useProgress * 0.88)
- }
- }
- exporter.completion = { [weak self] url in
- BFLog(message: "无水印的视频导出完成: \(url) 生成视频时长为:\(CMTimeGetSeconds(AVAsset(url: url).duration))")
- // 导出完成后取消导出
- if self?.exporter != nil {
- self?.exporter.cancel()
- }
- self?.remindView?.removeFromSuperview()
- if !(self?.isExportSuccess ?? false) {
- self?.isExportSuccess = true
- self?.exportEndDate = Date().timeIntervalSince1970
- BFLog(message: "视频导出完成-开始去发布视频 总时长为\((self?.exportEndDate ?? 0) - (self?.startExportDate ?? 0) * 1000)")
- self?.exportLocalURL = url
- /// fp2-1-1 - 请求权限
- // self?.authorizationStatus()
- /// fp2-2 - 保存草稿
- // self?.saveDraftbox()
- /// fp2 - 处理视频数据
- self?.dealWithVideoData()
- }
- }
- }
- /// fp2-1-1 - 请求权限
- func authorizationStatus() {
- let authStatus = PHPhotoLibrary.authorizationStatus()
- if authStatus == .notDetermined {
- // 第一次触发授权 alert
- PHPhotoLibrary.requestAuthorization { [weak self] (status: PHAuthorizationStatus) -> Void in
- if status != .authorized {
- cShowHUB(superView: nil, msg: "您尚未打开相册权限,请到设置页打开相册权限")
- } else {
- /// fp2-1-2 - 保存视频到相册
- self?.saveStuckPointVideo()
- }
- }
- } else if authStatus == .authorized {
- /// fp2-1-2 - 保存视频到相册
- saveStuckPointVideo()
- } else {
- // cShowHUB(superView: nil, msg: "您尚未打开相册权限,请到设置页打开相册权限")
- }
- }
- /// fp2-1-2 - 保存视频到相册
- /// - Parameter localPath: localPath description
- /// - Returns: <#description#>
- func saveStuckPointVideo() {
-
- if(saveMovieExportLocalURL == nil){
- BFLog(message: "保存相册的视频导出地址无效!!!")
- return
- }
- let authStatus = PHPhotoLibrary.authorizationStatus()
- if authStatus == .authorized {
- let photoLibrary = PHPhotoLibrary.shared()
- photoLibrary.performChanges({ [weak self] in
- PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: (self?.saveMovieExportLocalURL)!)
- }) { [weak self] isFinished, _ in
- DispatchQueue.main.async { [weak self] in
- if self?.view != nil {
- if isFinished {
- // cShowHUB(superView: self!.view, msg: "视频已保存至相册")
- } else {
- // cShowHUB(superView: self!.view, msg: "视频保存失败")
- }
- }
- }
- }
- } else {
- // cShowHUB(superView: nil, msg: "您尚未打开相册权限,请到设置页打开相册权限")
- }
- }
- /// fp2-2 - 保存草稿
- /// - Returns: <#description#>
- @objc func saveDraftbox() {
- let sdata = editProjectModel?.sData?.toJSONString(prettyPrint: false)
- if sdata != nil, (sdata?.count ?? 0) > 0 {
- DispatchQueue.global().async { [weak self] in
- PQBaseViewModel.saveDraftbox(draftboxId: self?.editProjectModel?.draftboxId, title: self?.editProjectModel?.sData?.videoMetaData?.title, coverUrl: self?.editProjectModel?.sData?.videoMetaData?.coverUrl, sdata: sdata!, videoFromScene: .stuckPoint, copyType: (self?.audioMixModel != nil && self?.audioMixModel?.originProjectId != nil && (self?.audioMixModel?.originProjectId?.count ?? 0) > 0) ? 3 : nil, originProjectId: self?.audioMixModel?.originProjectId) { [weak self] draftboxInfo, _ in
- if draftboxInfo != nil {
- self?.editProjectModel?.draftboxId = draftboxInfo?["draftboxId"] as? String ?? ""
- self?.editProjectModel?.sData?.videoMetaData?.title = draftboxInfo?["title"] as? String ?? ""
- self?.editProjectModel?.sData?.videoMetaData?.coverUrl = draftboxInfo?["coverUrl"] as? String ?? ""
- self?.editProjectModel?.dataVersionCode = draftboxInfo?["dataVersionCode"] as? Int ?? 0
- BFLog(message: "保存远程的草稿成功")
- self?.isSaveDraftSuccess = true
- /// fp3 - 保存项目
- self?.saveProject()
- } else {
- // 保存草稿失败-播放视频
- // self?.publicEnd(isError: true)
- }
- }
- }
- } else {
- cShowHUB(superView: nil, msg: "您尚未打开相册权限,请到设置页打开相册权限")
- // 保存草稿失败-播放视频
- publicEnd(isError: true)
- }
- }
- /// fp3 - 保存项目
- /// - Returns: description
- func saveProject() {
- if isSaveDraftSuccess {
- let sdata = editProjectModel?.sData?.toJSONString(prettyPrint: false) ?? ""
- let draftboxId: String? = editProjectModel?.draftboxId
- PQBaseViewModel.saveProject(draftboxId: draftboxId, sdata: sdata, videoFromScene: .stuckPoint, rhythmMode: rhythmMode) { [weak self] projectId, msg in
- BFLog(message: "生成的项目id1111 :\(projectId ?? ""),msg = \(msg ?? "")")
- if projectId == nil || (projectId?.count ?? 0) <= 0 {
- PQBaseViewModel.saveProject(draftboxId: draftboxId, sdata: sdata, videoFromScene: .stuckPoint, rhythmMode: self?.rhythmMode ?? .createStickersModelPoint) { [weak self] projectId, msg in
- BFLog(message: "生成的项目id222 :\(projectId ?? ""),msg = \(msg ?? "")")
- if projectId == nil || (projectId?.count ?? 0) <= 0 {
- PQBaseViewModel.saveProject(draftboxId: draftboxId, sdata: sdata, videoFromScene: .stuckPoint, rhythmMode: self?.rhythmMode ?? .createStickersModelPoint) { [weak self] projectId, msg in
- BFLog(message: "生成的项目id 3333:\(projectId ?? ""),msg = \(msg ?? "")")
- if projectId != nil, (projectId?.count ?? 0) > 0 {
- self?.editProjectModel?.projectId = projectId ?? ""
- }
- /// fp4 - 处理视频数据
- // self?.dealWithVideoData()
- }
- } else {
- self?.editProjectModel?.projectId = projectId ?? ""
- /// fp4 - 处理视频数据
- // self?.dealWithVideoData()
- }
- }
- } else {
- self?.editProjectModel?.projectId = projectId ?? ""
- /// fp4 - 处理视频数据
- // self?.dealWithVideoData()
- }
- }
- }
- }
- /// fp4 - 处理视频数据
- /// - Returns: description
- @objc func dealWithVideoData() {
- BFLog(message: "开始去发布视频12")
- isSaveProjectSuccess = true
- if isExportSuccess && exportLocalURL != nil {
- BFLog(message: "素材上传完成同时视频导出完成开始发布视频")
- // 更新项目
- PQBaseViewModel.updateProject(projectId: editProjectModel?.projectId ?? "", produceStatus: "5") { repseon, _ in
- BFLog(message: "updateProject 结果 is \(String(describing: repseon))")
- }
- let asset = AVURLAsset(url: exportLocalURL!, options: nil)
- let tempUploadData = PQUploadModel()
- tempUploadData.duration = CMTimeGetSeconds(asset.duration)
- tempUploadData.localPath = exportLocalURL?.absoluteString
- tempUploadData.videoWidth = CGFloat(editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0)
- tempUploadData.videoHeight = CGFloat(editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0)
- tempUploadData.image = PQVideoSnapshotUtil.videoSnapshot(videoURL: exportLocalURL!, time: 0)
- if tempUploadData.image == nil {
- tempUploadData.image = coverImage
- }
- tempUploadData.videoFromScene = .stuckPoint
- eventTrackData = getExportEventTrackData()
- eventTrackData?.projectId = editProjectModel?.projectId ?? ""
- uploadData = tempUploadData
- if uploadData?.image == nil {
- uploadData?.image = PQVideoSnapshotUtil.videoSnapshot(videoURL: exportLocalURL!, time: 0)
- }
- if uploadData?.image != nil {
- playerHeaderView.image = uploadData?.image
- coverImageView.image = uploadData?.image
- }
- if isExportSuccess, exportLocalURL != nil {
- let size = try! exportLocalURL?.resourceValues(forKeys: [.fileSizeKey])
- BFLog(message: "size = \(String(describing: size))")
- if Float64(size?.fileSize ?? 0) <= maxUploadSize {
- /// fp5 - 上传视频
- reUploadVideo()
- }
- }
- }
- }
- /// fp5 - 上传视频
- /// - Returns: <#description#>
- @objc func reUploadVideo() {
- if uploadData?.stsToken != nil {
- multipartUpload(response: uploadData?.stsToken)
- } else {
- uploadVideo()
- }
- }
- /// fp5-1 - 开始上传视频
- /// - Returns: <#description#>
- func uploadVideo() {
- let uploadRequest: OSSMultipartUploadRequest? = PQAliOssUtil.shared.allTasks[uploadData?.videoBucketKey ?? ""]
- if uploadRequest != nil, "\(uploadRequest?.callbackParam["code"] ?? "0")" == "1" {
- return
- }
- // 更新进度
- updatePublicCurrentProgress(useProgress: 0.89)
- DispatchQueue.global().async {
- PQBaseViewModel.getStsToken { [weak self] response, _ in
- if response == nil {
- self?.showUploadRemindView(isNetCollected: false, msg: "获取数据失败了哦~")
- return
- }
- // 更新进度
- self?.updatePublicCurrentProgress(useProgress: 0.90)
- BFLog(message: "取我方服务器STS 返回数据 \(String(describing: response))")
- self?.multipartUpload(response: response)
- }
- }
- }
- /// fp5-2 - 继续上传视频
- /// - Parameter response: <#response description#>
- func multipartUpload(response: [String: Any]?) {
- let FileName: String = "\(response?["FileName"] ?? "")"
- let uploadID: String = "\(response?["Upload"] ?? "")"
- uploadData?.stsToken = response
- uploadData?.videoBucketKey = FileName
- uploadData?.uploadID = uploadID
- if uploadData?.asset != nil && isValidURL(url: uploadData?.localPath) {
- PQPHAssetVideoParaseUtil.exportPHAssetToMP4(phAsset: (uploadData?.asset)!, isCancelCurrentExport: true) { [weak self] _, _, filePath, _ in
- if filePath != nil, (filePath?.count ?? 0) > 0 {
- self?.uploadData?.localPath = filePath
- PQAliOssUtil.multipartUpload(localPath: self?.uploadData?.localPath ?? "", response: response)
- }
- }
- } else {
- PQAliOssUtil.multipartUpload(localPath: uploadData?.localPath ?? "", response: response)
- }
- PQAliOssUtil.shared.aliOssHander = { [weak self] isMatarialUpload, materialType, _, code, objectkey, _, _, _, _, _, _, _, _, _ in
- if !isMatarialUpload, materialType == .VIDEO, self?.uploadData?.videoBucketKey == objectkey {
- if code == 6 { // 无网
- let uploadRequest: OSSMultipartUploadRequest? = PQAliOssUtil.shared.allTasks[self?.uploadData?.videoBucketKey ?? ""]
- if !(uploadRequest != nil && "\(uploadRequest?.callbackParam["code"] ?? "0")" == "1") {
- self?.showUploadRemindView()
- }
- } else if code == 260 {
- self?.showUploadRemindView(isNetCollected: false)
- } else if code != 1 {
- // 上传失败-播放视频
- self?.publicEnd(isError: true)
- }
- }
- }
- PQAliOssUtil.shared.aliOssProgressHander = { [weak self] bytesSent, totalBytesSent, totalBytesExpectedToSend, _, _ in
- let progress: Float = 0.90 + Float(Float(totalBytesSent) / Float(totalBytesExpectedToSend)) * 0.09
- BFLog(message: "卡点视频上传:bytesSent = \(bytesSent),totalBytesSent = \(totalBytesSent),totalBytesExpectedToSend = \(totalBytesExpectedToSend),progress = \(progress)")
- if progress >= 0.90, progress <= 0.99 {
- // 更新进度
- self?.updatePublicCurrentProgress(useProgress: progress)
- }
- }
- }
- /// fp6 - 视频上传成功,处理要发布视频数据
- /// - Parameter notify: <#notify description#>
- @objc func uploadSuccess(notify: NSNotification) {
- let objectKey: String = "\(notify.userInfo?["objectKey"] ?? "")"
- BFLog(message: "收到上传成功请求==\(notify.userInfo ?? [:])")
- if uploadData?.videoBucketKey == objectKey {
- // 上传成功
- isUploadSuccess = true
- /// fp7 - 处理要发布视频数据
- dealWithPublicData()
- }
- }
- /// fp7 - 处理要发布视频数据
- /// - Returns: <#description#>
- func dealWithPublicData() {
- if uploadData?.localPath != nil {
- let size = try! URL(string: uploadData?.localPath ?? "")?.resourceValues(forKeys: [.fileSizeKey])
- BFLog(message: "size = \(String(describing: size))")
- if Float64(size?.fileSize ?? 0) > maxUploadSize {
- cShowHUB(superView: nil, msg: "无法发布大于10G的视频,请重新选择/合成发布")
- // 上传失败-播放视频
- publicEnd(isError: true)
- return
- }
- }
- let projectId: String? = editProjectModel?.projectId
- let uploadRequest: OSSMultipartUploadRequest? = PQAliOssUtil.shared.allTasks[uploadData?.videoBucketKey ?? ""]
- if uploadRequest == nil {
- reUploadVideo()
- return
- }
- let tempModel = PQVideoListModel()
- tempModel.title = selectTitle
- tempModel.summary = ""
- tempModel.duration = CGFloat(uploadData?.duration ?? 0)
- tempModel.uplpadImage = uploadData?.image
- tempModel.uplpadBucketKey = uploadRequest?.objectKey
- tempModel.localPath = uploadData?.localPath
- tempModel.reCreateVideoData = reCreateData
- tempModel.eventTrackData = eventTrackData
- tempModel.uplpadStatus = 1
- tempModel.videoFromScene = .stuckPoint
- tempModel.uid = Int(BFLoginUserInfo.shared.uid) ?? 0
- tempModel.uplpadRequest = PQAliOssUtil.shared.allTasks[uploadData?.videoBucketKey ?? ""]
- tempModel.stsToken = uploadData?.stsToken
- tempModel.projectId = projectId
- /// fp8 - 发布视频
- publicVideo(videoData: tempModel)
- }
- /// fp8 - 发布视频
- /// - Parameter videoData: <#videoData description#>
- func publicVideo(videoData: PQVideoListModel) {
- if videoData.uplpadBucketKey == nil {
- BFLog(message: "发布视频:视频uplpadBucketKey为空-\(String(describing: videoData.uplpadBucketKey))")
- // 上传失败-播放视频
- publicEnd(isError: true)
- return
- }
- BFLog(message: "开始发布")
- if (videoData.eventTrackData?.endUploadDate ?? 0) <= 0 {
- // 结束上传时间
- videoData.eventTrackData?.endUploadDate = Date().timeIntervalSince1970
- }
- DispatchQueue.global().async {
- // PQBaseViewModel.ossTempToken { [weak self] response, _ in
- // let image: UIImage = videoData.uplpadImage ?? UIImage()
- // let data = image.jpegData(compressionQuality: 1)
- // let accessKeyId: String = "\(response?["accessKeyId"] ?? "")"
- // let secretKeyId: String = "\(response?["accessKeySecret"] ?? "")"
- // let securityToken: String = "\(response?["securityToken"] ?? "")"
- // let endpoint: String = "\(response?["uploadDomain"] ?? "")"
- // let bucketName: String = "\(response?["bucketName"] ?? "")"
- // let objectKey: String = "\(response?["objectKey"] ?? "")"
- // BFLog(message: "开始上传视频图片==\(videoData.title ?? ""),uplpadBucketKey = \(videoData.uplpadBucketKey ?? ""),objectKey =\(objectKey)")
- // PQAliOssUtil.shared
- // .startClient(
- // accessKeyId: accessKeyId,
- // secretKeyId: secretKeyId,
- // securityToken: securityToken,
- // endpoint: endpoint
- // )
- // .uploadObjectAsync(bucketName: bucketName, objectKey: objectKey, data: data!, fileExtensions: "png", imageUploadBlock: { _, code, ossObjectKey, _ in
- // BFLog(message: "图片上传完成==\(videoData.title ?? ""),uplpadBucketKey = \(videoData.uplpadBucketKey ?? ""),objectKey =\(objectKey),ossObjectKey = \(ossObjectKey)")
- // if code == 1 && ossObjectKey == objectKey && objectKey.count > 0 {
- // BFLog(message: "开始发布==\(videoData.title ?? ""),uplpadBucketKey = \(videoData.uplpadBucketKey ?? ""),objectKey =\(objectKey),ossObjectKey = \(ossObjectKey)")
- PQUploadViewModel.publishVideo(projectId: videoData.projectId, fileExtensions: videoData.localPath?.pathExtension, title: videoData.title ?? "", videoPath: videoData.uplpadBucketKey ?? "", coverImgPath: nil, descr: videoData.summary ?? "", videoFromScene: .stuckPoint, reCreateData: videoData.reCreateVideoData, eventTrackData: videoData.eventTrackData) { [weak self] newVideoData, _, _ in
- self?.videoData = newVideoData
- self?.videoData?.title = self?.titleLabel.text
- if self?.videoData?.reCreateVideoData == nil {
- let reCreateVideo = PQReCreateModel()
- reCreateVideo.reProduceVideoFlag = 1
- self?.videoData?.reCreateVideoData = reCreateVideo
- }
- postNotification(name: cPublishStuckPointSuccessKey, userInfo: ["newVideoData": self?.videoData ?? PQVideoListModel()])
- BFLog(message: "发布成功==\(videoData.title ?? ""),uplpadBucketKey = \(videoData.uplpadBucketKey ?? "")")
- // cShowHUB(superView: nil, msg: "视频发布成功")
- // 发布成功后续操作
- self?.publicEnd()
- PQEventTrackViewModel.publishReportUpload(projectId: videoData.projectId, businessType: .bt_publish_success, ossInfo: videoData.stsToken ?? [:], params: ["title": videoData.title ?? "", "videoPath": videoData.uplpadBucketKey ?? "", "descr": videoData.summary ?? ""])
- }
- // } else {
- // // 图片上传失败
- // BFLog(message: "图片上传失败重新发布视频==\(videoData.title ?? ""),\(videoData.uplpadBucketKey ?? "")")
- // self?.publicVideo(videoData: videoData)
- // }
- // })
- // }
- }
- }
- /// 发布结束操作
- /// - Parameter isError: <#isError description#>
- /// - Returns: <#description#>
- func publicEnd(isError: Bool = false) {
- UIApplication.shared.keyWindow?.viewWithTag(100_100)?.removeFromSuperview()
- isPublicSuccess = true
- progressView.removeFromSuperview()
- progressTipsLab.removeFromSuperview()
- oprationBgView.removeFromSuperview()
- playBtn.isHidden = true
- avPlayer.replaceCurrentItem(with: AVPlayerItem(url: URL(fileURLWithPath: (exportLocalURL?.absoluteString ?? "").replacingOccurrences(of: "file:///", with: ""))))
- avPlayer.play()
- if isError {
- cShowHUB(superView: nil, msg: "视频发布失败,请重新合成")
- } else {
- bottomOprationBgView.isHidden = false
- /// fp2-1-1 - 请求权限
- authorizationStatus()
- }
- }
- /// 生成创作工具埋点数据
- /// - Returns: <#description#>
- func getExportEventTrackData() -> PQVideoMakeEventTrackModel? {
- let eventTrackData = PQVideoMakeEventTrackModel(projectModel: editProjectModel, reCreateData: reCreateData)
- eventTrackData.entrance = .entranceStuckPointPublic
- eventTrackData.editTimeCost = 0
- eventTrackData.composeTimeCost = (exportEndDate - startExportDate) * 1000
- eventTrackData.musicName = audioMixModel?.musicName ?? ""
- eventTrackData.syncedUpMusicName = audioMixModel?.musicName ?? ""
- eventTrackData.musicId = audioMixModel?.musicId ?? ""
- eventTrackData.syncedUpMusicId = audioMixModel?.musicId ?? ""
- eventTrackData.musicUrl = audioMixModel?.selectVoiceType == 1 ? (audioMixModel?.musicPath ?? "") : (audioMixModel?.accompanimentPath ?? "")
- eventTrackData.musicType = audioMixModel != nil ? (audioMixModel?.selectVoiceType == 1 ? "original" : "accompaniment") : ""
- eventTrackData.isMusicClip = (audioMixModel?.startTime ?? 0) > 0
- if editProjectModel?.sData?.videoMetaData?.canvasType == videoCanvasType.origin.rawValue {
- eventTrackData.canvasRatio = "original"
- } else if editProjectModel?.sData?.videoMetaData?.canvasType == videoCanvasType.nineToSixteen.rawValue {
- eventTrackData.canvasRatio = "9:16"
- } else if editProjectModel?.sData?.videoMetaData?.canvasType == videoCanvasType.oneToOne.rawValue {
- eventTrackData.canvasRatio = "1:1"
- } else if editProjectModel?.sData?.videoMetaData?.canvasType == videoCanvasType.sixteenToNine.rawValue {
- eventTrackData.canvasRatio = "16:9"
- }
- eventTrackData.syncedUpVideoNumber = selectedDataCount - selectedImageDataCount
- eventTrackData.syncedUpImageNumber = selectedImageDataCount
- eventTrackData.syncedUpOriginalMaterialDuration = selectedTotalDuration * 1000
- eventTrackData.syncedUpRhythmNumber = audioMixModel?.speed ?? 2
- eventTrackData.syncedUpVideoDuration = ((audioMixModel?.endTime ?? 0) - (audioMixModel?.startTime ?? 0)) * 1000
- // add by ak
- eventTrackData.syncedUpVideoType = rhythmMode
- eventTrackData.syncedUpVideoSpeedMax = syncedUpVideoSpeedMax
- eventTrackData.syncedUpVideoSpeedMin = syncedUpVideoSpeedMin
- return eventTrackData
- }
- /// 播放视频
- /// - Returns: description
- @objc func playVideo() {
- playBtn.isHidden = !playBtn.isHidden
- changPlayerIsPause(isPause: !playBtn.isHidden)
- }
- /// 按钮点击事件
- /// - Parameter sender: <#sender description#>
- /// - Returns: <#description#>
- @objc func btnClick(sender: UIButton) {
- switch sender.tag {
- case 1:
- if !(isExportSuccess && isSaveProjectSuccess && isUploadSuccess && isPublicSuccess) {
- cShowHUB(superView: nil, msg: "视频发布失败,请重新合成")
- return
- }
- if !PQSingletoWXApiUtil.shared.isInstallWX() {
- cShowHUB(superView: nil, msg: "您还未安装微信客户端!")
- return
- }
- cShowHUB(superView: nil, msg: nil)
- let shareId = getUniqueId(desc: "\(videoData?.uniqueId ?? "")shareId")
- PQBaseViewModel.wxFriendShareInfo(videoId: (videoData?.uniqueId)!) { [weak self] imagePath, title, shareWeappRawId, msg in
- if msg != nil {
- cShowHUB(superView: nil, msg: "网络不佳哦")
- return
- }
- self?.isShared = true
- PQSingletoWXApiUtil.shared.share(type: 3, scene: Int32(WXSceneSession.rawValue), shareWeappRawId: shareWeappRawId, title: title, description: title, imageUrl: imagePath, path: self?.videoData?.videoPath, videoId: (self?.videoData?.uniqueId)!, pageSource: self?.videoData?.pageSource ?? .sp_category, shareId: shareId).wxApiUtilHander = { _, _ in
- }
- cHiddenHUB(superView: nil)
- }
- // 点击上报:分享微信
- PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_shareWechat, pageSource: .sp_stuck_publishSyncedUp, extParams: ["videoId": videoData?.uniqueId ?? ""], remindmsg: "卡点视频数据上报-(点击上报:分享微信)")
- case 2:
- if !(isExportSuccess && isSaveProjectSuccess && isUploadSuccess && isPublicSuccess) {
- cShowHUB(superView: nil, msg: "视频发布失败,请重新合成")
- return
- }
- if !PQSingletoWXApiUtil.shared.isInstallWX() {
- cShowHUB(superView: nil, msg: "您还未安装微信客户端!")
- return
- }
- let shareId = getUniqueId(desc: "\(videoData?.uniqueId ?? "")shareId")
- PQBaseViewModel.h5ShareLinkInfo(videoId: videoData?.uniqueId ?? "", pageSource: videoData?.pageSource ?? .sp_category) { [weak self] path, _ in
- cHiddenHUB(superView: nil)
- if path != nil {
- PQBaseViewModel.wxFriendShareInfo(videoId: (self?.videoData?.uniqueId)!) { [weak self] imagePath, _, _, msg in
- if msg != nil {
- cShowHUB(superView: nil, msg: "网络不佳哦")
- return
- }
- self?.isShared = true
- PQSingletoWXApiUtil.shared.share(type: 1, scene: Int32(WXSceneTimeline.rawValue), title: self?.videoData?.title ?? "\(BFLoginUserInfo.shared.nickName)made a music video for you", description: "", imageUrl: imagePath, path: path, videoId: (self?.videoData?.uniqueId)!, pageSource: self?.videoData?.pageSource ?? .sp_category, shareId: shareId).wxApiUtilHander = { _, _ in
- }
- }
- } else {
- cShowHUB(superView: nil, msg: "网络不佳哦")
- }
- }
- // 点击上报:分享朋友圈
- PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_shareWechatMoment, pageSource: .sp_stuck_publishSyncedUp, extParams: ["videoId": videoData?.uniqueId ?? ""], remindmsg: "卡点视频数据上报-(点击上报:分享朋友圈)")
- case 3:
- // 点击上报:完成
- PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_finished, pageSource: .sp_stuck_publishSyncedUp, extParams: ["videoId": videoData?.uniqueId ?? ""], remindmsg: "卡点视频数据上报-(点击上报:完成)")
- navigationController?.viewControllers = [(navigationController?.viewControllers.first)!]
- // 发送通知
- postNotification(name: cFinishedPublishedNotiKey)
- default:
- break
- }
- }
- /// 添加提示视图
- /// - Parameters:
- /// - isNetCollected: <#isNetCollected description#>
- /// - msg: <#msg description#>
- func showUploadRemindView(isNetCollected _: Bool = true, msg _: String? = nil) {
- view.endEditing(true)
- // PQUploadRemindView.showUploadRemindView(title: isNetCollected ? "上传中断" : "上传失败", summary: (isNetCollected ? "似乎已断开与互联网的连接" : (msg != nil ? msg : "视频文件已丢失"))!, confirmTitle: isNetCollected ? "重新连接网络" : "重新上传") { [weak self] _, _ in
- // if isNetCollected {
- // openAppSetting()
- // } else {
- // self?.navigationController?.popToViewController((self?.navigationController?.viewControllers[1])!, animated: true)
- // }
- // }
- }
- @objc func enterBackground() {
- BFLog(message: "进入到后台")
- // 取消导出
- if exporter != nil {
- exporter.cancel()
- }
- playBtn.isHidden = false
- avPlayer.pause()
- }
- @objc func willEnterForeground() {
- BFLog(message: "进入到前台")
- if !isExportSuccess {
- appendAudio()
- }
- playBtn.isHidden = true
- avPlayer.play()
- }
- @objc func didBecomeActiveNotification() {
- if isShared {
- DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { [weak self] in
- self?.isShared = false
- cShowHUB(superView: nil, msg: "分享成功")
- self?.playBtn.isHidden = true
- self?.avPlayer.play()
- }
- }
- }
- /// 更新进度
- /// - Returns: <#description#>
- func updatePublicCurrentProgress(useProgress: Float) {
- exportProgrss = Int(useProgress * 100)
- progressView.setProgress(useProgress, animated: true)
- let attributedText = NSMutableAttributedString(string: "\(exportProgrss)%\n视频正在处理中,请勿离开")
- attributedText.addAttributes([.font: UIFont.systemFont(ofSize: 34)], range: NSRange(location: 0, length: "\(exportProgrss)%".count))
- progressTipsLab.attributedText = attributedText
- }
- func changPlayerIsPause(isPause: Bool) {
- if isPause {
- playBtn.isHidden = false
- avPlayer.pause()
- playerHeaderCoverImageView.isHidden = false
- } else {
- playBtn.isHidden = true
- avPlayer.play()
- playerHeaderCoverImageView.isHidden = true
- }
- }
- @objc func titleLabelClick() {
- BFLog(message: "点击输入框")
- changPlayerIsPause(isPause: true)
- pinView.isHidden = true
- publicTitleView.show()
- if publicTitleView.inputTV.text.count > 0 {
- publicTitleView.inputTV.text = titleLabel.text
- }
- PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_clickButton_changeTitle, pageSource: .sp_stuck_publishSyncedUp, eventData: ["videoId": videoData?.uniqueId ?? "", "rootPageSource": isReCreate ? "shanyinApp-main-syncedUpMusicRecreate" : "shanyinApp-main-syncedUpMusic"], remindmsg: "")
- }
- @objc func settingCoverImage() {
- if exportLocalURL == nil {
- BFLog(message: "导出的视频地址错误???。。。")
- return
- }
- changPlayerIsPause(isPause: true)
- let asset = AVURLAsset(url: exportLocalURL!, options: nil)
- publicEditCoverView.show(videoURL: exportLocalURL!, duration: CMTimeGetSeconds(asset.duration))
- // 点击了确认 btn
- publicEditCoverView.selectImageCallBack = { [weak self] imageData in
- self?.changPlayerIsPause(isPause: false)
- if imageData != nil {
- self?.coverImageView.image = imageData
- self?.playerHeaderCoverImageView.image = imageData
- self?.uploadData?.image = imageData
- self?.updateCoverImagegOrTitle()
- }
- }
- // 点击了从相册选择
- publicEditCoverView.selectPhotoBtnCallBack = { [weak self] in
- let imageSelected = PQImageSelectedController()
- imageSelected.isAssetImage = true
- imageSelected.videoWidth = CGFloat(self?.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0)
- imageSelected.videoHeight = CGFloat(self?.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0)
- // imageSelected.uploadData = uploadData
- // imageSelected.updataVideoData = updataVideoData
- self?.navigationController?.pushViewController(imageSelected, animated: true)
- }
- PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_clickButton_changeCover, pageSource: .sp_stuck_publishSyncedUp, eventData: ["videoId": videoData?.uniqueId ?? "", "rootPageSource": isReCreate ? "shanyinApp-main-syncedUpMusicRecreate" : "shanyinApp-main-syncedUpMusic"], remindmsg: "")
- }
- // 更新标题或封面
- func updateCoverImagegOrTitle() {
- PQLoadingHUB.shared.showHUB(isMode: true)
- PQBaseViewModel.ossTempToken { [weak self] response, msg in
- let image: UIImage = (self?.uploadData?.image)!
- let data = image.jpegData(compressionQuality: 1)
- let accessKeyId: String = "\(response?["accessKeyId"] ?? "")"
- let secretKeyId: String = "\(response?["accessKeySecret"] ?? "")"
- let securityToken: String = "\(response?["securityToken"] ?? "")"
- let endpoint: String = "\(response?["endPoint"] ?? "")"
- let bucketName: String = "\(response?["bucketName"] ?? "")"
- let objectKey: String = "\(response?["objectKey"] ?? "")"
- _ = PQAliOssUtil.shared
- .startClient(
- accessKeyId: accessKeyId,
- secretKeyId: secretKeyId,
- securityToken: securityToken,
- endpoint: endpoint
- )
- .uploadObjectAsync(bucketName: bucketName, objectKey: objectKey, data: data!, fileExtensions: "png", imageUploadBlock: { _, code, ossObjectKey, _ in
- if code == 1 && ossObjectKey == objectKey && ossObjectKey.count > 0 {
- // add by ak 这里会在服务器生成分享使用的图片到1-2S 时间
- PQUploadViewModel.updateVideo(title: self?.videoData?.title ?? "", videoId: self?.videoData?.uniqueId ?? "", coverImgPath: objectKey, descr: "") { newVideoData, msg in
- if newVideoData == nil {
- cShowHUB(superView: self?.view, msg: msg)
- // 可能有敏感词 要刷一组新标题并自动更新
- let numberRandom: UInt32 = UInt32(arc4random_uniform(UInt32(self?.publicTitleView.titles.count ?? 0)))
- self?.setTitleText(text: self?.publicTitleView.titles[Int(numberRandom)] ?? "")
- self?.updateCoverImagegOrTitle()
- sleep(UInt32(1.5))
- PQLoadingHUB.shared.dismissHUB()
- return
- } else {
- PQLoadingHUB.shared.dismissHUB()
- }
- }
- } else {
- PQLoadingHUB.shared.dismissHUB()
- }
- })
- } }
- func setTitleText(text: String, textColor: UIColor = UIColor.hexColor(hexadecimal: "#ABABAB")) {
- selectTitle = text
- // 更新 UI
- titleLabel.text = text
- titleLabel.textColor = textColor
- publicTitleView.inputTV.placeHolder = text
- // 更新数据
- videoData?.title = text
- }
- // 取推荐的10个标题
- func getTitles() {
- PQBaseViewModel.getBaseConfig(completeHander: { [weak self] titles in
- if (titles?.count ?? 0) > 0 {
- self?.publicTitleView.titles = titles!
- let numberRandom: UInt32 = UInt32(arc4random_uniform(UInt32(titles!.count)))
- BFLog(message: "接收到的 titles\(String(describing: titles))")
- self?.setTitleText(text: titles?[Int(numberRandom)] ?? "")
- }
- })
- }
- @objc func imageSelectedImage(notify: Notification) {
- let imageData: UIImage? = notify.userInfo?["image"] as? UIImage
- if imageData != nil {
- changPlayerIsPause(isPause: false)
- BFLog(message: "从系统相册选择了一个照片")
- publicEditCoverView.isHidden = true
- coverImageView.image = imageData
- playerHeaderCoverImageView.image = imageData
- uploadData?.image = imageData
- updateCoverImagegOrTitle()
- }
- }
- }
- // MARK: - 导出带水印+片尾的视频相关方法
- extension PQStuckPointPublicController {
- func beginExportSaveMovie(inputAsset: AVURLAsset!) {
- if !(editProjectModel?.sData?.sections != nil && (editProjectModel?.sData?.sections.count ?? 0) > 0) {
- BFLog(message: "项目段落错误❌")
- return
- }
- // 输出视频地址
- var outPutMP4Path = exportVideosDirectory
- if !directoryIsExists(dicPath: outPutMP4Path) {
- BFLog(message: "文件夹不存在")
- createDirectory(path: outPutMP4Path)
- }
- outPutMP4Path.append("saveMovie_\(String.qe.timestamp()).mp4")
- let outPutMP4URL = URL(fileURLWithPath: outPutMP4Path)
- BFLog(message: "导出视频地址 \(outPutMP4URL)")
- saveMovieExporter = PQCompositionExporter(asset: inputAsset, videoComposition: nil, audioMix: nil, filters: nil, stickers: mStickers, animationTool: nil, exportURL: outPutMP4URL)
- saveMovieExporter.isAddWatermark = true
- var orgeBitRate = (editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) * (editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) * 3
- if mStickers != nil {
- for stick in mStickers! {
- if stick.type == StickerType.VIDEO.rawValue {
- let asset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + stick.locationPath), options: avAssertOptions)
- let cbr = asset.tracks(withMediaType: .video).first?.estimatedDataRate
- if Int(cbr ?? 0) > orgeBitRate {
- orgeBitRate = Int(cbr ?? 0)
- }
- }
- }
- }
- BFLog(message: "导出设置的码率为:\(orgeBitRate)")
- saveMovieExporter.showGaussianBlur = true
- if saveMovieExporter.prepare(videoSize: CGSize(width: editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0, height: editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0), videoAverageBitRate: orgeBitRate) {
- BFLog(message: "开始导出 \(String(describing: playeTimeRange.start)) 结束 \(String(describing: playeTimeRange.end))")
- saveMovieExporter.start(playeTimeRange: playeTimeRange)
- BFLog(message: "开始导出")
- }
- saveMovieExporter.progressClosure = { [weak self] _, _, progress in
- BFLog(message: "带水印的合成进度 \(progress) ")
-
- }
- saveMovieExporter.completion = { [weak self] url in
- BFLog(message: "有水印的视频导出完成: \(url) 生成视频时长为:\(CMTimeGetSeconds(AVAsset(url: url).duration))")
- // 导出完成后取消导出
- if self?.saveMovieExporter != nil {
- self?.saveMovieExporter.cancel()
- }
-
- self?.saveMovieExportLocalURL = url
-
- }
- }
- }
|