123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791 |
- //
- // BFRecordScreenController.swift
- // BFRecordScreenKit_Example
- //
- // Created by 胡志强 on 2021/11/23.
- // Copyright © 2021 CocoaPods. All rights reserved.
- //
- import Foundation
- import BFUIKit
- import GPUImage
- import Photos
- import BFCommonKit
- import BFFramework
- import UIKit
- public class BFRecordScreenController: BFBaseViewController {
-
- public var nextActionHandle:(()->Void)?
- public var closeActionHandle:(()->Void)?
-
- // MARK: - 录制参数
- public var assets = [PHAsset]()
-
- var currItemModelIndex = 0
- public var itemModels = [BFRecordItemModel]()
- // var shouldPlayRecordIndex:Int = -1 // 当前应该播放的录音资源序号
- var currentPlayRecordIndex:Int = -1 // >= 0 :当前正在播放的录音资源序号; -3: 刚录音完,不需要播放录音; -1:初始化阶段
- var isRecording = false { // 是否正在录音
- didSet{
- withDrawBtn.isHidden = isRecording
- changeVoiceBtn.isHidden = isRecording
-
- recordBtn.setTitle(isRecording ? "松手 完成" :"按住 说话", for: .normal)
- recordBtn.backgroundColor = UIColor.hexColor(hexadecimal: "#28BE67", alpha: isRecording ? 0.6 : 1)
- }
- }
-
- var isNormalPlaying = false { // 是否正在播放
- didSet{
- playBtn.isSelected = isNormalPlaying
- }
- }
- var currentAssetProgress : CMTime = .zero // 当前素材播放的进度
- // 视频素材
- public var avasset:AVURLAsset?
-
- // public var recordList:[PQVoiceModel] = [PQVoiceModel]()
-
- var assetPlayer:AVPlayer? // 原视频音频播放器
- var isCompletePlay = true
- var hadPrepareToPlayRecord = false // 录音播放器准备
- var recordPlayer:AVPlayer? // 录音音频播放器
- var movie :GPUImageMovie? // 视频预览
- var playView :GPUImageView? // 视频展示视图
- var isDragingProgressSlder : Bool = false // 是否在拖动进度条
-
- //定义音频的编码参数
- let recordSettings:[String : Any] = [AVSampleRateKey : 44100.0, //声音采样率
- AVFormatIDKey : kAudioFormatLinearPCM, //编码格式
- AVNumberOfChannelsKey : 1, //采集音轨
- AVEncoderBitDepthHintKey: 16, // 位深
- AVEncoderAudioQualityKey : AVAudioQuality.medium.rawValue] //音频质量
-
- // 录音相关
- lazy var recorderManager : BFVoiceRecordManager = {
-
- let manager = BFVoiceRecordManager()
- manager.cancelRecordHandle = { error in
-
- }
- manager.endRecordHandle = {[weak self] (model, error) in
- if let sself = self, let model = model, FileManager.default.fileExists(atPath: model.wavFilePath ?? ""){
- // 加入到语音数组里
- // TODO: 原逻辑要删除新录音后边的数据, 新逻辑是覆盖则删除
- var index = sself.itemModels[sself.currItemModelIndex].voiceStickers.count - 1
- while index >= 0{
- let m = sself.itemModels[sself.currItemModelIndex].voiceStickers[index]
- index -= 1
- if model.endTime > m.startTime && model.endTime <= m.endTime
- || model.startTime <= m.startTime && model.startTime > m.endTime{
- sself.itemModels[sself.currItemModelIndex].voiceStickers.remove(at: index+1)
- continue
- }
- if model.startTime < m.endTime {
- break
- }
- }
- BFLog(1, message: "添加录音文件:\(model.startTime) -- \(model.endTime)")
-
- sself.itemModels[sself.currItemModelIndex].voiceStickers.append(model)
-
- sself.drawOrUpdateRecordProgessLable()
-
- sself.currentPlayRecordIndex = -3 // 刚录音完,不需要播放录音
- }
-
- }
- return manager
- }()
- // MARK: - 视图参数
- var beginOnStartBtn:Bool = false
- var touchStart:CGPoint = CGPoint(x: 0, y: 0)
- var avplayerTimeObserver: NSKeyValueObservation?
- lazy var progreddL : UILabel = {
- let l = UILabel()
- l.textAlignment = .center
- l.font = UIFont.systemFont(ofSize: 10)
- l.textColor = .white
- l.shadowColor = .black
- l.shadowOffset = CGSize(width: 1, height: 1)
- return l
- }()
-
- lazy var playBtn:UIButton = {
- let btn = UIButton(frame: view.bounds)
- btn.addTarget(self, action: #selector(playVideo(btn:)), for: .touchUpInside)
- return btn
- }()
-
- lazy var bottomeView:UIImageView = {
- let iv = UIImageView(image: imageInRecordScreenKit(by: "bottom_shadow"))
- iv.contentMode = .scaleAspectFill
- iv.isUserInteractionEnabled = true
- // 拖曳手勢
- let pan = UIPanGestureRecognizer(target: self, action: #selector(pan(recognizer:)))
- pan.delegate = self
- pan.minimumNumberOfTouches = 1
- pan.maximumNumberOfTouches = 1
- iv.addGestureRecognizer(pan)
-
- return iv
- }()
-
- lazy var recordBtn:UIButton = {
- let btn = UIButton(type: .custom)
- btn.backgroundColor = ThemeStyleGreen()
- btn.setTitle("按住 说话", for: .normal)
- btn.adjustsImageWhenHighlighted = false
- btn.addTarget(self, action: #selector(startRecord), for: .touchDown)
- btn.addTarget(self, action: #selector(endRecord), for: .touchUpInside)
- return btn
- }()
-
- // lazy var progessSildeBackV : UIView = {
- // let vv = UIView()
- // vv.backgroundColor = .orange // .clear
- //
- // return vv
- // }()
-
- lazy var withDrawBtn:UIButton = {
- let btn = UIButton(type: .custom)
- btn.setImage(imageInRecordScreenKit(by: "withdraw_n"), for: .normal)
- btn.setImage(imageInRecordScreenKit(by: "withdraw_h"), for: .highlighted)
- btn.setTitle("撤销", for: .normal)
- btn.setTitleColor(.white, for: .normal)
- btn.setTitleColor(.gray, for: .highlighted)
- btn.titleLabel?.font = UIFont.systemFont(ofSize: 12)
- btn.contentVerticalAlignment = UIControl.ContentVerticalAlignment.center;
- btn.addTarget(self, action: #selector(withdrawAction), for: .touchUpInside)
- return btn
- }()
-
- lazy var changeVoiceBtn:UIButton = {
- let btn = UIButton(type: .custom)
- btn.setImage(imageInRecordScreenKit(by: "changeVoice_n"), for: .normal)
- btn.setImage(imageInRecordScreenKit(by: "changeVoice_h"), for: .highlighted)
- btn.setTitle("变声", for: .normal)
- btn.setTitleColor(.white, for: .normal)
- btn.setTitleColor(ThemeStyleGreen(), for: .highlighted)
- btn.titleLabel?.font = UIFont.systemFont(ofSize: 12)
- btn.contentVerticalAlignment = UIControl.ContentVerticalAlignment.center;
- btn.addTarget(self, action: #selector(changeVoiceAction), for: .touchUpInside)
- return btn
- }()
-
- lazy var toolV : BFIntroduceToolView = {
- let toolV = BFIntroduceToolView()
- toolV.centerY = view.centerY
-
- toolV.choosedToolHandle = {[weak self] tool in
- guard let sself = self else {
- return
- }
- tool.center = sself.view.center
- sself.view.addSubview(tool)
- }
-
- return toolV
-
- }()
-
- lazy var progressThumV : BFVideoThumbProgressView = {
- let vv = BFVideoThumbProgressView(frame: CGRect(x: 0, y: 54, width: cScreenWidth, height: 50))
- vv.dragScrollProgressHandle = {[weak self] process in
- DispatchQueue.main.async {[weak self] in
- guard let sself = self else {
- return
- }
- if sself.isNormalPlaying || sself.isRecording {
- sself.pause()
- sself.isDragingProgressSlder = true
- }
- sself.changeProgress(progress: process)
- }
- }
- vv.dragEndHandle = { [weak self] process in
-
- guard let sself = self else {
- return
- }
- sself.changeProgress(progress: process)
- sself.isDragingProgressSlder = false
- sself.currentPlayRecordIndex = -1
- sself.hadPrepareToPlayRecord = false
- }
- vv.isHidden = true
- return vv
- }()
-
- //MARK: ------------------ 生命周期
- deinit {
- cleanMovieTarget()
- NotificationCenter.default.removeObserver(self)
- avplayerTimeObserver?.invalidate()
- if isRecording{
- recorderManager.stopRecord(isCancel: true)
- }
- assetPlayer?.pause()
- recordPlayer?.pause()
-
- }
-
- public override func viewWillAppear(_ animated: Bool) {
- super.viewWillAppear(animated)
- self.navigationController?.isNavigationBarHidden = true
- hiddenNavigation()
- }
-
- public override func viewDidLoad(){
- super.viewDidLoad()
- _ = disablePopGesture()
-
- view.backgroundColor = .lightGray
-
- playView = GPUImageView(frame: view.bounds)
- view.addSubview(playView!)
- fetchVideo()
-
- view.addSubview(playBtn)
- view.addSubview(bottomeView)
- bottomeView.addSubview(progreddL)
- // view.addSubview(toolV)
- bottomeView.addSubview(recordBtn)
- bottomeView.addSubview(withDrawBtn)
- bottomeView.addSubview(changeVoiceBtn)
- bottomeView.addSubview(progressThumV)
- // progressThumV.addSubview(progessSildeBackV)
- if checkStatus() {
- try? AVAudioSession.sharedInstance().setCategory(.playAndRecord, options: .defaultToSpeaker)
- }
-
- layoutsubview()
- }
- func layoutsubview() {
- bottomeView.snp.makeConstraints { make in
- make.left.bottom.right.equalToSuperview()
- make.height.equalTo(adapterWidth(width: 220))
- }
-
- progreddL.snp.makeConstraints { make in
- make.width.equalTo(100)
- make.centerX.equalToSuperview()
- make.bottom.equalTo(progressThumV.snp.top)
- make.height.equalTo(18)
- }
-
- withDrawBtn.snp.makeConstraints { make in
- make.left.equalToSuperview()
- make.width.height.equalTo(65)
- make.top.equalTo(128)
- }
- changeVoiceBtn.snp.makeConstraints { make in
- make.right.equalToSuperview()
- make.top.width.height.equalTo(withDrawBtn)
- }
-
- recordBtn.snp.makeConstraints { make in
- make.left.equalTo(withDrawBtn.snp.right)
- make.right.equalTo(changeVoiceBtn.snp.left)
- make.height.equalTo(42)
- make.top.equalTo(withDrawBtn).offset(6)
- }
-
- // progessSildeBackV.snp.makeConstraints { make in
- // make.left.equalToSuperview()
- // make.right.equalToSuperview()
- // make.bottom.equalToSuperview()
- // make.height.equalTo(8)
- // }
-
- withDrawBtn.imageEdgeInsets = UIEdgeInsets(top: -withDrawBtn.imageView!.height, left: 0, bottom: 0, right: -withDrawBtn.titleLabel!.width);
- withDrawBtn.titleEdgeInsets = UIEdgeInsets(top: withDrawBtn.titleLabel!.height + 2, left: -withDrawBtn.imageView!.width, bottom: 0, right: 0);
-
- changeVoiceBtn.imageEdgeInsets = UIEdgeInsets(top: -changeVoiceBtn.imageView!.height-2, left: 0, bottom: 0, right: -changeVoiceBtn.titleLabel!.width);
- changeVoiceBtn.titleEdgeInsets = UIEdgeInsets(top: changeVoiceBtn.titleLabel!.height+2, left: -changeVoiceBtn.imageView!.width, bottom: 0, right: 0);
-
- }
-
- // public override func viewWillLayoutSubviews() {
- // super.viewWillLayoutSubviews()
- //
- // }
-
- // MARK: - 按钮事件响应
-
- public override func backBtnClick() {
- pause()
- }
-
- // 触发拖曳手势后,执行的动作
- @objc func pan(recognizer: UIPanGestureRecognizer) {
- // 设置 UIView 新的位置
- if !checkStatus(show: false) {
- return
- }
- let point = recognizer.location(in: bottomeView)
- switch recognizer.state {
- case .began:
- touchStart = recognizer.location(in: bottomeView)
- beginOnStartBtn = recordBtn.frame.contains(touchStart)
- case .changed:
- if beginOnStartBtn == true {
- let nowPoint = recognizer.location(in: bottomeView)
- BFLog(1, message: "nowPoint x: \(nowPoint.x) y:\(nowPoint.y)")
- if recordBtn.frame.contains(nowPoint) {
-
- } else {
-
- }
- }
- case .ended:
- BFLog(1, message: "移动结束 \(beginOnStartBtn)")
- if beginOnStartBtn == true {
- beginOnStartBtn = false
- if recordBtn.frame.contains(point) {
- // 结束录制
- endRecord()
- } else {
- cancleRecord()
- }
- }
- default:
- break
- }
- }
-
- @objc func startRecord(){
- BFLog(1, message: "start \(UIControl.Event.touchDown)")
- isRecording = true
- pause()
- let model = PQVoiceModel()
- model.startTime = self.currentAssetProgress.seconds
- model.volume = 100
- recorderManager.voiceModel = model
- recorderManager.startRecord(index: 1)
- // movie?.startProcessing()
- assetPlayer?.volume = 0
- assetPlayer?.play()
- }
-
- @objc func endRecord(){
-
- isRecording = false
- // 存储录音
- recorderManager.voiceModel?.endTime = self.currentAssetProgress.seconds
- recorderManager.endRecord()
- pause()
- }
-
- func cancleRecord(){
- isRecording = false
- recorderManager.cancleRecord()
-
- pause()
- }
- @objc func withdrawAction(){
- pause()
- if let model = itemModels[currItemModelIndex].voiceStickers.last {
- itemModels[currItemModelIndex].voiceStickers.removeLast()
- drawOrUpdateRecordProgessLable()
- if let dur = itemModels[currItemModelIndex].baseMaterial?.duration.seconds,dur > 0 {
- changeProgress(progress: Float(model.startTime / dur))
- isDragingProgressSlder = false
- currentPlayRecordIndex = -1
- hadPrepareToPlayRecord = false
- progressThumV.progress = model.startTime
- }
- }
- }
-
- @objc func changeVoiceAction(){
- // nextActionHandle?()
- pause()
- }
-
- @objc func playVideo(btn:UIButton){
- btn.isSelected ? pause() : play()
- }
- @objc func sliderTouchBegan(sender _: UISlider) {
- isDragingProgressSlder = true
- pause()
- }
- @objc func sliderTouchEnded(sender: UISlider) {
- changeProgress(progress: sender.value)
- isDragingProgressSlder = false
- currentPlayRecordIndex = -1
- hadPrepareToPlayRecord = false
- }
- @objc func sliderValueDidChanged(sender: UISlider) {
- changeProgress(progress: sender.value)
- }
-
- // MARK: - 权限申请
- func checkStatus(show: Bool = true) -> Bool {
- let status = AVCaptureDevice.authorizationStatus(for: .audio)
- switch status {
- case .denied, .restricted:
- BFLog(message: "麦克风权限被拒绝,请在设置中打开")
- if show {
- let remindData = BFBaseModel()
- remindData.title = "票圈视频需要访问你的麦克风才能录音"
- remindData.summary = ""
- let remindView = BFRemindView(frame: CGRect(x: 0, y: 0, width: cScreenWidth, height: cScreenHeigth))
- remindView.isBanned = true
- remindView.confirmBtn.setTitle("去设置", for: .normal)
- UIApplication.shared.keyWindow?.addSubview(remindView)
- remindView.remindData = remindData
- remindView.remindBlock = { item, _ in
- if item.tag == 2 {
- openAppSetting()
- }
- }
- }
- return false
- case .authorized:
- return true
- case .notDetermined:
- if show {
- requestMicroPhoneAuth()
- }
- return false
- default:
- break
- }
- return false
- }
-
- func requestMicroPhoneAuth() {
- AVCaptureDevice.requestAccess(for: .audio) { granted in
- if !granted {
- BFLog(message: "麦克风权限被拒绝,请在设置中打开")
- }
- }
- }
-
- // MARK: - 音视频处理
- func playRecord(at currentT:CMTime){
- if currentPlayRecordIndex == -3 { // 刚录音完,不需要播放
- return
- }
-
- let (shouldPlayRecordIndex, recordedAudio) = itemModels[currItemModelIndex].voiceStickers.enumerated().first { model in
- model.1.endTime > CMTimeGetSeconds(currentT)
- } ?? (-1, nil)
-
- guard let recordedAudio = recordedAudio else {
- return
- }
-
- BFLog(1, message: "当前时间:\(CMTimeGetSeconds(currentT)), 找到的音频:\(recordedAudio.startTime), \(recordedAudio.endTime)")
-
- // 创建播放器
- if self.recordPlayer == nil || (self.recordPlayer?.currentItem?.asset as? AVURLAsset)?.url.lastPathComponent != (recordedAudio.wavFilePath as NSString).lastPathComponent {
- let newItem = AVPlayerItem(url: URL(fileURLWithPath: recordedAudio.wavFilePath))
- if let player = self.recordPlayer {
- player.pause()
- if let playItem = player.currentItem {
- NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: playItem)
- recordPlayer?.replaceCurrentItem(with: newItem)
- }
- }else {
- self.recordPlayer = AVPlayer(playerItem: newItem)
- }
- self.recordPlayer!.volume = 1
- // self.recordPlayer?.prepareToPlay()
- currentPlayRecordIndex = -1
- hadPrepareToPlayRecord = false
- BFLog(1, message: "录音播放器初始化(有时候不准)")
-
- NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: newItem, queue: .main) { [weak self] notify in
- self?.hadPrepareToPlayRecord = false
- self?.currentPlayRecordIndex = -1
- }
- }
-
- synced(currentPlayRecordIndex) {
- if !hadPrepareToPlayRecord
- && CMTimeGetSeconds(currentT) >= recordedAudio.startTime
- && CMTimeGetSeconds(currentT) <= recordedAudio.endTime - 0.2 // 这个条件是避免录音结束后有小幅度回退导致播放最新录音
- {
- // 应当开始播放了
- // 两个逻辑:如果在播,则跳过;如果暂停拖动到中间,则seek
- if currentPlayRecordIndex == -1 && self.isNormalPlaying{
- let second = CMTimeGetSeconds(currentT) - recordedAudio.startTime
- DispatchQueue.main.async {[weak self] in
- self?.recordPlayer?.seek(to: CMTime(value: CMTimeValue(second), timescale: 100), toleranceBefore: CMTime(value: 1, timescale: 1000), toleranceAfter: CMTime(value: 1, timescale: 1000), completionHandler: {[weak self] finished in
- if finished && (self?.isNormalPlaying ?? false) {
- self?.recordPlayer?.play()
- BFLog(1, message: "录音开始播放 playing, \(second), \(CMTimeGetSeconds(self?.recordPlayer?.currentItem?.duration ?? .zero))")
- }
- })
- }
- currentPlayRecordIndex = shouldPlayRecordIndex
- hadPrepareToPlayRecord = true
- BFLog(1, message: "录音开始播放2, \(second), \(CMTimeGetSeconds(recordPlayer?.currentItem?.duration ?? .zero))")
- }
- }
-
- }
- BFLog(1, message: "应当播放:\(shouldPlayRecordIndex), 当前播放:\(currentPlayRecordIndex)")
- // if let recordedAudio = recordedAudio {
- //
- //
- // if shouldPlayRecordIndex != currentPlayRecordIndex {
- // // 设置新的播放资源
- //
- //// self.recordPlayer.delegate = self
- // self.recordPlayer.play()
- //
- // } else {
- // // 更新播放进度
- // let second = CMTimeGetSeconds(duration) - recordedAudio.startTime
- // self.recordPlayer.seek(to: CMTime(seconds: second, preferredTimescale: 25), toleranceBefore: CMTime(value: 1, timescale: 1000), toleranceAfter: CMTime(value: 1, timescale: 1000))
- // }
- // }
- }
-
- func play(){
- BFLog(1, message: "开始播放 \(self.currentAssetProgress.seconds)")
- isNormalPlaying = true
- assetPlayer?.volume = 0.2
- movie?.startProcessing()
-
- self.assetPlayer?.play()
- }
-
- func pause(){
- BFLog(1, message: "暂停播放")
- isNormalPlaying = false
- // movie?.cancelProcessing()
- assetPlayer?.pause()
- recordPlayer?.pause()
-
- assetPlayer?.seek(to: self.currentAssetProgress , toleranceBefore: CMTime(value: 1, timescale: 1000), toleranceAfter: CMTime(value: 1, timescale: 1000), completionHandler: { finished in
- })
- }
-
- func fetchVideo(){
- if self.assets.count > 0 {
- currItemModelIndex = 0
-
- for (index, asset) in self.assets.enumerated() {
- let itemModel = BFRecordItemModel()
- itemModel.index = 0
- itemModels.append(itemModel)
-
- let options = PHVideoRequestOptions()
- options.isNetworkAccessAllowed = true
- options.deliveryMode = .automatic
-
- PHImageManager.default().requestPlayerItem(forVideo:asset, options: options, resultHandler: { [weak self] playerItem, info in
- guard let item = playerItem else {
- cShowHUB(superView: nil, msg: "视频获取失败")
- return
- }
- if index == 0 {
- self?.setAudioPlay(item: item)
- self?.setVideoPlay(item: item)
- }
- })
-
- // let option = PHImageRequestOptions()
- // option.isNetworkAccessAllowed = true //允许下载iCloud的图片
- // option.resizeMode = .fast
- // option.deliveryMode = .highQualityFormat
- // PHImageManager.default().requestImage(for: asset,
- // targetSize: self.view.bounds.size,
- // contentMode: .aspectFit,
- // options: option)
- // { (image, nil) in
- // // 设置首帧/封面
- // if image != nil {
- // let pic = GPUImagePicture(image: image)
- // let filet = GPUImageFilter()
- // pic?.addTarget(filet)
- // filet.addTarget(self.playView)
- // pic?.processImage()
- // }
- // }
-
- PHCachingImageManager().requestAVAsset(forVideo: asset, options: options, resultHandler: {[weak self] (asset: AVAsset?, audioMix: AVAudioMix?, info) in
- if let urlasset = asset as? AVURLAsset {
- self?.avasset = urlasset
- itemModel.baseMaterial = urlasset
- DispatchQueue.main.async {[weak self] in
- self?.progressThumV.videoAsset = urlasset
- self?.progressThumV.isHidden = false
- }
- }
- })
- }
- }
- }
-
- func setVideoPlay(item:AVPlayerItem){
- if movie != nil {
- cleanMovieTarget()
- }
- movie = GPUImageMovie(playerItem: item)
- // movie?.runBenchmark = true
- movie?.playAtActualSpeed = true
- let filter = GPUImageFilter()
- movie?.addTarget(filter)
- filter.addTarget(playView)
-
- movie?.startProcessing()
- }
-
- func setAudioPlay(item:AVPlayerItem){
- if let playItem = assetPlayer?.currentItem {
- NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: playItem)
- assetPlayer?.replaceCurrentItem(with: item)
- }else {
- assetPlayer = AVPlayer(playerItem: item)
- avplayerTimeObserver = assetPlayer?.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 100), queue: DispatchQueue.global()) {[weak self] time in
- // 进度监控
- if !((self?.isNormalPlaying ?? false) || (self?.isRecording ?? false)) {
- return
- }
-
- // 播放对应的录音音频
- self?.playRecord(at: time)
- self?.currentAssetProgress = time
- BFLog(1, message: "curr:\(CMTimeGetSeconds(time))")
- if CMTimeGetSeconds(item.duration) > 0, !(self?.isDragingProgressSlder ?? false) {
- DispatchQueue.main.async { [weak self] in
- self?.progreddL.text = String(format: "%.2f", CMTimeGetSeconds(time), CMTimeGetSeconds(item.duration))
- self?.progressThumV.progress = time.seconds
- }
- }
- } as? NSKeyValueObservation
- }
-
- NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: assetPlayer?.currentItem, queue: .main) { [weak self] notify in
- BFLog(1, message: "AVPlayerItemDidPlayToEndTime = \(notify)")
- self?.isNormalPlaying = false
- self?.assetPlayer?.seek(to: CMTime.zero)
- self?.currentPlayRecordIndex = -1
- if self?.isRecording ?? false {
- self?.endRecord()
- }
- }
- }
-
- func cleanMovieTarget(){
- movie?.cancelProcessing()
- movie?.targets().forEach({ target in
- if let objc = target as? GPUImageOutput{
- objc.removeAllTargets()
- }
- })
- movie?.removeAllTargets()
- movie?.removeFramebuffer()
- GPUImageContext.sharedFramebufferCache().purgeAllUnassignedFramebuffers()
-
- }
-
- func generationTimeRanges() -> [CMTimeRange]{
-
- var ranges = [CMTimeRange]()
- var start : Double = 0
- for model in itemModels[currItemModelIndex].voiceStickers {
- if model.startTime > start{
- let range = CMTimeRange(start: CMTime(seconds: start, preferredTimescale: 100), duration: CMTime(seconds: model.startTime - start, preferredTimescale: 100))
- ranges.append(range)
-
- }
- ranges.append(CMTimeRange(start: CMTime(seconds: model.startTime, preferredTimescale: 100), end: CMTime(seconds: model.endTime, preferredTimescale: 100)))
- start = model.endTime
- }
-
- return ranges
- }
-
-
- //MARK: - 录音对应图像绘制
-
- func changeProgress(progress:Float) {
- if let duration = assetPlayer?.currentItem?.duration {
- self.currentAssetProgress = CMTime(value: CMTimeValue(progress * Float(CMTimeGetSeconds(duration)) * 100), timescale: 100)
- DispatchQueue.main.async {[weak self] in
- self!.progreddL.text = String(format: "%.2f", CMTimeGetSeconds(self!.currentAssetProgress))
- }
- assetPlayer!.seek(to: self.currentAssetProgress, toleranceBefore: CMTime(value: 1, timescale: 1000), toleranceAfter: CMTime(value: 1, timescale: 1000)) { finished in
- }
- }
- }
-
- func drawOrUpdateRecordProgessLable(){
- DispatchQueue.main.async {[weak self] in
- guard let sself = self else {
- return
- }
- sself.progressThumV.progessIndicateBackV.subviews.forEach { vv in
- vv.removeFromSuperview()
- }
-
- if let totalDur = sself.itemModels[sself.currItemModelIndex].baseMaterial?.duration.seconds, totalDur > 0, sself.itemModels[sself.currItemModelIndex].voiceStickers.count > 0 {
- let width = sself.progressThumV.progessIndicateBackV.width
- let height = sself.progressThumV.progessIndicateBackV.height
- sself.itemModels[sself.currItemModelIndex].voiceStickers.forEach { model in
- let lineV = UIView(frame: CGRect(x: model.startTime * width / totalDur , y: 0, width: (model.endTime - model.startTime) * width / totalDur, height: height))
- lineV.backgroundColor = ThemeStyleGreen()
- sself.progressThumV.progessIndicateBackV.addSubview(lineV)
- }
- }
- }
- }
- }
- extension BFRecordScreenController:GPUImageMovieDelegate {
- public func didCompletePlayingMovie(){
- BFLog(1, message: "播放结束")
- currentPlayRecordIndex = -1
- }
- }
- extension BFRecordScreenController:AVAudioRecorderDelegate {
- public func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
- BFLog(1, message: "录音结束")
- }
- }
- extension BFRecordScreenController : AVAudioPlayerDelegate {
- public func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
- BFLog(1, message: "录音播放结束")
- }
- }
|