TSVideoPlayer.swift 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. //
  2. // TSVideoPlayer.swift
  3. // AIEmoji
  4. //
  5. // Created by 100Years on 2025/7/16.
  6. //
  7. class AVPlayerPool {
  8. private static var pool: [URL: AVPlayer] = [:]
  9. private static let lock = NSLock()
  10. static func player(for url: URL) -> AVPlayer {
  11. lock.lock()
  12. defer { lock.unlock() }
  13. // 查找可复用的播放器
  14. if let player = pool[url] {
  15. player.seek(to: .zero)
  16. return player
  17. }
  18. // 创建新播放器
  19. let asset = AVAsset(url: url)
  20. let playerItem = AVPlayerItem(asset: asset)
  21. let player = AVPlayer(playerItem: playerItem)
  22. pool[url] = player
  23. // 设置自动清理
  24. NotificationCenter.default.addObserver(
  25. forName: .AVPlayerItemDidPlayToEndTime,
  26. object: playerItem,
  27. queue: nil
  28. ) { _ in
  29. player.seek(to: .zero)
  30. }
  31. return player
  32. }
  33. static func releasePlayer(for url: URL) {
  34. lock.lock()
  35. defer { lock.unlock() }
  36. pool[url]?.pause()
  37. pool[url] = nil
  38. }
  39. }
  40. import AVKit
  41. class TSVideoPlayer:NSObject {
  42. var player: AVPlayer?
  43. var playerLayer: AVPlayerLayer?
  44. var playerItem: AVPlayerItem?
  45. var loopInterval: TimeInterval = 0
  46. private var timer: Timer?
  47. private var isBackgroundPaused = false
  48. // 播放状态回调
  49. var onPlaybackStart: (() -> Void)?
  50. var onPlaybackEnd: (() -> Void)?
  51. // 最大同时播放数限制
  52. private static var activePlayers = 0
  53. private static let maxActivePlayers = 100 // 同时最多2个播放器
  54. var url:URL?
  55. deinit {
  56. cleanup()
  57. }
  58. func setupPlayer(with url: URL) {
  59. // 检查是否超过最大播放器数量
  60. guard Self.activePlayers < Self.maxActivePlayers else {
  61. print("已达到最大播放器数量限制")
  62. return
  63. }
  64. self.url = url
  65. playerItem = AVPlayerItem(asset: AVURLAsset(url: url, options: [AVURLAssetPreferPreciseDurationAndTimingKey: false]))
  66. player = AVPlayer(playerItem: playerItem)
  67. player?.volume = 0.5
  68. player?.isMuted = true // 静音播放减少资源占用
  69. player?.actionAtItemEnd = .pause
  70. playerLayer = AVPlayerLayer(player: player)
  71. playerLayer?.videoGravity = .resizeAspectFill
  72. playerLayer?.shouldRasterize = true
  73. playerLayer?.rasterizationScale = UIScreen.main.scale
  74. // 设置音频会话
  75. do {
  76. let audioSession = AVAudioSession.sharedInstance()
  77. try audioSession.setCategory(.playback)
  78. try audioSession.setActive(true)
  79. } catch {
  80. print("TSAudioPlayer 音频会话设置失败: \(error.localizedDescription)")
  81. }
  82. setupNotifications()
  83. Self.activePlayers += 1
  84. }
  85. func replaceUrl(with url: URL) {
  86. playerItem = AVPlayerItem(asset: AVURLAsset(url: url, options: [AVURLAssetPreferPreciseDurationAndTimingKey: false]))
  87. player?.replaceCurrentItem(with: playerItem)
  88. }
  89. private func setupNotifications() {
  90. NotificationCenter.default.addObserver(
  91. self,
  92. selector: #selector(playerItemDidReachEnd),
  93. name: .AVPlayerItemDidPlayToEndTime,
  94. object: playerItem
  95. )
  96. NotificationCenter.default.addObserver(
  97. self,
  98. selector: #selector(applicationDidEnterBackground),
  99. name: UIApplication.didEnterBackgroundNotification,
  100. object: nil
  101. )
  102. NotificationCenter.default.addObserver(
  103. self,
  104. selector: #selector(applicationWillEnterForeground),
  105. name: UIApplication.willEnterForegroundNotification,
  106. object: nil
  107. )
  108. }
  109. static func getFirstFrameOnly(url:URL)->UIImage? {
  110. let urlString = "TSVideoPlayer/" + url.lastPathComponent
  111. if let image = TSImageStoreTool.retrieveImageInMemoryCache(urlString: urlString) {
  112. dePrint("从缓存中取图片")
  113. return image
  114. }
  115. let asset = AVAsset(url: url)
  116. let generator = AVAssetImageGenerator(asset: asset)
  117. generator.appliesPreferredTrackTransform = true
  118. do {
  119. let cgImage = try generator.copyCGImage(at: CMTime(seconds: 0, preferredTimescale: 1), actualTime: nil)
  120. let image = UIImage(cgImage: cgImage)
  121. TSImageStoreTool.storeImage(image: image, urlString: urlString)
  122. return image
  123. } catch {
  124. dePrint("获取第一帧失败: \(error)")
  125. }
  126. return nil
  127. }
  128. @objc private func applicationDidEnterBackground() {
  129. if player?.rate != 0 {
  130. isBackgroundPaused = true
  131. pause()
  132. }
  133. }
  134. @objc private func applicationWillEnterForeground() {
  135. if isBackgroundPaused {
  136. play()
  137. isBackgroundPaused = false
  138. }
  139. }
  140. @objc private func playerItemDidReachEnd(notification: Notification) {
  141. onPlaybackEnd?()
  142. if loopInterval > 0 {
  143. timer?.invalidate()
  144. timer = Timer.scheduledTimer(
  145. withTimeInterval: loopInterval,
  146. repeats: false,
  147. block: { [weak self] _ in
  148. self?.restartPlayback()
  149. }
  150. )
  151. } else {
  152. restartPlayback()
  153. }
  154. }
  155. private func restartPlayback() {
  156. player?.seek(to: .zero)
  157. player?.play()
  158. onPlaybackStart?()
  159. }
  160. func addPlayerLayer(to view: UIView, frame: CGRect) {
  161. guard let playerLayer = playerLayer else { return }
  162. playerLayer.frame = frame
  163. view.layer.insertSublayer(playerLayer, at: 0)
  164. }
  165. func play() {
  166. playerLayer?.isHidden = false
  167. // 检查是否超过最大播放器数量
  168. guard Self.activePlayers < Self.maxActivePlayers else {
  169. print("已达到最大播放器数量限制")
  170. return
  171. }
  172. // if player?.currentItem?.status == .readyToPlay {
  173. player?.play()
  174. onPlaybackStart?()
  175. // }
  176. }
  177. func pause() {
  178. player?.pause()
  179. playerLayer?.isHidden = true
  180. }
  181. func stop() {
  182. pause()
  183. player?.seek(to: .zero)
  184. timer?.invalidate()
  185. timer = nil
  186. }
  187. func cleanup() {
  188. stop()
  189. NotificationCenter.default.removeObserver(self)
  190. self.playerLayer?.removeFromSuperlayer()
  191. playerItem = nil
  192. player = nil
  193. playerLayer = nil
  194. Self.activePlayers -= 1
  195. }
  196. func updateFrame(_ frame: CGRect) {
  197. playerLayer?.frame = frame
  198. }
  199. }