|
@@ -28,12 +28,12 @@ public class BFRecordScreenController: BFBaseViewController {
|
|
|
// MARK: - 录制参数
|
|
|
|
|
|
public var assets = [PHAsset]()
|
|
|
-
|
|
|
var currItemModelIndex = 0
|
|
|
public var itemModels = [BFRecordItemModel]()
|
|
|
var showSubtitleIndex = 0
|
|
|
// var shouldPlayRecordIndex:Int = -1 // 当前应该播放的录音资源序号
|
|
|
var currentPlayRecordIndex: Int = -1 // >= 0 :当前正在播放的录音资源序号; -3: 刚录音完,不需要播放录音; -1:初始化阶段
|
|
|
+ public var displayLink: CADisplayLink? // 图片素材播放控制
|
|
|
var isRecording = false { // 是否正在录音
|
|
|
didSet {
|
|
|
withDrawBtn.isHidden = isRecording
|
|
@@ -396,7 +396,7 @@ public class BFRecordScreenController: BFBaseViewController {
|
|
|
// add by ak 取 nsl token
|
|
|
BFRecordScreenViewModel.getNlsAccessToken { [weak self] token, appkey in
|
|
|
BFLog(message: "nls appkey is \(appkey), token is \(token)")
|
|
|
- self?.speechTranscriberUtil = PQSpeechTranscriberUtil(token, appid: appkey)
|
|
|
+// self?.speechTranscriberUtil = PQSpeechTranscriberUtil(token, appid: appkey)
|
|
|
}
|
|
|
|
|
|
view.backgroundColor = .black
|
|
@@ -699,9 +699,6 @@ public class BFRecordScreenController: BFBaseViewController {
|
|
|
|
|
|
// movie?.startProcessing()
|
|
|
// assetPlayer?.volume = 0
|
|
|
-
|
|
|
- assetPlayer?.play()
|
|
|
-
|
|
|
DispatchQueue.global().async { [weak self] in
|
|
|
guard let sself = self else {
|
|
|
return
|
|
@@ -710,6 +707,13 @@ public class BFRecordScreenController: BFBaseViewController {
|
|
|
sself.speechTranscriberUtil?.startTranscriber()
|
|
|
sself.speechTranscriberUtil?.currItemModelIndex = Int32(sself.currItemModelIndex)
|
|
|
}
|
|
|
+ if itemModels[currItemModelIndex].mediaType == .VIDEO {
|
|
|
+ assetPlayer?.play()
|
|
|
+ } else {
|
|
|
+ recorderManager.audioRecorder?.recorderProgross = { [weak self] progress in
|
|
|
+ self?.imageRecordProgress(progress: progress)
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
@objc func endRecord() {
|
|
@@ -905,7 +909,7 @@ public class BFRecordScreenController: BFBaseViewController {
|
|
|
|
|
|
// MARK: - 音视频处理
|
|
|
|
|
|
- func playRecord(at currentT: CMTime) {
|
|
|
+ func playRecord(at currentT: CMTime, periodicTimeObserver: @escaping (_ time: CMTime, _ currentItem: AVPlayerItem) -> Void, didPlayToEndTime: @escaping (_ currentT: CMTime, _ currentItem: AVPlayerItem) -> Void,playFailut: @escaping () -> Void) {
|
|
|
if currentPlayRecordIndex == -3 { // 刚录音完,不需要播放
|
|
|
return
|
|
|
}
|
|
@@ -943,16 +947,22 @@ public class BFRecordScreenController: BFBaseViewController {
|
|
|
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: newItem, queue: .main) { [weak self] _ in
|
|
|
self?.hadPrepareToPlayRecord = false
|
|
|
self?.currentPlayRecordIndex = -1
|
|
|
+ didPlayToEndTime(currentT, newItem)
|
|
|
}
|
|
|
+ _ = recordPlayer?.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 100), queue: DispatchQueue.global()) { [weak self] time in
|
|
|
+ periodicTimeObserver(time, newItem)
|
|
|
+ } as? NSKeyValueObservation
|
|
|
}
|
|
|
if recordPlayer?.currentItem?.duration.timescale == 0 {
|
|
|
BFLog(1, message: "时间timescale == 0")
|
|
|
+ playFailut()
|
|
|
+ return
|
|
|
}
|
|
|
synced(currentPlayRecordIndex) { [weak self] in
|
|
|
guard let self = self else {
|
|
|
return
|
|
|
}
|
|
|
- BFLog(1, message: "判断是否开始录音播放** hadPrepareToPlayRecord:\(hadPrepareToPlayRecord), currentPlayRecordIndex:\(currentPlayRecordIndex), isNormalPlaying :\(self.isNormalPlaying)")
|
|
|
+ BFLog(1, message: "判断是否开始录音播放** hadPrepareToPlayRecord:\(hadPrepareToPlayRecord), currentPlayRecordIndex:\(currentPlayRecordIndex), isNormalPlaying :\(self.isNormalPlaying),\(recordPlayer?.currentItem?.duration.timescale),\(CMTimeGetSeconds(currentT) >= recordedAudio.startTime),\(CMTimeGetSeconds(currentT) <= recordedAudio.endTime - 0.2)")
|
|
|
|
|
|
if !hadPrepareToPlayRecord,
|
|
|
recordPlayer?.currentItem?.duration.timescale != 0,
|
|
@@ -999,17 +1009,21 @@ public class BFRecordScreenController: BFBaseViewController {
|
|
|
func play() {
|
|
|
BFLog(1, message: "开始播放 \(currentAssetProgress.seconds)")
|
|
|
isNormalPlaying = true
|
|
|
-// assetPlayer?.volume = 0.2
|
|
|
- movie?.startProcessing()
|
|
|
- if isEndPlay {
|
|
|
- isEndPlay = false
|
|
|
- assetPlayer?.seek(to: CMTime.zero)
|
|
|
- progressThumV.progress = 0
|
|
|
- currentPlayRecordIndex = -1
|
|
|
- recordBtn.isHidden = false
|
|
|
+ if itemModels[currItemModelIndex].mediaType == .VIDEO {
|
|
|
+ // assetPlayer?.volume = 0.2
|
|
|
+ movie?.startProcessing()
|
|
|
+ if isEndPlay {
|
|
|
+ isEndPlay = false
|
|
|
+ assetPlayer?.seek(to: CMTime.zero)
|
|
|
+ progressThumV.progress = 0
|
|
|
+ currentPlayRecordIndex = -1
|
|
|
+ recordBtn.isHidden = false
|
|
|
+ }
|
|
|
+ assetPlayer?.play()
|
|
|
+ } else {
|
|
|
+ // 处理图片音频播放
|
|
|
+ imageRecordPlay()
|
|
|
}
|
|
|
-
|
|
|
- assetPlayer?.play()
|
|
|
}
|
|
|
|
|
|
func pause() {
|
|
@@ -1084,25 +1098,18 @@ public class BFRecordScreenController: BFBaseViewController {
|
|
|
assetPlayer = AVPlayer(playerItem: item)
|
|
|
assetPlayer?.volume = 0
|
|
|
avplayerTimeObserver = assetPlayer?.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 100), queue: DispatchQueue.global()) { [weak self] time in
|
|
|
- // 进度监控
|
|
|
- self?.currentAssetProgress = CMTime(seconds: time.seconds, preferredTimescale: 1000)
|
|
|
- BFLog(1, message: "curr:\(CMTimeGetSeconds(self?.currentAssetProgress ?? .zero))")
|
|
|
- if CMTimeGetSeconds(item?.duration ?? CMTime.zero) > 0 {
|
|
|
- DispatchQueue.main.async { [weak self] in
|
|
|
- self?.progreddL.text = String(format: "%@", CMTimeGetSeconds(time).formatDurationToHMS())
|
|
|
- let su = !(self?.isDragingProgressSlder ?? false) || (self?.isRecording ?? false && self?.isNormalPlaying ?? false)
|
|
|
- if su {
|
|
|
- self?.progressThumV.progress = time.seconds
|
|
|
- }
|
|
|
- self?.updateSubtitle(time: time)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
+ // 进度监控
|
|
|
+ self?.periodicTimeObserver(item: item, time: time)
|
|
|
if self?.isNormalPlaying ?? false {
|
|
|
// 播放对应的录音音频
|
|
|
- self?.playRecord(at: time)
|
|
|
+ self?.playRecord(at: time, periodicTimeObserver: { currentT, currentItem in
|
|
|
+ BFLog(message: "播放一段进度:\(currentT),\(currentItem)")
|
|
|
+ }, didPlayToEndTime: { startT, currentItem in
|
|
|
+ BFLog(message: "播放一段结束:\(startT),\(currentItem)")
|
|
|
+ }, playFailut: {
|
|
|
+
|
|
|
+ })
|
|
|
}
|
|
|
-
|
|
|
} as? NSKeyValueObservation
|
|
|
}
|
|
|
|
|
@@ -1120,6 +1127,24 @@ public class BFRecordScreenController: BFBaseViewController {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// 音视频播放进度-视频跟图片单独处理
|
|
|
+ /// - Parameter CMTime: <#CMTime description#>
|
|
|
+ func periodicTimeObserver(item: AVPlayerItem?, time: CMTime) {
|
|
|
+ // 进度监控
|
|
|
+ currentAssetProgress = CMTime(seconds: time.seconds, preferredTimescale: 1000)
|
|
|
+ BFLog(1, message: "curr:\(CMTimeGetSeconds(currentAssetProgress))")
|
|
|
+ if CMTimeGetSeconds(item?.duration ?? CMTime.zero) > 0 {
|
|
|
+ DispatchQueue.main.async { [weak self] in
|
|
|
+ self?.progreddL.text = String(format: "%@", CMTimeGetSeconds(time).formatDurationToHMS())
|
|
|
+ let su = !(self?.isDragingProgressSlder ?? false) || (self?.isRecording ?? false && self?.isNormalPlaying ?? false)
|
|
|
+ if su {
|
|
|
+ self?.progressThumV.progress = time.seconds
|
|
|
+ }
|
|
|
+ self?.updateSubtitle(time: time)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
func cleanMovieTarget() {
|
|
|
movie?.cancelProcessing()
|
|
|
movie?.targets().forEach { target in
|
|
@@ -1135,13 +1160,19 @@ public class BFRecordScreenController: BFBaseViewController {
|
|
|
// MARK: - 录音对应图像绘制
|
|
|
|
|
|
func changeProgress(progress: Float) {
|
|
|
- if let duration = assetPlayer?.currentItem?.duration {
|
|
|
- currentAssetProgress = CMTime(value: CMTimeValue(progress * Float(CMTimeGetSeconds(duration)) * 1000), timescale: 1000)
|
|
|
- DispatchQueue.main.async { [weak self] in
|
|
|
- self!.progreddL.text = String(format: "%@", CMTimeGetSeconds(self!.currentAssetProgress).formatDurationToHMS())
|
|
|
+ if itemModels[currItemModelIndex].mediaType == .VIDEO {
|
|
|
+ if let duration = assetPlayer?.currentItem?.duration {
|
|
|
+ currentAssetProgress = CMTime(value: CMTimeValue(progress * Float(CMTimeGetSeconds(duration)) * 1000), timescale: 1000)
|
|
|
+ DispatchQueue.main.async { [weak self] in
|
|
|
+ self!.progreddL.text = String(format: "%@", CMTimeGetSeconds(self!.currentAssetProgress).formatDurationToHMS())
|
|
|
+ }
|
|
|
+ assetPlayer!.seek(to: currentAssetProgress, toleranceBefore: CMTime(value: 1, timescale: 1000), toleranceAfter: CMTime(value: 1, timescale: 1000)) { _ in
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- assetPlayer!.seek(to: currentAssetProgress, toleranceBefore: CMTime(value: 1, timescale: 1000), toleranceAfter: CMTime(value: 1, timescale: 1000)) { _ in
|
|
|
+ } else {
|
|
|
+ if itemModels[currItemModelIndex].materialDuraion > 0 {
|
|
|
+ currentAssetProgress = CMTime(value: CMTimeValue(progress * Float(itemModels[currItemModelIndex].materialDuraion) * 1000), timescale: 1000)
|
|
|
+ BFLog(message: "progress = \(progress),currentAssetProgress = \(currentAssetProgress)")
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -1154,16 +1185,21 @@ public class BFRecordScreenController: BFBaseViewController {
|
|
|
sself.progressThumV.progessIndicateBackV.subviews.forEach { vv in
|
|
|
vv.removeFromSuperview()
|
|
|
}
|
|
|
-
|
|
|
let totalDur = sself.itemModels[sself.currItemModelIndex].materialDuraion
|
|
|
- if 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 * Double(width) / totalDur, y: 0, width: (model.endTime - model.startTime) * Double(width) / totalDur, height: Double(height)))
|
|
|
- lineV.backgroundColor = ThemeStyleColor
|
|
|
- sself.progressThumV.progessIndicateBackV.addSubview(lineV)
|
|
|
+ let height = sself.progressThumV.progessIndicateBackV.height
|
|
|
+ if sself.itemModels[sself.currItemModelIndex].mediaType == .VIDEO {
|
|
|
+ if totalDur > 0, sself.itemModels[sself.currItemModelIndex].voiceStickers.count > 0 {
|
|
|
+ let width = sself.progressThumV.progessIndicateBackV.width
|
|
|
+ sself.itemModels[sself.currItemModelIndex].voiceStickers.forEach { model in
|
|
|
+ let lineV = UIView(frame: CGRect(x: model.startTime * Double(width) / totalDur, y: 0, width: (model.endTime - model.startTime) * Double(width) / totalDur, height: Double(height)))
|
|
|
+ lineV.backgroundColor = ThemeStyleColor
|
|
|
+ sself.progressThumV.progessIndicateBackV.addSubview(lineV)
|
|
|
+ }
|
|
|
}
|
|
|
+ } else {
|
|
|
+ let lineV = UIView(frame: CGRect(x: 0, y: 0, width: totalDur * sself.progressThumV.thumbImageWidth / 2, height: Double(height)))
|
|
|
+ lineV.backgroundColor = ThemeStyleColor
|
|
|
+ sself.progressThumV.progessIndicateBackV.addSubview(lineV)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -1219,9 +1255,9 @@ extension BFRecordScreenController: PQSpeechTranscriberUtilDelegate {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// MARK: - Delegate
|
|
|
+// MARK: - UICollectionViewDelegate
|
|
|
|
|
|
-/// Delegate
|
|
|
+/// UICollectionViewDelegate
|
|
|
extension BFRecordScreenController: UICollectionViewDelegate, UICollectionViewDataSource, UIScrollViewDelegate {
|
|
|
public func collectionView(_: UICollectionView, numberOfItemsInSection _: Int) -> Int {
|
|
|
return itemModels.count
|
|
@@ -1248,8 +1284,12 @@ extension BFRecordScreenController: UICollectionViewDelegate, UICollectionViewDa
|
|
|
self?.setVideoPlay(item: recordItem.playItem, imageView: cell?.playView)
|
|
|
}
|
|
|
}
|
|
|
- cell.btnClickHandle = { [weak self] sender, _ in
|
|
|
- self?.playVideo(btn: sender)
|
|
|
+ cell.btnClickHandle = { [weak self] sender, tempItem in
|
|
|
+ if tempItem?.mediaType == .VIDEO {
|
|
|
+ self?.playVideo(btn: sender)
|
|
|
+ } else {
|
|
|
+ self?.imageRecordPlay()
|
|
|
+ }
|
|
|
}
|
|
|
cell.recordItem = recordItem
|
|
|
return cell
|
|
@@ -1263,7 +1303,7 @@ extension BFRecordScreenController: UICollectionViewDelegate, UICollectionViewDa
|
|
|
// 暂停
|
|
|
pause()
|
|
|
// 暂停状态
|
|
|
- let lastCell: BFVideoCoverViewCell? = collectionView.cellForItem(at: IndexPath(item: currItemModelIndex, section: 0)) as? BFVideoCoverViewCell
|
|
|
+ let lastCell: BFImageCoverViewCell? = collectionView.cellForItem(at: IndexPath(item: currItemModelIndex, section: 0)) as? BFImageCoverViewCell
|
|
|
lastCell?.playBtn.isSelected = false
|
|
|
// 更新当前page
|
|
|
currItemModelIndex = page
|
|
@@ -1272,7 +1312,7 @@ extension BFRecordScreenController: UICollectionViewDelegate, UICollectionViewDa
|
|
|
progressThumV.recordItem = recordItem
|
|
|
progressThumV.isHidden = false
|
|
|
if recordItem.mediaType == .VIDEO {
|
|
|
- let currCell: BFVideoCoverViewCell? = collectionView.cellForItem(at: IndexPath(item: currItemModelIndex, section: 0)) as? BFVideoCoverViewCell
|
|
|
+ let currCell: BFImageCoverViewCell? = collectionView.cellForItem(at: IndexPath(item: currItemModelIndex, section: 0)) as? BFImageCoverViewCell
|
|
|
setAudioPlay(item: recordItem.playItem)
|
|
|
setVideoPlay(item: recordItem.playItem, imageView: currCell?.playView)
|
|
|
}
|
|
@@ -1289,3 +1329,69 @@ extension BFRecordScreenController: UICollectionViewDelegate, UICollectionViewDa
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+// MARK: - 处理图片素材
|
|
|
+
|
|
|
+/// 处理图片素材
|
|
|
+public extension BFRecordScreenController {
|
|
|
+ /// 处理图片音频的播放
|
|
|
+ func imageRecordPlay() {
|
|
|
+ if itemModels[currItemModelIndex].mediaType == .IMAGE {
|
|
|
+ isNormalPlaying = true
|
|
|
+ playRecord(at: currentAssetProgress, periodicTimeObserver: { [weak self] currentT, currentItem in
|
|
|
+ BFLog(message: "播放一段进度:\(currentT),\(currentItem)")
|
|
|
+ self?.imageRecordProgress(progress:CMTimeGetSeconds(currentT))
|
|
|
+ }, didPlayToEndTime: { [weak self] startT, currentItem in
|
|
|
+ BFLog(message: "播放一段结束:\(startT),\(currentItem)")
|
|
|
+ self?.imageRecordPlay()
|
|
|
+ }) {[weak self] in
|
|
|
+ DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
|
|
|
+ self?.imageRecordPlay()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 处理图片素材录音
|
|
|
+ func imageRecordProgress(progress: Float64) {
|
|
|
+ BFLog(message: "图片录音进度:\(progress)")
|
|
|
+ if itemModels[currItemModelIndex].mediaType == .IMAGE {
|
|
|
+ currentAssetProgress = CMTime(seconds: progress, preferredTimescale: 1000)
|
|
|
+ DispatchQueue.main.async { [weak self] in
|
|
|
+ self?.progreddL.text = String(format: "%@", progress.formatDurationToHMS())
|
|
|
+ self?.progressThumV.progress = progress
|
|
|
+ self?.updateSubtitle(time: CMTime(value: CMTimeValue(progress), timescale: 1))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 开始计时
|
|
|
+ func startTimer() {
|
|
|
+ if displayLink == nil {
|
|
|
+ // 创建对象
|
|
|
+ displayLink = CADisplayLink(target: self, selector: #selector(displayLinkChange(_:)))
|
|
|
+ // 设置触发频率 这个周期可以通过frameInterval属性设置,CADisplayLink的selector每秒调用次数=60/frameInterval。比如当frameInterval设为2,每秒调用就变成30次
|
|
|
+ if #available(iOS 10.0, *) {
|
|
|
+ displayLink?.preferredFramesPerSecond = 1
|
|
|
+ } else {
|
|
|
+ displayLink?.frameInterval = 1
|
|
|
+ }
|
|
|
+ // 加入循环
|
|
|
+ displayLink?.add(to: RunLoop.main, forMode: RunLoop.Mode.default)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 停止计时
|
|
|
+ func stopTimer() {
|
|
|
+ if displayLink != nil {
|
|
|
+ displayLink?.isPaused = true
|
|
|
+ // 将定时器移除主循环
|
|
|
+ displayLink?.remove(from: RunLoop.main, forMode: RunLoop.Mode.default)
|
|
|
+ // 停止定时器
|
|
|
+ displayLink?.invalidate()
|
|
|
+ displayLink = nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc internal func displayLinkChange(_: CADisplayLink) {}
|
|
|
+}
|