BFVideoThumbProgressView.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. //
  2. // BFVideoThumbProgressView.swift
  3. // BFRecordScreenKit
  4. //
  5. // Created by 胡志强 on 2021/12/3.
  6. //
  7. import AVFoundation
  8. import BFCommonKit
  9. import BFUIKit
  10. import Foundation
  11. import SnapKit
  12. import UIKit
  13. class BFVideoThumbProgressView: UIView {
  14. var recordItem: BFRecordItemModel? {
  15. didSet {
  16. // 指针回归
  17. BFLog(1, message: "new recorditem")
  18. progress = 0
  19. if recordItem?.mediaType == .VIDEO {
  20. dealWithVideoThumb()
  21. } else if recordItem?.mediaType == .IMAGE {
  22. dealWithImageThumb()
  23. }
  24. }
  25. }
  26. var dragScrollProgressHandle: ((Bool, Float) -> Void)?
  27. var dragEndHandle: ((Float) -> Void)?
  28. var dragStartHandle: (() -> Void)?
  29. var isDrag = false
  30. let thumbImageWidth : CGFloat = 70.0
  31. let fetchThumbStrategy: BFVideoThumbProgressStrategyProtocol = BFVideoThumbProgressStrategy()
  32. var progress: Double = 0 {
  33. didSet {
  34. updateProgress(progress: progress)
  35. }
  36. }
  37. var thumbImgs = [UIImage]()
  38. lazy var progressView: BFAutolayoutScrollView = {
  39. let sv = BFAutolayoutScrollView()
  40. sv.bounces = false
  41. // sv.backgroundColor = .clear
  42. // sv.backgroundColor = UIColor.hexColor(hexadecimal: "#888888",alpha:0.3)
  43. sv.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.3)
  44. sv.decelerationRate = .fast
  45. sv.showsHorizontalScrollIndicator = false
  46. sv.delegate = self
  47. return sv
  48. }()
  49. lazy var progessIndicateBackV: UIView = {
  50. let vv = UIView()
  51. return vv
  52. }()
  53. // MARK: - 生命周期
  54. override init(frame: CGRect) {
  55. super.init(frame: frame)
  56. addSubview(progressView)
  57. let line = UIView()
  58. line.backgroundColor = .white
  59. line.layer.borderColor = UIColor.black.cgColor
  60. line.layer.borderWidth = 0.3
  61. line.layer.cornerRadius = 1.5
  62. addSubview(line)
  63. line.snp.makeConstraints { make in
  64. make.width.equalTo(3)
  65. make.center.height.equalToSuperview()
  66. }
  67. progressView.contentView.addSubview(progessIndicateBackV)
  68. progessIndicateBackV.snp.makeConstraints { make in
  69. make.left.equalTo(width * 0.5)
  70. make.right.equalTo(width * -0.5)
  71. make.bottom.equalToSuperview()
  72. make.height.equalTo(6)
  73. make.width.equalTo(1).priority(.high)
  74. }
  75. }
  76. required init?(coder _: NSCoder) {
  77. fatalError("init(coder:) has not been implemented")
  78. }
  79. override func layoutSubviews() {
  80. super.layoutSubviews()
  81. progressView.snp.makeConstraints { make in
  82. make.edges.equalToSuperview()
  83. }
  84. }
  85. /// 处理视频缩略图
  86. func dealWithVideoThumb() {
  87. guard let videoAsset = recordItem?.videoAsset else {
  88. return
  89. }
  90. self.addThumbImages(images: recordItem!.thumbImgs)
  91. if recordItem!.thumbImgs.count > 0{
  92. // 代表已经获取过了,不用在重新去获得
  93. return
  94. }
  95. let date = Date()
  96. let dur = videoAsset.duration.seconds
  97. if dur > 0 {
  98. let count = fetchThumbStrategy.frameNumberOfVideo(assetDuration: dur)
  99. progessIndicateBackV.snp.remakeConstraints { make in
  100. make.left.equalTo(width * 0.5)
  101. make.right.equalTo(width * -0.5)
  102. make.bottom.equalToSuperview()
  103. make.height.equalTo(6)
  104. make.width.equalTo(CGFloat(count) * thumbImageWidth).priority(.high)
  105. }
  106. recordItem!.splitVideoFileUrlFps(frames: count, firstImagesCount: Int(ceil(width/2.0/thumbImageWidth))) { [weak self, weak recordItem] hadGetAll, images in
  107. guard let sself = self, let sitem = recordItem else {
  108. return
  109. }
  110. BFLog(1, message: "获取缩略图:\(hadGetAll), \(Date().timeIntervalSince(date)), \( sitem.localPath ?? "aa")")
  111. sitem.thumbImgs.removeAll()
  112. sitem.thumbImgs.append(contentsOf: images)
  113. // 不足数则补充足够帧数
  114. while (hadGetAll && sitem.thumbImgs.count < count && images.count > 0) {
  115. sitem.thumbImgs.append(images.last!)
  116. }
  117. if sitem.localPath == sself.recordItem!.localPath {
  118. sself.addThumbImages(images: sitem.thumbImgs)
  119. }else{
  120. BFLog(1, message: "thumbImgs.count:\(sitem.thumbImgs.count)")
  121. }
  122. }
  123. }
  124. }
  125. /// 处理图片缩略图
  126. func dealWithImageThumb() {
  127. guard let image = recordItem?.coverImg else {
  128. return
  129. }
  130. addThumbImages(images: Array(repeating: image, count: 30))
  131. }
  132. /// 添加缩略图
  133. /// - Parameter images: <#images description#>
  134. func addThumbImages(images: [UIImage]) {
  135. DispatchQueue.main.async { [weak self] in
  136. self?.progressView.contentView.subviews.forEach { subview in
  137. if subview is UIImageView {
  138. subview.removeFromSuperview()
  139. }
  140. }
  141. if images.count > 0 {
  142. // self?.thumbImgs = images
  143. if let sself = self {
  144. var lastImg = UIImageView()
  145. for (i, img) in images.enumerated() {
  146. let iv = UIImageView(image: img)
  147. iv.contentMode = .scaleAspectFill
  148. iv.clipsToBounds = true
  149. sself.progressView.contentView.insertSubview(iv, belowSubview: sself.progessIndicateBackV)
  150. iv.snp.makeConstraints { make in
  151. make.left.equalTo(CGFloat(i) * CGFloat(sself.thumbImageWidth) + sself.width * 0.5)
  152. make.top.bottom.equalToSuperview()
  153. make.height.equalTo(50)
  154. make.width.equalTo(sself.thumbImageWidth)
  155. }
  156. lastImg = iv
  157. }
  158. if sself.recordItem?.mediaType == .IMAGE {
  159. // 图片需要动态修改宽度
  160. lastImg.snp.makeConstraints { make in
  161. make.right.equalTo(sself.width * -0.5)
  162. }
  163. }
  164. }
  165. }
  166. }
  167. }
  168. func appendThumb(progress: Double = 0) {
  169. let count: Int = Int(progress / 2)
  170. BFLog(message: "需要的图片个数:progress=\(progress),count=\(count)")
  171. if recordItem?.mediaType == .IMAGE, (thumbImgs.count - 5) < count {
  172. guard let image = recordItem?.coverImg else {
  173. return
  174. }
  175. var lastiv = UIImageView()
  176. let lastIndex = thumbImgs.count - 1
  177. for i in lastIndex ... lastIndex + 10 {
  178. let iv = UIImageView(image: image)
  179. iv.contentMode = .scaleAspectFill
  180. iv.clipsToBounds = true
  181. progressView.contentView.addSubview(iv)
  182. iv.snp.makeConstraints { make in
  183. make.left.equalTo(CGFloat(i) * CGFloat(thumbImageWidth) + width * 0.5)
  184. make.top.bottom.equalToSuperview()
  185. make.height.equalTo(50)
  186. make.width.equalTo(thumbImageWidth)
  187. }
  188. lastiv = iv
  189. thumbImgs.append(image)
  190. }
  191. lastiv.snp.makeConstraints { make in
  192. make.right.equalTo(width * -0.5)
  193. }
  194. }
  195. }
  196. /// 更新进度
  197. /// - Parameter progress: <#progress description#>
  198. func updateProgress(progress: Double = 0) {
  199. if progressView.contentSize.width <= 0 {
  200. return
  201. }
  202. if recordItem?.mediaType == .VIDEO {
  203. if let second = recordItem?.videoAsset?.duration.seconds, second > 0 {
  204. let w = progressView.contentSize.width - width
  205. BFLog(message: "录音进度--指示器:progress=\(progress),duration=\(second),w=\(w),perW=\(Double(w) / second),totalW:\(progress * Double(w) / second)")
  206. progressView.contentOffset = CGPoint(x: progress * Double(w) / second, y: 0)
  207. }
  208. } else if recordItem?.mediaType == .IMAGE {
  209. // if (recordItem?.materialDuraion ?? 0) > progress {
  210. BFLog(message: "updateProgress:\(progress)")
  211. progressView.contentOffset = CGPoint(x: progress * thumbImageWidth / 2.0, y: 0)
  212. appendThumb(progress: progress)
  213. // }
  214. }
  215. }
  216. }
  217. extension BFVideoThumbProgressView: UIScrollViewDelegate {
  218. func scrollViewDidScroll(_ scrollView: UIScrollView) {
  219. if recordItem?.mediaType == .VIDEO {
  220. if isDrag {
  221. let dur = scrollView.contentOffset.x / (scrollView.contentSize.width - width)
  222. dragScrollProgressHandle?(false, Float(dur))
  223. }
  224. } else if recordItem?.mediaType == .IMAGE {
  225. if isDrag {
  226. if scrollView.contentOffset.x > CGFloat(recordItem?.materialDuraion ?? 0) * thumbImageWidth / 2.0 {
  227. scrollView.contentOffset = CGPoint(x: CGFloat(recordItem?.materialDuraion ?? 0) * thumbImageWidth / 2.0, y: 0)
  228. }
  229. let dur = scrollView.contentOffset.x / (CGFloat(recordItem?.materialDuraion ?? 0) * thumbImageWidth / 2.0)
  230. dragScrollProgressHandle?(false, Float(dur))
  231. }
  232. }
  233. }
  234. func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
  235. isDrag = true
  236. var dur: CGFloat = 0
  237. if recordItem?.mediaType == .VIDEO {
  238. dur = scrollView.contentOffset.x / (scrollView.contentSize.width - width)
  239. } else {
  240. dur = scrollView.contentOffset.x / (CGFloat(recordItem?.materialDuraion ?? 0) * thumbImageWidth / 2.0)
  241. }
  242. dragStartHandle?()
  243. dragScrollProgressHandle?(true, Float(dur))
  244. }
  245. func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
  246. if !decelerate {
  247. var dur: CGFloat = 0
  248. if recordItem?.mediaType == .VIDEO {
  249. dur = scrollView.contentOffset.x / (scrollView.contentSize.width - width)
  250. } else {
  251. dur = scrollView.contentOffset.x / (CGFloat(recordItem?.materialDuraion ?? 0) * thumbImageWidth / 2.0)
  252. }
  253. isDrag = false
  254. dragEndHandle?(Float(dur))
  255. }
  256. }
  257. func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
  258. var dur: CGFloat = 0
  259. if recordItem?.mediaType == .VIDEO {
  260. dur = scrollView.contentOffset.x / (scrollView.contentSize.width - width)
  261. } else {
  262. dur = scrollView.contentOffset.x / (CGFloat(recordItem?.materialDuraion ?? 0) * thumbImageWidth / 2.0)
  263. }
  264. isDrag = false
  265. dragEndHandle?(Float(dur))
  266. }
  267. }